From: Arunpravin Paneer Selvam <[email protected]>

__alloc_contig_try_harder() anchors on a single buddy order and bails
when that order is empty. But a contiguous run can still exist as
adjacent lower-order blocks that never coalesce (their buddies are
allocated), which a single order misses, so callers passing
min_block_size < size get a spurious -ENOSPC. Descend the anchor search
from the size order down to min_order and let the existing stitch build
the span. A KUnit test fails without this and passes with it.

Assisted-by: LLM
Cc: Matthew Auld <[email protected]>
Cc: Christian König <[email protected]>
Signed-off-by: Arunpravin Paneer Selvam <[email protected]>
---
 drivers/gpu/buddy.c                | 15 +++++--
 drivers/gpu/tests/gpu_buddy_test.c | 65 ++++++++++++++++++++++++++++++
 2 files changed, 76 insertions(+), 4 deletions(-)

diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index d8e7b7990715..239249362c9e 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -1694,22 +1694,23 @@ static int __alloc_contig_try_harder(struct gpu_buddy 
*mm,
 {
        u64 rhs_offset, lhs_offset, filled, aligned;
        struct gpu_buddy_block *block;
+       unsigned int min_order, order;
        struct rb_root *root;
        struct rb_node *iter;
        unsigned long pages;
-       unsigned int order;
        u64 modify_size;
        int err;
 
+       min_order = ilog2(min_block_size) - ilog2(mm->chunk_size);
+
        modify_size = rounddown_pow_of_two(size);
        pages = modify_size >> ilog2(mm->chunk_size);
        order = fls(pages) - 1;
-       if (order == 0)
-               return -ENOSPC;
 
+retry_order:
        root = &mm->free_tree[order];
        if (RB_EMPTY_ROOT(root))
-               return -ENOSPC;
+               goto next_order;
 
        iter = rb_last(root);
        while (iter) {
@@ -1775,6 +1776,12 @@ static int __alloc_contig_try_harder(struct gpu_buddy 
*mm,
                iter = rb_prev(iter);
        }
 
+next_order:
+       if (order > min_order) {
+               order--;
+               goto retry_order;
+       }
+
        return -ENOSPC;
 }
 
diff --git a/drivers/gpu/tests/gpu_buddy_test.c 
b/drivers/gpu/tests/gpu_buddy_test.c
index 2c445870b808..2e8f69a9a283 100644
--- a/drivers/gpu/tests/gpu_buddy_test.c
+++ b/drivers/gpu/tests/gpu_buddy_test.c
@@ -1660,6 +1660,70 @@ static void gpu_test_buddy_addr_to_block(struct kunit 
*test)
        gpu_buddy_fini(&mm);
 }
 
+static void gpu_test_buddy_alloc_contiguous_descent(struct kunit *test)
+{
+       const unsigned long ps = SZ_4K, mm_size = 16 * ps;
+       struct gpu_buddy_block *block, *prev;
+       LIST_HEAD(allocated);
+       struct gpu_buddy mm;
+       LIST_HEAD(pin_lo);
+       LIST_HEAD(pin_hi);
+       u64 total;
+
+       KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_init(&mm, mm_size, ps),
+                              "buddy_init failed\n");
+
+       /*
+        * Pin [0, 16K) and [48K, 64K). The remaining [16K, 48K) is free as two
+        * adjacent 16K halves under different parents; being un-mergeable
+        * buddies they never coalesce, so free_tree[3] (32K) stays empty. A
+        * contiguous 32K request with min_block_size 4K therefore has no 32K
+        * anchor and must descend to order 2 and stitch [16K, 48K).
+        */
+       KUNIT_ASSERT_FALSE_MSG(test,
+                              gpu_buddy_alloc_blocks(&mm, 0, 4 * ps, 4 * ps,
+                                                     ps, &pin_lo, 0),
+                              "failed to pin low block\n");
+       KUNIT_ASSERT_FALSE_MSG(test,
+                              gpu_buddy_alloc_blocks(&mm, 12 * ps, 16 * ps,
+                                                     4 * ps, ps, &pin_hi, 0),
+                              "failed to pin high block\n");
+
+       KUNIT_ASSERT_FALSE_MSG(test,
+                              gpu_buddy_alloc_blocks(&mm, 0, mm_size, 8 * ps,
+                                                     ps, &allocated,
+                                                     
GPU_BUDDY_CONTIGUOUS_ALLOCATION),
+                              "contiguous alloc failed to descend\n");
+
+       /* The result must be exactly 8*ps, contiguous, within [16K, 48K). */
+       total = 0;
+       prev = NULL;
+       list_for_each_entry(block, &allocated, link) {
+               u64 offset = gpu_buddy_block_offset(block);
+               u64 bsize = gpu_buddy_block_size(&mm, block);
+
+               KUNIT_EXPECT_TRUE_MSG(test, offset >= 4 * ps &&
+                                     offset + bsize <= 12 * ps,
+                                     "block [%llx, %llx) outside stitched 
span\n",
+                                     offset, offset + bsize);
+               if (prev)
+                       KUNIT_EXPECT_EQ_MSG(test,
+                                           gpu_buddy_block_offset(prev) +
+                                           gpu_buddy_block_size(&mm, prev),
+                                           offset,
+                                           "block at %llx not contiguous\n",
+                                           offset);
+               prev = block;
+               total += bsize;
+       }
+       KUNIT_EXPECT_EQ(test, total, 8 * ps);
+
+       gpu_buddy_free_list(&mm, &allocated, 0);
+       gpu_buddy_free_list(&mm, &pin_lo, 0);
+       gpu_buddy_free_list(&mm, &pin_hi, 0);
+       gpu_buddy_fini(&mm);
+}
+
 static int gpu_buddy_suite_init(struct kunit_suite *suite)
 {
        while (!random_seed)
@@ -1678,6 +1742,7 @@ static struct kunit_case gpu_buddy_tests[] = {
        KUNIT_CASE(gpu_test_buddy_alloc_pathological),
        KUNIT_CASE(gpu_test_buddy_alloc_contiguous),
        KUNIT_CASE(gpu_test_buddy_alloc_range_contiguous),
+       KUNIT_CASE(gpu_test_buddy_alloc_contiguous_descent),
        KUNIT_CASE(gpu_test_buddy_alloc_clear),
        KUNIT_CASE(gpu_test_buddy_alloc_range),
        KUNIT_CASE(gpu_test_buddy_alloc_range_bias),
-- 
2.43.0

Reply via email to