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

Generated by: LCOV version 1.14