gpu/buddy: bail out of try_harder when alignment cannot be honoured

The try_harder contiguous fallback could return a range whose start
offset did not match the caller's min_block_size. When a candidate's
start is misaligned, realign it: free the misaligned run and reallocate
exactly @size at the next lower min_block_size boundary. This keeps the
returned size unchanged with no surplus to trim, and rejects the request
only when no aligned candidate fits.

v2: align misaligned candidates down to min_block_size instead of
    bailing out, for both the RHS and LHS paths (Matthew).

Fixes: 0a1844bf0b ("drm/buddy: Improve contiguous memory allocation")
Suggested-by: Christian König <christian.koenig@amd.com>
Cc: Matthew Auld <matthew.auld@intel.com>
Cc: Christian König <christian.koenig@amd.com>
Cc: Timur Kristóf <timur.kristof@gmail.com>
Cc: stable@vger.kernel.org
Reviewed-by: Matthew Auld <matthew.auld@intel.com>
Tested-by: John Olender <john.olender@gmail.com>
Signed-off-by: Arunpravin Paneer Selvam <Arunpravin.PaneerSelvam@amd.com>
Link: https://patch.msgid.link/20260709131050.1022759-1-Arunpravin.PaneerSelvam@amd.com
This commit is contained in:
Arunpravin Paneer Selvam
2026-07-09 18:40:50 +05:30
parent 06c2b8d7ea
commit 56bc638431

View File

@@ -1084,22 +1084,30 @@ 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,
struct list_head *blocks)
{
u64 aligned_offset = round_down(unaligned_offset, min_block_size);
return __gpu_buddy_alloc_range(mm, aligned_offset, size, NULL, blocks);
}
static int __alloc_contig_try_harder(struct gpu_buddy *mm,
u64 size,
u64 min_block_size,
struct list_head *blocks)
{
u64 rhs_offset, lhs_offset, lhs_size, filled;
u64 rhs_offset, lhs_offset, filled;
struct gpu_buddy_block *block;
unsigned int tree, order;
LIST_HEAD(blocks_lhs);
unsigned long pages;
u64 modify_size;
int err;
modify_size = rounddown_pow_of_two(size);
pages = modify_size >> ilog2(mm->chunk_size);
order = fls(pages) - 1;
order = ilog2(modify_size) - ilog2(mm->chunk_size);
if (order == 0)
return -ENOSPC;
@@ -1115,31 +1123,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm,
while (iter) {
block = rbtree_get_free_block(iter);
/* Allocate blocks traversing RHS */
rhs_offset = gpu_buddy_block_offset(block);
/* Allocate blocks traversing RHS */
err = __gpu_buddy_alloc_range(mm, rhs_offset, size,
&filled, blocks);
if (!err || err != -ENOSPC)
if (err && err != -ENOSPC)
return err;
lhs_size = max((size - filled), min_block_size);
if (!IS_ALIGNED(lhs_size, min_block_size))
lhs_size = round_up(lhs_size, min_block_size);
/* Allocate blocks traversing LHS */
lhs_offset = gpu_buddy_block_offset(block) - lhs_size;
err = __gpu_buddy_alloc_range(mm, lhs_offset, lhs_size,
NULL, &blocks_lhs);
if (!err) {
list_splice(&blocks_lhs, blocks);
if (!err && IS_ALIGNED(rhs_offset, min_block_size))
return 0;
} else if (err != -ENOSPC) {
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,
blocks);
if (!err)
return 0;
if (err != -ENOSPC) {
gpu_buddy_free_list_internal(mm, blocks);
return err;
}
goto next;
}
if (size - filled > rhs_offset)
goto next;
lhs_offset = rhs_offset - (size - filled);
/* 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, blocks);
if (!err)
return 0;
if (err != -ENOSPC) {
gpu_buddy_free_list_internal(mm, blocks);
return err;
}
/* Free blocks for the next iteration */
next:
gpu_buddy_free_list_internal(mm, blocks);
iter = rb_prev(iter);
}
}