LCOV - code coverage report
Current view: top level - drivers/gpu/drm/tests - drm_buddy_test.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 260 330 78.8 %
Date: 2023-03-27 20:00:47 Functions: 12 14 85.7 %

          Line data    Source code
       1             : // SPDX-License-Identifier: MIT
       2             : /*
       3             :  * Copyright © 2019 Intel Corporation
       4             :  * Copyright © 2022 Maíra Canal <mairacanal@riseup.net>
       5             :  */
       6             : 
       7             : #include <kunit/test.h>
       8             : 
       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 TIMEOUT(name__)                                                         \
      17             :         unsigned long name__ = jiffies + MAX_SCHEDULE_TIMEOUT
      18             : 
      19             : static unsigned int random_seed;
      20             : 
      21             : static inline u64 get_size(int order, u64 chunk_size)
      22             : {
      23        9770 :         return (1 << order) * chunk_size;
      24             : }
      25             : 
      26             : __printf(2, 3)
      27        9615 : static bool __timeout(unsigned long timeout, const char *fmt, ...)
      28             : {
      29             :         va_list va;
      30             : 
      31        9615 :         if (!signal_pending(current)) {
      32        9615 :                 cond_resched();
      33        9615 :                 if (time_before(jiffies, timeout))
      34             :                         return false;
      35             :         }
      36             : 
      37           0 :         if (fmt) {
      38           0 :                 va_start(va, fmt);
      39           0 :                 vprintk(fmt, va);
      40           0 :                 va_end(va);
      41             :         }
      42             : 
      43             :         return true;
      44             : }
      45             : 
      46           0 : static void __dump_block(struct kunit *test, struct drm_buddy *mm,
      47             :                          struct drm_buddy_block *block, bool buddy)
      48             : {
      49           0 :         kunit_err(test, "block info: header=%llx, state=%u, order=%d, offset=%llx size=%llx root=%d buddy=%d\n",
      50             :                   block->header, drm_buddy_block_state(block),
      51             :                           drm_buddy_block_order(block), drm_buddy_block_offset(block),
      52             :                           drm_buddy_block_size(mm, block), !block->parent, buddy);
      53           0 : }
      54             : 
      55           0 : static void dump_block(struct kunit *test, struct drm_buddy *mm,
      56             :                        struct drm_buddy_block *block)
      57             : {
      58             :         struct drm_buddy_block *buddy;
      59             : 
      60           0 :         __dump_block(test, mm, block, false);
      61             : 
      62           0 :         buddy = drm_get_buddy(block);
      63           0 :         if (buddy)
      64           0 :                 __dump_block(test, mm, buddy, true);
      65           0 : }
      66             : 
      67       10135 : static int check_block(struct kunit *test, struct drm_buddy *mm,
      68             :                        struct drm_buddy_block *block)
      69             : {
      70             :         struct drm_buddy_block *buddy;
      71             :         unsigned int block_state;
      72             :         u64 block_size;
      73             :         u64 offset;
      74       10135 :         int err = 0;
      75             : 
      76       20270 :         block_state = drm_buddy_block_state(block);
      77             : 
      78       20270 :         if (block_state != DRM_BUDDY_ALLOCATED &&
      79       10135 :             block_state != DRM_BUDDY_FREE && block_state != DRM_BUDDY_SPLIT) {
      80           0 :                 kunit_err(test, "block state mismatch\n");
      81           0 :                 err = -EINVAL;
      82             :         }
      83             : 
      84       20270 :         block_size = drm_buddy_block_size(mm, block);
      85       20270 :         offset = drm_buddy_block_offset(block);
      86             : 
      87       10135 :         if (block_size < mm->chunk_size) {
      88           0 :                 kunit_err(test, "block size smaller than min size\n");
      89           0 :                 err = -EINVAL;
      90             :         }
      91             : 
      92       10135 :         if (!is_power_of_2(block_size)) {
      93           0 :                 kunit_err(test, "block size not power of two\n");
      94           0 :                 err = -EINVAL;
      95             :         }
      96             : 
      97       10135 :         if (!IS_ALIGNED(block_size, mm->chunk_size)) {
      98           0 :                 kunit_err(test, "block size not aligned to min size\n");
      99           0 :                 err = -EINVAL;
     100             :         }
     101             : 
     102       10135 :         if (!IS_ALIGNED(offset, mm->chunk_size)) {
     103           0 :                 kunit_err(test, "block offset not aligned to min size\n");
     104           0 :                 err = -EINVAL;
     105             :         }
     106             : 
     107       10135 :         if (!IS_ALIGNED(offset, block_size)) {
     108           0 :                 kunit_err(test, "block offset not aligned to block size\n");
     109           0 :                 err = -EINVAL;
     110             :         }
     111             : 
     112       10135 :         buddy = drm_get_buddy(block);
     113             : 
     114       10135 :         if (!buddy && block->parent) {
     115           0 :                 kunit_err(test, "buddy has gone fishing\n");
     116           0 :                 err = -EINVAL;
     117             :         }
     118             : 
     119       10135 :         if (buddy) {
     120       19682 :                 if (drm_buddy_block_offset(buddy) != (offset ^ block_size)) {
     121           0 :                         kunit_err(test, "buddy has wrong offset\n");
     122           0 :                         err = -EINVAL;
     123             :                 }
     124             : 
     125       19682 :                 if (drm_buddy_block_size(mm, buddy) != block_size) {
     126           0 :                         kunit_err(test, "buddy size mismatch\n");
     127           0 :                         err = -EINVAL;
     128             :                 }
     129             : 
     130       19682 :                 if (drm_buddy_block_state(buddy) == block_state &&
     131             :                     block_state == DRM_BUDDY_FREE) {
     132           0 :                         kunit_err(test, "block and its buddy are free\n");
     133           0 :                         err = -EINVAL;
     134             :                 }
     135             :         }
     136             : 
     137       10135 :         return err;
     138             : }
     139             : 
     140          63 : static int check_blocks(struct kunit *test, struct drm_buddy *mm,
     141             :                         struct list_head *blocks, u64 expected_size, bool is_contiguous)
     142             : {
     143             :         struct drm_buddy_block *block;
     144             :         struct drm_buddy_block *prev;
     145             :         u64 total;
     146          63 :         int err = 0;
     147             : 
     148          63 :         block = NULL;
     149          63 :         prev = NULL;
     150          63 :         total = 0;
     151             : 
     152        9974 :         list_for_each_entry(block, blocks, link) {
     153        9911 :                 err = check_block(test, mm, block);
     154             : 
     155        9911 :                 if (!drm_buddy_block_is_allocated(block)) {
     156           0 :                         kunit_err(test, "block not allocated\n");
     157           0 :                         err = -EINVAL;
     158             :                 }
     159             : 
     160        9911 :                 if (is_contiguous && prev) {
     161             :                         u64 prev_block_size;
     162             :                         u64 prev_offset;
     163             :                         u64 offset;
     164             : 
     165         492 :                         prev_offset = drm_buddy_block_offset(prev);
     166         492 :                         prev_block_size = drm_buddy_block_size(mm, prev);
     167         492 :                         offset = drm_buddy_block_offset(block);
     168             : 
     169         246 :                         if (offset != (prev_offset + prev_block_size)) {
     170           0 :                                 kunit_err(test, "block offset mismatch\n");
     171           0 :                                 err = -EINVAL;
     172             :                         }
     173             :                 }
     174             : 
     175        9911 :                 if (err)
     176             :                         break;
     177             : 
     178       19822 :                 total += drm_buddy_block_size(mm, block);
     179        9911 :                 prev = block;
     180             :         }
     181             : 
     182          63 :         if (!err) {
     183          63 :                 if (total != expected_size) {
     184           0 :                         kunit_err(test, "size mismatch, expected=%llx, found=%llx\n",
     185             :                                   expected_size, total);
     186           0 :                         err = -EINVAL;
     187             :                 }
     188             :                 return err;
     189             :         }
     190             : 
     191           0 :         if (prev) {
     192           0 :                 kunit_err(test, "prev block, dump:\n");
     193           0 :                 dump_block(test, mm, prev);
     194             :         }
     195             : 
     196           0 :         kunit_err(test, "bad block, dump:\n");
     197           0 :         dump_block(test, mm, block);
     198             : 
     199           0 :         return err;
     200             : }
     201             : 
     202          28 : static int check_mm(struct kunit *test, struct drm_buddy *mm)
     203             : {
     204             :         struct drm_buddy_block *root;
     205             :         struct drm_buddy_block *prev;
     206             :         unsigned int i;
     207             :         u64 total;
     208          28 :         int err = 0;
     209             : 
     210          28 :         if (!mm->n_roots) {
     211           0 :                 kunit_err(test, "n_roots is zero\n");
     212           0 :                 return -EINVAL;
     213             :         }
     214             : 
     215          56 :         if (mm->n_roots != hweight64(mm->size)) {
     216           0 :                 kunit_err(test, "n_roots mismatch, n_roots=%u, expected=%lu\n",
     217             :                           mm->n_roots, hweight64(mm->size));
     218           0 :                 return -EINVAL;
     219             :         }
     220             : 
     221             :         root = NULL;
     222             :         prev = NULL;
     223             :         total = 0;
     224             : 
     225         224 :         for (i = 0; i < mm->n_roots; ++i) {
     226             :                 struct drm_buddy_block *block;
     227             :                 unsigned int order;
     228             : 
     229         224 :                 root = mm->roots[i];
     230         224 :                 if (!root) {
     231           0 :                         kunit_err(test, "root(%u) is NULL\n", i);
     232           0 :                         err = -EINVAL;
     233           0 :                         break;
     234             :                 }
     235             : 
     236         224 :                 err = check_block(test, mm, root);
     237             : 
     238         224 :                 if (!drm_buddy_block_is_free(root)) {
     239           0 :                         kunit_err(test, "root not free\n");
     240           0 :                         err = -EINVAL;
     241             :                 }
     242             : 
     243         448 :                 order = drm_buddy_block_order(root);
     244             : 
     245         224 :                 if (!i) {
     246          28 :                         if (order != mm->max_order) {
     247           0 :                                 kunit_err(test, "max order root missing\n");
     248           0 :                                 err = -EINVAL;
     249             :                         }
     250             :                 }
     251             : 
     252         224 :                 if (prev) {
     253             :                         u64 prev_block_size;
     254             :                         u64 prev_offset;
     255             :                         u64 offset;
     256             : 
     257         392 :                         prev_offset = drm_buddy_block_offset(prev);
     258         392 :                         prev_block_size = drm_buddy_block_size(mm, prev);
     259         392 :                         offset = drm_buddy_block_offset(root);
     260             : 
     261         196 :                         if (offset != (prev_offset + prev_block_size)) {
     262           0 :                                 kunit_err(test, "root offset mismatch\n");
     263           0 :                                 err = -EINVAL;
     264             :                         }
     265             :                 }
     266             : 
     267         224 :                 block = list_first_entry_or_null(&mm->free_list[order],
     268             :                                                  struct drm_buddy_block, link);
     269         224 :                 if (block != root) {
     270           0 :                         kunit_err(test, "root mismatch at order=%u\n", order);
     271           0 :                         err = -EINVAL;
     272             :                 }
     273             : 
     274         224 :                 if (err)
     275             :                         break;
     276             : 
     277         224 :                 prev = root;
     278         448 :                 total += drm_buddy_block_size(mm, root);
     279             :         }
     280             : 
     281          28 :         if (!err) {
     282          28 :                 if (total != mm->size) {
     283           0 :                         kunit_err(test, "expected mm size=%llx, found=%llx\n",
     284             :                                   mm->size, total);
     285           0 :                         err = -EINVAL;
     286             :                 }
     287             :                 return err;
     288             :         }
     289             : 
     290           0 :         if (prev) {
     291           0 :                 kunit_err(test, "prev root(%u), dump:\n", i - 1);
     292           0 :                 dump_block(test, mm, prev);
     293             :         }
     294             : 
     295           0 :         if (root) {
     296           0 :                 kunit_err(test, "bad root(%u), dump:\n", i);
     297           0 :                 dump_block(test, mm, root);
     298             :         }
     299             : 
     300             :         return err;
     301             : }
     302             : 
     303           2 : static void mm_config(u64 *size, u64 *chunk_size)
     304             : {
     305           2 :         DRM_RND_STATE(prng, random_seed);
     306             :         u32 s, ms;
     307             : 
     308             :         /* Nothing fancy, just try to get an interesting bit pattern */
     309             : 
     310           2 :         prandom_seed_state(&prng, random_seed);
     311             : 
     312             :         /* Let size be a random number of pages up to 8 GB (2M pages) */
     313           2 :         s = 1 + drm_prandom_u32_max_state((BIT(33 - 12)) - 1, &prng);
     314             :         /* Let the chunk size be a random power of 2 less than size */
     315           4 :         ms = BIT(drm_prandom_u32_max_state(ilog2(s), &prng));
     316             :         /* Round size down to the chunk size */
     317           2 :         s &= -ms;
     318             : 
     319             :         /* Convert from pages to bytes */
     320           2 :         *chunk_size = (u64)ms << 12;
     321           2 :         *size = (u64)s << 12;
     322           2 : }
     323             : 
     324           1 : static void drm_test_buddy_alloc_pathological(struct kunit *test)
     325             : {
     326           1 :         u64 mm_size, size, start = 0;
     327             :         struct drm_buddy_block *block;
     328           1 :         const int max_order = 3;
     329           1 :         unsigned long flags = 0;
     330             :         int order, top;
     331             :         struct drm_buddy mm;
     332           1 :         LIST_HEAD(blocks);
     333           1 :         LIST_HEAD(holes);
     334           1 :         LIST_HEAD(tmp);
     335             : 
     336             :         /*
     337             :          * Create a pot-sized mm, then allocate one of each possible
     338             :          * order within. This should leave the mm with exactly one
     339             :          * page left. Free the largest block, then whittle down again.
     340             :          * Eventually we will have a fully 50% fragmented mm.
     341             :          */
     342             : 
     343           1 :         mm_size = PAGE_SIZE << max_order;
     344           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE),
     345             :                                "buddy_init failed\n");
     346             : 
     347           1 :         KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
     348             : 
     349           3 :         for (top = max_order; top; top--) {
     350             :                 /* Make room by freeing the largest allocated block */
     351           3 :                 block = list_first_entry_or_null(&blocks, typeof(*block), link);
     352           3 :                 if (block) {
     353           4 :                         list_del(&block->link);
     354           2 :                         drm_buddy_free_block(&mm, block);
     355             :                 }
     356             : 
     357           9 :                 for (order = top; order--;) {
     358           6 :                         size = get_size(order, PAGE_SIZE);
     359           6 :                         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start,
     360             :                                                                             mm_size, size, size,
     361             :                                                                                 &tmp, flags),
     362             :                                         "buddy_alloc hit -ENOMEM with order=%d, top=%d\n",
     363             :                                         order, top);
     364             : 
     365           6 :                         block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     366           6 :                         KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     367             : 
     368           6 :                         list_move_tail(&block->link, &blocks);
     369             :                 }
     370             : 
     371             :                 /* There should be one final page for this sub-allocation */
     372           3 :                 size = get_size(0, PAGE_SIZE);
     373           3 :                 KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     374             :                                                                     size, size, &tmp, flags),
     375             :                                                            "buddy_alloc hit -ENOMEM for hole\n");
     376             : 
     377           3 :                 block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     378           3 :                 KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     379             : 
     380           6 :                 list_move_tail(&block->link, &holes);
     381             : 
     382           3 :                 size = get_size(top, PAGE_SIZE);
     383           3 :                 KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     384             :                                                                    size, size, &tmp, flags),
     385             :                                                           "buddy_alloc unexpectedly succeeded at top-order %d/%d, it should be full!",
     386             :                                                           top, max_order);
     387             :         }
     388             : 
     389           1 :         drm_buddy_free_list(&mm, &holes);
     390             : 
     391             :         /* Nothing larger than blocks of chunk_size now available */
     392           4 :         for (order = 1; order <= max_order; order++) {
     393           3 :                 size = get_size(order, PAGE_SIZE);
     394           3 :                 KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     395             :                                                                    size, size, &tmp, flags),
     396             :                                                           "buddy_alloc unexpectedly succeeded at order %d, it should be full!",
     397             :                                                           order);
     398             :         }
     399             : 
     400           1 :         list_splice_tail(&holes, &blocks);
     401           1 :         drm_buddy_free_list(&mm, &blocks);
     402           1 :         drm_buddy_fini(&mm);
     403           1 : }
     404             : 
     405           1 : static void drm_test_buddy_alloc_smoke(struct kunit *test)
     406             : {
     407           1 :         u64 mm_size, chunk_size, start = 0;
     408           1 :         unsigned long flags = 0;
     409             :         struct drm_buddy mm;
     410             :         int *order;
     411             :         int i;
     412             : 
     413           1 :         DRM_RND_STATE(prng, random_seed);
     414           1 :         TIMEOUT(end_time);
     415             : 
     416           1 :         mm_config(&mm_size, &chunk_size);
     417             : 
     418           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, chunk_size),
     419             :                                "buddy_init failed\n");
     420             : 
     421           1 :         order = drm_random_order(mm.max_order + 1, &prng);
     422           1 :         KUNIT_ASSERT_TRUE(test, order);
     423             : 
     424          13 :         for (i = 0; i <= mm.max_order; ++i) {
     425             :                 struct drm_buddy_block *block;
     426          13 :                 int max_order = order[i];
     427          13 :                 bool timeout = false;
     428          13 :                 LIST_HEAD(blocks);
     429             :                 u64 total, size;
     430          13 :                 LIST_HEAD(tmp);
     431             :                 int order, err;
     432             : 
     433          13 :                 KUNIT_ASSERT_FALSE_MSG(test, check_mm(test, &mm),
     434             :                                        "pre-mm check failed, abort\n");
     435             : 
     436             :                 order = max_order;
     437             :                 total = 0;
     438             : 
     439             :                 do {
     440             : retry:
     441       19386 :                         size = get_size(order, chunk_size);
     442        9693 :                         err = drm_buddy_alloc_blocks(&mm, start, mm_size, size, size, &tmp, flags);
     443        9693 :                         if (err) {
     444          78 :                                 if (err == -ENOMEM) {
     445           0 :                                         KUNIT_FAIL(test, "buddy_alloc hit -ENOMEM with order=%d\n",
     446             :                                                    order);
     447             :                                 } else {
     448          78 :                                         if (order--) {
     449             :                                                 err = 0;
     450             :                                                 goto retry;
     451             :                                         }
     452             : 
     453           0 :                                         KUNIT_FAIL(test, "buddy_alloc with order=%d failed\n",
     454             :                                                    order);
     455             :                                 }
     456             : 
     457             :                                 break;
     458             :                         }
     459             : 
     460        9615 :                         block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     461        9615 :                         KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     462             : 
     463       19230 :                         list_move_tail(&block->link, &blocks);
     464       19230 :                         KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), order,
     465             :                                             "buddy_alloc order mismatch\n");
     466             : 
     467       19230 :                         total += drm_buddy_block_size(&mm, block);
     468             : 
     469        9615 :                         if (__timeout(end_time, NULL)) {
     470             :                                 timeout = true;
     471             :                                 break;
     472             :                         }
     473        9615 :                 } while (total < mm.size);
     474             : 
     475          13 :                 if (!err)
     476          13 :                         err = check_blocks(test, &mm, &blocks, total, false);
     477             : 
     478          13 :                 drm_buddy_free_list(&mm, &blocks);
     479             : 
     480          13 :                 if (!err) {
     481          13 :                         KUNIT_EXPECT_FALSE_MSG(test, check_mm(test, &mm),
     482             :                                                "post-mm check failed\n");
     483             :                 }
     484             : 
     485          13 :                 if (err || timeout)
     486             :                         break;
     487             : 
     488          13 :                 cond_resched();
     489             :         }
     490             : 
     491           1 :         kfree(order);
     492           1 :         drm_buddy_fini(&mm);
     493           1 : }
     494             : 
     495           1 : static void drm_test_buddy_alloc_pessimistic(struct kunit *test)
     496             : {
     497           1 :         u64 mm_size, size, start = 0;
     498             :         struct drm_buddy_block *block, *bn;
     499           1 :         const unsigned int max_order = 16;
     500           1 :         unsigned long flags = 0;
     501             :         struct drm_buddy mm;
     502             :         unsigned int order;
     503           1 :         LIST_HEAD(blocks);
     504           1 :         LIST_HEAD(tmp);
     505             : 
     506             :         /*
     507             :          * Create a pot-sized mm, then allocate one of each possible
     508             :          * order within. This should leave the mm with exactly one
     509             :          * page left.
     510             :          */
     511             : 
     512           1 :         mm_size = PAGE_SIZE << max_order;
     513           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE),
     514             :                                "buddy_init failed\n");
     515             : 
     516           1 :         KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
     517             : 
     518          16 :         for (order = 0; order < max_order; order++) {
     519          32 :                 size = get_size(order, PAGE_SIZE);
     520          16 :                 KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     521             :                                                                     size, size, &tmp, flags),
     522             :                                                            "buddy_alloc hit -ENOMEM with order=%d\n",
     523             :                                                            order);
     524             : 
     525          16 :                 block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     526          16 :                 KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     527             : 
     528          32 :                 list_move_tail(&block->link, &blocks);
     529             :         }
     530             : 
     531             :         /* And now the last remaining block available */
     532           1 :         size = get_size(0, PAGE_SIZE);
     533           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     534             :                                                             size, size, &tmp, flags),
     535             :                                                    "buddy_alloc hit -ENOMEM on final alloc\n");
     536             : 
     537           1 :         block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     538           1 :         KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     539             : 
     540           2 :         list_move_tail(&block->link, &blocks);
     541             : 
     542             :         /* Should be completely full! */
     543          18 :         for (order = max_order; order--;) {
     544          32 :                 size = get_size(order, PAGE_SIZE);
     545          16 :                 KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     546             :                                                                    size, size, &tmp, flags),
     547             :                                                           "buddy_alloc unexpectedly succeeded, it should be full!");
     548             :         }
     549             : 
     550           1 :         block = list_last_entry(&blocks, typeof(*block), link);
     551           2 :         list_del(&block->link);
     552           1 :         drm_buddy_free_block(&mm, block);
     553             : 
     554             :         /* As we free in increasing size, we make available larger blocks */
     555           1 :         order = 1;
     556          17 :         list_for_each_entry_safe(block, bn, &blocks, link) {
     557          32 :                 list_del(&block->link);
     558          16 :                 drm_buddy_free_block(&mm, block);
     559             : 
     560          32 :                 size = get_size(order, PAGE_SIZE);
     561          16 :                 KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     562             :                                                                     size, size, &tmp, flags),
     563             :                                                            "buddy_alloc hit -ENOMEM with order=%d\n",
     564             :                                                            order);
     565             : 
     566          16 :                 block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     567          16 :                 KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     568             : 
     569          32 :                 list_del(&block->link);
     570          16 :                 drm_buddy_free_block(&mm, block);
     571          16 :                 order++;
     572             :         }
     573             : 
     574             :         /* To confirm, now the whole mm should be available */
     575           1 :         size = get_size(max_order, PAGE_SIZE);
     576           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     577             :                                                             size, size, &tmp, flags),
     578             :                                                    "buddy_alloc (realloc) hit -ENOMEM with order=%d\n",
     579             :                                                    max_order);
     580             : 
     581           1 :         block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     582           1 :         KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     583             : 
     584           2 :         list_del(&block->link);
     585           1 :         drm_buddy_free_block(&mm, block);
     586           1 :         drm_buddy_free_list(&mm, &blocks);
     587           1 :         drm_buddy_fini(&mm);
     588           1 : }
     589             : 
     590           1 : static void drm_test_buddy_alloc_optimistic(struct kunit *test)
     591             : {
     592           1 :         u64 mm_size, size, start = 0;
     593             :         struct drm_buddy_block *block;
     594           1 :         unsigned long flags = 0;
     595           1 :         const int max_order = 16;
     596             :         struct drm_buddy mm;
     597           1 :         LIST_HEAD(blocks);
     598           1 :         LIST_HEAD(tmp);
     599             :         int order;
     600             : 
     601             :         /*
     602             :          * Create a mm with one block of each order available, and
     603             :          * try to allocate them all.
     604             :          */
     605             : 
     606           1 :         mm_size = PAGE_SIZE * ((1 << (max_order + 1)) - 1);
     607             : 
     608           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, mm_size, PAGE_SIZE),
     609             :                                "buddy_init failed\n");
     610             : 
     611           1 :         KUNIT_EXPECT_EQ(test, mm.max_order, max_order);
     612             : 
     613          17 :         for (order = 0; order <= max_order; order++) {
     614          17 :                 size = get_size(order, PAGE_SIZE);
     615          17 :                 KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     616             :                                                                     size, size, &tmp, flags),
     617             :                                                            "buddy_alloc hit -ENOMEM with order=%d\n",
     618             :                                                            order);
     619             : 
     620          17 :                 block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     621          17 :                 KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_blocks has no blocks\n");
     622             : 
     623          34 :                 list_move_tail(&block->link, &blocks);
     624             :         }
     625             : 
     626             :         /* Should be completely full! */
     627           1 :         size = get_size(0, PAGE_SIZE);
     628           1 :         KUNIT_ASSERT_TRUE_MSG(test, drm_buddy_alloc_blocks(&mm, start, mm_size,
     629             :                                                            size, size, &tmp, flags),
     630             :                                                   "buddy_alloc unexpectedly succeeded, it should be full!");
     631             : 
     632           1 :         drm_buddy_free_list(&mm, &blocks);
     633           1 :         drm_buddy_fini(&mm);
     634           1 : }
     635             : 
     636           1 : static void drm_test_buddy_alloc_range(struct kunit *test)
     637             : {
     638           1 :         unsigned long flags = DRM_BUDDY_RANGE_ALLOCATION;
     639             :         u64 offset, size, rem, chunk_size, end;
     640             :         unsigned long page_num;
     641             :         struct drm_buddy mm;
     642           1 :         LIST_HEAD(blocks);
     643             : 
     644           1 :         mm_config(&size, &chunk_size);
     645             : 
     646           1 :         KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_init(&mm, size, chunk_size),
     647             :                                "buddy_init failed");
     648             : 
     649           1 :         KUNIT_ASSERT_FALSE_MSG(test, check_mm(test, &mm),
     650             :                                "pre-mm check failed, abort!");
     651             : 
     652           1 :         rem = mm.size;
     653           1 :         offset = 0;
     654             : 
     655          50 :         for_each_prime_number_from(page_num, 1, ULONG_MAX - 1) {
     656             :                 struct drm_buddy_block *block;
     657          50 :                 LIST_HEAD(tmp);
     658             : 
     659          50 :                 size = min(page_num * mm.chunk_size, rem);
     660          50 :                 end = offset + size;
     661             : 
     662          50 :                 KUNIT_ASSERT_FALSE_MSG(test, drm_buddy_alloc_blocks(&mm, offset, end,
     663             :                                                                     size, mm.chunk_size,
     664             :                                                                         &tmp, flags),
     665             :                                 "alloc_range with offset=%llx, size=%llx failed\n", offset, size);
     666             : 
     667          50 :                 block = list_first_entry_or_null(&tmp, struct drm_buddy_block, link);
     668          50 :                 KUNIT_ASSERT_TRUE_MSG(test, block, "alloc_range has no blocks\n");
     669             : 
     670         100 :                 KUNIT_ASSERT_EQ_MSG(test, drm_buddy_block_offset(block), offset,
     671             :                                     "alloc_range start offset mismatch, found=%llx, expected=%llx\n",
     672             :                                                         drm_buddy_block_offset(block), offset);
     673             : 
     674          50 :                 KUNIT_ASSERT_FALSE(test, check_blocks(test, &mm, &tmp, size, true));
     675             : 
     676          50 :                 list_splice_tail(&tmp, &blocks);
     677             : 
     678          50 :                 offset += size;
     679             : 
     680          50 :                 rem -= size;
     681          50 :                 if (!rem)
     682             :                         break;
     683             : 
     684          49 :                 cond_resched();
     685             :         }
     686             : 
     687           1 :         drm_buddy_free_list(&mm, &blocks);
     688             : 
     689           1 :         KUNIT_EXPECT_FALSE_MSG(test, check_mm(test, &mm), "post-mm check failed\n");
     690             : 
     691           1 :         drm_buddy_fini(&mm);
     692           1 : }
     693             : 
     694           1 : static void drm_test_buddy_alloc_limit(struct kunit *test)
     695             : {
     696           1 :         u64 size = U64_MAX, start = 0;
     697             :         struct drm_buddy_block *block;
     698           1 :         unsigned long flags = 0;
     699           1 :         LIST_HEAD(allocated);
     700             :         struct drm_buddy mm;
     701             : 
     702           1 :         KUNIT_EXPECT_FALSE(test, drm_buddy_init(&mm, size, PAGE_SIZE));
     703             : 
     704           1 :         KUNIT_EXPECT_EQ_MSG(test, mm.max_order, DRM_BUDDY_MAX_ORDER,
     705             :                             "mm.max_order(%d) != %d\n", mm.max_order,
     706             :                                                 DRM_BUDDY_MAX_ORDER);
     707             : 
     708           1 :         size = mm.chunk_size << mm.max_order;
     709           1 :         KUNIT_EXPECT_FALSE(test, drm_buddy_alloc_blocks(&mm, start, size, size,
     710             :                                                         PAGE_SIZE, &allocated, flags));
     711             : 
     712           1 :         block = list_first_entry_or_null(&allocated, struct drm_buddy_block, link);
     713           1 :         KUNIT_EXPECT_TRUE(test, block);
     714             : 
     715           2 :         KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_order(block), mm.max_order,
     716             :                             "block order(%d) != %d\n",
     717             :                                                 drm_buddy_block_order(block), mm.max_order);
     718             : 
     719           2 :         KUNIT_EXPECT_EQ_MSG(test, drm_buddy_block_size(&mm, block),
     720             :                             BIT_ULL(mm.max_order) * PAGE_SIZE,
     721             :                                                 "block size(%llu) != %llu\n",
     722             :                                                 drm_buddy_block_size(&mm, block),
     723             :                                                 BIT_ULL(mm.max_order) * PAGE_SIZE);
     724             : 
     725           1 :         drm_buddy_free_list(&mm, &allocated);
     726           1 :         drm_buddy_fini(&mm);
     727           1 : }
     728             : 
     729           1 : static int drm_buddy_suite_init(struct kunit_suite *suite)
     730             : {
     731           3 :         while (!random_seed)
     732           1 :                 random_seed = get_random_u32();
     733             : 
     734           1 :         kunit_info(suite, "Testing DRM buddy manager, with random_seed=0x%x\n", random_seed);
     735             : 
     736           1 :         return 0;
     737             : }
     738             : 
     739             : static struct kunit_case drm_buddy_tests[] = {
     740             :         KUNIT_CASE(drm_test_buddy_alloc_limit),
     741             :         KUNIT_CASE(drm_test_buddy_alloc_range),
     742             :         KUNIT_CASE(drm_test_buddy_alloc_optimistic),
     743             :         KUNIT_CASE(drm_test_buddy_alloc_pessimistic),
     744             :         KUNIT_CASE(drm_test_buddy_alloc_smoke),
     745             :         KUNIT_CASE(drm_test_buddy_alloc_pathological),
     746             :         {}
     747             : };
     748             : 
     749             : static struct kunit_suite drm_buddy_test_suite = {
     750             :         .name = "drm_buddy",
     751             :         .suite_init = drm_buddy_suite_init,
     752             :         .test_cases = drm_buddy_tests,
     753             : };
     754             : 
     755             : kunit_test_suite(drm_buddy_test_suite);
     756             : 
     757             : MODULE_AUTHOR("Intel Corporation");
     758             : MODULE_LICENSE("GPL");

Generated by: LCOV version 1.14