Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider: - [Medium] gpu/buddy: spurious -ENOSPC in __alloc_contig_try_harder() for range allocs --
commit 14acba1e552d60bba9eee2e544118f0040222647 Author: Arunpravin Paneer Selvam <[email protected]> gpu/buddy: add range-restricted contiguous allocation fallback This commit extends __alloc_contig_try_harder() with a [range_start, range_end) window and routes range-restricted contiguous allocations through it when no single naturally aligned power-of-two block is available. It also adds KUnit tests covering fragmented range-restricted contiguous allocations. > diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c > index 8eb5835d6944c..693176ee3daf9 100644 > --- a/drivers/gpu/buddy.c > +++ b/drivers/gpu/buddy.c > @@ -1723,43 +1723,32 @@ 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; > + 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) { > @@ -1767,25 +1756,48 @@ static int __alloc_contig_try_harder(struct gpu_buddy > *mm, > > rhs_offset = gpu_buddy_block_offset(block); > > + if (rhs_offset >= range_end) { > + iter = rb_prev(iter); > + continue; > + } > + if (rhs_offset < range_start) > + break; > + > /* Allocate blocks traversing RHS */ > err = __gpu_buddy_alloc_range(mm, rhs_offset, size, > flags, &filled, blocks); > if (err && err != -ENOSPC) > return err; > - if (!err && IS_ALIGNED(rhs_offset, min_block_size)) > + > + if (!err) > + filled = size; > + > + /* Clamp a full or partial fill that crosses range_end. */ > + if (rhs_offset + filled > range_end) { > + gpu_buddy_free_list_internal(mm, blocks); > + filled = range_end - rhs_offset; > + err = -ENOSPC; > + } > + > + if (!err && IS_ALIGNED(rhs_offset, min_block_size) && > + rhs_offset >= range_start) > 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); > + if (!err) > + return 0; > + if (err != -ENOSPC) { > + gpu_buddy_free_list_internal(mm, > blocks); > + return err; > + } > } > goto next; > } > @@ -1797,21 +1809,73 @@ 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); > iter = rb_prev(iter); > } > > +next_order: > + if (order > min_order) { > + order--; > + goto retry_order; > + } > + > + /* A non-range request would already have used a larger free block. */ > + if (!(flags & GPU_BUDDY_RANGE_ALLOCATION)) > + return -ENOSPC; > + > + /* > + * Last resort: the span may sit inside a larger coalesced free block > + * that the anchored search above never visits. Walk the higher-order > + * free trees (bounded by the number of free blocks, not by the window > + * size) and carve the highest aligned span that fits inside each block > + * overlapping the window. > + */ > + for (order = fls(modify_size >> ilog2(mm->chunk_size)); > + order <= mm->max_order; order++) { > + for (iter = rb_last(&mm->free_tree[order]); iter; > + iter = rb_prev(iter)) { > + u64 low, high; > + > + block = rbtree_get_free_block(iter); > + low = max(gpu_buddy_block_offset(block), range_start); > + high = min(gpu_buddy_block_offset(block) + > + gpu_buddy_block_size(mm, block), range_end); > + > + if (gpu_buddy_block_offset(block) >= range_end) > + continue; > + if (gpu_buddy_block_offset(block) + > + gpu_buddy_block_size(mm, block) <= range_start) > + break; > + if (high < low + size) > + continue; > + > + aligned = round_down(high - size, min_block_size); > + if (aligned < low) > + continue; > + > + err = __gpu_buddy_alloc_range(mm, aligned, size, flags, > + NULL, blocks); > + if (!err) > + return 0; > + gpu_buddy_free_list_internal(mm, blocks); > + if (err != -ENOSPC) > + return err; > + } > + } > + > return -ENOSPC; > } [Severity: Medium] Can __alloc_contig_try_harder() in drivers/gpu/buddy.c still return a spurious -ENOSPC when gpu_buddy_alloc_blocks() falls back to it for a range-restricted or min_block_size-aligned contiguous allocation, if the valid contiguous span straddles a higher-order block boundary, a block starting below range_start, or a trailing block smaller than min_block_size? In the first pass, the anchored search starts at order = fls(pages) - 1, breaks out of the free_tree walk as soon as rhs_offset < range_start: if (rhs_offset < range_start) break; and stops descending at min_order: if (order > min_order) { order--; goto retry_order; } In the second pass, the higher-order free_tree walk clips each individual free block to [range_start, range_end) via [low, high) and requires the entire request to fit inside that single block's clipped window: if (high < low + size) continue; aligned = round_down(high - size, min_block_size); if (aligned < low) continue; Because the second pass only tests placements contained within a single higher-order block and never stitches across adjacent free blocks, it looks like valid contiguous placements are missed in three cases: 1. A span straddling two higher-order blocks. For example, with chunk_size of 4K, if [16K, 32K) is free at order 2 and [32K, 64K) is free at order 3, a request for size = 12K, min_block_size = 4K in [24K, 40K) finds free_tree[1] and free_tree[0] empty in the first pass. The second pass clips [16K, 32K) to [24K, 32K) (8K < 12K) and [32K, 64K) to [32K, 40K) (8K < 12K), skipping both and returning -ENOSPC even though the entire [24K, 40K) window is free. 2. A span straddling a block that starts below range_start and an adjacent higher-order block. With the same free blocks [16K, 32K) at order 2 and [32K, 64K) at order 3, a request for size = 20K, min_block_size = 4K in [20K, 44K) starts at order 2, sees rhs_offset = 16K < range_start (20K), and breaks without probing from range_start. The second pass clips [32K, 64K) to [32K, 44K) (12K < 20K) and skips it, returning -ENOSPC when [20K, 44K) is completely free. 3. A span straddling a higher-order block and a trailing free block with order < min_order. Since min_block_size constrains the start alignment of the contiguous span rather than the size of trailing blocks stitched into it, if [0, 16K) is free at order 2 and [16K, 20K) is free at order 0, a request for size = 12K, min_block_size = 8K (min_order = 1) in [8K, 24K) stops descending at min_order = 1 without visiting [16K, 20K) at order 0, and the second pass skips [0, 16K) because [8K, 16K) is only 8K (< 12K), missing the valid 8K-aligned placement at [8K, 20K). Should the anchored search instead start from mm->max_order down to order 0, and clamp blocks that start below range_start (with rhs_offset + block_size > range_start) to start probing at range_start? [ ... ] -- Sashiko AI review ยท https://sashiko.dev/#/patchset/[email protected]?part=2
