summaryrefslogtreecommitdiffstats
path: root/drivers/gpu/nvgpu/gk20a/gk20a_allocator.c
diff options
context:
space:
mode:
authorArto Merilainen <amerilainen@nvidia.com>2014-03-19 03:38:25 -0400
committerDan Willemsen <dwillemsen@nvidia.com>2015-03-18 15:08:53 -0400
commita9785995d5f22aaeb659285f8aeb64d8b56982e0 (patch)
treecc75f75bcf43db316a002a7a240b81f299bf6d7f /drivers/gpu/nvgpu/gk20a/gk20a_allocator.c
parent61efaf843c22b85424036ec98015121c08f5f16c (diff)
gpu: nvgpu: Add NVIDIA GPU Driver
This patch moves the NVIDIA GPU driver to a new location. Bug 1482562 Change-Id: I24293810b9d0f1504fd9be00135e21dad656ccb6 Signed-off-by: Arto Merilainen <amerilainen@nvidia.com> Reviewed-on: http://git-master/r/383722 Reviewed-by: Terje Bergstrom <tbergstrom@nvidia.com>
Diffstat (limited to 'drivers/gpu/nvgpu/gk20a/gk20a_allocator.c')
-rw-r--r--drivers/gpu/nvgpu/gk20a/gk20a_allocator.c1247
1 files changed, 1247 insertions, 0 deletions
diff --git a/drivers/gpu/nvgpu/gk20a/gk20a_allocator.c b/drivers/gpu/nvgpu/gk20a/gk20a_allocator.c
new file mode 100644
index 00000000..32c003b6
--- /dev/null
+++ b/drivers/gpu/nvgpu/gk20a/gk20a_allocator.c
@@ -0,0 +1,1247 @@
1/*
2 * gk20a allocator
3 *
4 * Copyright (c) 2011-2014, NVIDIA CORPORATION. All rights reserved.
5 *
6 * This program is free software; you can redistribute it and/or modify it
7 * under the terms and conditions of the GNU General Public License,
8 * version 2, as published by the Free Software Foundation.
9 *
10 * This program is distributed in the hope it will be useful, but WITHOUT
11 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13 * more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#include "gk20a_allocator.h"
20
21static inline void link_block_list(struct gk20a_allocator *allocator,
22 struct gk20a_alloc_block *block,
23 struct gk20a_alloc_block *prev,
24 struct rb_node *rb_parent);
25static inline void link_block_rb(struct gk20a_allocator *allocator,
26 struct gk20a_alloc_block *block,
27 struct rb_node **rb_link,
28 struct rb_node *rb_parent);
29static void link_block(struct gk20a_allocator *allocator,
30 struct gk20a_alloc_block *block,
31 struct gk20a_alloc_block *prev, struct rb_node **rb_link,
32 struct rb_node *rb_parent);
33static void insert_block(struct gk20a_allocator *allocator,
34 struct gk20a_alloc_block *block);
35
36static void unlink_block(struct gk20a_allocator *allocator,
37 struct gk20a_alloc_block *block,
38 struct gk20a_alloc_block *prev);
39static struct gk20a_alloc_block *unlink_blocks(
40 struct gk20a_allocator *allocator,
41 struct gk20a_alloc_block *block,
42 struct gk20a_alloc_block *prev, u32 end);
43
44static struct gk20a_alloc_block *find_block(
45 struct gk20a_allocator *allocator, u32 addr);
46static struct gk20a_alloc_block *find_block_prev(
47 struct gk20a_allocator *allocator, u32 addr,
48 struct gk20a_alloc_block **pprev);
49static struct gk20a_alloc_block *find_block_prepare(
50 struct gk20a_allocator *allocator, u32 addr,
51 struct gk20a_alloc_block **pprev, struct rb_node ***rb_link,
52 struct rb_node **rb_parent);
53
54static u32 check_free_space(u32 addr, u32 limit, u32 len, u32 align);
55static void update_free_addr_cache(struct gk20a_allocator *allocator,
56 struct gk20a_alloc_block *block,
57 u32 addr, u32 len, bool free);
58static int find_free_area(struct gk20a_allocator *allocator,
59 u32 *addr, u32 len);
60static int find_free_area_nc(struct gk20a_allocator *allocator,
61 u32 *addr, u32 *len);
62
63static void adjust_block(struct gk20a_alloc_block *block,
64 u32 start, u32 end,
65 struct gk20a_alloc_block *insert);
66static struct gk20a_alloc_block *merge_block(
67 struct gk20a_allocator *allocator,
68 struct gk20a_alloc_block *block, u32 addr, u32 end);
69static int split_block(struct gk20a_allocator *allocator,
70 struct gk20a_alloc_block *block,
71 u32 addr, int new_below);
72
73static int block_alloc_single_locked(struct gk20a_allocator *allocator,
74 u32 *addr, u32 len);
75static int block_alloc_list_locked(struct gk20a_allocator *allocator,
76 u32 *addr, u32 len,
77 struct gk20a_alloc_block **pblock);
78static int block_free_locked(struct gk20a_allocator *allocator,
79 u32 addr, u32 len);
80static void block_free_list_locked(struct gk20a_allocator *allocator,
81 struct gk20a_alloc_block *list);
82
83/* link a block into allocator block list */
84static inline void link_block_list(struct gk20a_allocator *allocator,
85 struct gk20a_alloc_block *block,
86 struct gk20a_alloc_block *prev,
87 struct rb_node *rb_parent)
88{
89 struct gk20a_alloc_block *next;
90
91 block->prev = prev;
92 if (prev) {
93 next = prev->next;
94 prev->next = block;
95 } else {
96 allocator->block_first = block;
97 if (rb_parent)
98 next = rb_entry(rb_parent,
99 struct gk20a_alloc_block, rb);
100 else
101 next = NULL;
102 }
103 block->next = next;
104 if (next)
105 next->prev = block;
106}
107
108/* link a block into allocator rb tree */
109static inline void link_block_rb(struct gk20a_allocator *allocator,
110 struct gk20a_alloc_block *block, struct rb_node **rb_link,
111 struct rb_node *rb_parent)
112{
113 rb_link_node(&block->rb, rb_parent, rb_link);
114 rb_insert_color(&block->rb, &allocator->rb_root);
115}
116
117/* add a block to allocator with known location */
118static void link_block(struct gk20a_allocator *allocator,
119 struct gk20a_alloc_block *block,
120 struct gk20a_alloc_block *prev, struct rb_node **rb_link,
121 struct rb_node *rb_parent)
122{
123 struct gk20a_alloc_block *next;
124
125 link_block_list(allocator, block, prev, rb_parent);
126 link_block_rb(allocator, block, rb_link, rb_parent);
127 allocator->block_count++;
128
129 next = block->next;
130 allocator_dbg(allocator, "link new block %d:%d between block %d:%d and block %d:%d",
131 block->start, block->end,
132 prev ? prev->start : -1, prev ? prev->end : -1,
133 next ? next->start : -1, next ? next->end : -1);
134}
135
136/* add a block to allocator */
137static void insert_block(struct gk20a_allocator *allocator,
138 struct gk20a_alloc_block *block)
139{
140 struct gk20a_alloc_block *prev;
141 struct rb_node **rb_link, *rb_parent;
142
143 find_block_prepare(allocator, block->start,
144 &prev, &rb_link, &rb_parent);
145 link_block(allocator, block, prev, rb_link, rb_parent);
146}
147
148/* remove a block from allocator */
149static void unlink_block(struct gk20a_allocator *allocator,
150 struct gk20a_alloc_block *block,
151 struct gk20a_alloc_block *prev)
152{
153 struct gk20a_alloc_block *next = block->next;
154
155 allocator_dbg(allocator, "unlink block %d:%d between block %d:%d and block %d:%d",
156 block->start, block->end,
157 prev ? prev->start : -1, prev ? prev->end : -1,
158 next ? next->start : -1, next ? next->end : -1);
159
160 BUG_ON(block->start < allocator->base);
161 BUG_ON(block->end > allocator->limit);
162
163 if (prev)
164 prev->next = next;
165 else
166 allocator->block_first = next;
167
168 if (next)
169 next->prev = prev;
170 rb_erase(&block->rb, &allocator->rb_root);
171 if (allocator->block_recent == block)
172 allocator->block_recent = prev;
173
174 allocator->block_count--;
175}
176
177/* remove a list of blocks from allocator. the list can contain both
178 regular blocks and non-contiguous blocks. skip all non-contiguous
179 blocks, remove regular blocks into a separate list, return list head */
180static struct gk20a_alloc_block *
181unlink_blocks(struct gk20a_allocator *allocator,
182 struct gk20a_alloc_block *block,
183 struct gk20a_alloc_block *prev,
184 u32 end)
185{
186 struct gk20a_alloc_block **insertion_point;
187 struct gk20a_alloc_block *last_unfreed_block = prev;
188 struct gk20a_alloc_block *last_freed_block = NULL;
189 struct gk20a_alloc_block *first_freed_block = NULL;
190
191 insertion_point = (prev ? &prev->next : &allocator->block_first);
192 *insertion_point = NULL;
193
194 do {
195 if (!block->nc_block) {
196 allocator_dbg(allocator, "unlink block %d:%d",
197 block->start, block->end);
198 if (last_freed_block)
199 last_freed_block->next = block;
200 block->prev = last_freed_block;
201 rb_erase(&block->rb, &allocator->rb_root);
202 last_freed_block = block;
203 allocator->block_count--;
204 if (!first_freed_block)
205 first_freed_block = block;
206 } else {
207 allocator_dbg(allocator, "skip nc block %d:%d",
208 block->start, block->end);
209 if (!*insertion_point)
210 *insertion_point = block;
211 if (last_unfreed_block)
212 last_unfreed_block->next = block;
213 block->prev = last_unfreed_block;
214 last_unfreed_block = block;
215 }
216 block = block->next;
217 } while (block && block->start < end);
218
219 if (!*insertion_point)
220 *insertion_point = block;
221
222 if (block)
223 block->prev = last_unfreed_block;
224 if (last_unfreed_block)
225 last_unfreed_block->next = block;
226 if (last_freed_block)
227 last_freed_block->next = NULL;
228
229 allocator->block_recent = NULL;
230
231 return first_freed_block;
232}
233
234/* Look up the first block which satisfies addr < block->end,
235 NULL if none */
236static struct gk20a_alloc_block *
237find_block(struct gk20a_allocator *allocator, u32 addr)
238{
239 struct gk20a_alloc_block *block = allocator->block_recent;
240
241 if (!(block && block->end > addr && block->start <= addr)) {
242 struct rb_node *rb_node;
243
244 rb_node = allocator->rb_root.rb_node;
245 block = NULL;
246
247 while (rb_node) {
248 struct gk20a_alloc_block *block_tmp;
249
250 block_tmp = rb_entry(rb_node,
251 struct gk20a_alloc_block, rb);
252
253 if (block_tmp->end > addr) {
254 block = block_tmp;
255 if (block_tmp->start <= addr)
256 break;
257 rb_node = rb_node->rb_left;
258 } else
259 rb_node = rb_node->rb_right;
260 if (block)
261 allocator->block_recent = block;
262 }
263 }
264 return block;
265}
266
267/* Same as find_block, but also return a pointer to the previous block */
268static struct gk20a_alloc_block *
269find_block_prev(struct gk20a_allocator *allocator, u32 addr,
270 struct gk20a_alloc_block **pprev)
271{
272 struct gk20a_alloc_block *block = NULL, *prev = NULL;
273 struct rb_node *rb_node;
274 if (!allocator)
275 goto out;
276
277 block = allocator->block_first;
278
279 rb_node = allocator->rb_root.rb_node;
280
281 while (rb_node) {
282 struct gk20a_alloc_block *block_tmp;
283 block_tmp = rb_entry(rb_node, struct gk20a_alloc_block, rb);
284
285 if (addr < block_tmp->end)
286 rb_node = rb_node->rb_left;
287 else {
288 prev = block_tmp;
289 if (!prev->next || addr < prev->next->end)
290 break;
291 rb_node = rb_node->rb_right;
292 }
293 }
294
295out:
296 *pprev = prev;
297 return prev ? prev->next : block;
298}
299
300/* Same as find_block, but also return a pointer to the previous block
301 and return rb_node to prepare for rbtree insertion */
302static struct gk20a_alloc_block *
303find_block_prepare(struct gk20a_allocator *allocator, u32 addr,
304 struct gk20a_alloc_block **pprev, struct rb_node ***rb_link,
305 struct rb_node **rb_parent)
306{
307 struct gk20a_alloc_block *block;
308 struct rb_node **__rb_link, *__rb_parent, *rb_prev;
309
310 __rb_link = &allocator->rb_root.rb_node;
311 rb_prev = __rb_parent = NULL;
312 block = NULL;
313
314 while (*__rb_link) {
315 struct gk20a_alloc_block *block_tmp;
316
317 __rb_parent = *__rb_link;
318 block_tmp = rb_entry(__rb_parent,
319 struct gk20a_alloc_block, rb);
320
321 if (block_tmp->end > addr) {
322 block = block_tmp;
323 if (block_tmp->start <= addr)
324 break;
325 __rb_link = &__rb_parent->rb_left;
326 } else {
327 rb_prev = __rb_parent;
328 __rb_link = &__rb_parent->rb_right;
329 }
330 }
331
332 *pprev = NULL;
333 if (rb_prev)
334 *pprev = rb_entry(rb_prev, struct gk20a_alloc_block, rb);
335 *rb_link = __rb_link;
336 *rb_parent = __rb_parent;
337 return block;
338}
339
340/* return available space */
341static u32 check_free_space(u32 addr, u32 limit, u32 len, u32 align)
342{
343 if (addr >= limit)
344 return 0;
345 if (addr + len <= limit)
346 return len;
347 return (limit - addr) & ~(align - 1);
348}
349
350/* update first_free_addr/last_free_addr based on new free addr
351 called when free block(s) and allocate block(s) */
352static void update_free_addr_cache(struct gk20a_allocator *allocator,
353 struct gk20a_alloc_block *next,
354 u32 addr, u32 len, bool free)
355{
356 /* update from block free */
357 if (free) {
358 if (allocator->first_free_addr > addr)
359 allocator->first_free_addr = addr;
360 } else { /* update from block alloc */
361 if (allocator->last_free_addr < addr + len)
362 allocator->last_free_addr = addr + len;
363 if (allocator->first_free_addr == addr) {
364 if (!next || next->start > addr + len)
365 allocator->first_free_addr = addr + len;
366 else
367 allocator->first_free_addr = next->end;
368 }
369 }
370
371 if (allocator->first_free_addr > allocator->last_free_addr)
372 allocator->first_free_addr = allocator->last_free_addr;
373}
374
375/* find a free address range for a fixed len */
376static int find_free_area(struct gk20a_allocator *allocator,
377 u32 *addr, u32 len)
378{
379 struct gk20a_alloc_block *block;
380 u32 start_addr, search_base, search_limit;
381
382 /* fixed addr allocation */
383 /* note: constraints for fixed are handled by caller */
384 if (*addr) {
385 block = find_block(allocator, *addr);
386 if (allocator->limit - len >= *addr &&
387 (!block || *addr + len <= block->start)) {
388 update_free_addr_cache(allocator, block,
389 *addr, len, false);
390 return 0;
391 } else
392 return -ENOMEM;
393 }
394
395 if (!allocator->constraint.enable) {
396 search_base = allocator->base;
397 search_limit = allocator->limit;
398 } else {
399 start_addr = *addr = allocator->constraint.base;
400 search_base = allocator->constraint.base;
401 search_limit = allocator->constraint.limit;
402 }
403
404 /* cached_hole_size has max free space up to last_free_addr */
405 if (len > allocator->cached_hole_size)
406 start_addr = *addr = allocator->last_free_addr;
407 else {
408 start_addr = *addr = allocator->base;
409 allocator->cached_hole_size = 0;
410 }
411
412 allocator_dbg(allocator, "start search addr : %d", start_addr);
413
414full_search:
415 for (block = find_block(allocator, *addr);; block = block->next) {
416 if (search_limit - len < *addr) {
417 /* start a new search in case we missed any hole */
418 if (start_addr != search_base) {
419 start_addr = *addr = search_base;
420 allocator->cached_hole_size = 0;
421 allocator_dbg(allocator, "start a new search from base");
422 goto full_search;
423 }
424 return -ENOMEM;
425 }
426 if (!block || *addr + len <= block->start) {
427 update_free_addr_cache(allocator, block,
428 *addr, len, false);
429 allocator_dbg(allocator, "free space from %d, len %d",
430 *addr, len);
431 allocator_dbg(allocator, "next free addr: %d",
432 allocator->last_free_addr);
433 return 0;
434 }
435 if (*addr + allocator->cached_hole_size < block->start)
436 allocator->cached_hole_size = block->start - *addr;
437 *addr = block->end;
438 }
439}
440
441/* find a free address range for as long as it meets alignment or meet len */
442static int find_free_area_nc(struct gk20a_allocator *allocator,
443 u32 *addr, u32 *len)
444{
445 struct gk20a_alloc_block *block;
446 u32 start_addr;
447 u32 avail_len;
448
449 /* fixed addr allocation */
450 if (*addr) {
451 block = find_block(allocator, *addr);
452 if (allocator->limit - *len >= *addr) {
453 if (!block)
454 return 0;
455
456 avail_len = check_free_space(*addr, block->start,
457 *len, allocator->align);
458 if (avail_len != 0) {
459 update_free_addr_cache(allocator, block,
460 *addr, avail_len, false);
461 allocator_dbg(allocator,
462 "free space between %d, %d, len %d",
463 *addr, block->start, avail_len);
464 allocator_dbg(allocator, "next free addr: %d",
465 allocator->last_free_addr);
466 *len = avail_len;
467 return 0;
468 } else
469 return -ENOMEM;
470 } else
471 return -ENOMEM;
472 }
473
474 start_addr = *addr = allocator->first_free_addr;
475
476 allocator_dbg(allocator, "start search addr : %d", start_addr);
477
478 for (block = find_block(allocator, *addr);; block = block->next) {
479 if (allocator->limit - *len < *addr)
480 return -ENOMEM;
481 if (!block) {
482 update_free_addr_cache(allocator, block,
483 *addr, *len, false);
484 allocator_dbg(allocator, "free space from %d, len %d",
485 *addr, *len);
486 allocator_dbg(allocator, "next free addr: %d",
487 allocator->first_free_addr);
488 return 0;
489 }
490
491 avail_len = check_free_space(*addr, block->start,
492 *len, allocator->align);
493 if (avail_len != 0) {
494 update_free_addr_cache(allocator, block,
495 *addr, avail_len, false);
496 allocator_dbg(allocator, "free space between %d, %d, len %d",
497 *addr, block->start, avail_len);
498 allocator_dbg(allocator, "next free addr: %d",
499 allocator->first_free_addr);
500 *len = avail_len;
501 return 0;
502 }
503 if (*addr + allocator->cached_hole_size < block->start)
504 allocator->cached_hole_size = block->start - *addr;
505 *addr = block->end;
506 }
507}
508
509/* expand/shrink a block with new start and new end
510 split_block function provides insert block for shrink */
511static void adjust_block(struct gk20a_alloc_block *block,
512 u32 start, u32 end, struct gk20a_alloc_block *insert)
513{
514 struct gk20a_allocator *allocator = block->allocator;
515
516 allocator_dbg(allocator, "curr block %d:%d, new start %d, new end %d",
517 block->start, block->end, start, end);
518
519 /* expand */
520 if (!insert) {
521 if (start == block->end) {
522 struct gk20a_alloc_block *next = block->next;
523
524 if (next && end == next->start) {
525 /* ....AAAA.... */
526 /* PPPP....NNNN */
527 /* PPPPPPPPPPPP */
528 unlink_block(allocator, next, block);
529 block->end = next->end;
530 kmem_cache_free(allocator->block_cache, next);
531 } else {
532 /* ....AAAA.... */
533 /* PPPP........ */
534 /* PPPPPPPP.... */
535 block->end = end;
536 }
537 }
538
539 if (end == block->start) {
540 /* ....AAAA.... */
541 /* ........NNNN */
542 /* PP..NNNNNNNN ....NNNNNNNN */
543 block->start = start;
544 }
545 } else { /* shrink */
546 /* BBBBBBBB -> BBBBIIII OR BBBBBBBB -> IIIIBBBB */
547 block->start = start;
548 block->end = end;
549 insert_block(allocator, insert);
550 }
551}
552
553/* given a range [addr, end], merge it with blocks before or after or both
554 if they can be combined into a contiguous block */
555static struct gk20a_alloc_block *
556merge_block(struct gk20a_allocator *allocator,
557 struct gk20a_alloc_block *prev, u32 addr, u32 end)
558{
559 struct gk20a_alloc_block *next;
560
561 if (prev)
562 next = prev->next;
563 else
564 next = allocator->block_first;
565
566 allocator_dbg(allocator, "curr block %d:%d", addr, end);
567 if (prev)
568 allocator_dbg(allocator, "prev block %d:%d",
569 prev->start, prev->end);
570 if (next)
571 allocator_dbg(allocator, "next block %d:%d",
572 next->start, next->end);
573
574 /* don't merge with non-contiguous allocation block */
575 if (prev && prev->end == addr && !prev->nc_block) {
576 adjust_block(prev, addr, end, NULL);
577 return prev;
578 }
579
580 /* don't merge with non-contiguous allocation block */
581 if (next && end == next->start && !next->nc_block) {
582 adjust_block(next, addr, end, NULL);
583 return next;
584 }
585
586 return NULL;
587}
588
589/* split a block based on addr. addr must be within (start, end).
590 if new_below == 1, link new block before adjusted current block */
591static int split_block(struct gk20a_allocator *allocator,
592 struct gk20a_alloc_block *block, u32 addr, int new_below)
593{
594 struct gk20a_alloc_block *new_block;
595
596 allocator_dbg(allocator, "start %d, split %d, end %d, new_below %d",
597 block->start, addr, block->end, new_below);
598
599 BUG_ON(!(addr > block->start && addr < block->end));
600
601 new_block = kmem_cache_alloc(allocator->block_cache, GFP_KERNEL);
602 if (!new_block)
603 return -ENOMEM;
604
605 *new_block = *block;
606
607 if (new_below)
608 new_block->end = addr;
609 else
610 new_block->start = addr;
611
612 if (new_below)
613 adjust_block(block, addr, block->end, new_block);
614 else
615 adjust_block(block, block->start, addr, new_block);
616
617 return 0;
618}
619
620/* free a list of blocks */
621static void free_blocks(struct gk20a_allocator *allocator,
622 struct gk20a_alloc_block *block)
623{
624 struct gk20a_alloc_block *curr_block;
625 while (block) {
626 curr_block = block;
627 block = block->next;
628 kmem_cache_free(allocator->block_cache, curr_block);
629 }
630}
631
632/* called with rw_sema acquired */
633static int block_alloc_single_locked(struct gk20a_allocator *allocator,
634 u32 *addr_req, u32 len)
635{
636 struct gk20a_alloc_block *block, *prev;
637 struct rb_node **rb_link, *rb_parent;
638 u32 addr = *addr_req;
639 int err;
640
641 *addr_req = ~0;
642
643 err = find_free_area(allocator, &addr, len);
644 if (err)
645 return err;
646
647 find_block_prepare(allocator, addr, &prev, &rb_link, &rb_parent);
648
649 /* merge requested free space with existing block(s)
650 if they can be combined into one contiguous block */
651 block = merge_block(allocator, prev, addr, addr + len);
652 if (block) {
653 *addr_req = addr;
654 return 0;
655 }
656
657 /* create a new block if cannot merge */
658 block = kmem_cache_zalloc(allocator->block_cache, GFP_KERNEL);
659 if (!block)
660 return -ENOMEM;
661
662 block->allocator = allocator;
663 block->start = addr;
664 block->end = addr + len;
665
666 link_block(allocator, block, prev, rb_link, rb_parent);
667
668 *addr_req = addr;
669
670 return 0;
671}
672
673static int block_alloc_list_locked(struct gk20a_allocator *allocator,
674 u32 *addr_req, u32 nc_len, struct gk20a_alloc_block **pblock)
675{
676 struct gk20a_alloc_block *block;
677 struct gk20a_alloc_block *nc_head = NULL, *nc_prev = NULL;
678 u32 addr = *addr_req, len = nc_len;
679 int err = 0;
680
681 *addr_req = ~0;
682
683 while (nc_len > 0) {
684 err = find_free_area_nc(allocator, &addr, &len);
685 if (err) {
686 allocator_dbg(allocator, "not enough free space");
687 goto clean_up;
688 }
689
690 /* never merge non-contiguous allocation block,
691 just create a new block */
692 block = kmem_cache_zalloc(allocator->block_cache,
693 GFP_KERNEL);
694 if (!block) {
695 err = -ENOMEM;
696 goto clean_up;
697 }
698
699 block->allocator = allocator;
700 block->start = addr;
701 block->end = addr + len;
702
703 insert_block(allocator, block);
704
705 block->nc_prev = nc_prev;
706 if (nc_prev)
707 nc_prev->nc_next = block;
708 nc_prev = block;
709 block->nc_block = true;
710
711 if (!nc_head)
712 nc_head = block;
713
714 if (*addr_req == ~0)
715 *addr_req = addr;
716
717 addr = 0;
718 nc_len -= len;
719 len = nc_len;
720 allocator_dbg(allocator, "remaining length %d", nc_len);
721 }
722
723clean_up:
724 if (err) {
725 while (nc_head) {
726 unlink_block(allocator, nc_head, nc_head->prev);
727 nc_prev = nc_head;
728 nc_head = nc_head->nc_next;
729 kmem_cache_free(allocator->block_cache, nc_prev);
730 }
731 *pblock = NULL;
732 *addr_req = ~0;
733 } else {
734 *pblock = nc_head;
735 }
736
737 return err;
738}
739
740/* called with rw_sema acquired */
741static int block_free_locked(struct gk20a_allocator *allocator,
742 u32 addr, u32 len)
743{
744 struct gk20a_alloc_block *block, *prev, *last;
745 u32 end;
746 int err;
747
748 /* no block has block->end > addr, already free */
749 block = find_block_prev(allocator, addr, &prev);
750 if (!block)
751 return 0;
752
753 allocator_dbg(allocator, "first block in free range %d:%d",
754 block->start, block->end);
755
756 end = addr + len;
757 /* not in any block, already free */
758 if (block->start >= end)
759 return 0;
760
761 /* don't touch nc_block in range free */
762 if (addr > block->start && !block->nc_block) {
763 int err = split_block(allocator, block, addr, 0);
764 if (err)
765 return err;
766 prev = block;
767 }
768
769 last = find_block(allocator, end);
770 if (last && end > last->start && !last->nc_block) {
771
772 allocator_dbg(allocator, "last block in free range %d:%d",
773 last->start, last->end);
774
775 err = split_block(allocator, last, end, 1);
776 if (err)
777 return err;
778 }
779
780 block = prev ? prev->next : allocator->block_first;
781
782 allocator_dbg(allocator, "first block for free %d:%d",
783 block->start, block->end);
784
785 /* remove blocks between [addr, addr + len) from rb tree
786 and put them in a list */
787 block = unlink_blocks(allocator, block, prev, end);
788 free_blocks(allocator, block);
789
790 update_free_addr_cache(allocator, NULL, addr, len, true);
791
792 return 0;
793}
794
795/* called with rw_sema acquired */
796static void block_free_list_locked(struct gk20a_allocator *allocator,
797 struct gk20a_alloc_block *list)
798{
799 struct gk20a_alloc_block *block;
800 u32 len;
801
802 update_free_addr_cache(allocator, NULL,
803 list->start, list->end - list->start, true);
804
805 while (list) {
806 block = list;
807 unlink_block(allocator, block, block->prev);
808
809 len = block->end - block->start;
810 if (allocator->cached_hole_size < len)
811 allocator->cached_hole_size = len;
812
813 list = block->nc_next;
814 kmem_cache_free(allocator->block_cache, block);
815 }
816}
817
818static int
819gk20a_allocator_constrain(struct gk20a_allocator *a,
820 bool enable, u32 base, u32 limit)
821{
822 if (enable) {
823 a->constraint.enable = (base >= a->base &&
824 limit <= a->limit);
825 if (!a->constraint.enable)
826 return -EINVAL;
827 a->constraint.base = base;
828 a->constraint.limit = limit;
829 a->first_free_addr = a->last_free_addr = base;
830
831 } else {
832 a->constraint.enable = false;
833 a->first_free_addr = a->last_free_addr = a->base;
834 }
835
836 a->cached_hole_size = 0;
837
838 return 0;
839}
840
841/* init allocator struct */
842int gk20a_allocator_init(struct gk20a_allocator *allocator,
843 const char *name, u32 start, u32 len, u32 align)
844{
845 memset(allocator, 0, sizeof(struct gk20a_allocator));
846
847 strncpy(allocator->name, name, 32);
848
849 allocator->block_cache =
850 kmem_cache_create(allocator->name,
851 sizeof(struct gk20a_alloc_block), 0,
852 SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD, NULL);
853 if (!allocator->block_cache)
854 return -ENOMEM;
855
856 allocator->rb_root = RB_ROOT;
857
858 allocator->base = start;
859 allocator->limit = start + len - 1;
860 allocator->align = align;
861
862 allocator_dbg(allocator, "%s : base %d, limit %d, align %d",
863 allocator->name, allocator->base,
864 allocator->limit, allocator->align);
865
866 allocator->first_free_addr = allocator->last_free_addr = start;
867 allocator->cached_hole_size = len;
868
869 init_rwsem(&allocator->rw_sema);
870
871 allocator->alloc = gk20a_allocator_block_alloc;
872 allocator->alloc_nc = gk20a_allocator_block_alloc_nc;
873 allocator->free = gk20a_allocator_block_free;
874 allocator->free_nc = gk20a_allocator_block_free_nc;
875 allocator->constrain = gk20a_allocator_constrain;
876
877 return 0;
878}
879
880/* destroy allocator, free all remaining blocks if any */
881void gk20a_allocator_destroy(struct gk20a_allocator *allocator)
882{
883 struct gk20a_alloc_block *block, *next;
884 u32 free_count = 0;
885
886 down_write(&allocator->rw_sema);
887
888 for (block = allocator->block_first; block; ) {
889 allocator_dbg(allocator, "free remaining block %d:%d",
890 block->start, block->end);
891 next = block->next;
892 kmem_cache_free(allocator->block_cache, block);
893 free_count++;
894 block = next;
895 }
896
897 up_write(&allocator->rw_sema);
898
899 /* block_count doesn't match real number of blocks */
900 BUG_ON(free_count != allocator->block_count);
901
902 kmem_cache_destroy(allocator->block_cache);
903
904 memset(allocator, 0, sizeof(struct gk20a_allocator));
905}
906
907/*
908 * *addr != ~0 for fixed address allocation. if *addr == 0, base addr is
909 * returned to caller in *addr.
910 *
911 * contiguous allocation, which allocates one block of
912 * contiguous address.
913*/
914int gk20a_allocator_block_alloc(struct gk20a_allocator *allocator,
915 u32 *addr, u32 len)
916{
917 int ret;
918#if defined(ALLOCATOR_DEBUG)
919 struct gk20a_alloc_block *block;
920 bool should_fail = false;
921#endif
922
923 allocator_dbg(allocator, "[in] addr %d, len %d", *addr, len);
924
925 if (*addr + len > allocator->limit || /* check addr range */
926 *addr & (allocator->align - 1) || /* check addr alignment */
927 len == 0) /* check len */
928 return -EINVAL;
929
930 if (allocator->constraint.enable &&
931 (*addr + len > allocator->constraint.limit ||
932 *addr > allocator->constraint.base))
933 return -EINVAL;
934
935 len = ALIGN(len, allocator->align);
936 if (!len)
937 return -ENOMEM;
938
939 down_write(&allocator->rw_sema);
940
941#if defined(ALLOCATOR_DEBUG)
942 if (*addr) {
943 for (block = allocator->block_first;
944 block; block = block->next) {
945 if (block->end > *addr && block->start < *addr + len) {
946 should_fail = true;
947 break;
948 }
949 }
950 }
951#endif
952
953 ret = block_alloc_single_locked(allocator, addr, len);
954
955#if defined(ALLOCATOR_DEBUG)
956 if (!ret) {
957 bool allocated = false;
958 BUG_ON(should_fail);
959 BUG_ON(*addr < allocator->base);
960 BUG_ON(*addr + len > allocator->limit);
961 for (block = allocator->block_first;
962 block; block = block->next) {
963 if (!block->nc_block &&
964 block->start <= *addr &&
965 block->end >= *addr + len) {
966 allocated = true;
967 break;
968 }
969 }
970 BUG_ON(!allocated);
971 }
972#endif
973
974 up_write(&allocator->rw_sema);
975
976 allocator_dbg(allocator, "[out] addr %d, len %d", *addr, len);
977
978 return ret;
979}
980
981/*
982 * *addr != ~0 for fixed address allocation. if *addr == 0, base addr is
983 * returned to caller in *addr.
984 *
985 * non-contiguous allocation, which returns a list of blocks with aggregated
986 * size == len. Individual block size must meet alignment requirement.
987 */
988int gk20a_allocator_block_alloc_nc(struct gk20a_allocator *allocator,
989 u32 *addr, u32 len, struct gk20a_alloc_block **pblock)
990{
991 int ret;
992
993 allocator_dbg(allocator, "[in] addr %d, len %d", *addr, len);
994
995 BUG_ON(pblock == NULL);
996 *pblock = NULL;
997
998 if (*addr + len > allocator->limit || /* check addr range */
999 *addr & (allocator->align - 1) || /* check addr alignment */
1000 len == 0) /* check len */
1001 return -EINVAL;
1002
1003 len = ALIGN(len, allocator->align);
1004 if (!len)
1005 return -ENOMEM;
1006
1007 down_write(&allocator->rw_sema);
1008
1009 ret = block_alloc_list_locked(allocator, addr, len, pblock);
1010
1011#if defined(ALLOCATOR_DEBUG)
1012 if (!ret) {
1013 struct gk20a_alloc_block *block = *pblock;
1014 BUG_ON(!block);
1015 BUG_ON(block->start < allocator->base);
1016 while (block->nc_next) {
1017 BUG_ON(block->end > block->nc_next->start);
1018 block = block->nc_next;
1019 }
1020 BUG_ON(block->end > allocator->limit);
1021 }
1022#endif
1023
1024 up_write(&allocator->rw_sema);
1025
1026 allocator_dbg(allocator, "[out] addr %d, len %d", *addr, len);
1027
1028 return ret;
1029}
1030
1031/* free all blocks between start and end */
1032int gk20a_allocator_block_free(struct gk20a_allocator *allocator,
1033 u32 addr, u32 len)
1034{
1035 int ret;
1036
1037 allocator_dbg(allocator, "[in] addr %d, len %d", addr, len);
1038
1039 if (addr + len > allocator->limit || /* check addr range */
1040 addr < allocator->base ||
1041 addr & (allocator->align - 1)) /* check addr alignment */
1042 return -EINVAL;
1043
1044 len = ALIGN(len, allocator->align);
1045 if (!len)
1046 return -EINVAL;
1047
1048 down_write(&allocator->rw_sema);
1049
1050 ret = block_free_locked(allocator, addr, len);
1051
1052#if defined(ALLOCATOR_DEBUG)
1053 if (!ret) {
1054 struct gk20a_alloc_block *block;
1055 for (block = allocator->block_first;
1056 block; block = block->next) {
1057 if (!block->nc_block)
1058 BUG_ON(block->start >= addr &&
1059 block->end <= addr + len);
1060 }
1061 }
1062#endif
1063 up_write(&allocator->rw_sema);
1064
1065 allocator_dbg(allocator, "[out] addr %d, len %d", addr, len);
1066
1067 return ret;
1068}
1069
1070/* free non-contiguous allocation block list */
1071void gk20a_allocator_block_free_nc(struct gk20a_allocator *allocator,
1072 struct gk20a_alloc_block *block)
1073{
1074 /* nothing to free */
1075 if (!block)
1076 return;
1077
1078 down_write(&allocator->rw_sema);
1079 block_free_list_locked(allocator, block);
1080 up_write(&allocator->rw_sema);
1081}
1082
1083#if defined(ALLOCATOR_DEBUG)
1084
1085#include <linux/random.h>
1086
1087/* test suite */
1088void gk20a_allocator_test(void)
1089{
1090 struct gk20a_allocator allocator;
1091 struct gk20a_alloc_block *list[5];
1092 u32 addr, len;
1093 u32 count;
1094 int n;
1095
1096 gk20a_allocator_init(&allocator, "test", 0, 10, 1);
1097
1098 /* alloc/free a single block in the beginning */
1099 addr = 0;
1100 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1101 gk20a_allocator_dump(&allocator);
1102 gk20a_allocator_block_free(&allocator, addr, 2);
1103 gk20a_allocator_dump(&allocator);
1104 /* alloc/free a single block in the middle */
1105 addr = 4;
1106 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1107 gk20a_allocator_dump(&allocator);
1108 gk20a_allocator_block_free(&allocator, addr, 2);
1109 gk20a_allocator_dump(&allocator);
1110 /* alloc/free a single block in the end */
1111 addr = 8;
1112 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1113 gk20a_allocator_dump(&allocator);
1114 gk20a_allocator_block_free(&allocator, addr, 2);
1115 gk20a_allocator_dump(&allocator);
1116
1117 /* allocate contiguous blocks */
1118 addr = 0;
1119 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1120 gk20a_allocator_dump(&allocator);
1121 addr = 0;
1122 gk20a_allocator_block_alloc(&allocator, &addr, 4);
1123 gk20a_allocator_dump(&allocator);
1124 addr = 0;
1125 gk20a_allocator_block_alloc(&allocator, &addr, 4);
1126 gk20a_allocator_dump(&allocator);
1127
1128 /* no free space */
1129 addr = 0;
1130 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1131 gk20a_allocator_dump(&allocator);
1132
1133 /* free in the end */
1134 gk20a_allocator_block_free(&allocator, 8, 2);
1135 gk20a_allocator_dump(&allocator);
1136 /* free in the beginning */
1137 gk20a_allocator_block_free(&allocator, 0, 2);
1138 gk20a_allocator_dump(&allocator);
1139 /* free in the middle */
1140 gk20a_allocator_block_free(&allocator, 4, 2);
1141 gk20a_allocator_dump(&allocator);
1142
1143 /* merge case PPPPAAAANNNN */
1144 addr = 4;
1145 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1146 gk20a_allocator_dump(&allocator);
1147 /* merge case ....AAAANNNN */
1148 addr = 0;
1149 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1150 gk20a_allocator_dump(&allocator);
1151 /* merge case PPPPAAAA.... */
1152 addr = 8;
1153 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1154 gk20a_allocator_dump(&allocator);
1155
1156 /* test free across multiple blocks and split */
1157 gk20a_allocator_block_free(&allocator, 2, 2);
1158 gk20a_allocator_dump(&allocator);
1159 gk20a_allocator_block_free(&allocator, 6, 2);
1160 gk20a_allocator_dump(&allocator);
1161 gk20a_allocator_block_free(&allocator, 1, 8);
1162 gk20a_allocator_dump(&allocator);
1163
1164 /* test non-contiguous allocation */
1165 addr = 4;
1166 gk20a_allocator_block_alloc(&allocator, &addr, 2);
1167 gk20a_allocator_dump(&allocator);
1168 addr = 0;
1169 gk20a_allocator_block_alloc_nc(&allocator, &addr, 5, &list[0]);
1170 gk20a_allocator_dump(&allocator);
1171 gk20a_allocator_dump_nc_list(&allocator, list[0]);
1172
1173 /* test free a range overlaping non-contiguous blocks */
1174 gk20a_allocator_block_free(&allocator, 2, 6);
1175 gk20a_allocator_dump(&allocator);
1176
1177 /* test non-contiguous free */
1178 gk20a_allocator_block_free_nc(&allocator, list[0]);
1179 gk20a_allocator_dump(&allocator);
1180
1181 gk20a_allocator_destroy(&allocator);
1182
1183 /* random stress test */
1184 gk20a_allocator_init(&allocator, "test", 4096, 4096 * 1024, 4096);
1185 for (;;) {
1186 pr_debug("alloc tests...\n");
1187 for (count = 0; count < 50; count++) {
1188 addr = 0;
1189 len = random32() % (4096 * 1024 / 16);
1190 gk20a_allocator_block_alloc(&allocator, &addr, len);
1191 gk20a_allocator_dump(&allocator);
1192 }
1193
1194 pr_debug("free tests...\n");
1195 for (count = 0; count < 30; count++) {
1196 addr = (random32() % (4096 * 1024)) & ~(4096 - 1);
1197 len = random32() % (4096 * 1024 / 16);
1198 gk20a_allocator_block_free(&allocator, addr, len);
1199 gk20a_allocator_dump(&allocator);
1200 }
1201
1202 pr_debug("non-contiguous alloc tests...\n");
1203 for (n = 0; n < 5; n++) {
1204 addr = 0;
1205 len = random32() % (4096 * 1024 / 8);
1206 gk20a_allocator_block_alloc_nc(&allocator, &addr,
1207 len, &list[n]);
1208 gk20a_allocator_dump(&allocator);
1209 gk20a_allocator_dump_nc_list(&allocator, list[n]);
1210 }
1211
1212 pr_debug("free tests...\n");
1213 for (count = 0; count < 10; count++) {
1214 addr = (random32() % (4096 * 1024)) & ~(4096 - 1);
1215 len = random32() % (4096 * 1024 / 16);
1216 gk20a_allocator_block_free(&allocator, addr, len);
1217 gk20a_allocator_dump(&allocator);
1218 }
1219
1220 pr_debug("non-contiguous free tests...\n");
1221 for (n = 4; n >= 0; n--) {
1222 gk20a_allocator_dump_nc_list(&allocator, list[n]);
1223 gk20a_allocator_block_free_nc(&allocator, list[n]);
1224 gk20a_allocator_dump(&allocator);
1225 }
1226
1227 pr_debug("fixed addr alloc tests...\n");
1228 for (count = 0; count < 10; count++) {
1229 addr = (random32() % (4096 * 1024)) & ~(4096 - 1);
1230 len = random32() % (4096 * 1024 / 32);
1231 gk20a_allocator_block_alloc(&allocator, &addr, len);
1232 gk20a_allocator_dump(&allocator);
1233 }
1234
1235 pr_debug("free tests...\n");
1236 for (count = 0; count < 10; count++) {
1237 addr = (random32() % (4096 * 1024)) & ~(4096 - 1);
1238 len = random32() % (4096 * 1024 / 16);
1239 gk20a_allocator_block_free(&allocator, addr, len);
1240 gk20a_allocator_dump(&allocator);
1241 }
1242 }
1243 gk20a_allocator_destroy(&allocator);
1244}
1245
1246#endif /* ALLOCATOR_DEBUG */
1247