All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v4] drm_buddy: Fix power-of-two rounding errors
@ 2026-11-01  8:11 David Gow
  2026-11-01  8:21 ` sashiko-bot
  0 siblings, 1 reply; 2+ messages in thread
From: David Gow @ 2026-11-01  8:11 UTC (permalink / raw)
  To: Jim Cromie, Maciej W . Rozycki, Andrew Morton, Matthew Auld,
	Arun Pravin, Joel Fernandes, David Airlie, Simona Vetter
  Cc: David Gow, dri-devel, linux-kernel

The standard roundup_pow_of_two() and rounddown_pow_of_two() macros use
unsigned long internally, which on 32-bit architectures (such as i386 and
arm32) is only 32-bit. Similarly, is_power_of_2() operates on an unsigned
long.

drm_test_buddy_alloc_exceeds_max_order() uses these on a u64 value,
where they silently truncate the 10GB allocation, causing the KUnit tests
to fail:
[17:49:27]     # gpu_test_buddy_alloc_exceeds_max_order: EXPECTATION FAILED at drivers/gpu/tests/gpu_buddy_test.c:1540
[17:49:27]     Expected err == -22, but
[17:49:27]         err == 0 (0x0)
[17:49:27]     # gpu_test_buddy_alloc_exceeds_max_order: drivers/gpu/buddy.c:973: gpu_buddy_assert(gpu_buddy_block_is_free(mm->roots[i]))
[17:49:27]     # gpu_test_buddy_alloc_exceeds_max_order: drivers/gpu/buddy.c:978: gpu_buddy_assert(mm->avail == mm->size)
[17:49:27]     # gpu_test_buddy_alloc_exceeds_max_order: drivers/gpu/buddy.c:981: gpu_buddy_assert(!mm->used_scoreboard[i])
[17:49:27] # gpu_buddy: pass:13 fail:1 skip:0 total:14

Ultimately, the correct fix here is to make the various power-of-two
functions handle u64 better, but since this is a large treewide change,
provide u64-specific helpers for the buddy allocator, and use these.

Also update the page count ('pages') to be a u64 instead of an unsigned
long, in case this overflows on 32-bit systems. This doesn't actually
trigger the test failure -- and it's very unlikely a 32-bit system will
have enough VRAM to need that many pages -- but it's still theoretically
possible.

Fixes: 14d1b9a6247c ("drm/i915: buddy allocator")
Co-developed-by: Jim Cromie <jim.cromie@gmail.com>
Signed-off-by: Jim Cromie <jim.cromie@gmail.com>
Signed-off-by: David Gow <david@davidgow.net>
---

This began as a rebase of patch 39 of v12 of this series:
https://lore.kernel.org/all/20260326185413.1205870-40-jim.cromie@gmail.com/
previously sent out as v3:
https://lore.kernel.org/all/20260913084907.520877-2-david@davidgow.net/

However, it's had basically a complete rewrite:
- The Fixes tag has been updated to point to the original introduction of
  the buddy allocator. I don't expect this to actually get backported,
  but that was the commit that introduced the various power_of_two uses.
- Instead of open-coding the round{up,down}_pow_of_two implementations,
  we provide our own copies of the functions, adjusted for _u64 support.
  These will be replaced with a tree-wide implementation in a follow-up.
  (Thanks, Dave!)
- The calls to is_power_of_2 are similarly replaced. While these didn't
  cause the test to fail, they're similarly wrong on 32-bit systems.
- The 'page' counts are moved from unsigned long to u64, and code is
  updated accordingly. I think it's incredibly unlikely that we're going
  to see GPUs with enough memory for more than 2^32 _pages_ on a 32-bit
  system, but I can't see any reason it shouldn't theoretically work.
  Let me know if this is a terrible idea, and I'll revert it in the next
  version.

The test failures can be reproduced with:
./tools/testing/kunit/kunit.py run --arch i386 gpu_drm_buddy

(or with any other 32-bit arch. For 32-bit UML, use --arch um --kconfig_add CONFIG_64BIT=n)

Cheers,
-- David


---
 drivers/gpu/buddy.c | 55 ++++++++++++++++++++++++++++++++++-----------
 1 file changed, 42 insertions(+), 13 deletions(-)

diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
index 503f7d5934e4..98764ee786d7 100644
--- a/drivers/gpu/buddy.c
+++ b/drivers/gpu/buddy.c
@@ -36,6 +36,35 @@
 #define gpu_buddy_assert(condition) WARN_ON(!(condition))
 #endif
 
+/**
+ * Implementations of pow_of_2 functions targeting u64:
+ *
+ * The various pow_of_2 functions in log2.h work on longs, which are 32-bit on
+ * 32-bit systems. Since the GPU address space is 64-bit even on systems with
+ * a 32-bit CPU, this causes needless truncation. Work is underway to improve
+ * the general implementations to support 64-bit types on 32-bit systems, but
+ * in the meantime, we implement our own here.
+ */
+
+static __always_inline __attribute_const__
+bool is_power_of_2_u64(u64 n)
+{
+	return n - 1 < (n ^ (n - 1));
+}
+
+static inline __attribute_const__
+u64 roundup_pow_of_two_u64(u64 n)
+{
+	return 1ULL << fls64(n - 1);
+}
+
+static inline __attribute_const__
+u64 rounddown_pow_of_two_u64(u64 n)
+{
+	return 1ULL << (fls64(n) - 1);
+}
+
+
 static struct kmem_cache *slab_blocks;
 static struct kmem_cache *slab_extents;
 
@@ -414,7 +443,7 @@ gpu_dirty_tracker_find(struct gpu_dirty_tracker *dirty_tracker,
 	struct gpu_dirty_extent *root_extent;
 	struct rb_node *parent;
 
-	if (!min_size || !is_power_of_2(min_size))
+	if (!min_size || !is_power_of_2_u64(min_size))
 		return NULL;
 
 	if (!rb)
@@ -864,7 +893,7 @@ int gpu_buddy_init(struct gpu_buddy *mm, u64 size, u64 chunk_size)
 	if (chunk_size < SZ_4K)
 		return -EINVAL;
 
-	if (!is_power_of_2(chunk_size))
+	if (!is_power_of_2_u64(chunk_size))
 		return -EINVAL;
 
 	size = round_down(size, chunk_size);
@@ -1465,7 +1494,7 @@ alloc_from_freetree(struct gpu_buddy *mm,
 static bool
 gpu_buddy_can_offset_align(u64 size, u64 min_block_size)
 {
-	return size < min_block_size && is_power_of_2(size);
+	return size < min_block_size && is_power_of_2_u64(size);
 }
 
 static bool gpu_buddy_subtree_can_satisfy(struct rb_node *node,
@@ -1519,12 +1548,12 @@ gpu_buddy_offset_aligned_allocation(struct gpu_buddy *mm,
 {
 	struct gpu_buddy_block *block = NULL;
 	unsigned int order, tmp, alignment;
-	unsigned long pages;
+	u64 pages;
 	int err;
 
 	alignment = ilog2(min_block_size);
 	pages = size >> ilog2(mm->chunk_size);
-	order = fls(pages) - 1;
+	order = fls64(pages) - 1;
 
 	/*
 	 * Offset-aligned allocation is a strict address-placement policy: the
@@ -1708,14 +1737,14 @@ static int __alloc_contig_try_harder(struct gpu_buddy *mm,
 	struct gpu_buddy_block *block;
 	struct rb_root *root;
 	struct rb_node *iter;
-	unsigned long pages;
+	u64 pages;
 	unsigned int order;
 	u64 modify_size;
 	int err;
 
-	modify_size = rounddown_pow_of_two(size);
+	modify_size = rounddown_pow_of_two_u64(size);
 	pages = modify_size >> ilog2(mm->chunk_size);
-	order = fls(pages) - 1;
+	order = fls64(pages) - 1;
 	if (order == 0)
 		return -ENOSPC;
 
@@ -1970,7 +1999,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 	u64 original_size, original_min_size;
 	unsigned int min_order, order;
 	LIST_HEAD(allocated);
-	unsigned long pages;
+	u64 pages;
 	int err;
 
 	gpu_buddy_driver_lock_held(mm);
@@ -1981,7 +2010,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 	if (min_block_size < mm->chunk_size)
 		return -EINVAL;
 
-	if (!is_power_of_2(min_block_size))
+	if (!is_power_of_2_u64(min_block_size))
 		return -EINVAL;
 
 	if (!IS_ALIGNED(start | end | size, mm->chunk_size))
@@ -2006,7 +2035,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 
 	/* Roundup the size to power of 2 */
 	if (flags & GPU_BUDDY_CONTIGUOUS_ALLOCATION) {
-		size = roundup_pow_of_two(size);
+		size = roundup_pow_of_two_u64(size);
 		min_block_size = size;
 		/*
 		 * Normalize the requested size to min_block_size for regular allocations.
@@ -2017,7 +2046,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 	}
 
 	pages = size >> ilog2(mm->chunk_size);
-	order = fls(pages) - 1;
+	order = fls64(pages) - 1;
 	min_order = ilog2(min_block_size) - ilog2(mm->chunk_size);
 
 	if (order > mm->max_order || size > mm->size) {
@@ -2031,7 +2060,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
 	}
 
 	do {
-		order = min(order, (unsigned int)fls(pages) - 1);
+		order = min(order, (unsigned int)fls64(pages) - 1);
 		BUG_ON(order > mm->max_order);
 		/*
 		 * Regular allocations must not allocate blocks smaller than min_block_size.
-- 
2.56.0


^ permalink raw reply related	[flat|nested] 2+ messages in thread

* Re: [PATCH v4] drm_buddy: Fix power-of-two rounding errors
  2026-11-01  8:11 [PATCH v4] drm_buddy: Fix power-of-two rounding errors David Gow
@ 2026-11-01  8:21 ` sashiko-bot
  0 siblings, 0 replies; 2+ messages in thread
