diff options
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm/lockless_allocator.c')
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/lockless_allocator.c | 225 |
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 | |||
30 | static 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 | |||
37 | static 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 | |||
44 | static 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 | |||
53 | static 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 | |||
60 | static 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 | |||
91 | static 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 | |||
113 | static 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__ | ||
126 | static 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 | |||
144 | static 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 | |||
160 | int 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 | |||
222 | fail: | ||
223 | nvgpu_kfree(g, a); | ||
224 | return err; | ||
225 | } | ||