diff options
Diffstat (limited to 'drivers/gpu/nvgpu/common/mm/buddy_allocator.c')
-rw-r--r-- | drivers/gpu/nvgpu/common/mm/buddy_allocator.c | 1323 |
1 files changed, 1323 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/common/mm/buddy_allocator.c b/drivers/gpu/nvgpu/common/mm/buddy_allocator.c new file mode 100644 index 00000000..a2546e9d --- /dev/null +++ b/drivers/gpu/nvgpu/common/mm/buddy_allocator.c | |||
@@ -0,0 +1,1323 @@ | |||
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/allocator.h> | ||
24 | #include <nvgpu/kmem.h> | ||
25 | #include <nvgpu/bug.h> | ||
26 | #include <nvgpu/log2.h> | ||
27 | #include <nvgpu/barrier.h> | ||
28 | #include <nvgpu/mm.h> | ||
29 | #include <nvgpu/vm.h> | ||
30 | |||
31 | #include "buddy_allocator_priv.h" | ||
32 | |||
33 | /* Some other buddy allocator functions. */ | ||
34 | static struct nvgpu_buddy *balloc_free_buddy(struct nvgpu_buddy_allocator *a, | ||
35 | u64 addr); | ||
36 | static void balloc_coalesce(struct nvgpu_buddy_allocator *a, | ||
37 | struct nvgpu_buddy *b); | ||
38 | static void __balloc_do_free_fixed(struct nvgpu_buddy_allocator *a, | ||
39 | struct nvgpu_fixed_alloc *falloc); | ||
40 | |||
41 | /* | ||
42 | * This function is not present in older kernel's list.h code. | ||
43 | */ | ||
44 | #ifndef list_last_entry | ||
45 | #define list_last_entry(ptr, type, member) \ | ||
46 | list_entry((ptr)->prev, type, member) | ||
47 | #endif | ||
48 | |||
49 | /* | ||
50 | * GPU buddy allocator for various address spaces. | ||
51 | * | ||
52 | * Current limitations: | ||
53 | * o A fixed allocation could potentially be made that borders PDEs with | ||
54 | * different PTE sizes. This would require that fixed buffer to have | ||
55 | * different sized PTEs for different parts of the allocation. Probably | ||
56 | * best to just require PDE alignment for fixed address allocs. | ||
57 | * | ||
58 | * o It is currently possible to make an allocator that has a buddy alignment | ||
59 | * out of sync with the PDE block size alignment. A simple example is a | ||
60 | * 32GB address space starting at byte 1. Every buddy is shifted off by 1 | ||
61 | * which means each buddy corresponf to more than one actual GPU page. The | ||
62 | * best way to fix this is probably just require PDE blocksize alignment | ||
63 | * for the start of the address space. At the moment all allocators are | ||
64 | * easily PDE aligned so this hasn't been a problem. | ||
65 | */ | ||
66 | |||
67 | /* | ||
68 | * Pick a suitable maximum order for this allocator. | ||
69 | * | ||
70 | * Hueristic: Just guessing that the best max order is the largest single | ||
71 | * block that will fit in the address space. | ||
72 | */ | ||
73 | static void balloc_compute_max_order(struct nvgpu_buddy_allocator *a) | ||
74 | { | ||
75 | u64 true_max_order = ilog2(a->blks); | ||
76 | |||
77 | if (a->max_order == 0) { | ||
78 | a->max_order = true_max_order; | ||
79 | return; | ||
80 | } | ||
81 | |||
82 | if (a->max_order > true_max_order) | ||
83 | a->max_order = true_max_order; | ||
84 | if (a->max_order > GPU_BALLOC_MAX_ORDER) | ||
85 | a->max_order = GPU_BALLOC_MAX_ORDER; | ||
86 | } | ||
87 | |||
88 | /* | ||
89 | * Since we can only allocate in chucks of a->blk_size we need to trim off | ||
90 | * any excess data that is not aligned to a->blk_size. | ||
91 | */ | ||
92 | static void balloc_allocator_align(struct nvgpu_buddy_allocator *a) | ||
93 | { | ||
94 | a->start = ALIGN(a->base, a->blk_size); | ||
95 | WARN_ON(a->start != a->base); | ||
96 | a->end = (a->base + a->length) & ~(a->blk_size - 1); | ||
97 | a->count = a->end - a->start; | ||
98 | a->blks = a->count >> a->blk_shift; | ||
99 | } | ||
100 | |||
101 | /* | ||
102 | * Pass NULL for parent if you want a top level buddy. | ||
103 | */ | ||
104 | static struct nvgpu_buddy *balloc_new_buddy(struct nvgpu_buddy_allocator *a, | ||
105 | struct nvgpu_buddy *parent, | ||
106 | u64 start, u64 order) | ||
107 | { | ||
108 | struct nvgpu_buddy *new_buddy; | ||
109 | |||
110 | new_buddy = nvgpu_kmem_cache_alloc(a->buddy_cache); | ||
111 | if (!new_buddy) | ||
112 | return NULL; | ||
113 | |||
114 | memset(new_buddy, 0, sizeof(struct nvgpu_buddy)); | ||
115 | |||
116 | new_buddy->parent = parent; | ||
117 | new_buddy->start = start; | ||
118 | new_buddy->order = order; | ||
119 | new_buddy->end = start + (1 << order) * a->blk_size; | ||
120 | new_buddy->pte_size = BALLOC_PTE_SIZE_ANY; | ||
121 | |||
122 | return new_buddy; | ||
123 | } | ||
124 | |||
125 | static void __balloc_buddy_list_add(struct nvgpu_buddy_allocator *a, | ||
126 | struct nvgpu_buddy *b, | ||
127 | struct nvgpu_list_node *list) | ||
128 | { | ||
129 | if (buddy_is_in_list(b)) { | ||
130 | alloc_dbg(balloc_owner(a), | ||
131 | "Oops: adding added buddy (%llu:0x%llx)\n", | ||
132 | b->order, b->start); | ||
133 | BUG(); | ||
134 | } | ||
135 | |||
136 | /* | ||
137 | * Add big PTE blocks to the tail, small to the head for GVA spaces. | ||
138 | * This lets the code that checks if there are available blocks check | ||
139 | * without cycling through the entire list. | ||
140 | */ | ||
141 | if (a->flags & GPU_ALLOC_GVA_SPACE && | ||
142 | b->pte_size == gmmu_page_size_big) | ||
143 | nvgpu_list_add_tail(&b->buddy_entry, list); | ||
144 | else | ||
145 | nvgpu_list_add(&b->buddy_entry, list); | ||
146 | |||
147 | buddy_set_in_list(b); | ||
148 | } | ||
149 | |||
150 | static void __balloc_buddy_list_rem(struct nvgpu_buddy_allocator *a, | ||
151 | struct nvgpu_buddy *b) | ||
152 | { | ||
153 | if (!buddy_is_in_list(b)) { | ||
154 | alloc_dbg(balloc_owner(a), | ||
155 | "Oops: removing removed buddy (%llu:0x%llx)\n", | ||
156 | b->order, b->start); | ||
157 | BUG(); | ||
158 | } | ||
159 | |||
160 | nvgpu_list_del(&b->buddy_entry); | ||
161 | buddy_clr_in_list(b); | ||
162 | } | ||
163 | |||
164 | /* | ||
165 | * Add a buddy to one of the buddy lists and deal with the necessary | ||
166 | * book keeping. Adds the buddy to the list specified by the buddy's order. | ||
167 | */ | ||
168 | static void balloc_blist_add(struct nvgpu_buddy_allocator *a, | ||
169 | struct nvgpu_buddy *b) | ||
170 | { | ||
171 | __balloc_buddy_list_add(a, b, balloc_get_order_list(a, b->order)); | ||
172 | a->buddy_list_len[b->order]++; | ||
173 | } | ||
174 | |||
175 | static void balloc_blist_rem(struct nvgpu_buddy_allocator *a, | ||
176 | struct nvgpu_buddy *b) | ||
177 | { | ||
178 | __balloc_buddy_list_rem(a, b); | ||
179 | a->buddy_list_len[b->order]--; | ||
180 | } | ||
181 | |||
182 | static u64 balloc_get_order(struct nvgpu_buddy_allocator *a, u64 len) | ||
183 | { | ||
184 | if (len == 0) | ||
185 | return 0; | ||
186 | |||
187 | len--; | ||
188 | len >>= a->blk_shift; | ||
189 | |||
190 | return fls(len); | ||
191 | } | ||
192 | |||
193 | static u64 __balloc_max_order_in(struct nvgpu_buddy_allocator *a, | ||
194 | u64 start, u64 end) | ||
195 | { | ||
196 | u64 size = (end - start) >> a->blk_shift; | ||
197 | |||
198 | if (size > 0) | ||
199 | return min_t(u64, ilog2(size), a->max_order); | ||
200 | else | ||
201 | return GPU_BALLOC_MAX_ORDER; | ||
202 | } | ||
203 | |||
204 | /* | ||
205 | * Initialize the buddy lists. | ||
206 | */ | ||
207 | static int balloc_init_lists(struct nvgpu_buddy_allocator *a) | ||
208 | { | ||
209 | int i; | ||
210 | u64 bstart, bend, order; | ||
211 | struct nvgpu_buddy *buddy; | ||
212 | |||
213 | bstart = a->start; | ||
214 | bend = a->end; | ||
215 | |||
216 | /* First make sure the LLs are valid. */ | ||
217 | for (i = 0; i < GPU_BALLOC_ORDER_LIST_LEN; i++) | ||
218 | nvgpu_init_list_node(balloc_get_order_list(a, i)); | ||
219 | |||
220 | while (bstart < bend) { | ||
221 | order = __balloc_max_order_in(a, bstart, bend); | ||
222 | |||
223 | buddy = balloc_new_buddy(a, NULL, bstart, order); | ||
224 | if (!buddy) | ||
225 | goto cleanup; | ||
226 | |||
227 | balloc_blist_add(a, buddy); | ||
228 | bstart += balloc_order_to_len(a, order); | ||
229 | } | ||
230 | |||
231 | return 0; | ||
232 | |||
233 | cleanup: | ||
234 | for (i = 0; i < GPU_BALLOC_ORDER_LIST_LEN; i++) { | ||
235 | if (!nvgpu_list_empty(balloc_get_order_list(a, i))) { | ||
236 | buddy = nvgpu_list_first_entry( | ||
237 | balloc_get_order_list(a, i), | ||
238 | nvgpu_buddy, buddy_entry); | ||
239 | balloc_blist_rem(a, buddy); | ||
240 | nvgpu_kmem_cache_free(a->buddy_cache, buddy); | ||
241 | } | ||
242 | } | ||
243 | |||
244 | return -ENOMEM; | ||
245 | } | ||
246 | |||
247 | /* | ||
248 | * Clean up and destroy the passed allocator. | ||
249 | */ | ||
250 | static void nvgpu_buddy_allocator_destroy(struct nvgpu_allocator *__a) | ||
251 | { | ||
252 | int i; | ||
253 | struct nvgpu_rbtree_node *node = NULL; | ||
254 | struct nvgpu_buddy *bud; | ||
255 | struct nvgpu_fixed_alloc *falloc; | ||
256 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
257 | |||
258 | alloc_lock(__a); | ||
259 | |||
260 | #ifdef CONFIG_DEBUG_FS | ||
261 | nvgpu_fini_alloc_debug(__a); | ||
262 | #endif | ||
263 | |||
264 | /* | ||
265 | * Free the fixed allocs first. | ||
266 | */ | ||
267 | nvgpu_rbtree_enum_start(0, &node, a->fixed_allocs); | ||
268 | while (node) { | ||
269 | falloc = nvgpu_fixed_alloc_from_rbtree_node(node); | ||
270 | |||
271 | nvgpu_rbtree_unlink(node, &a->fixed_allocs); | ||
272 | __balloc_do_free_fixed(a, falloc); | ||
273 | |||
274 | nvgpu_rbtree_enum_start(0, &node, a->fixed_allocs); | ||
275 | } | ||
276 | |||
277 | /* | ||
278 | * And now free all outstanding allocations. | ||
279 | */ | ||
280 | nvgpu_rbtree_enum_start(0, &node, a->alloced_buddies); | ||
281 | while (node) { | ||
282 | bud = nvgpu_buddy_from_rbtree_node(node); | ||
283 | |||
284 | balloc_free_buddy(a, bud->start); | ||
285 | balloc_blist_add(a, bud); | ||
286 | balloc_coalesce(a, bud); | ||
287 | |||
288 | nvgpu_rbtree_enum_start(0, &node, a->alloced_buddies); | ||
289 | } | ||
290 | |||
291 | /* | ||
292 | * Now clean up the unallocated buddies. | ||
293 | */ | ||
294 | for (i = 0; i < GPU_BALLOC_ORDER_LIST_LEN; i++) { | ||
295 | BUG_ON(a->buddy_list_alloced[i] != 0); | ||
296 | |||
297 | while (!nvgpu_list_empty(balloc_get_order_list(a, i))) { | ||
298 | bud = nvgpu_list_first_entry( | ||
299 | balloc_get_order_list(a, i), | ||
300 | nvgpu_buddy, buddy_entry); | ||
301 | balloc_blist_rem(a, bud); | ||
302 | nvgpu_kmem_cache_free(a->buddy_cache, bud); | ||
303 | } | ||
304 | |||
305 | if (a->buddy_list_len[i] != 0) { | ||
306 | nvgpu_info(__a->g, | ||
307 | "Excess buddies!!! (%d: %llu)\n", | ||
308 | i, a->buddy_list_len[i]); | ||
309 | BUG(); | ||
310 | } | ||
311 | if (a->buddy_list_split[i] != 0) { | ||
312 | nvgpu_info(__a->g, | ||
313 | "Excess split nodes!!! (%d: %llu)\n", | ||
314 | i, a->buddy_list_split[i]); | ||
315 | BUG(); | ||
316 | } | ||
317 | if (a->buddy_list_alloced[i] != 0) { | ||
318 | nvgpu_info(__a->g, | ||
319 | "Excess alloced nodes!!! (%d: %llu)\n", | ||
320 | i, a->buddy_list_alloced[i]); | ||
321 | BUG(); | ||
322 | } | ||
323 | } | ||
324 | |||
325 | nvgpu_kmem_cache_destroy(a->buddy_cache); | ||
326 | nvgpu_kfree(nvgpu_alloc_to_gpu(__a), a); | ||
327 | |||
328 | alloc_unlock(__a); | ||
329 | } | ||
330 | |||
331 | /* | ||
332 | * Combine the passed buddy if possible. The pointer in @b may not be valid | ||
333 | * after this as the buddy may be freed. | ||
334 | * | ||
335 | * @a must be locked. | ||
336 | */ | ||
337 | static void balloc_coalesce(struct nvgpu_buddy_allocator *a, | ||
338 | struct nvgpu_buddy *b) | ||
339 | { | ||
340 | struct nvgpu_buddy *parent; | ||
341 | |||
342 | if (buddy_is_alloced(b) || buddy_is_split(b)) | ||
343 | return; | ||
344 | |||
345 | /* | ||
346 | * If both our buddy and I are both not allocated and not split then | ||
347 | * we can coalesce ourselves. | ||
348 | */ | ||
349 | if (!b->buddy) | ||
350 | return; | ||
351 | if (buddy_is_alloced(b->buddy) || buddy_is_split(b->buddy)) | ||
352 | return; | ||
353 | |||
354 | parent = b->parent; | ||
355 | |||
356 | balloc_blist_rem(a, b); | ||
357 | balloc_blist_rem(a, b->buddy); | ||
358 | |||
359 | buddy_clr_split(parent); | ||
360 | a->buddy_list_split[parent->order]--; | ||
361 | balloc_blist_add(a, parent); | ||
362 | |||
363 | /* | ||
364 | * Recursively coalesce as far as we can go. | ||
365 | */ | ||
366 | balloc_coalesce(a, parent); | ||
367 | |||
368 | /* Clean up the remains. */ | ||
369 | nvgpu_kmem_cache_free(a->buddy_cache, b->buddy); | ||
370 | nvgpu_kmem_cache_free(a->buddy_cache, b); | ||
371 | } | ||
372 | |||
373 | /* | ||
374 | * Split a buddy into two new buddies who are 1/2 the size of the parent buddy. | ||
375 | * | ||
376 | * @a must be locked. | ||
377 | */ | ||
378 | static int balloc_split_buddy(struct nvgpu_buddy_allocator *a, | ||
379 | struct nvgpu_buddy *b, int pte_size) | ||
380 | { | ||
381 | struct nvgpu_buddy *left, *right; | ||
382 | u64 half; | ||
383 | |||
384 | left = balloc_new_buddy(a, b, b->start, b->order - 1); | ||
385 | if (!left) | ||
386 | return -ENOMEM; | ||
387 | |||
388 | half = (b->end - b->start) / 2; | ||
389 | |||
390 | right = balloc_new_buddy(a, b, b->start + half, b->order - 1); | ||
391 | if (!right) { | ||
392 | nvgpu_kmem_cache_free(a->buddy_cache, left); | ||
393 | return -ENOMEM; | ||
394 | } | ||
395 | |||
396 | buddy_set_split(b); | ||
397 | a->buddy_list_split[b->order]++; | ||
398 | |||
399 | b->left = left; | ||
400 | b->right = right; | ||
401 | left->buddy = right; | ||
402 | right->buddy = left; | ||
403 | left->parent = b; | ||
404 | right->parent = b; | ||
405 | |||
406 | /* PTE considerations. */ | ||
407 | if (a->flags & GPU_ALLOC_GVA_SPACE && | ||
408 | left->order <= a->pte_blk_order) { | ||
409 | left->pte_size = pte_size; | ||
410 | right->pte_size = pte_size; | ||
411 | } | ||
412 | |||
413 | balloc_blist_rem(a, b); | ||
414 | balloc_blist_add(a, left); | ||
415 | balloc_blist_add(a, right); | ||
416 | |||
417 | return 0; | ||
418 | } | ||
419 | |||
420 | /* | ||
421 | * Place the passed buddy into the RB tree for allocated buddies. Never fails | ||
422 | * unless the passed entry is a duplicate which is a bug. | ||
423 | * | ||
424 | * @a must be locked. | ||
425 | */ | ||
426 | static void balloc_alloc_buddy(struct nvgpu_buddy_allocator *a, | ||
427 | struct nvgpu_buddy *b) | ||
428 | { | ||
429 | b->alloced_entry.key_start = b->start; | ||
430 | b->alloced_entry.key_end = b->end; | ||
431 | |||
432 | nvgpu_rbtree_insert(&b->alloced_entry, &a->alloced_buddies); | ||
433 | |||
434 | buddy_set_alloced(b); | ||
435 | a->buddy_list_alloced[b->order]++; | ||
436 | } | ||
437 | |||
438 | /* | ||
439 | * Remove the passed buddy from the allocated buddy RB tree. Returns the | ||
440 | * deallocated buddy for further processing. | ||
441 | * | ||
442 | * @a must be locked. | ||
443 | */ | ||
444 | static struct nvgpu_buddy *balloc_free_buddy(struct nvgpu_buddy_allocator *a, | ||
445 | u64 addr) | ||
446 | { | ||
447 | struct nvgpu_rbtree_node *node = NULL; | ||
448 | struct nvgpu_buddy *bud; | ||
449 | |||
450 | nvgpu_rbtree_search(addr, &node, a->alloced_buddies); | ||
451 | if (!node) | ||
452 | return NULL; | ||
453 | |||
454 | bud = nvgpu_buddy_from_rbtree_node(node); | ||
455 | |||
456 | nvgpu_rbtree_unlink(node, &a->alloced_buddies); | ||
457 | buddy_clr_alloced(bud); | ||
458 | a->buddy_list_alloced[bud->order]--; | ||
459 | |||
460 | return bud; | ||
461 | } | ||
462 | |||
463 | /* | ||
464 | * Find a suitable buddy for the given order and PTE type (big or little). | ||
465 | */ | ||
466 | static struct nvgpu_buddy *__balloc_find_buddy(struct nvgpu_buddy_allocator *a, | ||
467 | u64 order, int pte_size) | ||
468 | { | ||
469 | struct nvgpu_buddy *bud; | ||
470 | |||
471 | if (order > a->max_order || | ||
472 | nvgpu_list_empty(balloc_get_order_list(a, order))) | ||
473 | return NULL; | ||
474 | |||
475 | if (a->flags & GPU_ALLOC_GVA_SPACE && | ||
476 | pte_size == gmmu_page_size_big) | ||
477 | bud = nvgpu_list_last_entry(balloc_get_order_list(a, order), | ||
478 | nvgpu_buddy, buddy_entry); | ||
479 | else | ||
480 | bud = nvgpu_list_first_entry(balloc_get_order_list(a, order), | ||
481 | nvgpu_buddy, buddy_entry); | ||
482 | |||
483 | if (pte_size != BALLOC_PTE_SIZE_ANY && | ||
484 | pte_size != bud->pte_size && | ||
485 | bud->pte_size != BALLOC_PTE_SIZE_ANY) | ||
486 | return NULL; | ||
487 | |||
488 | return bud; | ||
489 | } | ||
490 | |||
491 | /* | ||
492 | * Allocate a suitably sized buddy. If no suitable buddy exists split higher | ||
493 | * order buddies until we have a suitable buddy to allocate. | ||
494 | * | ||
495 | * For PDE grouping add an extra check to see if a buddy is suitable: that the | ||
496 | * buddy exists in a PDE who's PTE size is reasonable | ||
497 | * | ||
498 | * @a must be locked. | ||
499 | */ | ||
500 | static u64 __balloc_do_alloc(struct nvgpu_buddy_allocator *a, | ||
501 | u64 order, int pte_size) | ||
502 | { | ||
503 | u64 split_order; | ||
504 | struct nvgpu_buddy *bud = NULL; | ||
505 | |||
506 | split_order = order; | ||
507 | while (split_order <= a->max_order && | ||
508 | !(bud = __balloc_find_buddy(a, split_order, pte_size))) | ||
509 | split_order++; | ||
510 | |||
511 | /* Out of memory! */ | ||
512 | if (!bud) | ||
513 | return 0; | ||
514 | |||
515 | while (bud->order != order) { | ||
516 | if (balloc_split_buddy(a, bud, pte_size)) | ||
517 | return 0; /* No mem... */ | ||
518 | bud = bud->left; | ||
519 | } | ||
520 | |||
521 | balloc_blist_rem(a, bud); | ||
522 | balloc_alloc_buddy(a, bud); | ||
523 | |||
524 | return bud->start; | ||
525 | } | ||
526 | |||
527 | /* | ||
528 | * See if the passed range is actually available for allocation. If so, then | ||
529 | * return 1, otherwise return 0. | ||
530 | * | ||
531 | * TODO: Right now this uses the unoptimal approach of going through all | ||
532 | * outstanding allocations and checking their base/ends. This could be better. | ||
533 | */ | ||
534 | static int balloc_is_range_free(struct nvgpu_buddy_allocator *a, | ||
535 | u64 base, u64 end) | ||
536 | { | ||
537 | struct nvgpu_rbtree_node *node = NULL; | ||
538 | struct nvgpu_buddy *bud; | ||
539 | |||
540 | nvgpu_rbtree_enum_start(0, &node, a->alloced_buddies); | ||
541 | if (!node) | ||
542 | return 1; /* No allocs yet. */ | ||
543 | |||
544 | bud = nvgpu_buddy_from_rbtree_node(node); | ||
545 | |||
546 | while (bud->start < end) { | ||
547 | if ((bud->start > base && bud->start < end) || | ||
548 | (bud->end > base && bud->end < end)) | ||
549 | return 0; | ||
550 | |||
551 | nvgpu_rbtree_enum_next(&node, node); | ||
552 | if (!node) | ||
553 | break; | ||
554 | bud = nvgpu_buddy_from_rbtree_node(node); | ||
555 | } | ||
556 | |||
557 | return 1; | ||
558 | } | ||
559 | |||
560 | static void balloc_alloc_fixed(struct nvgpu_buddy_allocator *a, | ||
561 | struct nvgpu_fixed_alloc *f) | ||
562 | { | ||
563 | f->alloced_entry.key_start = f->start; | ||
564 | f->alloced_entry.key_end = f->end; | ||
565 | |||
566 | nvgpu_rbtree_insert(&f->alloced_entry, &a->fixed_allocs); | ||
567 | } | ||
568 | |||
569 | /* | ||
570 | * Remove the passed buddy from the allocated buddy RB tree. Returns the | ||
571 | * deallocated buddy for further processing. | ||
572 | * | ||
573 | * @a must be locked. | ||
574 | */ | ||
575 | static struct nvgpu_fixed_alloc *balloc_free_fixed( | ||
576 | struct nvgpu_buddy_allocator *a, u64 addr) | ||
577 | { | ||
578 | struct nvgpu_fixed_alloc *falloc; | ||
579 | struct nvgpu_rbtree_node *node = NULL; | ||
580 | |||
581 | nvgpu_rbtree_search(addr, &node, a->fixed_allocs); | ||
582 | if (!node) | ||
583 | return NULL; | ||
584 | |||
585 | falloc = nvgpu_fixed_alloc_from_rbtree_node(node); | ||
586 | |||
587 | nvgpu_rbtree_unlink(node, &a->fixed_allocs); | ||
588 | |||
589 | return falloc; | ||
590 | } | ||
591 | |||
592 | /* | ||
593 | * Find the parent range - doesn't necessarily need the parent to actually exist | ||
594 | * as a buddy. Finding an existing parent comes later... | ||
595 | */ | ||
596 | static void __balloc_get_parent_range(struct nvgpu_buddy_allocator *a, | ||
597 | u64 base, u64 order, | ||
598 | u64 *pbase, u64 *porder) | ||
599 | { | ||
600 | u64 base_mask; | ||
601 | u64 shifted_base = balloc_base_shift(a, base); | ||
602 | |||
603 | order++; | ||
604 | base_mask = ~((a->blk_size << order) - 1); | ||
605 | |||
606 | shifted_base &= base_mask; | ||
607 | |||
608 | *pbase = balloc_base_unshift(a, shifted_base); | ||
609 | *porder = order; | ||
610 | } | ||
611 | |||
612 | /* | ||
613 | * Makes a buddy at the passed address. This will make all parent buddies | ||
614 | * necessary for this buddy to exist as well. | ||
615 | */ | ||
616 | static struct nvgpu_buddy *__balloc_make_fixed_buddy( | ||
617 | struct nvgpu_buddy_allocator *a, u64 base, u64 order, int pte_size) | ||
618 | { | ||
619 | struct nvgpu_buddy *bud = NULL; | ||
620 | struct nvgpu_list_node *order_list; | ||
621 | u64 cur_order = order, cur_base = base; | ||
622 | |||
623 | /* | ||
624 | * Algo: | ||
625 | * 1. Keep jumping up a buddy order until we find the real buddy that | ||
626 | * this buddy exists in. | ||
627 | * 2. Then work our way down through the buddy tree until we hit a dead | ||
628 | * end. | ||
629 | * 3. Start splitting buddies until we split to the one we need to | ||
630 | * make. | ||
631 | */ | ||
632 | while (cur_order <= a->max_order) { | ||
633 | int found = 0; | ||
634 | |||
635 | order_list = balloc_get_order_list(a, cur_order); | ||
636 | nvgpu_list_for_each_entry(bud, order_list, | ||
637 | nvgpu_buddy, buddy_entry) { | ||
638 | if (bud->start == cur_base) { | ||
639 | /* | ||
640 | * Make sure page size matches if it's smaller | ||
641 | * than a PDE sized buddy. | ||
642 | */ | ||
643 | if (bud->order <= a->pte_blk_order && | ||
644 | bud->pte_size != BALLOC_PTE_SIZE_ANY && | ||
645 | bud->pte_size != pte_size) { | ||
646 | /* Welp, that's the end of that. */ | ||
647 | alloc_dbg(balloc_owner(a), | ||
648 | "Fixed buddy PTE " | ||
649 | "size mismatch!\n"); | ||
650 | return NULL; | ||
651 | } | ||
652 | |||
653 | found = 1; | ||
654 | break; | ||
655 | } | ||
656 | } | ||
657 | |||
658 | if (found) | ||
659 | break; | ||
660 | |||
661 | __balloc_get_parent_range(a, cur_base, cur_order, | ||
662 | &cur_base, &cur_order); | ||
663 | } | ||
664 | |||
665 | if (cur_order > a->max_order) { | ||
666 | alloc_dbg(balloc_owner(a), "No buddy for range ???\n"); | ||
667 | return NULL; | ||
668 | } | ||
669 | |||
670 | /* Split this buddy as necessary until we get the target buddy. */ | ||
671 | while (bud->start != base || bud->order != order) { | ||
672 | if (balloc_split_buddy(a, bud, pte_size)) { | ||
673 | alloc_dbg(balloc_owner(a), | ||
674 | "split buddy failed? {0x%llx, %llu}\n", | ||
675 | bud->start, bud->order); | ||
676 | balloc_coalesce(a, bud); | ||
677 | return NULL; | ||
678 | } | ||
679 | |||
680 | if (base < bud->right->start) | ||
681 | bud = bud->left; | ||
682 | else | ||
683 | bud = bud->right; | ||
684 | |||
685 | } | ||
686 | |||
687 | return bud; | ||
688 | } | ||
689 | |||
690 | static u64 __balloc_do_alloc_fixed(struct nvgpu_buddy_allocator *a, | ||
691 | struct nvgpu_fixed_alloc *falloc, | ||
692 | u64 base, u64 len, int pte_size) | ||
693 | { | ||
694 | u64 shifted_base, inc_base; | ||
695 | u64 align_order; | ||
696 | |||
697 | shifted_base = balloc_base_shift(a, base); | ||
698 | if (shifted_base == 0) | ||
699 | align_order = __fls(len >> a->blk_shift); | ||
700 | else | ||
701 | align_order = min_t(u64, | ||
702 | __ffs(shifted_base >> a->blk_shift), | ||
703 | __fls(len >> a->blk_shift)); | ||
704 | |||
705 | if (align_order > a->max_order) { | ||
706 | alloc_dbg(balloc_owner(a), | ||
707 | "Align order too big: %llu > %llu\n", | ||
708 | align_order, a->max_order); | ||
709 | return 0; | ||
710 | } | ||
711 | |||
712 | /* | ||
713 | * Generate a list of buddies that satisfy this allocation. | ||
714 | */ | ||
715 | inc_base = shifted_base; | ||
716 | while (inc_base < (shifted_base + len)) { | ||
717 | u64 order_len = balloc_order_to_len(a, align_order); | ||
718 | u64 remaining; | ||
719 | struct nvgpu_buddy *bud; | ||
720 | |||
721 | bud = __balloc_make_fixed_buddy(a, | ||
722 | balloc_base_unshift(a, inc_base), | ||
723 | align_order, pte_size); | ||
724 | if (!bud) { | ||
725 | alloc_dbg(balloc_owner(a), | ||
726 | "Fixed buddy failed: {0x%llx, %llu}!\n", | ||
727 | balloc_base_unshift(a, inc_base), | ||
728 | align_order); | ||
729 | goto err_and_cleanup; | ||
730 | } | ||
731 | |||
732 | balloc_blist_rem(a, bud); | ||
733 | balloc_alloc_buddy(a, bud); | ||
734 | __balloc_buddy_list_add(a, bud, &falloc->buddies); | ||
735 | |||
736 | /* Book keeping. */ | ||
737 | inc_base += order_len; | ||
738 | remaining = (shifted_base + len) - inc_base; | ||
739 | align_order = __ffs(inc_base >> a->blk_shift); | ||
740 | |||
741 | /* If we don't have much left - trim down align_order. */ | ||
742 | if (balloc_order_to_len(a, align_order) > remaining) | ||
743 | align_order = __balloc_max_order_in(a, inc_base, | ||
744 | inc_base + remaining); | ||
745 | } | ||
746 | |||
747 | return base; | ||
748 | |||
749 | err_and_cleanup: | ||
750 | while (!nvgpu_list_empty(&falloc->buddies)) { | ||
751 | struct nvgpu_buddy *bud = nvgpu_list_first_entry( | ||
752 | &falloc->buddies, | ||
753 | nvgpu_buddy, buddy_entry); | ||
754 | |||
755 | __balloc_buddy_list_rem(a, bud); | ||
756 | balloc_free_buddy(a, bud->start); | ||
757 | nvgpu_kmem_cache_free(a->buddy_cache, bud); | ||
758 | } | ||
759 | |||
760 | return 0; | ||
761 | } | ||
762 | |||
763 | static void __balloc_do_free_fixed(struct nvgpu_buddy_allocator *a, | ||
764 | struct nvgpu_fixed_alloc *falloc) | ||
765 | { | ||
766 | struct nvgpu_buddy *bud; | ||
767 | |||
768 | while (!nvgpu_list_empty(&falloc->buddies)) { | ||
769 | bud = nvgpu_list_first_entry(&falloc->buddies, | ||
770 | nvgpu_buddy, | ||
771 | buddy_entry); | ||
772 | __balloc_buddy_list_rem(a, bud); | ||
773 | |||
774 | balloc_free_buddy(a, bud->start); | ||
775 | balloc_blist_add(a, bud); | ||
776 | a->bytes_freed += balloc_order_to_len(a, bud->order); | ||
777 | |||
778 | /* | ||
779 | * Attemp to defrag the allocation. | ||
780 | */ | ||
781 | balloc_coalesce(a, bud); | ||
782 | } | ||
783 | |||
784 | nvgpu_kfree(nvgpu_alloc_to_gpu(a->owner), falloc); | ||
785 | } | ||
786 | |||
787 | /* | ||
788 | * Allocate memory from the passed allocator. | ||
789 | */ | ||
790 | static u64 nvgpu_buddy_balloc(struct nvgpu_allocator *__a, u64 len) | ||
791 | { | ||
792 | u64 order, addr; | ||
793 | int pte_size; | ||
794 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
795 | |||
796 | alloc_lock(__a); | ||
797 | |||
798 | order = balloc_get_order(a, len); | ||
799 | |||
800 | if (order > a->max_order) { | ||
801 | alloc_unlock(__a); | ||
802 | alloc_dbg(balloc_owner(a), "Alloc fail\n"); | ||
803 | return 0; | ||
804 | } | ||
805 | |||
806 | if (a->flags & GPU_ALLOC_GVA_SPACE) | ||
807 | pte_size = __get_pte_size(a->vm, 0, len); | ||
808 | else | ||
809 | pte_size = BALLOC_PTE_SIZE_ANY; | ||
810 | |||
811 | addr = __balloc_do_alloc(a, order, pte_size); | ||
812 | |||
813 | if (addr) { | ||
814 | a->bytes_alloced += len; | ||
815 | a->bytes_alloced_real += balloc_order_to_len(a, order); | ||
816 | alloc_dbg(balloc_owner(a), | ||
817 | "Alloc 0x%-10llx %3lld:0x%-10llx pte_size=%s\n", | ||
818 | addr, order, len, | ||
819 | pte_size == gmmu_page_size_big ? "big" : | ||
820 | pte_size == gmmu_page_size_small ? "small" : | ||
821 | "NA/any"); | ||
822 | } else { | ||
823 | alloc_dbg(balloc_owner(a), "Alloc failed: no mem!\n"); | ||
824 | } | ||
825 | |||
826 | a->alloc_made = 1; | ||
827 | |||
828 | alloc_unlock(__a); | ||
829 | |||
830 | return addr; | ||
831 | } | ||
832 | |||
833 | /* | ||
834 | * Requires @__a to be locked. | ||
835 | */ | ||
836 | static u64 __nvgpu_balloc_fixed_buddy(struct nvgpu_allocator *__a, | ||
837 | u64 base, u64 len, u32 page_size) | ||
838 | { | ||
839 | int pte_size = BALLOC_PTE_SIZE_ANY; | ||
840 | u64 ret, real_bytes = 0; | ||
841 | struct nvgpu_buddy *bud; | ||
842 | struct nvgpu_fixed_alloc *falloc = NULL; | ||
843 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
844 | |||
845 | /* If base isn't aligned to an order 0 block, fail. */ | ||
846 | if (base & (a->blk_size - 1)) | ||
847 | goto fail; | ||
848 | |||
849 | if (len == 0) | ||
850 | goto fail; | ||
851 | |||
852 | /* Check that the page size is valid. */ | ||
853 | if (a->flags & GPU_ALLOC_GVA_SPACE && a->vm->big_pages) { | ||
854 | if (page_size == a->vm->big_page_size) | ||
855 | pte_size = gmmu_page_size_big; | ||
856 | else if (page_size == SZ_4K) | ||
857 | pte_size = gmmu_page_size_small; | ||
858 | else | ||
859 | goto fail; | ||
860 | } | ||
861 | |||
862 | falloc = nvgpu_kmalloc(nvgpu_alloc_to_gpu(__a), sizeof(*falloc)); | ||
863 | if (!falloc) | ||
864 | goto fail; | ||
865 | |||
866 | nvgpu_init_list_node(&falloc->buddies); | ||
867 | falloc->start = base; | ||
868 | falloc->end = base + len; | ||
869 | |||
870 | if (!balloc_is_range_free(a, base, base + len)) { | ||
871 | alloc_dbg(balloc_owner(a), | ||
872 | "Range not free: 0x%llx -> 0x%llx\n", | ||
873 | base, base + len); | ||
874 | goto fail_unlock; | ||
875 | } | ||
876 | |||
877 | ret = __balloc_do_alloc_fixed(a, falloc, base, len, pte_size); | ||
878 | if (!ret) { | ||
879 | alloc_dbg(balloc_owner(a), | ||
880 | "Alloc-fixed failed ?? 0x%llx -> 0x%llx\n", | ||
881 | base, base + len); | ||
882 | goto fail_unlock; | ||
883 | } | ||
884 | |||
885 | balloc_alloc_fixed(a, falloc); | ||
886 | |||
887 | nvgpu_list_for_each_entry(bud, &falloc->buddies, | ||
888 | nvgpu_buddy, buddy_entry) | ||
889 | real_bytes += (bud->end - bud->start); | ||
890 | |||
891 | a->bytes_alloced += len; | ||
892 | a->bytes_alloced_real += real_bytes; | ||
893 | |||
894 | alloc_dbg(balloc_owner(a), "Alloc (fixed) 0x%llx\n", base); | ||
895 | |||
896 | return base; | ||
897 | |||
898 | fail_unlock: | ||
899 | alloc_unlock(__a); | ||
900 | fail: | ||
901 | nvgpu_kfree(nvgpu_alloc_to_gpu(__a), falloc); | ||
902 | return 0; | ||
903 | } | ||
904 | |||
905 | /* | ||
906 | * Allocate a fixed address allocation. The address of the allocation is @base | ||
907 | * and the length is @len. This is not a typical buddy allocator operation and | ||
908 | * as such has a high posibility of failure if the address space is heavily in | ||
909 | * use. | ||
910 | * | ||
911 | * Please do not use this function unless _absolutely_ necessary. | ||
912 | */ | ||
913 | static u64 nvgpu_balloc_fixed_buddy(struct nvgpu_allocator *__a, | ||
914 | u64 base, u64 len, u32 page_size) | ||
915 | { | ||
916 | u64 alloc; | ||
917 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
918 | |||
919 | alloc_lock(__a); | ||
920 | alloc = __nvgpu_balloc_fixed_buddy(__a, base, len, page_size); | ||
921 | a->alloc_made = 1; | ||
922 | alloc_unlock(__a); | ||
923 | |||
924 | return alloc; | ||
925 | } | ||
926 | |||
927 | /* | ||
928 | * Free the passed allocation. | ||
929 | */ | ||
930 | static void nvgpu_buddy_bfree(struct nvgpu_allocator *__a, u64 addr) | ||
931 | { | ||
932 | struct nvgpu_buddy *bud; | ||
933 | struct nvgpu_fixed_alloc *falloc; | ||
934 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
935 | |||
936 | if (!addr) | ||
937 | return; | ||
938 | |||
939 | alloc_lock(__a); | ||
940 | |||
941 | /* | ||
942 | * First see if this is a fixed alloc. If not fall back to a regular | ||
943 | * buddy. | ||
944 | */ | ||
945 | falloc = balloc_free_fixed(a, addr); | ||
946 | if (falloc) { | ||
947 | __balloc_do_free_fixed(a, falloc); | ||
948 | goto done; | ||
949 | } | ||
950 | |||
951 | bud = balloc_free_buddy(a, addr); | ||
952 | if (!bud) | ||
953 | goto done; | ||
954 | |||
955 | balloc_blist_add(a, bud); | ||
956 | a->bytes_freed += balloc_order_to_len(a, bud->order); | ||
957 | |||
958 | /* | ||
959 | * Attemp to defrag the allocation. | ||
960 | */ | ||
961 | balloc_coalesce(a, bud); | ||
962 | |||
963 | done: | ||
964 | alloc_unlock(__a); | ||
965 | alloc_dbg(balloc_owner(a), "Free 0x%llx\n", addr); | ||
966 | return; | ||
967 | } | ||
968 | |||
969 | static bool nvgpu_buddy_reserve_is_possible(struct nvgpu_buddy_allocator *a, | ||
970 | struct nvgpu_alloc_carveout *co) | ||
971 | { | ||
972 | struct nvgpu_alloc_carveout *tmp; | ||
973 | u64 co_base, co_end; | ||
974 | |||
975 | co_base = co->base; | ||
976 | co_end = co->base + co->length; | ||
977 | |||
978 | /* | ||
979 | * Not the fastest approach but we should not have that many carveouts | ||
980 | * for any reasonable allocator. | ||
981 | */ | ||
982 | nvgpu_list_for_each_entry(tmp, &a->co_list, | ||
983 | nvgpu_alloc_carveout, co_entry) { | ||
984 | if ((co_base >= tmp->base && | ||
985 | co_base < (tmp->base + tmp->length)) || | ||
986 | (co_end >= tmp->base && | ||
987 | co_end < (tmp->base + tmp->length))) | ||
988 | return false; | ||
989 | } | ||
990 | |||
991 | return true; | ||
992 | } | ||
993 | |||
994 | /* | ||
995 | * Carveouts can only be reserved before any regular allocations have been | ||
996 | * made. | ||
997 | */ | ||
998 | static int nvgpu_buddy_reserve_co(struct nvgpu_allocator *__a, | ||
999 | struct nvgpu_alloc_carveout *co) | ||
1000 | { | ||
1001 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
1002 | u64 addr; | ||
1003 | int err = 0; | ||
1004 | |||
1005 | if (co->base < a->start || (co->base + co->length) > a->end || | ||
1006 | a->alloc_made) | ||
1007 | return -EINVAL; | ||
1008 | |||
1009 | alloc_lock(__a); | ||
1010 | |||
1011 | if (!nvgpu_buddy_reserve_is_possible(a, co)) { | ||
1012 | err = -EBUSY; | ||
1013 | goto done; | ||
1014 | } | ||
1015 | |||
1016 | /* Should not be possible to fail... */ | ||
1017 | addr = __nvgpu_balloc_fixed_buddy(__a, co->base, co->length, 0); | ||
1018 | if (!addr) { | ||
1019 | err = -ENOMEM; | ||
1020 | nvgpu_warn(__a->g, | ||
1021 | "%s: Failed to reserve a valid carveout!\n", | ||
1022 | __func__); | ||
1023 | goto done; | ||
1024 | } | ||
1025 | |||
1026 | nvgpu_list_add(&co->co_entry, &a->co_list); | ||
1027 | |||
1028 | done: | ||
1029 | alloc_unlock(__a); | ||
1030 | return err; | ||
1031 | } | ||
1032 | |||
1033 | /* | ||
1034 | * Carveouts can be release at any time. | ||
1035 | */ | ||
1036 | static void nvgpu_buddy_release_co(struct nvgpu_allocator *__a, | ||
1037 | struct nvgpu_alloc_carveout *co) | ||
1038 | { | ||
1039 | alloc_lock(__a); | ||
1040 | |||
1041 | nvgpu_list_del(&co->co_entry); | ||
1042 | nvgpu_free(__a, co->base); | ||
1043 | |||
1044 | alloc_unlock(__a); | ||
1045 | } | ||
1046 | |||
1047 | static u64 nvgpu_buddy_alloc_length(struct nvgpu_allocator *a) | ||
1048 | { | ||
1049 | struct nvgpu_buddy_allocator *ba = a->priv; | ||
1050 | |||
1051 | return ba->length; | ||
1052 | } | ||
1053 | |||
1054 | static u64 nvgpu_buddy_alloc_base(struct nvgpu_allocator *a) | ||
1055 | { | ||
1056 | struct nvgpu_buddy_allocator *ba = a->priv; | ||
1057 | |||
1058 | return ba->start; | ||
1059 | } | ||
1060 | |||
1061 | static int nvgpu_buddy_alloc_inited(struct nvgpu_allocator *a) | ||
1062 | { | ||
1063 | struct nvgpu_buddy_allocator *ba = a->priv; | ||
1064 | int inited = ba->initialized; | ||
1065 | |||
1066 | nvgpu_smp_rmb(); | ||
1067 | return inited; | ||
1068 | } | ||
1069 | |||
1070 | static u64 nvgpu_buddy_alloc_end(struct nvgpu_allocator *a) | ||
1071 | { | ||
1072 | struct nvgpu_buddy_allocator *ba = a->priv; | ||
1073 | |||
1074 | return ba->end; | ||
1075 | } | ||
1076 | |||
1077 | static u64 nvgpu_buddy_alloc_space(struct nvgpu_allocator *a) | ||
1078 | { | ||
1079 | struct nvgpu_buddy_allocator *ba = a->priv; | ||
1080 | u64 space; | ||
1081 | |||
1082 | alloc_lock(a); | ||
1083 | space = ba->end - ba->start - | ||
1084 | (ba->bytes_alloced_real - ba->bytes_freed); | ||
1085 | alloc_unlock(a); | ||
1086 | |||
1087 | return space; | ||
1088 | } | ||
1089 | |||
1090 | #ifdef __KERNEL__ | ||
1091 | /* | ||
1092 | * Print the buddy allocator top level stats. If you pass @s as NULL then the | ||
1093 | * stats are printed to the kernel log. This lets this code be used for | ||
1094 | * debugging purposes internal to the allocator. | ||
1095 | */ | ||
1096 | static void nvgpu_buddy_print_stats(struct nvgpu_allocator *__a, | ||
1097 | struct seq_file *s, int lock) | ||
1098 | { | ||
1099 | int i = 0; | ||
1100 | struct nvgpu_rbtree_node *node = NULL; | ||
1101 | struct nvgpu_fixed_alloc *falloc; | ||
1102 | struct nvgpu_alloc_carveout *tmp; | ||
1103 | struct nvgpu_buddy_allocator *a = __a->priv; | ||
1104 | |||
1105 | __alloc_pstat(s, __a, "base = %llu, limit = %llu, blk_size = %llu\n", | ||
1106 | a->base, a->length, a->blk_size); | ||
1107 | __alloc_pstat(s, __a, "Internal params:\n"); | ||
1108 | __alloc_pstat(s, __a, " start = 0x%llx\n", a->start); | ||
1109 | __alloc_pstat(s, __a, " end = 0x%llx\n", a->end); | ||
1110 | __alloc_pstat(s, __a, " count = 0x%llx\n", a->count); | ||
1111 | __alloc_pstat(s, __a, " blks = 0x%llx\n", a->blks); | ||
1112 | __alloc_pstat(s, __a, " max_order = %llu\n", a->max_order); | ||
1113 | |||
1114 | if (lock) | ||
1115 | alloc_lock(__a); | ||
1116 | |||
1117 | if (!nvgpu_list_empty(&a->co_list)) { | ||
1118 | __alloc_pstat(s, __a, "\n"); | ||
1119 | __alloc_pstat(s, __a, "Carveouts:\n"); | ||
1120 | nvgpu_list_for_each_entry(tmp, &a->co_list, | ||
1121 | nvgpu_alloc_carveout, co_entry) | ||
1122 | __alloc_pstat(s, __a, | ||
1123 | " CO %2d: %-20s 0x%010llx + 0x%llx\n", | ||
1124 | i++, tmp->name, tmp->base, tmp->length); | ||
1125 | } | ||
1126 | |||
1127 | __alloc_pstat(s, __a, "\n"); | ||
1128 | __alloc_pstat(s, __a, "Buddy blocks:\n"); | ||
1129 | __alloc_pstat(s, __a, " Order Free Alloced Split\n"); | ||
1130 | __alloc_pstat(s, __a, " ----- ---- ------- -----\n"); | ||
1131 | |||
1132 | for (i = a->max_order; i >= 0; i--) { | ||
1133 | if (a->buddy_list_len[i] == 0 && | ||
1134 | a->buddy_list_alloced[i] == 0 && | ||
1135 | a->buddy_list_split[i] == 0) | ||
1136 | continue; | ||
1137 | |||
1138 | __alloc_pstat(s, __a, " %3d %-7llu %-9llu %llu\n", i, | ||
1139 | a->buddy_list_len[i], | ||
1140 | a->buddy_list_alloced[i], | ||
1141 | a->buddy_list_split[i]); | ||
1142 | } | ||
1143 | |||
1144 | __alloc_pstat(s, __a, "\n"); | ||
1145 | |||
1146 | nvgpu_rbtree_enum_start(0, &node, a->fixed_allocs); | ||
1147 | i = 1; | ||
1148 | while (node) { | ||
1149 | falloc = nvgpu_fixed_alloc_from_rbtree_node(node); | ||
1150 | |||
1151 | __alloc_pstat(s, __a, "Fixed alloc (%d): [0x%llx -> 0x%llx]\n", | ||
1152 | i, falloc->start, falloc->end); | ||
1153 | |||
1154 | nvgpu_rbtree_enum_next(&node, a->fixed_allocs); | ||
1155 | } | ||
1156 | |||
1157 | __alloc_pstat(s, __a, "\n"); | ||
1158 | __alloc_pstat(s, __a, "Bytes allocated: %llu\n", | ||
1159 | a->bytes_alloced); | ||
1160 | __alloc_pstat(s, __a, "Bytes allocated (real): %llu\n", | ||
1161 | a->bytes_alloced_real); | ||
1162 | __alloc_pstat(s, __a, "Bytes freed: %llu\n", | ||
1163 | a->bytes_freed); | ||
1164 | |||
1165 | if (lock) | ||
1166 | alloc_unlock(__a); | ||
1167 | } | ||
1168 | #endif | ||
1169 | |||
1170 | static const struct nvgpu_allocator_ops buddy_ops = { | ||
1171 | .alloc = nvgpu_buddy_balloc, | ||
1172 | .free = nvgpu_buddy_bfree, | ||
1173 | |||
1174 | .alloc_fixed = nvgpu_balloc_fixed_buddy, | ||
1175 | /* .free_fixed not needed. */ | ||
1176 | |||
1177 | .reserve_carveout = nvgpu_buddy_reserve_co, | ||
1178 | .release_carveout = nvgpu_buddy_release_co, | ||
1179 | |||
1180 | .base = nvgpu_buddy_alloc_base, | ||
1181 | .length = nvgpu_buddy_alloc_length, | ||
1182 | .end = nvgpu_buddy_alloc_end, | ||
1183 | .inited = nvgpu_buddy_alloc_inited, | ||
1184 | .space = nvgpu_buddy_alloc_space, | ||
1185 | |||
1186 | .fini = nvgpu_buddy_allocator_destroy, | ||
1187 | |||
1188 | #ifdef __KERNEL__ | ||
1189 | .print_stats = nvgpu_buddy_print_stats, | ||
1190 | #endif | ||
1191 | }; | ||
1192 | |||
1193 | /* | ||
1194 | * Initialize a buddy allocator. Returns 0 on success. This allocator does | ||
1195 | * not necessarily manage bytes. It manages distinct ranges of resources. This | ||
1196 | * allows the allocator to work for things like comp_tags, semaphores, etc. | ||
1197 | * | ||
1198 | * @allocator: Ptr to an allocator struct to init. | ||
1199 | * @vm: GPU VM to associate this allocator with. Can be NULL. Will be used to | ||
1200 | * get PTE size for GVA spaces. | ||
1201 | * @name: Name of the allocator. Doesn't have to be static storage. | ||
1202 | * @base: The base address of the resource pool being managed. | ||
1203 | * @size: Number of resources in the pool. | ||
1204 | * @blk_size: Minimum number of resources to allocate at once. For things like | ||
1205 | * semaphores this is 1. For GVA this might be as much as 64k. This | ||
1206 | * corresponds to order 0. Must be power of 2. | ||
1207 | * @max_order: Pick a maximum order. If you leave this as 0, the buddy allocator | ||
1208 | * will try and pick a reasonable max order. | ||
1209 | * @flags: Extra flags necessary. See GPU_BALLOC_*. | ||
1210 | */ | ||
1211 | int __nvgpu_buddy_allocator_init(struct gk20a *g, struct nvgpu_allocator *__a, | ||
1212 | struct vm_gk20a *vm, const char *name, | ||
1213 | u64 base, u64 size, u64 blk_size, | ||
1214 | u64 max_order, u64 flags) | ||
1215 | { | ||
1216 | int err; | ||
1217 | u64 pde_size; | ||
1218 | struct nvgpu_buddy_allocator *a; | ||
1219 | |||
1220 | /* blk_size must be greater than 0 and a power of 2. */ | ||
1221 | if (blk_size == 0) | ||
1222 | return -EINVAL; | ||
1223 | if (blk_size & (blk_size - 1)) | ||
1224 | return -EINVAL; | ||
1225 | |||
1226 | if (max_order > GPU_BALLOC_MAX_ORDER) | ||
1227 | return -EINVAL; | ||
1228 | |||
1229 | /* If this is to manage a GVA space we need a VM. */ | ||
1230 | if (flags & GPU_ALLOC_GVA_SPACE && !vm) | ||
1231 | return -EINVAL; | ||
1232 | |||
1233 | a = nvgpu_kzalloc(g, sizeof(struct nvgpu_buddy_allocator)); | ||
1234 | if (!a) | ||
1235 | return -ENOMEM; | ||
1236 | |||
1237 | err = __nvgpu_alloc_common_init(__a, g, name, a, false, &buddy_ops); | ||
1238 | if (err) | ||
1239 | goto fail; | ||
1240 | |||
1241 | a->base = base; | ||
1242 | a->length = size; | ||
1243 | a->blk_size = blk_size; | ||
1244 | a->blk_shift = __ffs(blk_size); | ||
1245 | a->owner = __a; | ||
1246 | |||
1247 | /* | ||
1248 | * If base is 0 then modfy base to be the size of one block so that we | ||
1249 | * can return errors by returning addr == 0. | ||
1250 | */ | ||
1251 | if (a->base == 0) { | ||
1252 | a->base = a->blk_size; | ||
1253 | a->length -= a->blk_size; | ||
1254 | } | ||
1255 | |||
1256 | a->vm = vm; | ||
1257 | if (flags & GPU_ALLOC_GVA_SPACE) { | ||
1258 | pde_size = ((u64)vm->big_page_size) << 10; | ||
1259 | a->pte_blk_order = balloc_get_order(a, pde_size); | ||
1260 | } | ||
1261 | |||
1262 | /* | ||
1263 | * When we have a GVA space with big_pages enabled the size and base | ||
1264 | * must be PDE aligned. If big_pages are not enabled then this | ||
1265 | * requirement is not necessary. | ||
1266 | */ | ||
1267 | if (flags & GPU_ALLOC_GVA_SPACE && vm->big_pages && | ||
1268 | (base & ((vm->big_page_size << 10) - 1) || | ||
1269 | size & ((vm->big_page_size << 10) - 1))) | ||
1270 | return -EINVAL; | ||
1271 | |||
1272 | a->flags = flags; | ||
1273 | a->max_order = max_order; | ||
1274 | |||
1275 | balloc_allocator_align(a); | ||
1276 | balloc_compute_max_order(a); | ||
1277 | |||
1278 | a->buddy_cache = nvgpu_kmem_cache_create(g, sizeof(struct nvgpu_buddy)); | ||
1279 | if (!a->buddy_cache) { | ||
1280 | err = -ENOMEM; | ||
1281 | goto fail; | ||
1282 | } | ||
1283 | |||
1284 | a->alloced_buddies = NULL; | ||
1285 | a->fixed_allocs = NULL; | ||
1286 | nvgpu_init_list_node(&a->co_list); | ||
1287 | err = balloc_init_lists(a); | ||
1288 | if (err) | ||
1289 | goto fail; | ||
1290 | |||
1291 | nvgpu_smp_wmb(); | ||
1292 | a->initialized = 1; | ||
1293 | |||
1294 | #ifdef CONFIG_DEBUG_FS | ||
1295 | nvgpu_init_alloc_debug(g, __a); | ||
1296 | #endif | ||
1297 | alloc_dbg(__a, "New allocator: type buddy\n"); | ||
1298 | alloc_dbg(__a, " base 0x%llx\n", a->base); | ||
1299 | alloc_dbg(__a, " size 0x%llx\n", a->length); | ||
1300 | alloc_dbg(__a, " blk_size 0x%llx\n", a->blk_size); | ||
1301 | if (flags & GPU_ALLOC_GVA_SPACE) | ||
1302 | alloc_dbg(balloc_owner(a), | ||
1303 | " pde_size 0x%llx\n", | ||
1304 | balloc_order_to_len(a, a->pte_blk_order)); | ||
1305 | alloc_dbg(__a, " max_order %llu\n", a->max_order); | ||
1306 | alloc_dbg(__a, " flags 0x%llx\n", a->flags); | ||
1307 | |||
1308 | return 0; | ||
1309 | |||
1310 | fail: | ||
1311 | if (a->buddy_cache) | ||
1312 | nvgpu_kmem_cache_destroy(a->buddy_cache); | ||
1313 | nvgpu_kfree(g, a); | ||
1314 | return err; | ||
1315 | } | ||
1316 | |||
1317 | int nvgpu_buddy_allocator_init(struct gk20a *g, struct nvgpu_allocator *a, | ||
1318 | const char *name, u64 base, u64 size, | ||
1319 | u64 blk_size, u64 flags) | ||
1320 | { | ||
1321 | return __nvgpu_buddy_allocator_init(g, a, NULL, name, | ||
1322 | base, size, blk_size, 0, 0); | ||
1323 | } | ||