From: sashiko-bot @ 2026-11-01  8:21 UTC (permalink / raw)
  To: David Gow; +Cc: dri-devel

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] gpu/buddy: 32-bit shift overflow in gpu_buddy_alloc_blocks() on BIT(order)
- [Low] gpu/buddy: malformed kernel-doc comment marker above is_power_of_2_u64()
--

commit 73704a4c8e819013a9e0b34c59f4447160bbf9ae
Author: David Gow <david@davidgow.net>

drm_buddy: Fix power-of-two rounding errors

This commit introduces u64-specific power-of-two helpers in
drivers/gpu/buddy.c and widens page count variables from unsigned long to u64
to avoid 32-bit truncation when handling 64-bit GPU allocation sizes on
32-bit architectures.

> diff --git a/drivers/gpu/buddy.c b/drivers/gpu/buddy.c
> index 503f7d5934e4b..504bd013f4ea2 100644
> --- a/drivers/gpu/buddy.c
> +++ b/drivers/gpu/buddy.c
> @@ -36,6 +36,35 @@
>  #define gpu_buddy_assert(condition) WARN_ON(!(condition))
>  #endif
>  
> +/**
> + * Implementations of pow_of_2 functions targeting u64:
> + *
> + * The various pow_of_2 functions in log2.h work on longs, which are 32-bit on
> + * 32-bit systems. Since the GPU address space is 64-bit even on systems with
> + * a 32-bit CPU, this causes needless truncation. Work is underway to improve
> + * the general implementations to support 64-bit types on 32-bit systems, but
> + * in the meantime, we implement our own here.
> + */

