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
