From: Yury Norov <ynorov@nvidia.com>
To: Eliot Courtney <ecourtney@nvidia.com>
Cc: "Alice Ryhl" <aliceryhl@google.com>,
"Burak Emir" <burak.emir@gmail.com>,
"Yury Norov" <yury.norov@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Boqun Feng" <boqun@kernel.org>, "Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
"Daniel Almeida" <daniel.almeida@collabora.com>,
"Tamir Duberstein" <tamird@kernel.org>,
"Alexandre Courbot" <acourbot@nvidia.com>,
"Onur Özkan" <work@onurozkan.dev>,
"David Airlie" <airlied@gmail.com>,
"Simona Vetter" <simona@ffwll.ch>,
"Greg Kroah-Hartman" <gregkh@linuxfoundation.org>,
"John Hubbard" <jhubbard@nvidia.com>,
"Alistair Popple" <apopple@nvidia.com>,
"Timur Tabi" <ttabi@nvidia.com>, "Zhi Wang" <zhiw@nvidia.com>,
rust-for-linux@vger.kernel.org, linux-kernel@vger.kernel.org,
nova-gpu@lists.linux.dev, dri-devel@lists.freedesktop.org
Subject: Re: [PATCH v3 2/4] rust: bitmap: add contiguous area operations
Date: Thu, 30 Jul 2026 00:56:17 -0400 [thread overview]
Message-ID: <amrZcX33BGJCTDMp@yury> (raw)
In-Reply-To: <20260729-chid-v3-2-20cc08032bbc@nvidia.com>
On Wed, Jul 29, 2026 at 03:54:13PM +0900, Eliot Courtney wrote:
> Add bindings for area operations on bitmaps. Each one is
> made safe by adding some extra checks compared to the underlying C code
> (for example, checking bounds) and with additional checks to catch
> likely erroneous usage if `CONFIG_RUST_BITMAP_HARDENED` is on.
>
> The C code uses signed integers for some parameters, for example the
> length for `__bitmap_set`, so bounds check against i32::MAX. We can't
> rely on `BitmapVec::MAX_LEN` because `Bitmap` may not necessarily be
> backed by `BitmapVec`.
>
> Add tests demonstrating the edge cases.
>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> ---
> rust/kernel/bitmap.rs | 194 ++++++++++++++++++++++++++++++++++++++++++++++++++
> 1 file changed, 194 insertions(+)
>
> diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
> index a43bfe0ec3dc..f4b0b8ae39d8 100644
> --- a/rust/kernel/bitmap.rs
> +++ b/rust/kernel/bitmap.rs
> @@ -10,6 +10,7 @@
> use crate::bindings;
> #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
> use crate::pr_err;
> +use crate::ptr::Alignment;
> use core::ptr::NonNull;
>
> /// Represents a C bitmap. Wraps underlying C bitmap API.
Some comments use indicative form in the file, but the imperative
'represent' is a more standard way. Can you please use it instead?
> @@ -497,6 +498,116 @@ pub fn next_zero_bit(&self, start: usize) -> Option<usize> {
> Some(index)
> }
> }
> +
> + /// Finds a contiguous area of `nbits` zero bits at or after `start`, aligned to `align`.
> + ///
> + /// Returns the bit index of the start of the area, or [`None`] if no such area fitting in
> + /// the bitmap exists.
> + ///
> + /// The returned index is a multiple of `align`. Alignments where `self.len() + align - 1`
> + /// overflows a `usize` can hang the underlying C code.
> + ///
> + /// # Panics
> + ///
> + /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `start` is out of bounds.
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::alloc::{AllocError, flags::GFP_KERNEL};
> + /// use kernel::bitmap::BitmapVec;
> + /// use kernel::ptr::Alignment;
> + ///
> + /// let mut b = BitmapVec::new(64, GFP_KERNEL)?;
> + /// let unaligned = Alignment::new::<1>();
> + ///
> + /// assert_eq!(Some(0), b.next_zero_area(0, 8, unaligned));
> + /// b.set(0, 5);
> + /// assert_eq!(Some(5), b.next_zero_area(0, 8, unaligned));
> + /// assert_eq!(Some(8), b.next_zero_area(0, 8, Alignment::new::<8>()));
> + /// assert_eq!(None, b.next_zero_area(0, 65, unaligned));
> + /// # Ok::<(), AllocError>(())
> + /// ```
> + #[inline]
> + pub fn next_zero_area(&self, start: usize, nbits: usize, align: Alignment) -> Option<usize> {
Please create the rust wrapper next_zero_area_off() around
bitmap_find_next_zero_area_off(), then in rust create the
next_zero_area(), if you need it.
> + bitmap_assert!(
> + start < self.len(),
> + "`start` must be < {}, was {}",
> + self.len(),
> + start
> + );
> +
> + let nr = u32::try_from(nbits).ok()?;
> +
> + // SAFETY: `bitmap_find_next_zero_area_off` is safe to use with an out of bounds `start`
> + // value and never reads beyond `self.len()` bits.
> + let index = unsafe {
> + bindings::bitmap_find_next_zero_area_off(
> + self.as_ptr().cast_mut(),
> + self.len(),
> + start,
> + nr,
> + align.as_usize() - 1,
> + 0,
> + )
> + };
> +
> + // In case of overflow, we may get back a range outside of what we requested.
No, we can't. We've got the test_bitmap_find_next_zero_area_off() for
it (in next). If you think the test is incomplete, please extend it.
If you believe that bitmap_find_next_zero_area_off() may return something
like that, it means the function is buggy, and you shouldn't trust it at
all.
> + let end = index.checked_add(nbits)?;
> + if index < start || index >= self.len() || end > self.len() {
> + None
> + } else {
> + Some(index)
> + }
So, this should be a simple:
(i < len).then_some(i)
> + }
> +
> + /// Sets a contiguous area of `nbits` bits starting at `start`.
> + ///
> + /// If CONFIG_RUST_BITMAP_HARDENED is not enabled and the area `start..start + nbits` is out of
> + /// bounds, does nothing.
> + ///
> + /// # Panics
> + ///
> + /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and the area `start..start + nbits` is out
> + /// of bounds.
> + #[inline]
> + pub fn set(&mut self, start: usize, nbits: usize) {
> + bitmap_assert_return!(
> + start
> + .checked_add(nbits)
> + .is_some_and(|end| end <= self.len() && end <= i32::MAX as usize),
> + "Area `start..start + nbits` ({}..{}) must be within bounds {}",
> + start,
> + start.saturating_add(nbits),
> + self.len()
> + );
> + // SAFETY: The area `start..start + nbits` is within bounds.
Not sure I understand. In the above assertion block you check for it,
now you say it's always true...
I think, your language should be similar to the
find_next_zero_area_off() case: it's safe to call the function with
the out-of-bounds start and nbits.
> + unsafe { bindings::__bitmap_set(self.as_mut_ptr(), start as u32, nbits as i32) };
> + }
> +
> + /// Clears a contiguous area of `nbits` bits starting at `start`.
> + ///
> + /// If CONFIG_RUST_BITMAP_HARDENED is not enabled and the area `start..start + nbits` is out of
> + /// bounds, does nothing.
> + ///
> + /// # Panics
> + ///
> + /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and the area `start..start + nbits` is out
> + /// of bounds.
> + #[inline]
> + pub fn clear(&mut self, start: usize, nbits: usize) {
> + bitmap_assert_return!(
> + start
> + .checked_add(nbits)
> + .is_some_and(|end| end <= self.len() && end <= i32::MAX as usize),
> + "Area `start..start + nbits` ({}..{}) must be within bounds {}",
> + start,
> + start.saturating_add(nbits),
> + self.len()
> + );
> + // SAFETY: The area `start..start + nbits` is within bounds.
> + unsafe { bindings::__bitmap_clear(self.as_mut_ptr(), start as u32, nbits as i32) };
> + }
> }
>
> #[cfg(CONFIG_RUST_BITMAP_KUNIT_TEST)]
> @@ -614,4 +725,87 @@ fn bitmap_copy_and_extend() -> Result<(), AllocError> {
> assert_eq!(Some(17), long_bitmap.last_bit());
> Ok(())
> }
> +
> + #[test]
> + fn bitmap_area_set_clear_find() -> Result<(), AllocError> {
> + let mut b = BitmapVec::new(128, GFP_KERNEL)?;
> + let unaligned = Alignment::new::<1>();
> +
> + assert_eq!(Some(0), b.next_zero_area(0, 5, unaligned));
> + b.set(0, 5); // Now contains {[0, 5)}.
> +
> + assert_eq!(Some(0), b.next_bit(0));
> + assert_eq!(Some(4), b.next_bit(4));
> + assert_eq!(Some(5), b.next_zero_bit(0));
> + assert_eq!(Some(5), b.next_zero_area(0, 5, unaligned));
> + assert_eq!(Some(8), b.next_zero_area(0, 5, Alignment::new::<8>()));
> +
> + b.set(8, 8); // Now contains {[0, 5), [8, 16)}.
> + assert_eq!(Some(16), b.next_zero_area(0, 4, Alignment::new::<16>()));
> + assert_eq!(Some(16), b.next_zero_area(0, 4, unaligned));
> +
> + b.clear(0, 5); // Now contains {[8, 16)}.
> + assert_eq!(Some(0), b.next_zero_area(0, 5, unaligned));
> + assert_eq!(Some(8), b.next_bit(0));
> + assert_eq!(Some(15), b.last_bit());
> +
> + b.clear(16, 0); // Zero-length in-bounds clears are no-ops.
> + assert_eq!(Some(8), b.next_bit(0));
> + assert_eq!(Some(15), b.last_bit());
> +
> + // A zero-length request returns the first aligned position at or
> + // after the next zero bit, even if that position's own bit is set.
> + assert_eq!(Some(1), b.next_zero_area(1, 0, unaligned));
> + assert_eq!(Some(8), b.next_zero_area(1, 0, Alignment::new::<8>()));
> +
> + b.set(60, 10); // Now contains {[8, 16), [60, 70)}.
> + assert_eq!(Some(60), b.next_bit(16));
> + assert_eq!(Some(69), b.last_bit());
> + assert_eq!(Some(16), b.next_zero_area(9, 40, unaligned));
> + assert_eq!(Some(70), b.next_zero_area(0, 45, unaligned));
> +
> + b.clear(62, 6); // Now contains {[8, 16), [60, 62), [68, 70)}.
> + assert_eq!(Some(62), b.next_zero_area(60, 6, unaligned));
> + assert_eq!(Some(61), b.next_bit(61));
> + assert_eq!(Some(69), b.last_bit());
> +
> + b.set(64, 0); // Zero-length in-bounds sets are no-ops.
> + assert_eq!(Some(62), b.next_zero_bit(62));
> + Ok(())
> + }
> +
> + #[test]
> + fn bitmap_area_exhaustion() -> Result<(), AllocError> {
> + let mut b = BitmapVec::new(64, GFP_KERNEL)?;
> + let unaligned = Alignment::new::<1>();
> +
> + assert_eq!(None, b.next_zero_area(0, 65, unaligned));
> + assert_eq!(None, b.next_zero_area(0, usize::MAX, unaligned));
> + assert_eq!(None, b.next_zero_area(1, usize::MAX, unaligned));
> +
> + b.set_bit(0); // Now contains {[0, 1)}.
> + assert_eq!(None, b.next_zero_area(0, usize::MAX, unaligned));
> +
> + b.set(0, 61); // Now contains {[0, 61)}.
> + assert_eq!(None, b.next_zero_area(0, 4, unaligned));
> + assert_eq!(Some(61), b.next_zero_area(0, 3, unaligned));
> + assert_eq!(None, b.next_zero_area(0, 1, Alignment::new::<64>()));
> + Ok(())
> + }
> +
> + #[test]
> + #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
> + fn owned_bitmap_area_out_of_bounds() -> Result<(), AllocError> {
> + let mut b = BitmapVec::new(64, GFP_KERNEL)?;
> +
> + // Should be ignored since out of bounds.
> + b.set(64, 4);
> + b.set(62, 8);
> + b.set(usize::MAX, 0);
> + b.clear(usize::MAX, 0);
> + b.clear(2048, 8);
> + assert_eq!(None, b.next_bit(0));
> + assert_eq!(None, b.next_zero_area(64, 1, Alignment::new::<1>()));
> + Ok(())
> + }
> }
>
> --
> 2.55.0
next prev parent reply other threads:[~2026-07-30 4:56 UTC|newest]
Thread overview: 7+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-29 6:54 [PATCH v3 0/4] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-07-29 6:54 ` [PATCH v3 1/4] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-07-29 6:54 ` [PATCH v3 2/4] rust: bitmap: add contiguous area operations Eliot Courtney
2026-07-29 7:06 ` sashiko-bot
2026-07-30 4:56 ` Yury Norov [this message]
2026-07-29 6:54 ` [PATCH v3 3/4] rust: id_pool: add contiguous area allocation Eliot Courtney
2026-07-29 6:54 ` [PATCH v3 4/4] gpu: nova-core: add ChannelIdPool Eliot Courtney
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=amrZcX33BGJCTDMp@yury \
--to=ynorov@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=acourbot@nvidia.com \
--cc=airlied@gmail.com \
--cc=aliceryhl@google.com \
--cc=apopple@nvidia.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun@kernel.org \
--cc=burak.emir@gmail.com \
--cc=dakr@kernel.org \
--cc=daniel.almeida@collabora.com \
--cc=dri-devel@lists.freedesktop.org \
--cc=ecourtney@nvidia.com \
--cc=gary@garyguo.net \
--cc=gregkh@linuxfoundation.org \
--cc=jhubbard@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nova-gpu@lists.linux.dev \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=simona@ffwll.ch \
--cc=tamird@kernel.org \
--cc=tmgross@umich.edu \
--cc=ttabi@nvidia.com \
--cc=work@onurozkan.dev \
--cc=yury.norov@gmail.com \
--cc=zhiw@nvidia.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.