[Severity: Low]
Since drivers/gpu/buddy.c is included by Documentation/gpu/drm-mm.rst for
kernel-doc processing, starting this comment block above is_power_of_2_u64()
with /** instead of /* causes scripts/kernel-doc to warn during W=1 and
documentation builds because the block is not formatted as a kernel-doc
comment. Should this comment open with /* instead?

[ ... ]

> @@ -1970,7 +1999,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>  	u64 original_size, original_min_size;
>  	unsigned int min_order, order;
>  	LIST_HEAD(allocated);
> -	unsigned long pages;
> +	u64 pages;
>  	int err;

[ ... ]

> @@ -2017,7 +2046,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>  	}
>  
>  	pages = size >> ilog2(mm->chunk_size);
> -	order = fls(pages) - 1;
> +	order = fls64(pages) - 1;
>  	min_order = ilog2(min_block_size) - ilog2(mm->chunk_size);
>  
>  	if (order > mm->max_order || size > mm->size) {
> @@ -2031,7 +2060,7 @@ int gpu_buddy_alloc_blocks(struct gpu_buddy *mm,
>  	}
>  
>  	do {
> -		order = min(order, (unsigned int)fls(pages) - 1);
> +		order = min(order, (unsigned int)fls64(pages) - 1);
>  		BUG_ON(order > mm->max_order);
>  		/*
>  		 * Regular allocations must not allocate blocks smaller than min_block_size.

[Severity: Medium]
In gpu_buddy_alloc_blocks(), widening pages to u64 and computing order with
fls64(pages) - 1 allows order to be 32 or greater on 32-bit architectures
when an allocation spans at least 1ULL << 32 chunks.

However, at the bottom of this allocation loop in gpu_buddy_alloc_blocks(),
pages is still decremented using BIT(order):

		kmemleak_update_trace(block);
		list_add_tail(&block->link, &allocated);

		pages -= BIT(order);

		if (!pages)
			break;

Because BIT(order) expands to (1UL << (order)), on 32-bit systems an order of
32 or more shifts a 32-bit unsigned long past its width, invoking undefined
behavior and corrupting the remaining pages count so the loop does not
terminate normally.

Should that decrement in gpu_buddy_alloc_blocks() use BIT_ULL(order) instead?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261101081121.3273967-2-david@davidgow.net?part=1

^ permalink raw reply	[flat|nested] 2+ messages in thread

end of thread, other threads:[~2026-11-01  8:21 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-01  8:11 [PATCH v4] drm_buddy: Fix power-of-two rounding errors David Gow
2026-11-01  8:21 ` sashiko-bot

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.