Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
1// SPDX-License-Identifier: MIT
2/*
3 * Copyright © 2019 Intel Corporation
4 */
5
6#define pr_fmt(fmt) "drm_buddy: " fmt
7
8#include <linux/module.h>
9#include <linux/prime_numbers.h>
10#include <linux/sched/signal.h>
11
12#include <drm/drm_buddy.h>
13
14#include "../lib/drm_random.h"
15
16#define TESTS "drm_buddy_selftests.h"
17#include "drm_selftest.h"
18
19#define IGT_TIMEOUT(name__) \
20 unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT
21
22static unsigned int random_seed;
23
24static inline u64 get_size(int order, u64 chunk_size)
25{
26 return (1 << order) * chunk_size;
27}
28
29__printf(2, 3)
30static bool __igt_timeout(unsigned long timeout, const char *fmt, ...)
31{
32 va_list va;
33
34 if (!signal_pending(current)) {
35 cond_resched();
36 if (time_before(jiffies, timeout))
37 return false;
38 }
39
40 if (fmt) {
41 va_start(va, fmt);
42 vprintk(fmt, va);
43 va_end(va);
44 }
45
46 return true;
47}
48
49static inline const char *yesno(bool v)
50{
51 return v ? "yes" : "no";
52}
53
54static void __igt_dump_block(struct drm_buddy *mm,
55 struct drm_buddy_block *block,
56 bool buddy)
57{
58 pr_err("block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%s buddy=%s\n",
59 block->header,
60 drm_buddy_block_state(block),
61 drm_buddy_block_order(block),
62 drm_buddy_block_offset(block),
63 drm_buddy_block_size(mm, block),
64 yesno(!block->parent),
65 yesno(buddy));
66}
67
68static void igt_dump_block(struct drm_buddy *mm,
69 struct drm_buddy_block *block)
70{
71 struct drm_buddy_block *buddy;
72
73 __igt_dump_block(mm, block, false);
74
75 buddy = drm_get_buddy(block);
76 if (buddy)
77 __igt_dump_block(mm, buddy, true);
78}
79
80static int igt_check_block(struct drm_buddy *mm,
81 struct drm_buddy_block *block)
82{
83 struct drm_buddy_block *buddy;
84 unsigned int block_state;
85 u64 block_size;
86 u64 offset;
87 int err = 0;
88
89 block_state = drm_buddy_block_state(block);
90
91 if (block_state != DRM_BUDDY_ALLOCATED &&
92 block_state != DRM_BUDDY_FREE &&
93 block_state != DRM_BUDDY_SPLIT) {
94 pr_err("block state mismatch\n");
95 err = -EINVAL;
96 }
97
98 block_size = drm_buddy_block_size(mm, block);
99 offset = drm_buddy_block_offset(block);
100
101 if (block_size < mm->chunk_size) {
102 pr_err("block size smaller than min size\n");
103 err = -EINVAL;
104 }
105
106 if (!is_power_of_2(block_size)) {
107 pr_err("block size not power of two\n");
108 err = -EINVAL;
109 }
110
111 if (!IS_ALIGNED(block_size, mm->chunk_size)) {
112 pr_err("block size not aligned to min size\n");
113 err = -EINVAL;
114 }
115
116 if (!IS_ALIGNED(offset, mm->chunk_size)) {
117 pr_err("block offset not aligned to min size\n");
118 err = -EINVAL;
119 }
120
121 if (!IS_ALIGNED(offset, block_size)) {
122 pr_err("block offset not aligned to block size\n");
123 err = -EINVAL;
124 }
125
126 buddy = drm_get_buddy(block);
127
128 if (!buddy && block->parent) {
129 pr_err("buddy has gone fishing\n");
130 err = -EINVAL;
131 }
132
133 if (buddy) {
134 if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) {
135 pr_err("buddy has wrong offset\n");
136 err = -EINVAL;
137 }
138
139 if (drm_buddy_block_size(mm, buddy) != block_size) {
140 pr_err("buddy size mismatch\n");
141 err = -EINVAL;
142 }
143
144 if (drm_buddy_block_state(buddy) == block_state &&
145 block_state == DRM_BUDDY_FREE) {
146 pr_err("block and its buddy are free\n");
147 err = -EINVAL;
148 }
149 }
150
151 return err;
152}
153
154static int igt_check_blocks(struct drm_buddy *mm,
155 struct list_head *blocks,
156 u64 expected_size,
157 bool is_contiguous)
158{
159 struct drm_buddy_block *block;
160 struct drm_buddy_block *prev;
161 u64 total;
162 int err = 0;
163
164 block = NULL;
165 prev = NULL;
166 total = 0;
167
168 list_for_each_entry(block, blocks, link) {
169 err = igt_check_block(mm, block);
170
171 if (!drm_buddy_block_is_allocated(block)) {
172 pr_err("block not allocated\n"),
173 err = -EINVAL;
174 }
175
176 if (is_contiguous && prev) {
177 u64 prev_block_size;
178 u64 prev_offset;
179 u64 offset;
180
181 prev_offset = drm_buddy_block_offset(prev);
182 prev_block_size = drm_buddy_block_size(mm, prev);
183 offset = drm_buddy_block_offset(block);
184
185 if (offset != (prev_offset + prev_block_size)) {
186 pr_err("block offset mismatch\n");
187 err = -EINVAL;
188 }
189 }
190
191 if (err)
192 break;
193
194 total += drm_buddy_block_size(mm, block);
195 prev = block;
196 }
197
198 if (!err) {
199 if (total != expected_size) {
200 pr_err("size mismatch, expected=%llx, found=%llx\n",
201 expected_size, total);
202 err = -EINVAL;
203 }
204 return err;
205 }
206
207 if (prev) {
208 pr_err("prev block, dump:\n");
209 igt_dump_block(mm, prev);
210 }
211
212 pr_err("bad block, dump:\n");
213 igt_dump_block(mm, block);
214
215 return err;
216}
217
218static int igt_check_mm(struct drm_buddy *mm)
219{
220 struct drm_buddy_block *root;
221 struct drm_buddy_block *prev;
222 unsigned int i;
223 u64 total;
224 int err = 0;
225
226 if (!mm->n_roots) {
227 pr_err("n_roots is zero\n");
228 return -EINVAL;
229 }
230
231 if (mm->n_roots != hweight64(mm->size)) {
232 pr_err("n_roots mismatch, n_roots=%u, expected=%lu\n",
233 mm->n_roots, hweight64(mm->size));
234 return -EINVAL;
235 }
236
237 root = NULL;
238 prev = NULL;
239 total = 0;
240
241 for (i = 0; i < mm->n_roots; ++i) {
242 struct drm_buddy_block *block;
243 unsigned int order;
244
245 root = mm->roots[i];
246 if (!root) {
247 pr_err("root(%u) is NULL\n", i);
248 err = -EINVAL;
249 break;
250 }
251
252 err = igt_check_block(mm, root);
253
254 if (!drm_buddy_block_is_free(root)) {
255 pr_err("root not free\n");
256 err = -EINVAL;
257 }
258
259 order = drm_buddy_block_order(root);
260
261 if (!i) {
262 if (order != mm->max_order) {
263 pr_err("max order root missing\n");
264 err = -EINVAL;
265 }
266 }
267
268 if (prev) {
269 u64 prev_block_size;
270 u64 prev_offset;
271 u64 offset;
272
273 prev_offset = drm_buddy_block_offset(prev);
274 prev_block_size = drm_buddy_block_size(mm, prev);
275 offset = drm_buddy_block_offset(root);
276
277 if (offset != (prev_offset + prev_block_size)) {
278 pr_err("root offset mismatch\n");
279 err = -EINVAL;
280 }
281 }
282
283 block = list_first_entry_or_null(&mm->free_list[order],
284 struct drm_buddy_block,
285 link);
286 if (block != root) {
287 pr_err("root mismatch at order=%u\n", order);
288 err = -EINVAL;
289 }
290
291 if (err)
292 break;
293
294 prev = root;
295 total += drm_buddy_block_size(mm, root);
296 }
297
298 if (!err) {
299 if (total != mm->size) {
300 pr_err("expected mm size=%llx, found=%llx\n", mm->size,
301 total);
302 err = -EINVAL;
303 }
304 return err;
305 }
306
307 if (prev) {
308 pr_err("prev root(%u), dump:\n", i - 1);
309 igt_dump_block(mm, prev);
310 }
311
312 if (root) {
313 pr_err("bad root(%u), dump:\n", i);
314 igt_dump_block(mm, root);
315 }
316
317 return err;
318}
319
320static void igt_mm_config(u64 *size, u64 *chunk_size)
321{
322 DRM_RND_STATE(prng, random_seed);
323 u32 s, ms;
324
325 /* Nothing fancy, just try to get an interesting bit pattern */
326
327 prandom_seed_state(&prng, random_seed);
328
329 /* Let size be a random number of pages up to 8 GB (2M pages) */
330 s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng);
331 /* Let the chunk size be a random power of 2 less than size */
332 ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng));
333 /* Round size down to the chunk size */
334 s &= -ms;
335
336 /* Convert from pages to bytes */
337 *chunk_size = (u64)ms << 12;
338 *size = (u64)s << 12;
339}
340
341static int igt_buddy_alloc_pathological(void *arg)
342{
343 u64 mm_size, size, min_page_size, start = 0;
344 struct drm_buddy_block *block;
345 const int max_order = 3;
346 unsigned long flags = 0;
347 int order, top, err;
348 struct drm_buddy mm;
349 LIST_HEAD(blocks);
350 LIST_HEAD(holes);
351 LIST_HEAD(tmp);
352
353 /*
354 * Create a pot-sized mm, then allocate one of each possible
355 * order within. This should leave the mm with exactly one
356 * page left. Free the largest block, then whittle down again.
357 * Eventually we will have a fully 50% fragmented mm.
358 */
359
360 mm_size = PAGE_SIZE << max_order;
361 err = drm_buddy_init(&mm, mm_size, PAGE_SIZE);
362 if (err) {
363 pr_err("buddy_init failed(%d)\n", err);
364 return err;
365 }
366 BUG_ON(mm.max_order != max_order);
367
368 for (top = max_order; top; top--) {
369 /* Make room by freeing the largest allocated block */
370 block = list_first_entry_or_null(&blocks, typeof(*block), link);
371 if (block) {
372 list_del(&block->link);
373 drm_buddy_free_block(&mm, block);
374 }
375
376 for (order = top; order--; ) {
377 size = min_page_size = get_size(order, PAGE_SIZE);
378 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size,
379 min_page_size, &tmp, flags);
380 if (err) {
381 pr_info("buddy_alloc hit -ENOMEM with order=%d, top=%d\n",
382 order, top);
383 goto err;
384 }
385
386 block = list_first_entry_or_null(&tmp,
387 struct drm_buddy_block,
388 link);
389 if (!block) {
390 pr_err("alloc_blocks has no blocks\n");
391 err = -EINVAL;
392 goto err;
393 }
394
395 list_move_tail(&block->link, &blocks);
396 }
397
398 /* There should be one final page for this sub-allocation */
399 size = min_page_size = get_size(0, PAGE_SIZE);
400 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
401 if (err) {
402 pr_info("buddy_alloc hit -ENOMEM for hole\n");
403 goto err;
404 }
405
406 block = list_first_entry_or_null(&tmp,
407 struct drm_buddy_block,
408 link);
409 if (!block) {
410 pr_err("alloc_blocks has no blocks\n");
411 err = -EINVAL;
412 goto err;
413 }
414
415 list_move_tail(&block->link, &holes);
416
417 size = min_page_size = get_size(top, PAGE_SIZE);
418 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
419 if (!err) {
420 pr_info("buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!",
421 top, max_order);
422 block = list_first_entry_or_null(&tmp,
423 struct drm_buddy_block,
424 link);
425 if (!block) {
426 pr_err("alloc_blocks has no blocks\n");
427 err = -EINVAL;
428 goto err;
429 }
430
431 list_move_tail(&block->link, &blocks);
432 err = -EINVAL;
433 goto err;
434 }
435 }
436
437 drm_buddy_free_list(&mm, &holes);
438
439 /* Nothing larger than blocks of chunk_size now available */
440 for (order = 1; order <= max_order; order++) {
441 size = min_page_size = get_size(order, PAGE_SIZE);
442 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
443 if (!err) {
444 pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
445 order);
446 block = list_first_entry_or_null(&tmp,
447 struct drm_buddy_block,
448 link);
449 if (!block) {
450 pr_err("alloc_blocks has no blocks\n");
451 err = -EINVAL;
452 goto err;
453 }
454
455 list_move_tail(&block->link, &blocks);
456 err = -EINVAL;
457 goto err;
458 }
459 }
460
461 if (err)
462 err = 0;
463
464err:
465 list_splice_tail(&holes, &blocks);
466 drm_buddy_free_list(&mm, &blocks);
467 drm_buddy_fini(&mm);
468 return err;
469}
470
471static int igt_buddy_alloc_smoke(void *arg)
472{
473 u64 mm_size, min_page_size, chunk_size, start = 0;
474 unsigned long flags = 0;
475 struct drm_buddy mm;
476 int *order;
477 int err, i;
478
479 DRM_RND_STATE(prng, random_seed);
480 IGT_TIMEOUT(end_time);
481
482 igt_mm_config(&mm_size, &chunk_size);
483
484 err = drm_buddy_init(&mm, mm_size, chunk_size);
485 if (err) {
486 pr_err("buddy_init failed(%d)\n", err);
487 return err;
488 }
489
490 order = drm_random_order(mm.max_order + 1, &prng);
491 if (!order) {
492 err = -ENOMEM;
493 goto out_fini;
494 }
495
496 for (i = 0; i <= mm.max_order; ++i) {
497 struct drm_buddy_block *block;
498 int max_order = order[i];
499 bool timeout = false;
500 LIST_HEAD(blocks);
501 u64 total, size;
502 LIST_HEAD(tmp);
503 int order;
504
505 err = igt_check_mm(&mm);
506 if (err) {
507 pr_err("pre-mm check failed, abort\n");
508 break;
509 }
510
511 order = max_order;
512 total = 0;
513
514 do {
515retry:
516 size = min_page_size = get_size(order, chunk_size);
517 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size,
518 min_page_size, &tmp, flags);
519 if (err) {
520 if (err == -ENOMEM) {
521 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
522 order);
523 } else {
524 if (order--) {
525 err = 0;
526 goto retry;
527 }
528
529 pr_err("buddy_alloc with order=%d failed(%d)\n",
530 order, err);
531 }
532
533 break;
534 }
535
536 block = list_first_entry_or_null(&tmp,
537 struct drm_buddy_block,
538 link);
539 if (!block) {
540 pr_err("alloc_blocks has no blocks\n");
541 err = -EINVAL;
542 break;
543 }
544
545 list_move_tail(&block->link, &blocks);
546
547 if (drm_buddy_block_order(block) != order) {
548 pr_err("buddy_alloc order mismatch\n");
549 err = -EINVAL;
550 break;
551 }
552
553 total += drm_buddy_block_size(&mm, block);
554
555 if (__igt_timeout(end_time, NULL)) {
556 timeout = true;
557 break;
558 }
559 } while (total < mm.size);
560
561 if (!err)
562 err = igt_check_blocks(&mm, &blocks, total, false);
563
564 drm_buddy_free_list(&mm, &blocks);
565
566 if (!err) {
567 err = igt_check_mm(&mm);
568 if (err)
569 pr_err("post-mm check failed\n");
570 }
571
572 if (err || timeout)
573 break;
574
575 cond_resched();
576 }
577
578 if (err == -ENOMEM)
579 err = 0;
580
581 kfree(order);
582out_fini:
583 drm_buddy_fini(&mm);
584
585 return err;
586}
587
588static int igt_buddy_alloc_pessimistic(void *arg)
589{
590 u64 mm_size, size, min_page_size, start = 0;
591 struct drm_buddy_block *block, *bn;
592 const unsigned int max_order = 16;
593 unsigned long flags = 0;
594 struct drm_buddy mm;
595 unsigned int order;
596 LIST_HEAD(blocks);
597 LIST_HEAD(tmp);
598 int err;
599
600 /*
601 * Create a pot-sized mm, then allocate one of each possible
602 * order within. This should leave the mm with exactly one
603 * page left.
604 */
605
606 mm_size = PAGE_SIZE << max_order;
607 err = drm_buddy_init(&mm, mm_size, PAGE_SIZE);
608 if (err) {
609 pr_err("buddy_init failed(%d)\n", err);
610 return err;
611 }
612 BUG_ON(mm.max_order != max_order);
613
614 for (order = 0; order < max_order; order++) {
615 size = min_page_size = get_size(order, PAGE_SIZE);
616 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
617 if (err) {
618 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
619 order);
620 goto err;
621 }
622
623 block = list_first_entry_or_null(&tmp,
624 struct drm_buddy_block,
625 link);
626 if (!block) {
627 pr_err("alloc_blocks has no blocks\n");
628 err = -EINVAL;
629 goto err;
630 }
631
632 list_move_tail(&block->link, &blocks);
633 }
634
635 /* And now the last remaining block available */
636 size = min_page_size = get_size(0, PAGE_SIZE);
637 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
638 if (err) {
639 pr_info("buddy_alloc hit -ENOMEM on final alloc\n");
640 goto err;
641 }
642
643 block = list_first_entry_or_null(&tmp,
644 struct drm_buddy_block,
645 link);
646 if (!block) {
647 pr_err("alloc_blocks has no blocks\n");
648 err = -EINVAL;
649 goto err;
650 }
651
652 list_move_tail(&block->link, &blocks);
653
654 /* Should be completely full! */
655 for (order = max_order; order--; ) {
656 size = min_page_size = get_size(order, PAGE_SIZE);
657 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
658 if (!err) {
659 pr_info("buddy_alloc unexpectedly succeeded at order %d, it should be full!",
660 order);
661 block = list_first_entry_or_null(&tmp,
662 struct drm_buddy_block,
663 link);
664 if (!block) {
665 pr_err("alloc_blocks has no blocks\n");
666 err = -EINVAL;
667 goto err;
668 }
669
670 list_move_tail(&block->link, &blocks);
671 err = -EINVAL;
672 goto err;
673 }
674 }
675
676 block = list_last_entry(&blocks, typeof(*block), link);
677 list_del(&block->link);
678 drm_buddy_free_block(&mm, block);
679
680 /* As we free in increasing size, we make available larger blocks */
681 order = 1;
682 list_for_each_entry_safe(block, bn, &blocks, link) {
683 list_del(&block->link);
684 drm_buddy_free_block(&mm, block);
685
686 size = min_page_size = get_size(order, PAGE_SIZE);
687 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
688 if (err) {
689 pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
690 order);
691 goto err;
692 }
693
694 block = list_first_entry_or_null(&tmp,
695 struct drm_buddy_block,
696 link);
697 if (!block) {
698 pr_err("alloc_blocks has no blocks\n");
699 err = -EINVAL;
700 goto err;
701 }
702
703 list_del(&block->link);
704 drm_buddy_free_block(&mm, block);
705 order++;
706 }
707
708 /* To confirm, now the whole mm should be available */
709 size = min_page_size = get_size(max_order, PAGE_SIZE);
710 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
711 if (err) {
712 pr_info("buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
713 max_order);
714 goto err;
715 }
716
717 block = list_first_entry_or_null(&tmp,
718 struct drm_buddy_block,
719 link);
720 if (!block) {
721 pr_err("alloc_blocks has no blocks\n");
722 err = -EINVAL;
723 goto err;
724 }
725
726 list_del(&block->link);
727 drm_buddy_free_block(&mm, block);
728
729err:
730 drm_buddy_free_list(&mm, &blocks);
731 drm_buddy_fini(&mm);
732 return err;
733}
734
735static int igt_buddy_alloc_optimistic(void *arg)
736{
737 u64 mm_size, size, min_page_size, start = 0;
738 struct drm_buddy_block *block;
739 unsigned long flags = 0;
740 const int max_order = 16;
741 struct drm_buddy mm;
742 LIST_HEAD(blocks);
743 LIST_HEAD(tmp);
744 int order, err;
745
746 /*
747 * Create a mm with one block of each order available, and
748 * try to allocate them all.
749 */
750
751 mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1);
752 err = drm_buddy_init(&mm,
753 mm_size,
754 PAGE_SIZE);
755 if (err) {
756 pr_err("buddy_init failed(%d)\n", err);
757 return err;
758 }
759
760 BUG_ON(mm.max_order != max_order);
761
762 for (order = 0; order <= max_order; order++) {
763 size = min_page_size = get_size(order, PAGE_SIZE);
764 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
765 if (err) {
766 pr_info("buddy_alloc hit -ENOMEM with order=%d\n",
767 order);
768 goto err;
769 }
770
771 block = list_first_entry_or_null(&tmp,
772 struct drm_buddy_block,
773 link);
774 if (!block) {
775 pr_err("alloc_blocks has no blocks\n");
776 err = -EINVAL;
777 goto err;
778 }
779
780 list_move_tail(&block->link, &blocks);
781 }
782
783 /* Should be completely full! */
784 size = min_page_size = get_size(0, PAGE_SIZE);
785 err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, min_page_size, &tmp, flags);
786 if (!err) {
787 pr_info("buddy_alloc unexpectedly succeeded, it should be full!");
788 block = list_first_entry_or_null(&tmp,
789 struct drm_buddy_block,
790 link);
791 if (!block) {
792 pr_err("alloc_blocks has no blocks\n");
793 err = -EINVAL;
794 goto err;
795 }
796
797 list_move_tail(&block->link, &blocks);
798 err = -EINVAL;
799 goto err;
800 } else {
801 err = 0;
802 }
803
804err:
805 drm_buddy_free_list(&mm, &blocks);
806 drm_buddy_fini(&mm);
807 return err;
808}
809
810static int igt_buddy_alloc_range(void *arg)
811{
812 unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION;
813 u64 offset, size, rem, chunk_size, end;
814 unsigned long page_num;
815 struct drm_buddy mm;
816 LIST_HEAD(blocks);
817 int err;
818
819 igt_mm_config(&size, &chunk_size);
820
821 err = drm_buddy_init(&mm, size, chunk_size);
822 if (err) {
823 pr_err("buddy_init failed(%d)\n", err);
824 return err;
825 }
826
827 err = igt_check_mm(&mm);
828 if (err) {
829 pr_err("pre-mm check failed, abort, abort, abort!\n");
830 goto err_fini;
831 }
832
833 rem = mm.size;
834 offset = 0;
835
836 for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) {
837 struct drm_buddy_block *block;
838 LIST_HEAD(tmp);
839
840 size = min(page_num * mm.chunk_size, rem);
841 end = offset + size;
842
843 err = drm_buddy_alloc_blocks(&mm, offset, end, size, mm.chunk_size, &tmp, flags);
844 if (err) {
845 if (err == -ENOMEM) {
846 pr_info("alloc_range hit -ENOMEM with size=%llx\n",
847 size);
848 } else {
849 pr_err("alloc_range with offset=%llx, size=%llx failed(%d)\n",
850 offset, size, err);
851 }
852
853 break;
854 }
855
856 block = list_first_entry_or_null(&tmp,
857 struct drm_buddy_block,
858 link);
859 if (!block) {
860 pr_err("alloc_range has no blocks\n");
861 err = -EINVAL;
862 break;
863 }
864
865 if (drm_buddy_block_offset(block) != offset) {
866 pr_err("alloc_range start offset mismatch, found=%llx, expected=%llx\n",
867 drm_buddy_block_offset(block), offset);
868 err = -EINVAL;
869 }
870
871 if (!err)
872 err = igt_check_blocks(&mm, &tmp, size, true);
873
874 list_splice_tail(&tmp, &blocks);
875
876 if (err)
877 break;
878
879 offset += size;
880
881 rem -= size;
882 if (!rem)
883 break;
884
885 cond_resched();
886 }
887
888 if (err == -ENOMEM)
889 err = 0;
890
891 drm_buddy_free_list(&mm, &blocks);
892
893 if (!err) {
894 err = igt_check_mm(&mm);
895 if (err)
896 pr_err("post-mm check failed\n");
897 }
898
899err_fini:
900 drm_buddy_fini(&mm);
901
902 return err;
903}
904
905static int igt_buddy_alloc_limit(void *arg)
906{
907 u64 size = U64_MAX, start = 0;
908 struct drm_buddy_block *block;
909 unsigned long flags = 0;
910 LIST_HEAD(allocated);
911 struct drm_buddy mm;
912 int err;
913
914 err = drm_buddy_init(&mm, size, PAGE_SIZE);
915 if (err)
916 return err;
917
918 if (mm.max_order != DRM_BUDDY_MAX_ORDER) {
919 pr_err("mm.max_order(%d) != %d\n",
920 mm.max_order, DRM_BUDDY_MAX_ORDER);
921 err = -EINVAL;
922 goto out_fini;
923 }
924
925 size = mm.chunk_size << mm.max_order;
926 err = drm_buddy_alloc_blocks(&mm, start, size, size,
927 PAGE_SIZE, &allocated, flags);
928
929 if (unlikely(err))
930 goto out_free;
931
932 block = list_first_entry_or_null(&allocated,
933 struct drm_buddy_block,
934 link);
935
936 if (!block) {
937 err = -EINVAL;
938 goto out_fini;
939 }
940
941 if (drm_buddy_block_order(block) != mm.max_order) {
942 pr_err("block order(%d) != %d\n",
943 drm_buddy_block_order(block), mm.max_order);
944 err = -EINVAL;
945 goto out_free;
946 }
947
948 if (drm_buddy_block_size(&mm, block) !=
949 BIT_ULL(mm.max_order) * PAGE_SIZE) {
950 pr_err("block size(%llu) != %llu\n",
951 drm_buddy_block_size(&mm, block),
952 BIT_ULL(mm.max_order) * PAGE_SIZE);
953 err = -EINVAL;
954 goto out_free;
955 }
956
957out_free:
958 drm_buddy_free_list(&mm, &allocated);
959out_fini:
960 drm_buddy_fini(&mm);
961 return err;
962}
963
964static int igt_sanitycheck(void *ignored)
965{
966 pr_info("%s - ok!\n", __func__);
967 return 0;
968}
969
970#include "drm_selftest.c"
971
972static int __init test_drm_buddy_init(void)
973{
974 int err;
975
976 while (!random_seed)
977 random_seed = get_random_int();
978
979 pr_info("Testing DRM buddy manager (struct drm_buddy), with random_seed=0x%x\n",
980 random_seed);
981 err = run_selftests(selftests, ARRAY_SIZE(selftests), NULL);
982
983 return err > 0 ? 0 : err;
984}
985
986static void __exit test_drm_buddy_exit(void)
987{
988}
989
990module_init(test_drm_buddy_init);
991module_exit(test_drm_buddy_exit);
992
993MODULE_AUTHOR("Intel Corporation");
994MODULE_LICENSE("GPL");