From: Arunpravin Paneer Selvam <arunpravin.paneerselvam@amd.com>
To: Matthew Auld <matthew.auld@intel.com>,
dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org,
intel-xe@lists.freedesktop.org, amd-gfx@lists.freedesktop.org
Cc: christian.koenig@amd.com, alexander.deucher@amd.com,
Anand.Raghavendra@amd.com
Subject: Re: [PATCH v2] gpu/buddy: add range-restricted contiguous allocation fallback
Date: Mon, 5 Oct 2026 19:44:19 +0530 [thread overview]
Message-ID: <b86fd371-289d-479f-bdfe-b2d6136d8d21@amd.com> (raw)
In-Reply-To: <254159a2-66f8-4fce-b185-84e81d126e25@intel.com>
On 10/1/2026 11:58 PM, Matthew Auld wrote:
> On 30/09/2026 07:21, Arunpravin Paneer Selvam wrote:
>> From: Arunpravin Paneer Selvam <Arunpravin.PaneerSelvam@amd.com>
>>
>> A range + contiguous allocation (e.g. a scanout FB confined to the
>> CPU-visible VRAM aperture) rounds its size up to a power of two and
>> requires a naturally aligned free block of that size; on a fragmented
>> aperture no such aligned block may exist even though enough contiguous
>> space is free, so the allocation fails with -ENOSPC.
>>
>> The non-range contiguous path already recovers from this via
>> __alloc_contig_try_harder(), which stitches an exact-size span from
>> smaller adjacent blocks, but that fallback was unreachable once
>> GPU_BUDDY_RANGE_ALLOCATION was set. Give __alloc_contig_try_harder()
>> a [range_start, range_end) window and route the range + contiguous
>> case through it: each candidate placement is confined to the window
>> and aligned to min_block_size. Non-range callers pass [0, mm->size),
>> where the guards are no-ops, so the existing behaviour is unchanged.
>> A KUnit regression test covers the fragmentation pattern.
>>
>> Resolves the igt@kms_plane@plane-panning-bottom-right@pipe-a/pipe-b
>> regression.
>>
>> v2:
>> - Drop the split-undo patch; the range-bias search always descends to
>> an exact-order block or fails the split (already handled), so the
>> extra undo was redundant. (Matthew)
>> - Verified this fallback alone fixes the kms_plane regression.
>>
>> Fixes: 1ad5e807f716 ("gpu/buddy: replace dual-tree/force_merge with
>> decoupled dirty tracker")
>
> Patch looks more like totally new functionally/feature, so the fixes
> here is maybe unexpected? Did something in that fixes commit change
> something such that the try_harder is now needed, but that needs some
> expansion with bias + contig? Do we know exactly what changed here?
That commit removed __force_merge(), which previously helped recover
larger contiguous allocations on demand. As a result, some
range-restricted contiguous allocation requests
that used to succeed now fail with -ENOSPC (the kms_plane regression).
This patch restores that capability through the try_harder() stitching
logic, so the Fixes: tag reflects a
regression introduced by that commit rather than new functionality.
>
>> Assisted-by: Claude:claude-opus-4-8
>
> Assisted-by: LLM
>
>> Cc: Matthew Auld <matthew.auld@intel.com>
>> Cc: Christian König <christian.koenig@amd.com>
>> Signed-off-by: Arunpravin Paneer Selvam
>> <Arunpravin.PaneerSelvam@amd.com>
>> ---
>> drivers/gpu/buddy.c | 88 ++++++++++++++++--------------
>> drivers/gpu/tests/gpu_buddy_test.c | 83 ++++++++++++++++++++++++++--
>> 2 files changed, 126 insertions(+), 45 deletions(-)
>>
>> diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
>> index 2f2aaadafe35..5265e1f6a318 100644
>> --- a/drivers/gpu/buddy.c
>> +++ b/drivers/gpu/buddy.c
>> @@ -1685,26 +1685,14 @@ static int __gpu_buddy_alloc_range(struct
>> gpu_buddy *mm,
>> blocks, total_allocated_on_err);
>> }
>> -static int __alloc_contig_aligned_retry(struct gpu_buddy *mm,
>> - u64 unaligned_offset,
>> - u64 size,
>> - u64 min_block_size,
>> - unsigned long flags,
>> - struct list_head *blocks)
>> -{
>> - u64 aligned_offset = round_down(unaligned_offset, min_block_size);
>> -
>> - return __gpu_buddy_alloc_range(mm, aligned_offset, size, flags,
>> - NULL, blocks);
>> -}
>> -
>> static int __alloc_contig_try_harder(struct gpu_buddy *mm,
>> + u64 range_start, u64 range_end,
>> u64 size,
>> u64 min_block_size,
>> unsigned long flags,
>> struct list_head *blocks)
>> {
>> - u64 rhs_offset, lhs_offset, filled;
>> + u64 rhs_offset, lhs_offset, filled, aligned;
>> struct gpu_buddy_block *block;
>> struct rb_root *root;
>> struct rb_node *iter;
>> @@ -1734,20 +1722,24 @@ static int __alloc_contig_try_harder(struct
>> gpu_buddy *mm,
>> flags, &filled, blocks);
>> if (err && err != -ENOSPC)
>> return err;
>> - if (!err && IS_ALIGNED(rhs_offset, min_block_size))
>> + if (!err && IS_ALIGNED(rhs_offset, min_block_size) &&
>> + rhs_offset >= range_start && rhs_offset + size <=
>> range_end)
>> return 0;
>> if (!err) {
>> /* Allocate the unaligned RHS offset using round_down */
>> gpu_buddy_free_list_internal(mm, blocks);
>> - err = __alloc_contig_aligned_retry(mm, rhs_offset,
>> - size,
>> - min_block_size,
>> - flags, blocks);
>> - if (!err)
>> - return 0;
>> - if (err != -ENOSPC) {
>> - gpu_buddy_free_list_internal(mm, blocks);
>> - return err;
>> +
>> + aligned = round_down(rhs_offset, min_block_size);
>> + if (aligned >= range_start &&
>> + aligned + size <= range_end) {
>> + err = __gpu_buddy_alloc_range(mm, aligned, size,
>> + flags, NULL, blocks);
>
> Did you consider doing a bias-range for [start, end] using
> min_block_size and using what it returns as the starting point,
> extending left/right? If that fails advance start and try again? Maybe
> what you have here is much better for the case you have in mind?
I took a closer look at the alloc_range_bias approach. My understanding
is that this would repeatedly bias within [start, end], use the returned
min_block_size block as a starting point, and then extend left/right to
build the requested run.
While that should work, every failed attempt may require splitting
higher-order blocks to obtain a min_block_size block, followed by a
free/merge back when the extension fails.
The free-tree descent evaluates the available starting points through a
tree walk without any splitting or allocation just for enumeration, so I
kept that approach.
Please let me know if there is a case where alloc_range_bias would
provide an advantage over the free-tree descent.
While evaluating it, I found two issues in the current try_harder()
implementation:
1. It only searches free_tree[size_order]. This is insufficient when
min_block_size < size. For example, for a 128 KiB allocation (order-5)
with min_block_size = 4 KiB, free_tree[5] may be empty
while two adjacent order-4 (64 KiB) blocks covering [64 KiB, 128 KiB]
and [128 KiB, 192 KiB] are free. These blocks still form a valid
order-5-sized contiguous run, but they can never appear in
free_tree[5] because they are not mergeable buddies. As a result, a
search that only considers free_tree[5] will never find this placement.
v3 addresses this by walking the free tree from the
requested size order down to min_order, while reusing the existing
extend/stitch logic and min_block_size alignment checks unchanged.
2. The search is not restricted to the requested range. v3 confines the
walk to [range_start, range_end], skipping candidates at or above
range_end and stopping once the walk falls below range_start.
This ensures that range-constrained allocations only consider free
blocks within the requested window and avoids performing allocation/free
attempts on blocks outside the specified range.
For non-range callers, the effective window remains [0, mm->size], so
the behavior is unchanged.
Regards,
Arun.
>
>> + if (!err)
>> + return 0;
>> + if (err != -ENOSPC) {
>> + gpu_buddy_free_list_internal(mm, blocks);
>> + return err;
>> + }
>> }
>> goto next;
>> }
>> @@ -1759,15 +1751,17 @@ static int __alloc_contig_try_harder(struct
>> gpu_buddy *mm,
>> /* Allocate the unaligned LHS offset using round_down */
>> gpu_buddy_free_list_internal(mm, blocks);
>> - err = __alloc_contig_aligned_retry(mm, lhs_offset,
>> - size,
>> - min_block_size,
>> - flags, blocks);
>> - if (!err)
>> - return 0;
>> - if (err != -ENOSPC) {
>> - gpu_buddy_free_list_internal(mm, blocks);
>> - return err;
>> +
>> + aligned = round_down(lhs_offset, min_block_size);
>> + if (aligned >= range_start && aligned + size <= range_end) {
>> + err = __gpu_buddy_alloc_range(mm, aligned, size,
>> + flags, NULL, blocks);
>> + if (!err)
>> + return 0;
>> + if (err != -ENOSPC) {
>> + gpu_buddy_free_list_internal(mm, blocks);
>> + return err;
>> + }
>> }
>> next:
>> gpu_buddy_free_list_internal(mm, blocks);
>> @@ -2021,11 +2015,18 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>> min_order = ilog2(min_block_size) - ilog2(mm->chunk_size);
>> if (order > mm->max_order || size > mm->size) {
>> - if ((flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) &&
>> - !(flags & GPU_BUDDY_RANGE_ALLOCATION))
>> - return __alloc_contig_try_harder(mm, original_size,
>> + if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) {
>> + u64 range_start, range_end;
>> +
>> + range_start = (flags & GPU_BUDDY_RANGE_ALLOCATION) ?
>> start : 0;
>> + range_end = (flags & GPU_BUDDY_RANGE_ALLOCATION) ? end :
>> mm->size;
>> +
>> + return __alloc_contig_try_harder(mm, range_start,
>> + range_end,
>> + original_size,
>> original_min_size,
>> flags, blocks);
>> + }
>> return -EINVAL;
>> }
>> @@ -2058,9 +2059,14 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>> * Try contiguous block allocation through
>> * try harder method.
>> */
>> - if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION &&
>> - !(flags & GPU_BUDDY_RANGE_ALLOCATION)) {
>> - err = __alloc_contig_try_harder(mm,
>> + if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) {
>> + u64 range_start, range_end;
>> +
>> + range_start = (flags & GPU_BUDDY_RANGE_ALLOCATION) ?
>> start : 0;
>> + range_end = (flags & GPU_BUDDY_RANGE_ALLOCATION) ?
>> end : mm->size;
>> +
>> + err = __alloc_contig_try_harder(mm, range_start,
>> + range_end,
>> original_size,
>> original_min_size,
>> flags,
>> @@ -2068,9 +2074,9 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>> if (!err)
>> return 0;
>> if (err != -ENOSPC)
>> - return err;
>> - goto err_free;
>> + goto err_free;
>> }
>> +
>> err = -ENOSPC;
>> goto err_free;
>> } while (1);
>> diff --git a/drivers/gpu/tests/gpu_buddy_test.c
>> b/drivers/gpu/tests/gpu_buddy_test.c
>> index b75d32ca6ca0..2c445870b808 100644
>> --- a/drivers/gpu/tests/gpu_buddy_test.c
>> +++ b/drivers/gpu/tests/gpu_buddy_test.c
>> @@ -1251,6 +1251,77 @@ static void
>> gpu_test_buddy_alloc_contiguous(struct kunit *test)
>> gpu_buddy_fini(&mm);
>> }
>> +static void gpu_test_buddy_alloc_range_contiguous(struct kunit *test)
>> +{
>> + const unsigned long ps = SZ_4K, mm_size = 16 * ps;
>> + const unsigned long range_end = 8 * 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");
>> +
>> + /*
>> + * Idea is to confine the test to the sub-range [0, 32K), which
>> a 12K
>> + * contiguous request (rounded up to 16K) splits into two naturally
>> + * aligned 16K slots: [0, 16K) and [16K, 32K). We pin the first
>> 4K page
>> + * of each slot ([0, 4K) and [16K, 20K)) so that neither aligned
>> slot
>> + * can satisfy the rounded-up 16K allocation, yet the freed
>> remainder
>> + * still leaves a contiguous 12K hole at offset 4K, which is
>> page-aligned
>> + * but not 16K-aligned. A 12K contiguous+range allocation must
>> therefore
>> + * fall back to stitching that span instead of returning -ENOSPC.
>> + */
>> + KUNIT_ASSERT_FALSE_MSG(test,
>> + gpu_buddy_alloc_blocks(&mm, 0, ps, ps, ps,
>> + &pin_lo, 0),
>> + "failed to pin low page\n");
>> + KUNIT_ASSERT_FALSE_MSG(test,
>> + gpu_buddy_alloc_blocks(&mm, 4 * ps, 5 * ps, ps,
>> + ps, &pin_hi, 0),
>> + "failed to pin high page\n");
>> +
>> + /* No aligned 16K block is free; the range-aware fallback must
>> stitch
>> + * the unaligned [ps, 4*ps) hole instead of returning -ENOSPC.
>> + */
>> + KUNIT_ASSERT_FALSE_MSG(test,
>> + gpu_buddy_alloc_blocks(&mm, 0, range_end, 3 * ps,
>> + ps, &allocated,
>> + GPU_BUDDY_CONTIGUOUS_ALLOCATION |
>> + GPU_BUDDY_RANGE_ALLOCATION),
>> + "range-restricted contiguous alloc failed\n");
>> +
>> + /* The result must be exactly 3*ps, contiguous, and inside the
>> range. */
>> + 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 + bsize <= range_end,
>> + "block [%llx, %llx) outside range\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, 3 * 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 void gpu_test_buddy_alloc_pathological(struct kunit *test)
>> {
>> u64 mm_size, size, start = 0;
>> @@ -1534,10 +1605,13 @@ static void
>> gpu_test_buddy_alloc_exceeds_max_order(struct kunit *test)
>> GPU_BUDDY_RANGE_ALLOCATION);
>> KUNIT_EXPECT_EQ(test, err, -EINVAL);
>> - /* CONTIGUOUS + RANGE should return -EINVAL (no try_harder for
>> RANGE) */
>> - err = gpu_buddy_alloc_blocks(&mm, 0, mm_size, size, SZ_4K, &blocks,
>> - GPU_BUDDY_CONTIGUOUS_ALLOCATION |
>> GPU_BUDDY_RANGE_ALLOCATION);
>> - KUNIT_EXPECT_EQ(test, err, -EINVAL);
>> + /* CONTIGUOUS + RANGE should succeed via the range-aware
>> try_harder */
>> + KUNIT_ASSERT_FALSE_MSG(test, gpu_buddy_alloc_blocks(&mm, 0,
>> mm_size, size,
>> + SZ_4K, &blocks,
>> + GPU_BUDDY_CONTIGUOUS_ALLOCATION |
>> + GPU_BUDDY_RANGE_ALLOCATION),
>> + "range contiguous alloc hit an error
>> size=%llu\n", size);
>> + gpu_buddy_free_list(&mm, &blocks, 0);
>> gpu_buddy_fini(&mm);
>> }
>> @@ -1603,6 +1677,7 @@ static struct kunit_case gpu_buddy_tests[] = {
>> KUNIT_CASE(gpu_test_buddy_alloc_pessimistic),
>> 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_clear),
>> KUNIT_CASE(gpu_test_buddy_alloc_range),
>> KUNIT_CASE(gpu_test_buddy_alloc_range_bias),
>>
>> base-commit: 744f262401cc2e1f3827496c72a47e072d31a852
>
next prev parent reply other threads:[~2026-10-05 14:14 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-09-30 6:21 [PATCH v2] gpu/buddy: add range-restricted contiguous allocation fallback Arunpravin Paneer Selvam
2026-09-30 6:29 ` ✓ CI.KUnit: success for " Patchwork
2026-09-30 6:31 ` [PATCH v2] " sashiko-bot
2026-09-30 7:32 ` ✓ Xe.CI.BAT: success for " Patchwork
2026-09-30 8:00 ` ✓ i915.CI.BAT: " Patchwork
2026-09-30 11:53 ` ✗ Xe.CI.FULL: failure " Patchwork
2026-09-30 18:34 ` ✗ i915.CI.Full: " Patchwork
2026-10-01 7:06 ` [PATCH v2] " Arunpravin Paneer Selvam
2026-10-01 18:28 ` Matthew Auld
2026-10-05 14:14 ` Arunpravin Paneer Selvam [this message]
2026-10-06 9:57 ` Matthew Auld
2026-10-09 9:14 ` Arunpravin Paneer Selvam
2026-10-09 10:17 ` Matthew Auld
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=b86fd371-289d-479f-bdfe-b2d6136d8d21@amd.com \
--to=arunpravin.paneerselvam@amd.com \
--cc=Anand.Raghavendra@amd.com \
--cc=alexander.deucher@amd.com \
--cc=amd-gfx@lists.freedesktop.org \
--cc=christian.koenig@amd.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=intel-gfx@lists.freedesktop.org \
--cc=intel-xe@lists.freedesktop.org \
--cc=matthew.auld@intel.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.