summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/common/mm/bitmap_allocator.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm/bitmap_allocator.c')
-rw-r--r--drivers/gpu/nvgpu/common/mm/bitmap_allocator.c438
1 files changed, 438 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/mm/bitmap_allocator.c b/drivers/gpu/nvgpu/common/mm/bitmap_allocator.c
new file mode 100644
index 00000000..6bd654b8
--- /dev/null
+++ b/drivers/gpu/nvgpu/common/mm/bitmap_allocator.c
@@ -0,0 +1,438 @@
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/bitops.h>
24#include <nvgpu/allocator.h>
25#include <nvgpu/kmem.h>
26#include <nvgpu/bug.h>
27#include <nvgpu/barrier.h>
28
29#include "bitmap_allocator_priv.h"
30
31static u64 nvgpu_bitmap_alloc_length(struct nvgpu_allocator *a)
32{
33 struct nvgpu_bitmap_allocator *ba = a->priv;
34
35 return ba->length;
36}
37
38static u64 nvgpu_bitmap_alloc_base(struct nvgpu_allocator *a)
39{
40 struct nvgpu_bitmap_allocator *ba = a->priv;
41
42 return ba->base;
43}
44
45static int nvgpu_bitmap_alloc_inited(struct nvgpu_allocator *a)
46{
47 struct nvgpu_bitmap_allocator *ba = a->priv;
48 int inited = ba->inited;
49
50 nvgpu_smp_rmb();
51 return inited;
52}
53
54static u64 nvgpu_bitmap_alloc_end(struct nvgpu_allocator *a)
55{
56 struct nvgpu_bitmap_allocator *ba = a->priv;
57
58 return ba->base + ba->length;
59}
60
61/*
62 * @page_size is ignored.
63 */
64static u64 nvgpu_bitmap_alloc_fixed(struct nvgpu_allocator *__a,
65 u64 base, u64 len, u32 page_size)
66{
67 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
68 u64 blks, offs, ret;
69
70 /* Compute the bit offset and make sure it's aligned to a block. */
71 offs = base >> a->blk_shift;
72 if (offs * a->blk_size != base)
73 return 0;
74
75 offs -= a->bit_offs;
76
77 blks = len >> a->blk_shift;
78 if (blks * a->blk_size != len)
79 blks++;
80
81 alloc_lock(__a);
82
83 /* Check if the space requested is already occupied. */
84 ret = bitmap_find_next_zero_area(a->bitmap, a->num_bits, offs, blks, 0);
85 if (ret != offs)
86 goto fail;
87
88 bitmap_set(a->bitmap, offs, blks);
89
90 a->bytes_alloced += blks * a->blk_size;
91 a->nr_fixed_allocs++;
92 alloc_unlock(__a);
93
94 alloc_dbg(__a, "Alloc-fixed 0x%-10llx 0x%-5llx [bits=0x%llx (%llu)]\n",
95 base, len, blks, blks);
96 return base;
97
98fail:
99 alloc_unlock(__a);
100 alloc_dbg(__a, "Alloc-fixed failed! (0x%llx)\n", base);
101 return 0;
102}
103
104/*
105 * Two possibilities for this function: either we are freeing a fixed allocation
106 * or we are freeing a regular alloc but with GPU_ALLOC_NO_ALLOC_PAGE defined.
107 *
108 * Note: this function won't do much error checking. Thus you could really
109 * confuse the allocator if you misuse this function.
110 */
111static void nvgpu_bitmap_free_fixed(struct nvgpu_allocator *__a,
112 u64 base, u64 len)
113{
114 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
115 u64 blks, offs;
116
117 offs = base >> a->blk_shift;
118 if (WARN_ON(offs * a->blk_size != base))
119 return;
120
121 offs -= a->bit_offs;
122
123 blks = len >> a->blk_shift;
124 if (blks * a->blk_size != len)
125 blks++;
126
127 alloc_lock(__a);
128 bitmap_clear(a->bitmap, offs, blks);
129 a->bytes_freed += blks * a->blk_size;
130 alloc_unlock(__a);
131
132 alloc_dbg(__a, "Free-fixed 0x%-10llx 0x%-5llx [bits=0x%llx (%llu)]\n",
133 base, len, blks, blks);
134}
135
136/*
137 * Add the passed alloc to the tree of stored allocations.
138 */
139static void insert_alloc_metadata(struct nvgpu_bitmap_allocator *a,
140 struct nvgpu_bitmap_alloc *alloc)
141{
142 alloc->alloc_entry.key_start = alloc->base;
143 alloc->alloc_entry.key_end = alloc->base + alloc->length;
144
145 nvgpu_rbtree_insert(&alloc->alloc_entry, &a->allocs);
146}
147
148/*
149 * Find and remove meta-data from the outstanding allocations.
150 */
151static struct nvgpu_bitmap_alloc *find_alloc_metadata(
152 struct nvgpu_bitmap_allocator *a, u64 addr)
153{
154 struct nvgpu_bitmap_alloc *alloc;
155 struct nvgpu_rbtree_node *node = NULL;
156
157 nvgpu_rbtree_search(addr, &node, a->allocs);
158 if (!node)
159 return NULL;
160
161 alloc = nvgpu_bitmap_alloc_from_rbtree_node(node);
162
163 nvgpu_rbtree_unlink(node, &a->allocs);
164
165 return alloc;
166}
167
168/*
169 * Tree of alloc meta data stores the address of the alloc not the bit offset.
170 */
171static int __nvgpu_bitmap_store_alloc(struct nvgpu_bitmap_allocator *a,
172 u64 addr, u64 len)
173{
174 struct nvgpu_bitmap_alloc *alloc =
175 nvgpu_kmem_cache_alloc(a->meta_data_cache);
176
177 if (!alloc)
178 return -ENOMEM;
179
180 alloc->base = addr;
181 alloc->length = len;
182
183 insert_alloc_metadata(a, alloc);
184
185 return 0;
186}
187
188/*
189 * @len is in bytes. This routine will figure out the right number of bits to
190 * actually allocate. The return is the address in bytes as well.
191 */
192static u64 nvgpu_bitmap_alloc(struct nvgpu_allocator *__a, u64 len)
193{
194 u64 blks, addr;
195 unsigned long offs, adjusted_offs, limit;
196 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
197
198 blks = len >> a->blk_shift;
199
200 if (blks * a->blk_size != len)
201 blks++;
202
203 alloc_lock(__a);
204
205 /*
206 * First look from next_blk and onwards...
207 */
208 offs = bitmap_find_next_zero_area(a->bitmap, a->num_bits,
209 a->next_blk, blks, 0);
210 if (offs >= a->num_bits) {
211 /*
212 * If that didn't work try the remaining area. Since there can
213 * be available space that spans across a->next_blk we need to
214 * search up to the first set bit after that.
215 */
216 limit = find_next_bit(a->bitmap, a->num_bits, a->next_blk);
217 offs = bitmap_find_next_zero_area(a->bitmap, limit,
218 0, blks, 0);
219 if (offs >= a->next_blk)
220 goto fail;
221 }
222
223 bitmap_set(a->bitmap, offs, blks);
224 a->next_blk = offs + blks;
225
226 adjusted_offs = offs + a->bit_offs;
227 addr = ((u64)adjusted_offs) * a->blk_size;
228
229 /*
230 * Only do meta-data storage if we are allowed to allocate storage for
231 * that meta-data. The issue with using malloc and friends is that
232 * in latency and success critical paths an alloc_page() call can either
233 * sleep for potentially a long time or fail. Since we might not want
234 * either of these possibilities assume that the caller will keep what
235 * data it needs around to successfully free this allocation.
236 */
237 if (!(a->flags & GPU_ALLOC_NO_ALLOC_PAGE) &&
238 __nvgpu_bitmap_store_alloc(a, addr, blks * a->blk_size))
239 goto fail_reset_bitmap;
240
241 alloc_dbg(__a, "Alloc 0x%-10llx 0x%-5llx [bits=0x%llx (%llu)]\n",
242 addr, len, blks, blks);
243
244 a->nr_allocs++;
245 a->bytes_alloced += (blks * a->blk_size);
246 alloc_unlock(__a);
247
248 return addr;
249
250fail_reset_bitmap:
251 bitmap_clear(a->bitmap, offs, blks);
252fail:
253 a->next_blk = 0;
254 alloc_unlock(__a);
255 alloc_dbg(__a, "Alloc failed!\n");
256 return 0;
257}
258
259static void nvgpu_bitmap_free(struct nvgpu_allocator *__a, u64 addr)
260{
261 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
262 struct nvgpu_bitmap_alloc *alloc = NULL;
263 u64 offs, adjusted_offs, blks;
264
265 alloc_lock(__a);
266
267 if (a->flags & GPU_ALLOC_NO_ALLOC_PAGE) {
268 WARN(1, "Using wrong free for NO_ALLOC_PAGE bitmap allocator");
269 goto done;
270 }
271
272 alloc = find_alloc_metadata(a, addr);
273 if (!alloc)
274 goto done;
275
276 /*
277 * Address comes from adjusted offset (i.e the bit offset with
278 * a->bit_offs added. So start with that and then work out the real
279 * offs into the bitmap.
280 */
281 adjusted_offs = addr >> a->blk_shift;
282 offs = adjusted_offs - a->bit_offs;
283 blks = alloc->length >> a->blk_shift;
284
285 bitmap_clear(a->bitmap, offs, blks);
286 alloc_dbg(__a, "Free 0x%-10llx\n", addr);
287
288 a->bytes_freed += alloc->length;
289
290done:
291 if (a->meta_data_cache && alloc)
292 nvgpu_kmem_cache_free(a->meta_data_cache, alloc);
293 alloc_unlock(__a);
294}
295
296static void nvgpu_bitmap_alloc_destroy(struct nvgpu_allocator *__a)
297{
298 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
299 struct nvgpu_bitmap_alloc *alloc;
300 struct nvgpu_rbtree_node *node;
301
302 /*
303 * Kill any outstanding allocations.
304 */
305 nvgpu_rbtree_enum_start(0, &node, a->allocs);
306 while (node) {
307 alloc = nvgpu_bitmap_alloc_from_rbtree_node(node);
308
309 nvgpu_rbtree_unlink(node, &a->allocs);
310 nvgpu_kmem_cache_free(a->meta_data_cache, alloc);
311
312 nvgpu_rbtree_enum_start(0, &node, a->allocs);
313 }
314
315 nvgpu_kmem_cache_destroy(a->meta_data_cache);
316 nvgpu_kfree(nvgpu_alloc_to_gpu(__a), a->bitmap);
317 nvgpu_kfree(nvgpu_alloc_to_gpu(__a), a);
318}
319
320#ifdef __KERNEL__
321static void nvgpu_bitmap_print_stats(struct nvgpu_allocator *__a,
322 struct seq_file *s, int lock)
323{
324 struct nvgpu_bitmap_allocator *a = bitmap_allocator(__a);
325
326 __alloc_pstat(s, __a, "Bitmap allocator params:\n");
327 __alloc_pstat(s, __a, " start = 0x%llx\n", a->base);
328 __alloc_pstat(s, __a, " end = 0x%llx\n", a->base + a->length);
329 __alloc_pstat(s, __a, " blks = 0x%llx\n", a->num_bits);
330
331 /* Actual stats. */
332 __alloc_pstat(s, __a, "Stats:\n");
333 __alloc_pstat(s, __a, " Number allocs = 0x%llx\n", a->nr_allocs);
334 __alloc_pstat(s, __a, " Number fixed = 0x%llx\n", a->nr_fixed_allocs);
335 __alloc_pstat(s, __a, " Bytes alloced = 0x%llx\n", a->bytes_alloced);
336 __alloc_pstat(s, __a, " Bytes freed = 0x%llx\n", a->bytes_freed);
337 __alloc_pstat(s, __a, " Outstanding = 0x%llx\n",
338 a->bytes_alloced - a->bytes_freed);
339}
340#endif
341
342static const struct nvgpu_allocator_ops bitmap_ops = {
343 .alloc = nvgpu_bitmap_alloc,
344 .free = nvgpu_bitmap_free,
345
346 .alloc_fixed = nvgpu_bitmap_alloc_fixed,
347 .free_fixed = nvgpu_bitmap_free_fixed,
348
349 .base = nvgpu_bitmap_alloc_base,
350 .length = nvgpu_bitmap_alloc_length,
351 .end = nvgpu_bitmap_alloc_end,
352 .inited = nvgpu_bitmap_alloc_inited,
353
354 .fini = nvgpu_bitmap_alloc_destroy,
355
356#ifdef __KERNEL__
357 .print_stats = nvgpu_bitmap_print_stats,
358#endif
359};
360
361
362int nvgpu_bitmap_allocator_init(struct gk20a *g, struct nvgpu_allocator *__a,
363 const char *name, u64 base, u64 length,
364 u64 blk_size, u64 flags)
365{
366 int err;
367 struct nvgpu_bitmap_allocator *a;
368
369 if (WARN_ON(blk_size & (blk_size - 1)))
370 return -EINVAL;
371
372 /*
373 * blk_size must be a power-of-2; base length also need to be aligned
374 * to blk_size.
375 */
376 if (blk_size & (blk_size - 1) ||
377 base & (blk_size - 1) || length & (blk_size - 1))
378 return -EINVAL;
379
380 if (base == 0) {
381 base = blk_size;
382 length -= blk_size;
383 }
384
385 a = nvgpu_kzalloc(g, sizeof(struct nvgpu_bitmap_allocator));
386 if (!a)
387 return -ENOMEM;
388
389 err = __nvgpu_alloc_common_init(__a, g, name, a, false, &bitmap_ops);
390 if (err)
391 goto fail;
392
393 if (!(flags & GPU_ALLOC_NO_ALLOC_PAGE)) {
394 a->meta_data_cache = nvgpu_kmem_cache_create(g,
395 sizeof(struct nvgpu_bitmap_alloc));
396 if (!a->meta_data_cache) {
397 err = -ENOMEM;
398 goto fail;
399 }
400 }
401
402 a->base = base;
403 a->length = length;
404 a->blk_size = blk_size;
405 a->blk_shift = __ffs(a->blk_size);
406 a->num_bits = length >> a->blk_shift;
407 a->bit_offs = a->base >> a->blk_shift;
408 a->flags = flags;
409 a->allocs = NULL;
410
411 a->bitmap = nvgpu_kcalloc(g, BITS_TO_LONGS(a->num_bits),
412 sizeof(*a->bitmap));
413 if (!a->bitmap) {
414 err = -ENOMEM;
415 goto fail;
416 }
417
418 nvgpu_smp_wmb();
419 a->inited = true;
420
421#ifdef CONFIG_DEBUG_FS
422 nvgpu_init_alloc_debug(g, __a);
423#endif
424 alloc_dbg(__a, "New allocator: type bitmap\n");
425 alloc_dbg(__a, " base 0x%llx\n", a->base);
426 alloc_dbg(__a, " bit_offs 0x%llx\n", a->bit_offs);
427 alloc_dbg(__a, " size 0x%llx\n", a->length);
428 alloc_dbg(__a, " blk_size 0x%llx\n", a->blk_size);
429 alloc_dbg(__a, " flags 0x%llx\n", a->flags);
430
431 return 0;
432
433fail:
434 if (a->meta_data_cache)
435 nvgpu_kmem_cache_destroy(a->meta_data_cache);
436 nvgpu_kfree(g, a);
437 return err;
438}