1 // SPDX-License-Identifier: GPL-2.0-only
3 * Test cases for the drm_mm range manager
5 * Copyright (c) 2022 Arthur Grillo <arthur.grillo@usp.br>
8 #include <kunit/test.h>
10 #include <linux/prime_numbers.h>
11 #include <linux/slab.h>
12 #include <linux/random.h>
13 #include <linux/vmalloc.h>
14 #include <linux/ktime.h>
16 #include <drm/drm_mm.h>
18 #include "../lib/drm_random.h"
20 static unsigned int random_seed;
21 static unsigned int max_iterations = 8192;
22 static unsigned int max_prime = 128;
31 static const struct insert_mode {
33 enum drm_mm_insert_mode mode;
35 [BEST] = { "best", DRM_MM_INSERT_BEST },
36 [BOTTOMUP] = { "bottom-up", DRM_MM_INSERT_LOW },
37 [TOPDOWN] = { "top-down", DRM_MM_INSERT_HIGH },
38 [EVICT] = { "evict", DRM_MM_INSERT_EVICT },
41 { "bottom-up", DRM_MM_INSERT_LOW },
42 { "top-down", DRM_MM_INSERT_HIGH },
46 static bool assert_no_holes(struct kunit *test, const struct drm_mm *mm)
48 struct drm_mm_node *hole;
49 u64 hole_start, __always_unused hole_end;
53 drm_mm_for_each_hole(hole, mm, hole_start, hole_end)
57 "Expected to find no holes (after reserve), found %lu instead\n", count);
61 drm_mm_for_each_node(hole, mm) {
62 if (drm_mm_hole_follows(hole)) {
63 KUNIT_FAIL(test, "Hole follows node, expected none!\n");
71 static bool assert_one_hole(struct kunit *test, const struct drm_mm *mm, u64 start, u64 end)
73 struct drm_mm_node *hole;
74 u64 hole_start, hole_end;
82 drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
83 if (start != hole_start || end != hole_end) {
86 "empty mm has incorrect hole, found (%llx, %llx), expect (%llx, %llx)\n",
87 hole_start, hole_end, start, end);
93 KUNIT_FAIL(test, "Expected to find one hole, found %lu instead\n", count);
100 static bool assert_continuous(struct kunit *test, const struct drm_mm *mm, u64 size)
102 struct drm_mm_node *node, *check, *found;
106 if (!assert_no_holes(test, mm))
111 drm_mm_for_each_node(node, mm) {
112 if (node->start != addr) {
113 KUNIT_FAIL(test, "node[%ld] list out of order, expected %llx found %llx\n",
114 n, addr, node->start);
118 if (node->size != size) {
119 KUNIT_FAIL(test, "node[%ld].size incorrect, expected %llx, found %llx\n",
120 n, size, node->size);
124 if (drm_mm_hole_follows(node)) {
125 KUNIT_FAIL(test, "node[%ld] is followed by a hole!\n", n);
130 drm_mm_for_each_node_in_range(check, mm, addr, addr + size) {
133 "lookup return wrong node, expected start %llx, found %llx\n",
134 node->start, check->start);
140 KUNIT_FAIL(test, "lookup failed for node %llx + %llx\n", addr, size);
151 static u64 misalignment(struct drm_mm_node *node, u64 alignment)
158 div64_u64_rem(node->start, alignment, &rem);
162 static bool assert_node(struct kunit *test, struct drm_mm_node *node, struct drm_mm *mm,
163 u64 size, u64 alignment, unsigned long color)
167 if (!drm_mm_node_allocated(node) || node->mm != mm) {
168 KUNIT_FAIL(test, "node not allocated\n");
172 if (node->size != size) {
173 KUNIT_FAIL(test, "node has wrong size, found %llu, expected %llu\n",
178 if (misalignment(node, alignment)) {
180 "node is misaligned, start %llx rem %llu, expected alignment %llu\n",
181 node->start, misalignment(node, alignment), alignment);
185 if (node->color != color) {
186 KUNIT_FAIL(test, "node has wrong color, found %lu, expected %lu\n",
194 static void drm_test_mm_init(struct kunit *test)
196 const unsigned int size = 4096;
198 struct drm_mm_node tmp;
200 /* Start with some simple checks on initialising the struct drm_mm */
201 memset(&mm, 0, sizeof(mm));
202 KUNIT_ASSERT_FALSE_MSG(test, drm_mm_initialized(&mm),
203 "zeroed mm claims to be initialized\n");
205 memset(&mm, 0xff, sizeof(mm));
206 drm_mm_init(&mm, 0, size);
207 if (!drm_mm_initialized(&mm)) {
208 KUNIT_FAIL(test, "mm claims not to be initialized\n");
212 if (!drm_mm_clean(&mm)) {
213 KUNIT_FAIL(test, "mm not empty on creation\n");
217 /* After creation, it should all be one massive hole */
218 if (!assert_one_hole(test, &mm, 0, size)) {
219 KUNIT_FAIL(test, "");
223 memset(&tmp, 0, sizeof(tmp));
226 if (drm_mm_reserve_node(&mm, &tmp)) {
227 KUNIT_FAIL(test, "failed to reserve whole drm_mm\n");
231 /* After filling the range entirely, there should be no holes */
232 if (!assert_no_holes(test, &mm)) {
233 KUNIT_FAIL(test, "");
237 /* And then after emptying it again, the massive hole should be back */
238 drm_mm_remove_node(&tmp);
239 if (!assert_one_hole(test, &mm, 0, size)) {
240 KUNIT_FAIL(test, "");
245 drm_mm_takedown(&mm);
248 static void drm_test_mm_debug(struct kunit *test)
251 struct drm_mm_node nodes[2];
253 /* Create a small drm_mm with a couple of nodes and a few holes, and
254 * check that the debug iterator doesn't explode over a trivial drm_mm.
257 drm_mm_init(&mm, 0, 4096);
259 memset(nodes, 0, sizeof(nodes));
260 nodes[0].start = 512;
261 nodes[0].size = 1024;
262 KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[0]),
263 "failed to reserve node[0] {start=%lld, size=%lld)\n",
264 nodes[0].start, nodes[0].size);
266 nodes[1].size = 1024;
267 nodes[1].start = 4096 - 512 - nodes[1].size;
268 KUNIT_ASSERT_FALSE_MSG(test, drm_mm_reserve_node(&mm, &nodes[1]),
269 "failed to reserve node[0] {start=%lld, size=%lld)\n",
270 nodes[0].start, nodes[0].size);
273 static struct drm_mm_node *set_node(struct drm_mm_node *node,
281 static bool expect_reserve_fail(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node)
285 err = drm_mm_reserve_node(mm, node);
286 if (likely(err == -ENOSPC))
290 KUNIT_FAIL(test, "impossible reserve succeeded, node %llu + %llu\n",
291 node->start, node->size);
292 drm_mm_remove_node(node);
295 "impossible reserve failed with wrong error %d [expected %d], node %llu + %llu\n",
296 err, -ENOSPC, node->start, node->size);
301 static bool noinline_for_stack check_reserve_boundaries(struct kunit *test, struct drm_mm *mm,
305 const struct boundary {
309 #define B(st, sz) { (st), (sz), "{ " #st ", " #sz "}" }
319 B(count * size, size),
320 B(count * size, -size),
321 B(count * size, count * size),
322 B(count * size, -count * size),
323 B(count * size, -(count + 1) * size),
324 B((count + 1) * size, size),
325 B((count + 1) * size, -size),
326 B((count + 1) * size, -2 * size),
329 struct drm_mm_node tmp = {};
332 for (n = 0; n < ARRAY_SIZE(boundaries); n++) {
333 if (!expect_reserve_fail(test, mm, set_node(&tmp, boundaries[n].start,
334 boundaries[n].size))) {
335 KUNIT_FAIL(test, "boundary[%d:%s] failed, count=%u, size=%lld\n",
336 n, boundaries[n].name, count, size);
344 static int __drm_test_mm_reserve(struct kunit *test, unsigned int count, u64 size)
346 DRM_RND_STATE(prng, random_seed);
348 struct drm_mm_node tmp, *nodes, *node, *next;
349 unsigned int *order, n, m, o = 0;
352 /* For exercising drm_mm_reserve_node(), we want to check that
353 * reservations outside of the drm_mm range are rejected, and to
354 * overlapping and otherwise already occupied ranges. Afterwards,
355 * the tree and nodes should be intact.
358 DRM_MM_BUG_ON(!count);
359 DRM_MM_BUG_ON(!size);
362 order = drm_random_order(count, &prng);
366 nodes = vzalloc(array_size(count, sizeof(*nodes)));
367 KUNIT_ASSERT_TRUE(test, nodes);
370 drm_mm_init(&mm, 0, count * size);
372 if (!check_reserve_boundaries(test, &mm, count, size))
375 for (n = 0; n < count; n++) {
376 nodes[n].start = order[n] * size;
377 nodes[n].size = size;
379 err = drm_mm_reserve_node(&mm, &nodes[n]);
381 KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
387 if (!drm_mm_node_allocated(&nodes[n])) {
388 KUNIT_FAIL(test, "reserved node not allocated! step %d, start %llu\n",
393 if (!expect_reserve_fail(test, &mm, &nodes[n]))
397 /* After random insertion the nodes should be in order */
398 if (!assert_continuous(test, &mm, size))
401 /* Repeated use should then fail */
402 drm_random_reorder(order, count, &prng);
403 for (n = 0; n < count; n++) {
404 if (!expect_reserve_fail(test, &mm, set_node(&tmp, order[n] * size, 1)))
407 /* Remove and reinsert should work */
408 drm_mm_remove_node(&nodes[order[n]]);
409 err = drm_mm_reserve_node(&mm, &nodes[order[n]]);
411 KUNIT_FAIL(test, "reserve failed, step %d, start %llu\n",
418 if (!assert_continuous(test, &mm, size))
421 /* Overlapping use should then fail */
422 for (n = 0; n < count; n++) {
423 if (!expect_reserve_fail(test, &mm, set_node(&tmp, 0, size * count)))
426 for (n = 0; n < count; n++) {
427 if (!expect_reserve_fail(test, &mm, set_node(&tmp, size * n, size * (count - n))))
431 /* Remove several, reinsert, check full */
432 for_each_prime_number(n, min(max_prime, count)) {
433 for (m = 0; m < n; m++) {
434 node = &nodes[order[(o + m) % count]];
435 drm_mm_remove_node(node);
438 for (m = 0; m < n; m++) {
439 node = &nodes[order[(o + m) % count]];
440 err = drm_mm_reserve_node(&mm, node);
442 KUNIT_FAIL(test, "reserve failed, step %d/%d, start %llu\n",
451 if (!assert_continuous(test, &mm, size))
457 drm_mm_for_each_node_safe(node, next, &mm)
458 drm_mm_remove_node(node);
459 drm_mm_takedown(&mm);
466 static void drm_test_mm_reserve(struct kunit *test)
468 const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
471 for_each_prime_number_from(n, 1, 54) {
472 u64 size = BIT_ULL(n);
474 KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size - 1));
475 KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size));
476 KUNIT_ASSERT_FALSE(test, __drm_test_mm_reserve(test, count, size + 1));
482 static bool expect_insert(struct kunit *test, struct drm_mm *mm,
483 struct drm_mm_node *node, u64 size, u64 alignment, unsigned long color,
484 const struct insert_mode *mode)
488 err = drm_mm_insert_node_generic(mm, node,
489 size, alignment, color,
493 "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) failed with err=%d\n",
494 size, alignment, color, mode->name, err);
498 if (!assert_node(test, node, mm, size, alignment, color)) {
499 drm_mm_remove_node(node);
506 static bool expect_insert_fail(struct kunit *test, struct drm_mm *mm, u64 size)
508 struct drm_mm_node tmp = {};
511 err = drm_mm_insert_node(mm, &tmp, size);
512 if (likely(err == -ENOSPC))
516 KUNIT_FAIL(test, "impossible insert succeeded, node %llu + %llu\n",
517 tmp.start, tmp.size);
518 drm_mm_remove_node(&tmp);
521 "impossible insert failed with wrong error %d [expected %d], size %llu\n",
527 static int __drm_test_mm_insert(struct kunit *test, unsigned int count, u64 size, bool replace)
529 DRM_RND_STATE(prng, random_seed);
530 const struct insert_mode *mode;
532 struct drm_mm_node *nodes, *node, *next;
533 unsigned int *order, n, m, o = 0;
536 /* Fill a range with lots of nodes, check it doesn't fail too early */
538 DRM_MM_BUG_ON(!count);
539 DRM_MM_BUG_ON(!size);
542 nodes = vmalloc(array_size(count, sizeof(*nodes)));
543 KUNIT_ASSERT_TRUE(test, nodes);
545 order = drm_random_order(count, &prng);
550 drm_mm_init(&mm, 0, count * size);
552 for (mode = insert_modes; mode->name; mode++) {
553 for (n = 0; n < count; n++) {
554 struct drm_mm_node tmp;
556 node = replace ? &tmp : &nodes[n];
557 memset(node, 0, sizeof(*node));
558 if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
559 KUNIT_FAIL(test, "%s insert failed, size %llu step %d\n",
560 mode->name, size, n);
565 drm_mm_replace_node(&tmp, &nodes[n]);
566 if (drm_mm_node_allocated(&tmp)) {
568 "replaced old-node still allocated! step %d\n",
573 if (!assert_node(test, &nodes[n], &mm, size, 0, n)) {
575 "replaced node did not inherit parameters, size %llu step %d\n",
580 if (tmp.start != nodes[n].start) {
582 "replaced node mismatch location expected [%llx + %llx], found [%llx + %llx]\n",
583 tmp.start, size, nodes[n].start, nodes[n].size);
589 /* After random insertion the nodes should be in order */
590 if (!assert_continuous(test, &mm, size))
593 /* Repeated use should then fail */
594 if (!expect_insert_fail(test, &mm, size))
597 /* Remove one and reinsert, as the only hole it should refill itself */
598 for (n = 0; n < count; n++) {
599 u64 addr = nodes[n].start;
601 drm_mm_remove_node(&nodes[n]);
602 if (!expect_insert(test, &mm, &nodes[n], size, 0, n, mode)) {
603 KUNIT_FAIL(test, "%s reinsert failed, size %llu step %d\n",
604 mode->name, size, n);
608 if (nodes[n].start != addr) {
610 "%s reinsert node moved, step %d, expected %llx, found %llx\n",
611 mode->name, n, addr, nodes[n].start);
615 if (!assert_continuous(test, &mm, size))
619 /* Remove several, reinsert, check full */
620 for_each_prime_number(n, min(max_prime, count)) {
621 for (m = 0; m < n; m++) {
622 node = &nodes[order[(o + m) % count]];
623 drm_mm_remove_node(node);
626 for (m = 0; m < n; m++) {
627 node = &nodes[order[(o + m) % count]];
628 if (!expect_insert(test, &mm, node, size, 0, n, mode)) {
630 "%s multiple reinsert failed, size %llu step %d\n",
631 mode->name, size, n);
638 if (!assert_continuous(test, &mm, size))
641 if (!expect_insert_fail(test, &mm, size))
645 drm_mm_for_each_node_safe(node, next, &mm)
646 drm_mm_remove_node(node);
647 DRM_MM_BUG_ON(!drm_mm_clean(&mm));
654 drm_mm_for_each_node_safe(node, next, &mm)
655 drm_mm_remove_node(node);
656 drm_mm_takedown(&mm);
663 static void drm_test_mm_insert(struct kunit *test)
665 const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
668 for_each_prime_number_from(n, 1, 54) {
669 u64 size = BIT_ULL(n);
671 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, false));
672 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, false));
673 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, false));
679 static void drm_test_mm_replace(struct kunit *test)
681 const unsigned int count = min_t(unsigned int, BIT(10), max_iterations);
684 /* Reuse __drm_test_mm_insert to exercise replacement by inserting a dummy node,
685 * then replacing it with the intended node. We want to check that
686 * the tree is intact and all the information we need is carried
687 * across to the target node.
690 for_each_prime_number_from(n, 1, 54) {
691 u64 size = BIT_ULL(n);
693 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size - 1, true));
694 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size, true));
695 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert(test, count, size + 1, true));
701 static bool expect_insert_in_range(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *node,
702 u64 size, u64 alignment, unsigned long color,
703 u64 range_start, u64 range_end, const struct insert_mode *mode)
707 err = drm_mm_insert_node_in_range(mm, node,
708 size, alignment, color,
709 range_start, range_end,
713 "insert (size=%llu, alignment=%llu, color=%lu, mode=%s) nto range [%llx, %llx] failed with err=%d\n",
714 size, alignment, color, mode->name,
715 range_start, range_end, err);
719 if (!assert_node(test, node, mm, size, alignment, color)) {
720 drm_mm_remove_node(node);
727 static bool expect_insert_in_range_fail(struct kunit *test, struct drm_mm *mm,
728 u64 size, u64 range_start, u64 range_end)
730 struct drm_mm_node tmp = {};
733 err = drm_mm_insert_node_in_range(mm, &tmp, size, 0, 0, range_start, range_end,
735 if (likely(err == -ENOSPC))
740 "impossible insert succeeded, node %llx + %llu, range [%llx, %llx]\n",
741 tmp.start, tmp.size, range_start, range_end);
742 drm_mm_remove_node(&tmp);
745 "impossible insert failed with wrong error %d [expected %d], size %llu, range [%llx, %llx]\n",
746 err, -ENOSPC, size, range_start, range_end);
752 static bool assert_contiguous_in_range(struct kunit *test, struct drm_mm *mm,
753 u64 size, u64 start, u64 end)
755 struct drm_mm_node *node;
758 if (!expect_insert_in_range_fail(test, mm, size, start, end))
761 n = div64_u64(start + size - 1, size);
762 drm_mm_for_each_node(node, mm) {
763 if (node->start < start || node->start + node->size > end) {
765 "node %d out of range, address [%llx + %llu], range [%llx, %llx]\n",
766 n, node->start, node->start + node->size, start, end);
770 if (node->start != n * size) {
771 KUNIT_FAIL(test, "node %d out of order, expected start %llx, found %llx\n",
772 n, n * size, node->start);
776 if (node->size != size) {
777 KUNIT_FAIL(test, "node %d has wrong size, expected size %llx, found %llx\n",
778 n, size, node->size);
782 if (drm_mm_hole_follows(node) && drm_mm_hole_node_end(node) < end) {
783 KUNIT_FAIL(test, "node %d is followed by a hole!\n", n);
791 node = __drm_mm_interval_first(mm, 0, start - 1);
792 if (drm_mm_node_allocated(node)) {
793 KUNIT_FAIL(test, "node before start: node=%llx+%llu, start=%llx\n",
794 node->start, node->size, start);
800 node = __drm_mm_interval_first(mm, end, U64_MAX);
801 if (drm_mm_node_allocated(node)) {
802 KUNIT_FAIL(test, "node after end: node=%llx+%llu, end=%llx\n",
803 node->start, node->size, end);
811 static int __drm_test_mm_insert_range(struct kunit *test, unsigned int count, u64 size,
814 const struct insert_mode *mode;
816 struct drm_mm_node *nodes, *node, *next;
817 unsigned int n, start_n, end_n;
820 DRM_MM_BUG_ON(!count);
821 DRM_MM_BUG_ON(!size);
822 DRM_MM_BUG_ON(end <= start);
824 /* Very similar to __drm_test_mm_insert(), but now instead of populating the
825 * full range of the drm_mm, we try to fill a small portion of it.
829 nodes = vzalloc(array_size(count, sizeof(*nodes)));
830 KUNIT_ASSERT_TRUE(test, nodes);
833 drm_mm_init(&mm, 0, count * size);
835 start_n = div64_u64(start + size - 1, size);
836 end_n = div64_u64(end - size, size);
838 for (mode = insert_modes; mode->name; mode++) {
839 for (n = start_n; n <= end_n; n++) {
840 if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
843 "%s insert failed, size %llu, step %d [%d, %d], range [%llx, %llx]\n",
844 mode->name, size, n, start_n, end_n, start, end);
849 if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
851 "%s: range [%llx, %llx] not full after initialisation, size=%llu\n",
852 mode->name, start, end, size);
856 /* Remove one and reinsert, it should refill itself */
857 for (n = start_n; n <= end_n; n++) {
858 u64 addr = nodes[n].start;
860 drm_mm_remove_node(&nodes[n]);
861 if (!expect_insert_in_range(test, &mm, &nodes[n], size, size, n,
863 KUNIT_FAIL(test, "%s reinsert failed, step %d\n", mode->name, n);
867 if (nodes[n].start != addr) {
869 "%s reinsert node moved, step %d, expected %llx, found %llx\n",
870 mode->name, n, addr, nodes[n].start);
875 if (!assert_contiguous_in_range(test, &mm, size, start, end)) {
877 "%s: range [%llx, %llx] not full after reinsertion, size=%llu\n",
878 mode->name, start, end, size);
882 drm_mm_for_each_node_safe(node, next, &mm)
883 drm_mm_remove_node(node);
884 DRM_MM_BUG_ON(!drm_mm_clean(&mm));
891 drm_mm_for_each_node_safe(node, next, &mm)
892 drm_mm_remove_node(node);
893 drm_mm_takedown(&mm);
898 static int insert_outside_range(struct kunit *test)
901 const unsigned int start = 1024;
902 const unsigned int end = 2048;
903 const unsigned int size = end - start;
905 drm_mm_init(&mm, start, size);
907 if (!expect_insert_in_range_fail(test, &mm, 1, 0, start))
910 if (!expect_insert_in_range_fail(test, &mm, size,
911 start - size / 2, start + (size + 1) / 2))
914 if (!expect_insert_in_range_fail(test, &mm, size,
915 end - (size + 1) / 2, end + size / 2))
918 if (!expect_insert_in_range_fail(test, &mm, 1, end, end + size))
921 drm_mm_takedown(&mm);
925 static void drm_test_mm_insert_range(struct kunit *test)
927 const unsigned int count = min_t(unsigned int, BIT(13), max_iterations);
930 /* Check that requests outside the bounds of drm_mm are rejected. */
931 KUNIT_ASSERT_FALSE(test, insert_outside_range(test));
933 for_each_prime_number_from(n, 1, 50) {
934 const u64 size = BIT_ULL(n);
935 const u64 max = count * size;
937 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max));
938 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 1, max));
939 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max - 1));
940 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size, 0, max / 2));
941 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size,
943 KUNIT_ASSERT_FALSE(test, __drm_test_mm_insert_range(test, count, size,
944 max / 4 + 1, 3 * max / 4 - 1));
950 static int prepare_frag(struct kunit *test, struct drm_mm *mm, struct drm_mm_node *nodes,
951 unsigned int num_insert, const struct insert_mode *mode)
953 unsigned int size = 4096;
956 for (i = 0; i < num_insert; i++) {
957 if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
958 KUNIT_FAIL(test, "%s insert failed\n", mode->name);
963 /* introduce fragmentation by freeing every other node */
964 for (i = 0; i < num_insert; i++) {
966 drm_mm_remove_node(&nodes[i]);
972 static u64 get_insert_time(struct kunit *test, struct drm_mm *mm,
973 unsigned int num_insert, struct drm_mm_node *nodes,
974 const struct insert_mode *mode)
976 unsigned int size = 8192;
981 for (i = 0; i < num_insert; i++) {
982 if (!expect_insert(test, mm, &nodes[i], size, 0, i, mode) != 0) {
983 KUNIT_FAIL(test, "%s insert failed\n", mode->name);
988 return ktime_to_ns(ktime_sub(ktime_get(), start));
991 static void drm_test_mm_frag(struct kunit *test)
994 const struct insert_mode *mode;
995 struct drm_mm_node *nodes, *node, *next;
996 unsigned int insert_size = 10000;
997 unsigned int scale_factor = 4;
999 /* We need 4 * insert_size nodes to hold intermediate allocated
1001 * 1 times for prepare_frag()
1002 * 1 times for get_insert_time()
1003 * 2 times for get_insert_time()
1005 nodes = vzalloc(array_size(insert_size * 4, sizeof(*nodes)));
1006 KUNIT_ASSERT_TRUE(test, nodes);
1008 /* For BOTTOMUP and TOPDOWN, we first fragment the
1009 * address space using prepare_frag() and then try to verify
1010 * that insertions scale quadratically from 10k to 20k insertions
1012 drm_mm_init(&mm, 1, U64_MAX - 2);
1013 for (mode = insert_modes; mode->name; mode++) {
1014 u64 insert_time1, insert_time2;
1016 if (mode->mode != DRM_MM_INSERT_LOW &&
1017 mode->mode != DRM_MM_INSERT_HIGH)
1020 if (prepare_frag(test, &mm, nodes, insert_size, mode))
1023 insert_time1 = get_insert_time(test, &mm, insert_size,
1024 nodes + insert_size, mode);
1025 if (insert_time1 == 0)
1028 insert_time2 = get_insert_time(test, &mm, (insert_size * 2),
1029 nodes + insert_size * 2, mode);
1030 if (insert_time2 == 0)
1033 kunit_info(test, "%s fragmented insert of %u and %u insertions took %llu and %llu nsecs\n",
1034 mode->name, insert_size, insert_size * 2, insert_time1, insert_time2);
1036 if (insert_time2 > (scale_factor * insert_time1)) {
1037 KUNIT_FAIL(test, "%s fragmented insert took %llu nsecs more\n",
1038 mode->name, insert_time2 - (scale_factor * insert_time1));
1042 drm_mm_for_each_node_safe(node, next, &mm)
1043 drm_mm_remove_node(node);
1047 drm_mm_for_each_node_safe(node, next, &mm)
1048 drm_mm_remove_node(node);
1049 drm_mm_takedown(&mm);
1053 static void drm_test_mm_align(struct kunit *test)
1055 const struct insert_mode *mode;
1056 const unsigned int max_count = min(8192u, max_prime);
1058 struct drm_mm_node *nodes, *node, *next;
1061 /* For each of the possible insertion modes, we pick a few
1062 * arbitrary alignments and check that the inserted node
1063 * meets our requirements.
1066 nodes = vzalloc(array_size(max_count, sizeof(*nodes)));
1067 KUNIT_ASSERT_TRUE(test, nodes);
1069 drm_mm_init(&mm, 1, U64_MAX - 2);
1071 for (mode = insert_modes; mode->name; mode++) {
1074 for_each_prime_number_from(prime, 1, max_count) {
1075 u64 size = next_prime_number(prime);
1077 if (!expect_insert(test, &mm, &nodes[i], size, prime, i, mode)) {
1078 KUNIT_FAIL(test, "%s insert failed with alignment=%d",
1086 drm_mm_for_each_node_safe(node, next, &mm)
1087 drm_mm_remove_node(node);
1088 DRM_MM_BUG_ON(!drm_mm_clean(&mm));
1094 drm_mm_for_each_node_safe(node, next, &mm)
1095 drm_mm_remove_node(node);
1096 drm_mm_takedown(&mm);
1100 static void drm_test_mm_align_pot(struct kunit *test, int max)
1103 struct drm_mm_node *node, *next;
1106 /* Check that we can align to the full u64 address space */
1108 drm_mm_init(&mm, 1, U64_MAX - 2);
1110 for (bit = max - 1; bit; bit--) {
1113 node = kzalloc(sizeof(*node), GFP_KERNEL);
1115 KUNIT_FAIL(test, "failed to allocate node");
1119 align = BIT_ULL(bit);
1120 size = BIT_ULL(bit - 1) + 1;
1121 if (!expect_insert(test, &mm, node, size, align, bit, &insert_modes[0])) {
1122 KUNIT_FAIL(test, "insert failed with alignment=%llx [%d]", align, bit);
1130 drm_mm_for_each_node_safe(node, next, &mm) {
1131 drm_mm_remove_node(node);
1134 drm_mm_takedown(&mm);
1137 static void drm_test_mm_align32(struct kunit *test)
1139 drm_test_mm_align_pot(test, 32);
1142 static void drm_test_mm_align64(struct kunit *test)
1144 drm_test_mm_align_pot(test, 64);
1147 static void show_scan(struct kunit *test, const struct drm_mm_scan *scan)
1149 kunit_info(test, "scan: hit [%llx, %llx], size=%lld, align=%lld, color=%ld\n",
1150 scan->hit_start, scan->hit_end, scan->size, scan->alignment, scan->color);
1153 static void show_holes(struct kunit *test, const struct drm_mm *mm, int count)
1155 u64 hole_start, hole_end;
1156 struct drm_mm_node *hole;
1158 drm_mm_for_each_hole(hole, mm, hole_start, hole_end) {
1159 struct drm_mm_node *next = list_next_entry(hole, node_list);
1160 const char *node1 = NULL, *node2 = NULL;
1162 if (drm_mm_node_allocated(hole))
1163 node1 = kasprintf(GFP_KERNEL, "[%llx + %lld, color=%ld], ",
1164 hole->start, hole->size, hole->color);
1166 if (drm_mm_node_allocated(next))
1167 node2 = kasprintf(GFP_KERNEL, ", [%llx + %lld, color=%ld]",
1168 next->start, next->size, next->color);
1170 kunit_info(test, "%sHole [%llx - %llx, size %lld]%s\n", node1,
1171 hole_start, hole_end, hole_end - hole_start, node2);
1182 struct drm_mm_node node;
1183 struct list_head link;
1186 static bool evict_nodes(struct kunit *test, struct drm_mm_scan *scan,
1187 struct evict_node *nodes, unsigned int *order, unsigned int count,
1188 bool use_color, struct list_head *evict_list)
1190 struct evict_node *e, *en;
1193 for (i = 0; i < count; i++) {
1194 e = &nodes[order ? order[i] : i];
1195 list_add(&e->link, evict_list);
1196 if (drm_mm_scan_add_block(scan, &e->node))
1199 list_for_each_entry_safe(e, en, evict_list, link) {
1200 if (!drm_mm_scan_remove_block(scan, &e->node))
1203 if (list_empty(evict_list)) {
1205 "Failed to find eviction: size=%lld [avail=%d], align=%lld (color=%lu)\n",
1206 scan->size, count, scan->alignment, scan->color);
1210 list_for_each_entry(e, evict_list, link)
1211 drm_mm_remove_node(&e->node);
1214 struct drm_mm_node *node;
1216 while ((node = drm_mm_scan_color_evict(scan))) {
1217 e = container_of(node, typeof(*e), node);
1218 drm_mm_remove_node(&e->node);
1219 list_add(&e->link, evict_list);
1222 if (drm_mm_scan_color_evict(scan)) {
1224 "drm_mm_scan_color_evict unexpectedly reported overlapping nodes!\n");
1232 static bool evict_nothing(struct kunit *test, struct drm_mm *mm,
1233 unsigned int total_size, struct evict_node *nodes)
1235 struct drm_mm_scan scan;
1236 LIST_HEAD(evict_list);
1237 struct evict_node *e;
1238 struct drm_mm_node *node;
1241 drm_mm_scan_init(&scan, mm, 1, 0, 0, 0);
1242 for (n = 0; n < total_size; n++) {
1244 list_add(&e->link, &evict_list);
1245 drm_mm_scan_add_block(&scan, &e->node);
1247 list_for_each_entry(e, &evict_list, link)
1248 drm_mm_scan_remove_block(&scan, &e->node);
1250 for (n = 0; n < total_size; n++) {
1253 if (!drm_mm_node_allocated(&e->node)) {
1254 KUNIT_FAIL(test, "node[%d] no longer allocated!\n", n);
1258 e->link.next = NULL;
1261 drm_mm_for_each_node(node, mm) {
1262 e = container_of(node, typeof(*e), node);
1263 e->link.next = &e->link;
1266 for (n = 0; n < total_size; n++) {
1269 if (!e->link.next) {
1270 KUNIT_FAIL(test, "node[%d] no longer connected!\n", n);
1275 return assert_continuous(test, mm, nodes[0].node.size);
1278 static bool evict_everything(struct kunit *test, struct drm_mm *mm,
1279 unsigned int total_size, struct evict_node *nodes)
1281 struct drm_mm_scan scan;
1282 LIST_HEAD(evict_list);
1283 struct evict_node *e;
1287 drm_mm_scan_init(&scan, mm, total_size, 0, 0, 0);
1288 for (n = 0; n < total_size; n++) {
1290 list_add(&e->link, &evict_list);
1291 if (drm_mm_scan_add_block(&scan, &e->node))
1296 list_for_each_entry(e, &evict_list, link) {
1297 if (!drm_mm_scan_remove_block(&scan, &e->node)) {
1299 KUNIT_FAIL(test, "Node %lld not marked for eviction!\n",
1308 list_for_each_entry(e, &evict_list, link)
1309 drm_mm_remove_node(&e->node);
1311 if (!assert_one_hole(test, mm, 0, total_size))
1314 list_for_each_entry(e, &evict_list, link) {
1315 err = drm_mm_reserve_node(mm, &e->node);
1317 KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
1323 return assert_continuous(test, mm, nodes[0].node.size);
1326 static int evict_something(struct kunit *test, struct drm_mm *mm,
1327 u64 range_start, u64 range_end, struct evict_node *nodes,
1328 unsigned int *order, unsigned int count, unsigned int size,
1329 unsigned int alignment, const struct insert_mode *mode)
1331 struct drm_mm_scan scan;
1332 LIST_HEAD(evict_list);
1333 struct evict_node *e;
1334 struct drm_mm_node tmp;
1337 drm_mm_scan_init_with_range(&scan, mm, size, alignment, 0, range_start,
1338 range_end, mode->mode);
1339 if (!evict_nodes(test, &scan, nodes, order, count, false, &evict_list))
1342 memset(&tmp, 0, sizeof(tmp));
1343 err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, 0,
1344 DRM_MM_INSERT_EVICT);
1346 KUNIT_FAIL(test, "Failed to insert into eviction hole: size=%d, align=%d\n",
1348 show_scan(test, &scan);
1349 show_holes(test, mm, 3);
1353 if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
1355 "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
1356 tmp.start, tmp.size, range_start, range_end);
1360 if (!assert_node(test, &tmp, mm, size, alignment, 0) ||
1361 drm_mm_hole_follows(&tmp)) {
1363 "Inserted did not fill the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx, hole-follows?=%d\n",
1364 tmp.size, size, alignment, misalignment(&tmp, alignment),
1365 tmp.start, drm_mm_hole_follows(&tmp));
1369 drm_mm_remove_node(&tmp);
1373 list_for_each_entry(e, &evict_list, link) {
1374 err = drm_mm_reserve_node(mm, &e->node);
1376 KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
1382 if (!assert_continuous(test, mm, nodes[0].node.size)) {
1383 KUNIT_FAIL(test, "range is no longer continuous\n");
1390 static void drm_test_mm_evict(struct kunit *test)
1392 DRM_RND_STATE(prng, random_seed);
1393 const unsigned int size = 8192;
1394 const struct insert_mode *mode;
1396 struct evict_node *nodes;
1397 struct drm_mm_node *node, *next;
1398 unsigned int *order, n;
1400 /* Here we populate a full drm_mm and then try and insert a new node
1401 * by evicting other nodes in a random order. The drm_mm_scan should
1402 * pick the first matching hole it finds from the random list. We
1403 * repeat that for different allocation strategies, alignments and
1404 * sizes to try and stress the hole finder.
1407 nodes = vzalloc(array_size(size, sizeof(*nodes)));
1408 KUNIT_ASSERT_TRUE(test, nodes);
1410 order = drm_random_order(size, &prng);
1414 drm_mm_init(&mm, 0, size);
1415 for (n = 0; n < size; n++) {
1416 if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
1417 KUNIT_FAIL(test, "insert failed, step %d\n", n);
1422 /* First check that using the scanner doesn't break the mm */
1423 if (!evict_nothing(test, &mm, size, nodes)) {
1424 KUNIT_FAIL(test, "evict_nothing() failed\n");
1427 if (!evict_everything(test, &mm, size, nodes)) {
1428 KUNIT_FAIL(test, "evict_everything() failed\n");
1432 for (mode = evict_modes; mode->name; mode++) {
1433 for (n = 1; n <= size; n <<= 1) {
1434 drm_random_reorder(order, size, &prng);
1435 if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size, n, 1,
1437 KUNIT_FAIL(test, "%s evict_something(size=%u) failed\n",
1443 for (n = 1; n < size; n <<= 1) {
1444 drm_random_reorder(order, size, &prng);
1445 if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
1446 size / 2, n, mode)) {
1448 "%s evict_something(size=%u, alignment=%u) failed\n",
1449 mode->name, size / 2, n);
1454 for_each_prime_number_from(n, 1, min(size, max_prime)) {
1455 unsigned int nsize = (size - n + 1) / 2;
1457 DRM_MM_BUG_ON(!nsize);
1459 drm_random_reorder(order, size, &prng);
1460 if (evict_something(test, &mm, 0, U64_MAX, nodes, order, size,
1463 "%s evict_something(size=%u, alignment=%u) failed\n",
1464 mode->name, nsize, n);
1473 drm_mm_for_each_node_safe(node, next, &mm)
1474 drm_mm_remove_node(node);
1475 drm_mm_takedown(&mm);
1481 static void drm_test_mm_evict_range(struct kunit *test)
1483 DRM_RND_STATE(prng, random_seed);
1484 const unsigned int size = 8192;
1485 const unsigned int range_size = size / 2;
1486 const unsigned int range_start = size / 4;
1487 const unsigned int range_end = range_start + range_size;
1488 const struct insert_mode *mode;
1490 struct evict_node *nodes;
1491 struct drm_mm_node *node, *next;
1492 unsigned int *order, n;
1494 /* Like drm_test_mm_evict() but now we are limiting the search to a
1495 * small portion of the full drm_mm.
1498 nodes = vzalloc(array_size(size, sizeof(*nodes)));
1499 KUNIT_ASSERT_TRUE(test, nodes);
1501 order = drm_random_order(size, &prng);
1505 drm_mm_init(&mm, 0, size);
1506 for (n = 0; n < size; n++) {
1507 if (drm_mm_insert_node(&mm, &nodes[n].node, 1)) {
1508 KUNIT_FAIL(test, "insert failed, step %d\n", n);
1513 for (mode = evict_modes; mode->name; mode++) {
1514 for (n = 1; n <= range_size; n <<= 1) {
1515 drm_random_reorder(order, size, &prng);
1516 if (evict_something(test, &mm, range_start, range_end, nodes,
1517 order, size, n, 1, mode)) {
1519 "%s evict_something(size=%u) failed with range [%u, %u]\n",
1520 mode->name, n, range_start, range_end);
1525 for (n = 1; n <= range_size; n <<= 1) {
1526 drm_random_reorder(order, size, &prng);
1527 if (evict_something(test, &mm, range_start, range_end, nodes,
1528 order, size, range_size / 2, n, mode)) {
1530 "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
1531 mode->name, range_size / 2, n, range_start, range_end);
1536 for_each_prime_number_from(n, 1, min(range_size, max_prime)) {
1537 unsigned int nsize = (range_size - n + 1) / 2;
1539 DRM_MM_BUG_ON(!nsize);
1541 drm_random_reorder(order, size, &prng);
1542 if (evict_something(test, &mm, range_start, range_end, nodes,
1543 order, size, nsize, n, mode)) {
1545 "%s evict_something(size=%u, alignment=%u) failed with range [%u, %u]\n",
1546 mode->name, nsize, n, range_start, range_end);
1555 drm_mm_for_each_node_safe(node, next, &mm)
1556 drm_mm_remove_node(node);
1557 drm_mm_takedown(&mm);
1563 static unsigned int node_index(const struct drm_mm_node *node)
1565 return div64_u64(node->start, node->size);
1568 static void drm_test_mm_topdown(struct kunit *test)
1570 const struct insert_mode *topdown = &insert_modes[TOPDOWN];
1572 DRM_RND_STATE(prng, random_seed);
1573 const unsigned int count = 8192;
1575 unsigned long *bitmap;
1577 struct drm_mm_node *nodes, *node, *next;
1578 unsigned int *order, n, m, o = 0;
1580 /* When allocating top-down, we expect to be returned a node
1581 * from a suitable hole at the top of the drm_mm. We check that
1582 * the returned node does match the highest available slot.
1585 nodes = vzalloc(array_size(count, sizeof(*nodes)));
1586 KUNIT_ASSERT_TRUE(test, nodes);
1588 bitmap = bitmap_zalloc(count, GFP_KERNEL);
1592 order = drm_random_order(count, &prng);
1596 for (size = 1; size <= 64; size <<= 1) {
1597 drm_mm_init(&mm, 0, size * count);
1598 for (n = 0; n < count; n++) {
1599 if (!expect_insert(test, &mm, &nodes[n], size, 0, n, topdown)) {
1600 KUNIT_FAIL(test, "insert failed, size %u step %d\n", size, n);
1604 if (drm_mm_hole_follows(&nodes[n])) {
1606 "hole after topdown insert %d, start=%llx\n, size=%u",
1607 n, nodes[n].start, size);
1611 if (!assert_one_hole(test, &mm, 0, size * (count - n - 1)))
1615 if (!assert_continuous(test, &mm, size))
1618 drm_random_reorder(order, count, &prng);
1619 for_each_prime_number_from(n, 1, min(count, max_prime)) {
1620 for (m = 0; m < n; m++) {
1621 node = &nodes[order[(o + m) % count]];
1622 drm_mm_remove_node(node);
1623 __set_bit(node_index(node), bitmap);
1626 for (m = 0; m < n; m++) {
1629 node = &nodes[order[(o + m) % count]];
1630 if (!expect_insert(test, &mm, node, size, 0, 0, topdown)) {
1631 KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
1635 if (drm_mm_hole_follows(node)) {
1637 "hole after topdown insert %d/%d, start=%llx\n",
1642 last = find_last_bit(bitmap, count);
1643 if (node_index(node) != last) {
1645 "node %d/%d, size %d, not inserted into upmost hole, expected %d, found %d\n",
1646 m, n, size, last, node_index(node));
1650 __clear_bit(last, bitmap);
1653 DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count);
1658 drm_mm_for_each_node_safe(node, next, &mm)
1659 drm_mm_remove_node(node);
1660 DRM_MM_BUG_ON(!drm_mm_clean(&mm));
1665 drm_mm_for_each_node_safe(node, next, &mm)
1666 drm_mm_remove_node(node);
1667 drm_mm_takedown(&mm);
1670 bitmap_free(bitmap);
1675 static void drm_test_mm_bottomup(struct kunit *test)
1677 const struct insert_mode *bottomup = &insert_modes[BOTTOMUP];
1679 DRM_RND_STATE(prng, random_seed);
1680 const unsigned int count = 8192;
1682 unsigned long *bitmap;
1684 struct drm_mm_node *nodes, *node, *next;
1685 unsigned int *order, n, m, o = 0;
1687 /* Like drm_test_mm_topdown, but instead of searching for the last hole,
1688 * we search for the first.
1691 nodes = vzalloc(array_size(count, sizeof(*nodes)));
1692 KUNIT_ASSERT_TRUE(test, nodes);
1694 bitmap = bitmap_zalloc(count, GFP_KERNEL);
1698 order = drm_random_order(count, &prng);
1702 for (size = 1; size <= 64; size <<= 1) {
1703 drm_mm_init(&mm, 0, size * count);
1704 for (n = 0; n < count; n++) {
1705 if (!expect_insert(test, &mm, &nodes[n], size, 0, n, bottomup)) {
1707 "bottomup insert failed, size %u step %d\n", size, n);
1711 if (!assert_one_hole(test, &mm, size * (n + 1), size * count))
1715 if (!assert_continuous(test, &mm, size))
1718 drm_random_reorder(order, count, &prng);
1719 for_each_prime_number_from(n, 1, min(count, max_prime)) {
1720 for (m = 0; m < n; m++) {
1721 node = &nodes[order[(o + m) % count]];
1722 drm_mm_remove_node(node);
1723 __set_bit(node_index(node), bitmap);
1726 for (m = 0; m < n; m++) {
1729 node = &nodes[order[(o + m) % count]];
1730 if (!expect_insert(test, &mm, node, size, 0, 0, bottomup)) {
1731 KUNIT_FAIL(test, "insert failed, step %d/%d\n", m, n);
1735 first = find_first_bit(bitmap, count);
1736 if (node_index(node) != first) {
1738 "node %d/%d not inserted into bottom hole, expected %d, found %d\n",
1739 m, n, first, node_index(node));
1742 __clear_bit(first, bitmap);
1745 DRM_MM_BUG_ON(find_first_bit(bitmap, count) != count);
1750 drm_mm_for_each_node_safe(node, next, &mm)
1751 drm_mm_remove_node(node);
1752 DRM_MM_BUG_ON(!drm_mm_clean(&mm));
1757 drm_mm_for_each_node_safe(node, next, &mm)
1758 drm_mm_remove_node(node);
1759 drm_mm_takedown(&mm);
1762 bitmap_free(bitmap);
1767 static void drm_test_mm_once(struct kunit *test, unsigned int mode)
1770 struct drm_mm_node rsvd_lo, rsvd_hi, node;
1772 drm_mm_init(&mm, 0, 7);
1774 memset(&rsvd_lo, 0, sizeof(rsvd_lo));
1777 if (drm_mm_reserve_node(&mm, &rsvd_lo)) {
1778 KUNIT_FAIL(test, "Could not reserve low node\n");
1782 memset(&rsvd_hi, 0, sizeof(rsvd_hi));
1785 if (drm_mm_reserve_node(&mm, &rsvd_hi)) {
1786 KUNIT_FAIL(test, "Could not reserve low node\n");
1790 if (!drm_mm_hole_follows(&rsvd_lo) || !drm_mm_hole_follows(&rsvd_hi)) {
1791 KUNIT_FAIL(test, "Expected a hole after lo and high nodes!\n");
1795 memset(&node, 0, sizeof(node));
1796 if (drm_mm_insert_node_generic(&mm, &node, 2, 0, 0, mode)) {
1797 KUNIT_FAIL(test, "Could not insert the node into the available hole!\n");
1801 drm_mm_remove_node(&node);
1803 drm_mm_remove_node(&rsvd_hi);
1805 drm_mm_remove_node(&rsvd_lo);
1807 drm_mm_takedown(&mm);
1810 static void drm_test_mm_lowest(struct kunit *test)
1812 drm_test_mm_once(test, DRM_MM_INSERT_LOW);
1815 static void drm_test_mm_highest(struct kunit *test)
1817 drm_test_mm_once(test, DRM_MM_INSERT_HIGH);
1820 static void separate_adjacent_colors(const struct drm_mm_node *node,
1821 unsigned long color, u64 *start, u64 *end)
1823 if (drm_mm_node_allocated(node) && node->color != color)
1826 node = list_next_entry(node, node_list);
1827 if (drm_mm_node_allocated(node) && node->color != color)
1831 static bool colors_abutt(struct kunit *test, const struct drm_mm_node *node)
1833 if (!drm_mm_hole_follows(node) &&
1834 drm_mm_node_allocated(list_next_entry(node, node_list))) {
1835 KUNIT_FAIL(test, "colors abutt; %ld [%llx + %llx] is next to %ld [%llx + %llx]!\n",
1836 node->color, node->start, node->size,
1837 list_next_entry(node, node_list)->color,
1838 list_next_entry(node, node_list)->start,
1839 list_next_entry(node, node_list)->size);
1846 static void drm_test_mm_color(struct kunit *test)
1848 const unsigned int count = min(4096u, max_iterations);
1849 const struct insert_mode *mode;
1851 struct drm_mm_node *node, *nn;
1854 /* Color adjustment complicates everything. First we just check
1855 * that when we insert a node we apply any color_adjustment callback.
1856 * The callback we use should ensure that there is a gap between
1857 * any two nodes, and so after each insertion we check that those
1858 * holes are inserted and that they are preserved.
1861 drm_mm_init(&mm, 0, U64_MAX);
1863 for (n = 1; n <= count; n++) {
1864 node = kzalloc(sizeof(*node), GFP_KERNEL);
1868 if (!expect_insert(test, &mm, node, n, 0, n, &insert_modes[0])) {
1869 KUNIT_FAIL(test, "insert failed, step %d\n", n);
1875 drm_mm_for_each_node_safe(node, nn, &mm) {
1876 if (node->color != node->size) {
1877 KUNIT_FAIL(test, "invalid color stored: expected %lld, found %ld\n",
1878 node->size, node->color);
1883 drm_mm_remove_node(node);
1887 /* Now, let's start experimenting with applying a color callback */
1888 mm.color_adjust = separate_adjacent_colors;
1889 for (mode = insert_modes; mode->name; mode++) {
1892 node = kzalloc(sizeof(*node), GFP_KERNEL);
1896 node->size = 1 + 2 * count;
1897 node->color = node->size;
1899 if (drm_mm_reserve_node(&mm, node)) {
1900 KUNIT_FAIL(test, "initial reserve failed!\n");
1904 last = node->start + node->size;
1906 for (n = 1; n <= count; n++) {
1909 node = kzalloc(sizeof(*node), GFP_KERNEL);
1914 node->size = n + count;
1915 node->color = node->size;
1917 if (drm_mm_reserve_node(&mm, node) != -ENOSPC) {
1918 KUNIT_FAIL(test, "reserve %d did not report color overlap!", n);
1922 node->start += n + 1;
1923 rem = misalignment(node, n + count);
1924 node->start += n + count - rem;
1926 if (drm_mm_reserve_node(&mm, node)) {
1927 KUNIT_FAIL(test, "reserve %d failed", n);
1931 last = node->start + node->size;
1934 for (n = 1; n <= count; n++) {
1935 node = kzalloc(sizeof(*node), GFP_KERNEL);
1939 if (!expect_insert(test, &mm, node, n, n, n, mode)) {
1940 KUNIT_FAIL(test, "%s insert failed, step %d\n", mode->name, n);
1946 drm_mm_for_each_node_safe(node, nn, &mm) {
1949 if (node->color != node->size) {
1951 "%s invalid color stored: expected %lld, found %ld\n",
1952 mode->name, node->size, node->color);
1957 if (colors_abutt(test, node))
1960 div64_u64_rem(node->start, node->size, &rem);
1963 "%s colored node misaligned, start=%llx expected alignment=%lld [rem=%lld]\n",
1964 mode->name, node->start, node->size, rem);
1968 drm_mm_remove_node(node);
1976 drm_mm_for_each_node_safe(node, nn, &mm) {
1977 drm_mm_remove_node(node);
1980 drm_mm_takedown(&mm);
1983 static int evict_color(struct kunit *test, struct drm_mm *mm, u64 range_start,
1984 u64 range_end, struct evict_node *nodes, unsigned int *order,
1985 unsigned int count, unsigned int size, unsigned int alignment,
1986 unsigned long color, const struct insert_mode *mode)
1988 struct drm_mm_scan scan;
1989 LIST_HEAD(evict_list);
1990 struct evict_node *e;
1991 struct drm_mm_node tmp;
1994 drm_mm_scan_init_with_range(&scan, mm, size, alignment, color, range_start,
1995 range_end, mode->mode);
1996 if (!evict_nodes(test, &scan, nodes, order, count, true, &evict_list))
1999 memset(&tmp, 0, sizeof(tmp));
2000 err = drm_mm_insert_node_generic(mm, &tmp, size, alignment, color,
2001 DRM_MM_INSERT_EVICT);
2004 "Failed to insert into eviction hole: size=%d, align=%d, color=%lu, err=%d\n",
2005 size, alignment, color, err);
2006 show_scan(test, &scan);
2007 show_holes(test, mm, 3);
2011 if (tmp.start < range_start || tmp.start + tmp.size > range_end) {
2013 "Inserted [address=%llu + %llu] did not fit into the request range [%llu, %llu]\n",
2014 tmp.start, tmp.size, range_start, range_end);
2018 if (colors_abutt(test, &tmp))
2021 if (!assert_node(test, &tmp, mm, size, alignment, color)) {
2023 "Inserted did not fit the eviction hole: size=%lld [%d], align=%d [rem=%lld], start=%llx\n",
2024 tmp.size, size, alignment, misalignment(&tmp, alignment), tmp.start);
2028 drm_mm_remove_node(&tmp);
2032 list_for_each_entry(e, &evict_list, link) {
2033 err = drm_mm_reserve_node(mm, &e->node);
2035 KUNIT_FAIL(test, "Failed to reinsert node after eviction: start=%llx\n",
2045 static void drm_test_mm_color_evict(struct kunit *test)
2047 DRM_RND_STATE(prng, random_seed);
2048 const unsigned int total_size = min(8192u, max_iterations);
2049 const struct insert_mode *mode;
2050 unsigned long color = 0;
2052 struct evict_node *nodes;
2053 struct drm_mm_node *node, *next;
2054 unsigned int *order, n;
2056 /* Check that the drm_mm_scan also honours color adjustment when
2057 * choosing its victims to create a hole. Our color_adjust does not
2058 * allow two nodes to be placed together without an intervening hole
2059 * enlarging the set of victims that must be evicted.
2062 nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
2063 KUNIT_ASSERT_TRUE(test, nodes);
2065 order = drm_random_order(total_size, &prng);
2069 drm_mm_init(&mm, 0, 2 * total_size - 1);
2070 mm.color_adjust = separate_adjacent_colors;
2071 for (n = 0; n < total_size; n++) {
2072 if (!expect_insert(test, &mm, &nodes[n].node,
2074 &insert_modes[0])) {
2075 KUNIT_FAIL(test, "insert failed, step %d\n", n);
2080 for (mode = evict_modes; mode->name; mode++) {
2081 for (n = 1; n <= total_size; n <<= 1) {
2082 drm_random_reorder(order, total_size, &prng);
2083 if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
2084 n, 1, color++, mode)) {
2085 KUNIT_FAIL(test, "%s evict_color(size=%u) failed\n", mode->name, n);
2090 for (n = 1; n < total_size; n <<= 1) {
2091 drm_random_reorder(order, total_size, &prng);
2092 if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
2093 total_size / 2, n, color++, mode)) {
2094 KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
2095 mode->name, total_size / 2, n);
2100 for_each_prime_number_from(n, 1, min(total_size, max_prime)) {
2101 unsigned int nsize = (total_size - n + 1) / 2;
2103 DRM_MM_BUG_ON(!nsize);
2105 drm_random_reorder(order, total_size, &prng);
2106 if (evict_color(test, &mm, 0, U64_MAX, nodes, order, total_size,
2107 nsize, n, color++, mode)) {
2108 KUNIT_FAIL(test, "%s evict_color(size=%u, alignment=%u) failed\n",
2109 mode->name, nsize, n);
2118 drm_mm_for_each_node_safe(node, next, &mm)
2119 drm_mm_remove_node(node);
2120 drm_mm_takedown(&mm);
2126 static void drm_test_mm_color_evict_range(struct kunit *test)
2128 DRM_RND_STATE(prng, random_seed);
2129 const unsigned int total_size = 8192;
2130 const unsigned int range_size = total_size / 2;
2131 const unsigned int range_start = total_size / 4;
2132 const unsigned int range_end = range_start + range_size;
2133 const struct insert_mode *mode;
2134 unsigned long color = 0;
2136 struct evict_node *nodes;
2137 struct drm_mm_node *node, *next;
2138 unsigned int *order, n;
2140 /* Like drm_test_mm_color_evict(), but limited to small portion of the full
2144 nodes = vzalloc(array_size(total_size, sizeof(*nodes)));
2145 KUNIT_ASSERT_TRUE(test, nodes);
2147 order = drm_random_order(total_size, &prng);
2151 drm_mm_init(&mm, 0, 2 * total_size - 1);
2152 mm.color_adjust = separate_adjacent_colors;
2153 for (n = 0; n < total_size; n++) {
2154 if (!expect_insert(test, &mm, &nodes[n].node,
2156 &insert_modes[0])) {
2157 KUNIT_FAIL(test, "insert failed, step %d\n", n);
2162 for (mode = evict_modes; mode->name; mode++) {
2163 for (n = 1; n <= range_size; n <<= 1) {
2164 drm_random_reorder(order, range_size, &prng);
2165 if (evict_color(test, &mm, range_start, range_end, nodes, order,
2166 total_size, n, 1, color++, mode)) {
2168 "%s evict_color(size=%u) failed for range [%x, %x]\n",
2169 mode->name, n, range_start, range_end);
2174 for (n = 1; n < range_size; n <<= 1) {
2175 drm_random_reorder(order, total_size, &prng);
2176 if (evict_color(test, &mm, range_start, range_end, nodes, order,
2177 total_size, range_size / 2, n, color++, mode)) {
2179 "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
2180 mode->name, total_size / 2, n, range_start, range_end);
2185 for_each_prime_number_from(n, 1, min(range_size, max_prime)) {
2186 unsigned int nsize = (range_size - n + 1) / 2;
2188 DRM_MM_BUG_ON(!nsize);
2190 drm_random_reorder(order, total_size, &prng);
2191 if (evict_color(test, &mm, range_start, range_end, nodes, order,
2192 total_size, nsize, n, color++, mode)) {
2194 "%s evict_color(size=%u, alignment=%u) failed for range [%x, %x]\n",
2195 mode->name, nsize, n, range_start, range_end);
2204 drm_mm_for_each_node_safe(node, next, &mm)
2205 drm_mm_remove_node(node);
2206 drm_mm_takedown(&mm);
2212 static int drm_mm_suite_init(struct kunit_suite *suite)
2214 while (!random_seed)
2215 random_seed = get_random_u32();
2218 "Testing DRM range manager, with random_seed=0x%x max_iterations=%u max_prime=%u\n",
2219 random_seed, max_iterations, max_prime);
2224 module_param(random_seed, uint, 0400);
2225 module_param(max_iterations, uint, 0400);
2226 module_param(max_prime, uint, 0400);
2228 static struct kunit_case drm_mm_tests[] = {
2229 KUNIT_CASE(drm_test_mm_init),
2230 KUNIT_CASE(drm_test_mm_debug),
2231 KUNIT_CASE(drm_test_mm_reserve),
2232 KUNIT_CASE(drm_test_mm_insert),
2233 KUNIT_CASE(drm_test_mm_replace),
2234 KUNIT_CASE(drm_test_mm_insert_range),
2235 KUNIT_CASE(drm_test_mm_frag),
2236 KUNIT_CASE(drm_test_mm_align),
2237 KUNIT_CASE(drm_test_mm_align32),
2238 KUNIT_CASE(drm_test_mm_align64),
2239 KUNIT_CASE(drm_test_mm_evict),
2240 KUNIT_CASE(drm_test_mm_evict_range),
2241 KUNIT_CASE(drm_test_mm_topdown),
2242 KUNIT_CASE(drm_test_mm_bottomup),
2243 KUNIT_CASE(drm_test_mm_lowest),
2244 KUNIT_CASE(drm_test_mm_highest),
2245 KUNIT_CASE(drm_test_mm_color),
2246 KUNIT_CASE(drm_test_mm_color_evict),
2247 KUNIT_CASE(drm_test_mm_color_evict_range),
2251 static struct kunit_suite drm_mm_test_suite = {
2253 .suite_init = drm_mm_suite_init,
2254 .test_cases = drm_mm_tests,
2257 kunit_test_suite(drm_mm_test_suite);
2259 MODULE_AUTHOR("Intel Corporation");
2260 MODULE_LICENSE("GPL");