Intel-XE Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: sashiko-bot@kernel.org
To: "Arunpravin Paneer Selvam" <arunpravin.paneerselvam@amd.com>,
	dri-devel@lists.freedesktop.org, intel-gfx@lists.freedesktop.org,
	intel-xe@lists.freedesktop.org
Subject: Re: [PATCH v6 2/2] gpu/buddy: add range-restricted contiguous allocation fallback
Date: Wed, 21 Oct 2026 12:46:37 +0000	[thread overview]
Message-ID: <sashiko-outbox-180272@kernel.org> (raw)
In-Reply-To: <20261021121813.1820345-2-arunpravin.paneerselvam@amd.com>

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 <Arunpravin.PaneerSelvam@amd.com>

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/20261021121813.1820345-1-arunpravin.paneerselvam@amd.com?part=2

  reply	other threads:[~2026-10-21 12:46 UTC|newest]

Thread overview: 5+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-10-21 12:18 [PATCH v6 1/2] gpu/buddy: restore two-pass clear/dirty guard for range-biased allocation Arunpravin Paneer Selvam
2026-10-21 12:18 ` [PATCH v6 2/2] gpu/buddy: add range-restricted contiguous allocation fallback Arunpravin Paneer Selvam
2026-10-21 12:46   ` sashiko-bot [this message]
2026-10-21 12:49 ` [PATCH v6 1/2] gpu/buddy: restore two-pass clear/dirty guard for range-biased allocation Matthew Auld
2026-10-21 13:45   ` Arunpravin Paneer Selvam

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=sashiko-outbox-180272@kernel.org \
    --to=sashiko-bot@kernel.org \
    --cc=arunpravin.paneerselvam@amd.com \
    --cc=dri-devel@lists.freedesktop.org \
    --cc=intel-gfx@lists.freedesktop.org \
    --cc=intel-xe@lists.freedesktop.org \
    --cc=sashiko-reviews@lists.linux.dev \
    /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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox