summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm/lockless_allocator.c')
-rw-r--r--drivers/gpu/nvgpu/common/mm/lockless_allocator.c225
1 files changed, 225 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/mm/lockless_allocator.c b/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
new file mode 100644
index 00000000..3eb10fc4
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/mm/lockless_allocator.c
@@ -0,0 +1,225 @@
1/*
2 * Copyright (c) 2016-2017, NVIDIA CORPORATION. All rights reserved.
3 *
4 * Permission is hereby granted, free of charge, to any person obtaining a
5 * copy of this software and associated documentation files (the "Software"),
6 * to deal in the Software without restriction, including without limitation
7 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
8 * and/or sell copies of the Software, and to permit persons to whom the
9 * Software is furnished to do so, subject to the following conditions:
10 *
11 * The above copyright notice and this permission notice shall be included in
12 * all copies or substantial portions of the Software.
13 *
14 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
16 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
17 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
18 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
19 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
20 * DEALINGS IN THE SOFTWARE.
21 */
22
23#include <nvgpu/atomic.h>
24#include <nvgpu/allocator.h>
25#include <nvgpu/kmem.h>
26#include <nvgpu/barrier.h>
27
28#include "lockless_allocator_priv.h"
29
30static u64 nvgpu_lockless_alloc_length(struct nvgpu_allocator *a)
31{
32 struct nvgpu_lockless_allocator *pa = a->priv;
33
34 return pa->length;
35}
36
37static u64 nvgpu_lockless_alloc_base(struct nvgpu_allocator *a)
38{
39 struct nvgpu_lockless_allocator *pa = a->priv;
40
41 return pa->base;
42}
43
44static int nvgpu_lockless_alloc_inited(struct nvgpu_allocator *a)
45{
46 struct nvgpu_lockless_allocator *pa = a->priv;
47 int inited = pa->inited;
48
49 nvgpu_smp_rmb();
50 return inited;
51}
52
53static u64 nvgpu_lockless_alloc_end(struct nvgpu_allocator *a)
54{
55 struct nvgpu_lockless_allocator *pa = a->priv;
56
57 return pa->base + pa->length;
58}
59
60static u64 nvgpu_lockless_alloc(struct nvgpu_allocator *a, u64 len)
61{
62 struct nvgpu_lockless_allocator *pa = a->priv;
63 int head, new_head, ret;
64 u64 addr = 0;
65
66 if (len != pa->blk_size)
67 return 0;
68
69 head = NV_ACCESS_ONCE(pa->head);
70 while (head >= 0) {
71 new_head = NV_ACCESS_ONCE(pa->next[head]);
72 ret = cmpxchg(&pa->head, head, new_head);
73 if (ret == head) {
74 addr = pa->base + head * pa->blk_size;
75 nvgpu_atomic_inc(&pa->nr_allocs);
76 alloc_dbg(a, "Alloc node # %d @ addr 0x%llx\n", head,
77 addr);
78 break;
79 }
80 head = NV_ACCESS_ONCE(pa->head);
81 }
82
83 if (addr)
84 alloc_dbg(a, "Alloc node # %d @ addr 0x%llx\n", head, addr);
85 else
86 alloc_dbg(a, "Alloc failed!\n");
87
88 return addr;
89}
90
91static void nvgpu_lockless_free(struct nvgpu_allocator *a, u64 addr)
92{
93 struct nvgpu_lockless_allocator *pa = a->priv;
94 int head, ret;
95 u64 cur_idx;
96
97 cur_idx = (addr - pa->base) / pa->blk_size;
98
99 alloc_dbg(a, "Free node # %llu @ addr 0x%llx\n", cur_idx, addr);
100
101 while (1) {
102 head = NV_ACCESS_ONCE(pa->head);
103 NV_ACCESS_ONCE(pa->next[cur_idx]) = head;
104 ret = cmpxchg(&pa->head, head, cur_idx);
105 if (ret == head) {
106 nvgpu_atomic_dec(&pa->nr_allocs);
107 alloc_dbg(a, "Free node # %llu\n", cur_idx);
108 break;
109 }
110 }
111}
112
113static void nvgpu_lockless_alloc_destroy(struct nvgpu_allocator *a)
114{
115 struct nvgpu_lockless_allocator *pa = a->priv;
116
117#ifdef CONFIG_DEBUG_FS
118 nvgpu_fini_alloc_debug(a);
119#endif
120
121 nvgpu_vfree(a->g, pa->next);
122 nvgpu_kfree(nvgpu_alloc_to_gpu(a), pa);
123}
124
125#ifdef __KERNEL__
126static void nvgpu_lockless_print_stats(struct nvgpu_allocator *a,
127 struct seq_file *s, int lock)
128{
129 struct nvgpu_lockless_allocator *pa = a->priv;
130
131 __alloc_pstat(s, a, "Lockless allocator params:\n");
132 __alloc_pstat(s, a, " start = 0x%llx\n", pa->base);
133 __alloc_pstat(s, a, " end = 0x%llx\n", pa->base + pa->length);
134
135 /* Actual stats. */
136 __alloc_pstat(s, a, "Stats:\n");
137 __alloc_pstat(s, a, " Number allocs = %d\n",
138 nvgpu_atomic_read(&pa->nr_allocs));
139 __alloc_pstat(s, a, " Number free = %d\n",
140 pa->nr_nodes - nvgpu_atomic_read(&pa->nr_allocs));
141}
142#endif
143
144static const struct nvgpu_allocator_ops pool_ops = {
145 .alloc = nvgpu_lockless_alloc,
146 .free = nvgpu_lockless_free,
147
148 .base = nvgpu_lockless_alloc_base,
149 .length = nvgpu_lockless_alloc_length,
150 .end = nvgpu_lockless_alloc_end,
151 .inited = nvgpu_lockless_alloc_inited,
152
153 .fini = nvgpu_lockless_alloc_destroy,
154
155#ifdef __KERNEL__
156 .print_stats = nvgpu_lockless_print_stats,
157#endif
158};
159
160int nvgpu_lockless_allocator_init(struct gk20a *g, struct nvgpu_allocator *__a,
161 const char *name, u64 base, u64 length,
162 u64 blk_size, u64 flags)
163{
164 int i;
165 int err;
166 int nr_nodes;
167 u64 count;
168 struct nvgpu_lockless_allocator *a;
169
170 if (!blk_size)
171 return -EINVAL;
172
173 /*
174 * Ensure we have space for at least one node & there's no overflow.
175 * In order to control memory footprint, we require count < INT_MAX
176 */
177 count = length / blk_size;
178 if (!base || !count || count > INT_MAX)
179 return -EINVAL;
180
181 a = nvgpu_kzalloc(g, sizeof(struct nvgpu_lockless_allocator));
182 if (!a)
183 return -ENOMEM;
184
185 err = __nvgpu_alloc_common_init(__a, g, name, a, false, &pool_ops);
186 if (err)
187 goto fail;
188
189 a->next = nvgpu_vzalloc(g, sizeof(*a->next) * count);
190 if (!a->next) {
191 err = -ENOMEM;
192 goto fail;
193 }
194
195 /* chain the elements together to form the initial free list */
196 nr_nodes = (int)count;
197 for (i = 0; i < nr_nodes; i++)
198 a->next[i] = i + 1;
199 a->next[nr_nodes - 1] = -1;
200
201 a->base = base;
202 a->length = length;
203 a->blk_size = blk_size;
204 a->nr_nodes = nr_nodes;
205 a->flags = flags;
206 nvgpu_atomic_set(&a->nr_allocs, 0);
207
208 nvgpu_smp_wmb();
209 a->inited = true;
210
211#ifdef CONFIG_DEBUG_FS
212 nvgpu_init_alloc_debug(g, __a);
213#endif
214 alloc_dbg(__a, "New allocator: type lockless\n");
215 alloc_dbg(__a, " base 0x%llx\n", a->base);
216 alloc_dbg(__a, " nodes %d\n", a->nr_nodes);
217 alloc_dbg(__a, " blk_size 0x%llx\n", a->blk_size);
218 alloc_dbg(__a, " flags 0x%llx\n", a->flags);
219
220 return 0;
221
222fail:
223 nvgpu_kfree(g, a);
224 return err;
225}