* [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs
@ 2026-08-13 7:34 Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 1/7] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
` (6 more replies)
0 siblings, 7 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney, Yury Norov
Add support for reserving of ranges of IDs, with a usage in nova-core
for channel IDs. This entails adding bindings for the C bitmap
API for ranges of bits, then users of that in `IdPool`, and finally a
user of `IdPool` in nova-core, `ChannelIdPool`.
Channel ID tracking is needed for allotting ranges of channel IDs to
vGPU guests, and later for regular host channel ID reservation.
nova-core needs allocation of a contiguous sequence of IDs with a
specific length and sometimes a specific alignment [1].
About the tradeoffs between different data structures:
- IDA/xarray do not support allocating a contiguous sequence of IDs
(ida_alloc_range() allocates a single ID within a range, not a contiguous
sequence).
- A maple tree works, but is not as good a fit. The ID space is small
(limited to 2048) and aligned allocation needs an alloc_range()+erase() retry
loop (plus a Mutex around it, or new mas_empty_area() bindings) that
essentially reimplements bitmap_find_next_zero_area(). See the maple tree
version at [2]. For 2048 IDs a bitmap is also considerably faster and smaller
[3].
- The bitmap API natively supports aligned contiguous area allocation
(bitmap_find_next_zero_area()).
This is based on drm-rust-next.
[1]: https://lore.kernel.org/all/84bc8bd2-e292-4b84-9580-a1b5df4c5bdc@nvidia.com/
[2]: https://lore.kernel.org/all/20260710-chid-maple-v1-1-4ee869055268@nvidia.com/
[3]: https://lore.kernel.org/all/20260717053241.916441-1-ynorov@nvidia.com/
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Changes in v6:
- Take a NonZero nbits in `next_zero_area_off`, `set` and `clear` (Yury)
- Remove `UnusedArea`, `IdPool::alloc_area` now allocates directly (Yury)
- Add patch: take a NonZero capacity in `IdPool::with_capacity()`
- Add patch: do not round the capacity up to `BitmapVec::MAX_INLINE_LEN`,
which also removes the bounds check in `ChannelIdPool::alloc_area`
- Add more testing of Drop for `ChannelIdPool` (Yury)
- Link to v5: https://patch.msgid.link/20260812-chid-v5-0-6c767770b3f4@nvidia.com
Changes in v5:
- `bitmap_assert!` i32::MAX length for Bitmap::from_raw* (Yury)
- Only run overflow check on 32-bit (Yury)
- Link to v4: https://patch.msgid.link/20260810-chid-v4-0-c9f206fdcb97@nvidia.com
Changes in v4:
- Add `next_zero_area_off` to match C code (Yury)
- Replace overflow checks to match C code in bitmap-for-next.
- Tighten `Bitmap` unsafe contract to disallow Bitmaps larger than i32::MAX
- Link to v3: https://patch.msgid.link/20260729-chid-v3-0-20cc08032bbc@nvidia.com
Changes in v3:
- Use `Alignment` type in id_pool and bitmap (Alice)
- Remove hang check on the basis that it's extraordinarily rare.
- Link to v2: https://patch.msgid.link/20260723-chid-v2-0-c35e5e9fb3d9@nvidia.com
Changes in v2:
- Collected Alice's Reviewed-by on patch 1.
- Address Yury's comments w.r.t. using __bitmap_set etc directly.
- Address Yury's comments w.r.t. following the C names
- Additionally check for an overflow case that causes a hang
- Added more info to cover letter + patch 4 w.r.t. channel ID allottment
requirements
- Add align parameter to ChannelIdPool::alloc_area() plus an aligned
allocation test
- Add missing INVARIANT comment when constructing UnusedArea
- Link to v1:
https://patch.msgid.link/20260703-chid-v1-0-84fe8259e46e@nvidia.com
---
Eliot Courtney (7):
rust: bitmap: use function-level cfg on kunit test
rust: bitmap: restrict bitmap length to at most i32::MAX
rust: bitmap: add contiguous area operations
rust: id_pool: add contiguous area allocation
rust: id_pool: take a NonZero capacity in with_capacity
rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
gpu: nova-core: add ChannelIdPool
drivers/gpu/nova-core/gpu.rs | 2 +
drivers/gpu/nova-core/gpu/channel.rs | 201 +++++++++++++++++++++
rust/kernel/bitmap.rs | 341 +++++++++++++++++++++++++++++++----
rust/kernel/id_pool.rs | 47 ++++-
4 files changed, 546 insertions(+), 45 deletions(-)
---
base-commit: 4c9ba407018e8deb06dbc643112bac8f40404f95
change-id: 20260608-chid-18fa943c6d6c
Best regards,
--
Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 8+ messages in thread
* [PATCH v6 1/7] rust: bitmap: use function-level cfg on kunit test
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 2/7] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
` (5 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney, Yury Norov
Since commit c652dc44192d ("rust: kunit: allow `cfg` on `test`s"),
we no longer need this workaround.
Reviewed-by: Alice Ryhl <aliceryhl@google.com>
Reviewed-by: Yury Norov <ynorov@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/bitmap.rs | 25 +++++++++++--------------
1 file changed, 11 insertions(+), 14 deletions(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index b27e0ec80d64..a43bfe0ec3dc 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -572,24 +572,21 @@ fn bitmap_set_clear_find() -> Result<(), AllocError> {
}
#[test]
+ #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
fn owned_bitmap_out_of_bounds() -> Result<(), AllocError> {
- // TODO: Kunit #[test]s do not support `cfg` yet,
- // so we add it here in the body.
- #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
- {
- let mut b = BitmapVec::new(128, GFP_KERNEL)?;
- b.set_bit(2048);
- b.set_bit_atomic(2048);
- b.clear_bit(2048);
- b.clear_bit_atomic(2048);
- assert_eq!(None, b.next_bit(2048));
- assert_eq!(None, b.next_zero_bit(2048));
- assert_eq!(None, b.last_bit());
- }
+ let mut b = BitmapVec::new(128, GFP_KERNEL)?;
+
+ b.set_bit(2048);
+ b.set_bit_atomic(2048);
+ b.clear_bit(2048);
+ b.clear_bit_atomic(2048);
+ assert_eq!(None, b.next_bit(2048));
+ assert_eq!(None, b.next_zero_bit(2048));
+ assert_eq!(None, b.last_bit());
Ok(())
}
- // TODO: uncomment once kunit supports [should_panic] and `cfg`.
+ // TODO: uncomment once kunit supports `#[should_panic]`.
// #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
// #[test]
// #[should_panic]
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 2/7] rust: bitmap: restrict bitmap length to at most i32::MAX
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 1/7] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 3/7] rust: bitmap: add contiguous area operations Eliot Courtney
` (4 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney, Yury Norov
It is currently possible to construct a non-`BitmapVec` backed
`Bitmap` using `Bitmap::from_raw` that is larger than `i32::MAX`, and
it is not part of the unsafe requirements. Restricting all bitmaps
(even non-`BitmapVec` backed ones) to a maximum size of `i32::MAX`
simplifies a few things and matches `BitmapVec::MAX_LEN`.
Add that requirement to the unsafe requirements on `Bitmap::from_raw`
and `Bitmap::from_raw_mut`, and to the invariants on `Bitmap`.
This also fixes u32 casts truncating in `copy_and_extend`, which could
otherwise lead to OOB writes.
Fixes: 11eca92a2cae ("rust: add bitmap API.")
Link: https://lore.kernel.org/DKG0U8RLO7LZ.2I1AIH0S38PAP@nvidia.com
Reviewed-by: Yury Norov <ynorov@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/bitmap.rs | 68 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 47 insertions(+), 21 deletions(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index a43bfe0ec3dc..fdcfc0409773 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -17,24 +17,57 @@
/// # Invariants
///
/// Must reference a `[c_ulong]` long enough to fit `data.len()` bits.
+/// Must not be longer than `i32::MAX` bits.
#[cfg_attr(CONFIG_64BIT, repr(align(8)))]
#[cfg_attr(not(CONFIG_64BIT), repr(align(4)))]
pub struct Bitmap {
data: [()],
}
+macro_rules! bitmap_assert {
+ ($cond:expr, $($arg:tt)+) => {
+ #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
+ assert!($cond, $($arg)*);
+ }
+}
+
+macro_rules! bitmap_assert_return {
+ ($cond:expr, $($arg:tt)+) => {
+ #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
+ assert!($cond, $($arg)*);
+
+ #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
+ if !($cond) {
+ pr_err!($($arg)*);
+ return
+ }
+ }
+}
+
impl Bitmap {
/// Borrows a C bitmap.
///
+ /// # Panics
+ ///
+ /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `nbits` exceeds `i32::MAX`.
+ ///
/// # Safety
///
/// * `ptr` holds a non-null address of an initialized array of `unsigned long`
/// that is large enough to hold `nbits` bits.
+ /// * `nbits` must not exceed `i32::MAX`.
/// * the array must not be freed for the lifetime of this [`Bitmap`]
/// * concurrent access only happens through atomic operations
pub unsafe fn from_raw<'a>(ptr: *const usize, nbits: usize) -> &'a Bitmap {
+ bitmap_assert!(
+ nbits <= i32::MAX as usize,
+ "`nbits` must be <= {}, was {}",
+ i32::MAX,
+ nbits
+ );
let data: *const [()] = core::ptr::slice_from_raw_parts(ptr.cast(), nbits);
// INVARIANT: `data` references an initialized array that can hold `nbits` bits.
+ // INVARIANT: the caller guarantees that `nbits` does not exceed `i32::MAX`.
// SAFETY:
// The caller guarantees that `data` (derived from `ptr` and `nbits`)
// points to a valid, initialized, and appropriately sized memory region
@@ -51,15 +84,27 @@ pub unsafe fn from_raw<'a>(ptr: *const usize, nbits: usize) -> &'a Bitmap {
/// Borrows a C bitmap exclusively.
///
+ /// # Panics
+ ///
+ /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `nbits` exceeds `i32::MAX`.
+ ///
/// # Safety
///
/// * `ptr` holds a non-null address of an initialized array of `unsigned long`
/// that is large enough to hold `nbits` bits.
+ /// * `nbits` must not exceed `i32::MAX`.
/// * the array must not be freed for the lifetime of this [`Bitmap`]
/// * no concurrent access may happen.
pub unsafe fn from_raw_mut<'a>(ptr: *mut usize, nbits: usize) -> &'a mut Bitmap {
+ bitmap_assert!(
+ nbits <= i32::MAX as usize,
+ "`nbits` must be <= {}, was {}",
+ i32::MAX,
+ nbits
+ );
let data: *mut [()] = core::ptr::slice_from_raw_parts_mut(ptr.cast(), nbits);
// INVARIANT: `data` references an initialized array that can hold `nbits` bits.
+ // INVARIANT: the caller guarantees that `nbits` does not exceed `i32::MAX`.
// SAFETY:
// The caller guarantees that `data` (derived from `ptr` and `nbits`)
// points to a valid, initialized, and appropriately sized memory region
@@ -96,26 +141,6 @@ union BitmapRepr {
ptr: NonNull<usize>,
}
-macro_rules! bitmap_assert {
- ($cond:expr, $($arg:tt)+) => {
- #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
- assert!($cond, $($arg)*);
- }
-}
-
-macro_rules! bitmap_assert_return {
- ($cond:expr, $($arg:tt)+) => {
- #[cfg(CONFIG_RUST_BITMAP_HARDENED)]
- assert!($cond, $($arg)*);
-
- #[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
- if !($cond) {
- pr_err!($($arg)*);
- return
- }
- }
-}
-
/// Represents an owned bitmap.
///
/// Wraps underlying C bitmap API. See [`Bitmap`] for available
@@ -415,7 +440,8 @@ pub fn clear_bit_atomic(&self, index: usize) {
#[inline]
pub fn copy_and_extend(&mut self, src: &Bitmap) {
let len = core::cmp::min(src.len(), self.len());
- // SAFETY: access to `self` and `src` is within bounds.
+ // SAFETY: access to `self` and `src` is within bounds. Both lengths fit in `u32`
+ // because a `Bitmap` is at most `i32::MAX` bits, so the casts are lossless.
unsafe {
bindings::bitmap_copy_and_extend(
self.as_mut_ptr(),
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 3/7] rust: bitmap: add contiguous area operations
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 1/7] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 2/7] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 4/7] rust: id_pool: add contiguous area allocation Eliot Courtney
` (3 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
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.
Add tests demonstrating the edge cases.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/bitmap.rs | 248 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 247 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index fdcfc0409773..ad63f248a6ae 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -10,7 +10,11 @@
use crate::bindings;
#[cfg(not(CONFIG_RUST_BITMAP_HARDENED))]
use crate::pr_err;
-use core::ptr::NonNull;
+use crate::ptr::Alignment;
+use core::{
+ num::NonZero,
+ ptr::NonNull, //
+};
/// Represents a C bitmap. Wraps underlying C bitmap API.
///
@@ -523,6 +527,148 @@ pub fn next_zero_bit(&self, start: usize) -> Option<usize> {
Some(index)
}
}
+
+ /// Finds a contiguous area of `nbits` zero bits at or after `start`, where the area plus
+ /// `align_offset` is 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 plus `align_offset` is a multiple of `align`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `start` is out of bounds.
+ #[inline]
+ pub fn next_zero_area_off(
+ &self,
+ start: usize,
+ nbits: NonZero<usize>,
+ align: Alignment,
+ align_offset: usize,
+ ) -> Option<usize> {
+ bitmap_assert!(
+ start < self.len(),
+ "`start` must be < {}, was {}",
+ self.len(),
+ start
+ );
+
+ let nr = u32::try_from(nbits.get()).ok()?;
+ let align_mask = align.as_usize() - 1;
+
+ // The C alignment and end arithmetic must not overflow, or it can read out of bounds.
+ // Overflow is only possible on 32-bit.
+ #[cfg(not(CONFIG_64BIT))]
+ align_mask
+ .checked_add(self.len())?
+ .checked_add(nbits.get())?;
+
+ // SAFETY: `bitmap_find_next_zero_area_off` is safe to use with an out of bounds `start`
+ // value and, given the overflow check above, 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_mask,
+ align_offset,
+ )
+ };
+
+ (index < self.len()).then_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`.
+ ///
+ /// # Panics
+ ///
+ /// Panics if CONFIG_RUST_BITMAP_HARDENED is enabled and `start` is out of bounds.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use core::num::NonZero;
+ /// 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>();
+ /// let eight = NonZero::new(8).unwrap();
+ ///
+ /// assert_eq!(Some(0), b.next_zero_area(0, eight, unaligned));
+ /// b.set(0, NonZero::new(5).unwrap());
+ /// assert_eq!(Some(5), b.next_zero_area(0, eight, unaligned));
+ /// assert_eq!(Some(8), b.next_zero_area(0, eight, Alignment::new::<8>()));
+ /// assert_eq!(None, b.next_zero_area(0, NonZero::new(65).unwrap(), unaligned));
+ /// # Ok::<(), AllocError>(())
+ /// ```
+ #[inline]
+ pub fn next_zero_area(
+ &self,
+ start: usize,
+ nbits: NonZero<usize>,
+ align: Alignment,
+ ) -> Option<usize> {
+ self.next_zero_area_off(start, nbits, align, 0)
+ }
+
+ /// 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: NonZero<usize>) {
+ bitmap_assert_return!(
+ start
+ .checked_add(nbits.get())
+ .is_some_and(|end| end <= self.len()),
+ "Area `start..start + nbits` ({}..{}) must be within bounds {}",
+ start,
+ start.saturating_add(nbits.get()),
+ self.len()
+ );
+ // SAFETY: The area `start..start + nbits` is within bounds and a `Bitmap` is at most
+ // `i32::MAX` bits, so the casts are lossless.
+ unsafe { bindings::__bitmap_set(self.as_mut_ptr(), start as u32, nbits.get() 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: NonZero<usize>) {
+ bitmap_assert_return!(
+ start
+ .checked_add(nbits.get())
+ .is_some_and(|end| end <= self.len()),
+ "Area `start..start + nbits` ({}..{}) must be within bounds {}",
+ start,
+ start.saturating_add(nbits.get()),
+ self.len()
+ );
+ // SAFETY: The area `start..start + nbits` is within bounds and a `Bitmap` is at most
+ // `i32::MAX` bits, so the casts are lossless.
+ unsafe { bindings::__bitmap_clear(self.as_mut_ptr(), start as u32, nbits.get() as i32) };
+ }
}
#[cfg(CONFIG_RUST_BITMAP_KUNIT_TEST)]
@@ -531,6 +677,10 @@ mod tests {
use super::*;
use kernel::alloc::flags::GFP_KERNEL;
+ const fn nz<const N: usize>() -> NonZero<usize> {
+ const { NonZero::new(N).unwrap() }
+ }
+
#[test]
fn bitmap_borrow() {
let fake_bitmap: [usize; 2] = [0, 0];
@@ -640,4 +790,100 @@ 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, nz::<5>(), unaligned));
+ b.set(0, nz::<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, nz::<5>(), unaligned));
+ assert_eq!(
+ Some(8),
+ b.next_zero_area(0, nz::<5>(), Alignment::new::<8>())
+ );
+
+ b.set(8, nz::<8>()); // Now contains {[0, 5), [8, 16)}.
+ assert_eq!(
+ Some(16),
+ b.next_zero_area(0, nz::<4>(), Alignment::new::<16>())
+ );
+ assert_eq!(Some(16), b.next_zero_area(0, nz::<4>(), unaligned));
+
+ b.clear(0, nz::<5>()); // Now contains {[8, 16)}.
+ assert_eq!(Some(0), b.next_zero_area(0, nz::<5>(), unaligned));
+ assert_eq!(Some(8), b.next_bit(0));
+ assert_eq!(Some(15), b.last_bit());
+
+ b.set(60, nz::<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, nz::<40>(), unaligned));
+ assert_eq!(Some(70), b.next_zero_area(0, nz::<45>(), unaligned));
+
+ b.clear(62, nz::<6>()); // Now contains {[8, 16), [60, 62), [68, 70)}.
+ assert_eq!(Some(62), b.next_zero_area(60, nz::<6>(), unaligned));
+ assert_eq!(Some(61), b.next_bit(61));
+ assert_eq!(Some(69), b.last_bit());
+ 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, nz::<65>(), unaligned));
+ assert_eq!(None, b.next_zero_area(0, nz::<{ usize::MAX }>(), unaligned));
+ assert_eq!(None, b.next_zero_area(1, nz::<{ usize::MAX }>(), unaligned));
+
+ b.set_bit(0); // Now contains {[0, 1)}.
+ assert_eq!(None, b.next_zero_area(0, nz::<{ usize::MAX }>(), unaligned));
+
+ b.set(0, nz::<61>()); // Now contains {[0, 61)}.
+ assert_eq!(None, b.next_zero_area(0, nz::<4>(), unaligned));
+ assert_eq!(Some(61), b.next_zero_area(0, nz::<3>(), unaligned));
+ assert_eq!(None, b.next_zero_area(0, nz::<1>(), Alignment::new::<64>()));
+ Ok(())
+ }
+
+ #[test]
+ fn bitmap_area_off() -> Result<(), AllocError> {
+ let mut b = BitmapVec::new(64, GFP_KERNEL)?;
+ let align8 = Alignment::new::<8>();
+
+ b.set(0, nz::<5>()); // Now contains {[0, 5)}.
+
+ // The area plus align_offset starts at a multiple of the alignment.
+ assert_eq!(Some(7), b.next_zero_area_off(0, nz::<8>(), align8, 1));
+ assert_eq!(Some(5), b.next_zero_area_off(0, nz::<8>(), align8, 3));
+
+ // A zero offset behaves like next_zero_area().
+ assert_eq!(
+ b.next_zero_area(0, nz::<8>(), align8),
+ b.next_zero_area_off(0, nz::<8>(), align8, 0)
+ );
+ 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, nz::<4>());
+ b.set(62, nz::<8>());
+ b.set(usize::MAX, nz::<1>());
+ b.clear(usize::MAX, nz::<1>());
+ b.clear(2048, nz::<8>());
+ assert_eq!(None, b.next_bit(0));
+ assert_eq!(None, b.next_zero_area(64, nz::<1>(), Alignment::new::<1>()));
+ Ok(())
+ }
}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 4/7] rust: id_pool: add contiguous area allocation
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (2 preceding siblings ...)
2026-08-13 7:34 ` [PATCH v6 3/7] rust: bitmap: add contiguous area operations Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 5/7] rust: id_pool: take a NonZero capacity in with_capacity Eliot Courtney
` (2 subsequent siblings)
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
Add `IdPool::alloc_area` which allocates a contiguous area with the
given offset, count, and alignment.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/id_pool.rs | 32 ++++++++++++++++++++++++++++++++
1 file changed, 32 insertions(+)
diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
index 384753fe0e44..a4a2ff417fe8 100644
--- a/rust/kernel/id_pool.rs
+++ b/rust/kernel/id_pool.rs
@@ -4,8 +4,14 @@
//! Rust API for an ID pool backed by a [`BitmapVec`].
+use core::{
+ num::NonZero,
+ ops::Range, //
+};
+
use crate::alloc::{AllocError, Flags};
use crate::bitmap::BitmapVec;
+use crate::ptr::Alignment;
/// Represents a dynamic ID pool backed by a [`BitmapVec`].
///
@@ -240,6 +246,32 @@ pub fn find_unused_id(&mut self, offset: usize) -> Option<UnusedId<'_>> {
pub fn release_id(&mut self, id: usize) {
self.map.clear_bit(id);
}
+
+ /// Allocates a contiguous area of `count` IDs at or after `offset`.
+ ///
+ /// The start of the returned area is a multiple of `align`.
+ ///
+ /// Returns the allocated range upon success, or [`None`] if no such area could be found.
+ #[inline]
+ #[must_use]
+ pub fn alloc_area(
+ &mut self,
+ offset: usize,
+ count: NonZero<usize>,
+ align: Alignment,
+ ) -> Option<Range<usize>> {
+ let start = self.map.next_zero_area(offset, count, align)?;
+ self.map.set(start, count);
+ Some(start..start + count.get())
+ }
+
+ /// Releases a contiguous area of IDs.
+ #[inline]
+ pub fn release_area(&mut self, range: &Range<usize>) {
+ if let Some(nbits) = NonZero::new(range.len()) {
+ self.map.clear(range.start, nbits);
+ }
+ }
}
/// Represents an unused id in an [`IdPool`].
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 5/7] rust: id_pool: take a NonZero capacity in with_capacity
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (3 preceding siblings ...)
2026-08-13 7:34 ` [PATCH v6 4/7] rust: id_pool: add contiguous area allocation Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 6/7] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 7/7] gpu: nova-core: add ChannelIdPool Eliot Courtney
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
There is no good reason to allocate an IdPool with zero capacity.
Reflect this in IdPool::with_capacity.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/id_pool.rs | 10 ++++++----
1 file changed, 6 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
index a4a2ff417fe8..1423a5fbca69 100644
--- a/rust/kernel/id_pool.rs
+++ b/rust/kernel/id_pool.rs
@@ -29,10 +29,11 @@
/// Basic usage
///
/// ```
+/// use core::num::NonZero;
/// use kernel::alloc::AllocError;
/// use kernel::id_pool::{IdPool, UnusedId};
///
-/// let mut pool = IdPool::with_capacity(64, GFP_KERNEL)?;
+/// let mut pool = IdPool::with_capacity(NonZero::new(64).unwrap(), GFP_KERNEL)?;
/// for i in 0..64 {
/// assert_eq!(i, pool.find_unused_id(i).ok_or(ENOSPC)?.acquire());
/// }
@@ -117,8 +118,8 @@ pub fn new() -> Self {
///
/// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
#[inline]
- pub fn with_capacity(num_ids: usize, flags: Flags) -> Result<Self, AllocError> {
- let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
+ pub fn with_capacity(num_ids: NonZero<usize>, flags: Flags) -> Result<Self, AllocError> {
+ let num_ids = usize::max(num_ids.get(), BitmapVec::MAX_INLINE_LEN);
let map = BitmapVec::new(num_ids, flags)?;
Ok(Self { map })
}
@@ -138,6 +139,7 @@ pub fn capacity(&self) -> usize {
/// # Examples
///
/// ```
+ /// use core::num::NonZero;
/// use kernel::{
/// alloc::AllocError,
/// bitmap::BitmapVec,
@@ -147,7 +149,7 @@ pub fn capacity(&self) -> usize {
/// },
/// };
///
- /// let mut pool = IdPool::with_capacity(1024, GFP_KERNEL)?;
+ /// let mut pool = IdPool::with_capacity(NonZero::new(1024).unwrap(), GFP_KERNEL)?;
/// let alloc_request = pool.shrink_request().ok_or(AllocError)?;
/// let resizer = alloc_request.realloc(GFP_KERNEL)?;
/// pool.shrink(resizer);
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 6/7] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (4 preceding siblings ...)
2026-08-13 7:34 ` [PATCH v6 5/7] rust: id_pool: take a NonZero capacity in with_capacity Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 7/7] gpu: nova-core: add ChannelIdPool Eliot Courtney
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
Current code in IdPool::with_capacity rounds the capacity up to
BitmapVec::MAX_INLINE_LEN, but BitmapVec::new works fine with values
smaller than this and still uses an inline representation. Remove this
behaviour.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/id_pool.rs | 7 +------
1 file changed, 1 insertion(+), 6 deletions(-)
diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
index 1423a5fbca69..9baafc1732be 100644
--- a/rust/kernel/id_pool.rs
+++ b/rust/kernel/id_pool.rs
@@ -113,14 +113,9 @@ pub fn new() -> Self {
}
/// Constructs a new [`IdPool`] with space for a specific number of bits.
- ///
- /// A capacity below [`MAX_INLINE_LEN`] is adjusted to [`MAX_INLINE_LEN`].
- ///
- /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
#[inline]
pub fn with_capacity(num_ids: NonZero<usize>, flags: Flags) -> Result<Self, AllocError> {
- let num_ids = usize::max(num_ids.get(), BitmapVec::MAX_INLINE_LEN);
- let map = BitmapVec::new(num_ids, flags)?;
+ let map = BitmapVec::new(num_ids.get(), flags)?;
Ok(Self { map })
}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH v6 7/7] gpu: nova-core: add ChannelIdPool
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (5 preceding siblings ...)
2026-08-13 7:34 ` [PATCH v6 6/7] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
@ 2026-08-13 7:34 ` Eliot Courtney
6 siblings, 0 replies; 8+ messages in thread
From: Eliot Courtney @ 2026-08-13 7:34 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
Add `ChannelIdPool` which adds automatic tracking and releasing of
channel IDs on top of `IdPool`. This is necessary for apportioning
ranges of channel IDs to be used in e.g. vGPU.
Channel IDs are allocated as a contiguous sequence with a specific
length and sometimes a specific alignment [1] for vGPU. The ID space is
small (limited to 2048) and allocation is not on a hot path, so a
bitmap-backed `IdPool` is a better fit than IDA/xarray (which allocate a
single ID within a range, not a contiguous sequence) or a maple tree
(where aligned allocation needs an alloc_range()+erase() retry loop that
essentially reimplements bitmap_find_next_zero_area()) [2]. It is
also faster than maple tree [3].
Link: https://lore.kernel.org/all/84bc8bd2-e292-4b84-9580-a1b5df4c5bdc@nvidia.com/ # [1]
Link: https://lore.kernel.org/all/20260710-chid-maple-v1-1-4ee869055268@nvidia.com/ # [2]
Link: https://lore.kernel.org/all/20260717053241.916441-1-ynorov@nvidia.com/ # [3]
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/gpu.rs | 2 +
drivers/gpu/nova-core/gpu/channel.rs | 201 +++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index 42a4cd7971fa..66ea697a89f8 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -33,6 +33,8 @@
vgpu::VgpuManager, //
};
+#[cfg_attr(not(CONFIG_KUNIT = "y"), expect(dead_code))]
+mod channel;
mod hal;
macro_rules! define_chipset {
diff --git a/drivers/gpu/nova-core/gpu/channel.rs b/drivers/gpu/nova-core/gpu/channel.rs
new file mode 100644
index 000000000000..520436c6f6f2
--- /dev/null
+++ b/drivers/gpu/nova-core/gpu/channel.rs
@@ -0,0 +1,201 @@
+// SPDX-License-Identifier: GPL-2.0
+// SPDX-FileCopyrightText: Copyright (c) 2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
+
+//! Channel ID allocation.
+
+use core::{
+ num::NonZero,
+ ops::{
+ Deref,
+ Range, //
+ }, //
+};
+
+use kernel::{
+ id_pool::IdPool,
+ prelude::*,
+ ptr::Alignment,
+ sync::{
+ new_mutex,
+ Mutex, //
+ }, //
+};
+
+/// Pool for tracking reservations of channel IDs.
+#[pin_data]
+pub(crate) struct ChannelIdPool {
+ #[pin]
+ inner: Mutex<IdPool>,
+}
+
+impl ChannelIdPool {
+ /// Creates a pool managing `num_chids` channel IDs.
+ pub(crate) fn new(num_chids: NonZero<usize>) -> impl PinInit<Self, Error> {
+ try_pin_init!(Self {
+ inner <- new_mutex!(IdPool::with_capacity(num_chids, GFP_KERNEL)?),
+ })
+ }
+
+ /// Reserves a contiguous area of `count` channel IDs starting at a multiple of `align`,
+ /// returning a guard that releases the area on drop.
+ pub(crate) fn alloc_area(
+ &self,
+ count: NonZero<usize>,
+ align: Alignment,
+ ) -> Result<ChannelIdArea<'_>> {
+ let mut ids = self.inner.lock();
+ let range = ids.alloc_area(0, count, align).ok_or(ENOSPC)?;
+ Ok(ChannelIdArea { pool: self, range })
+ }
+}
+
+/// A reserved contiguous area of channel IDs.
+///
+/// Releases the whole area back to its [`ChannelIdPool`] when dropped. Releasing locks a
+/// sleeping [`Mutex`], so the area must be dropped in a context that is allowed to sleep.
+#[must_use = "the channel ID area is released immediately when unused"]
+pub(crate) struct ChannelIdArea<'a> {
+ pool: &'a ChannelIdPool,
+ range: Range<usize>,
+}
+
+impl Drop for ChannelIdArea<'_> {
+ fn drop(&mut self) {
+ self.pool.inner.lock().release_area(&self.range);
+ }
+}
+
+impl Deref for ChannelIdArea<'_> {
+ type Target = Range<usize>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.range
+ }
+}
+
+#[kunit_tests(nova_core_channel)]
+mod tests {
+ use super::*;
+
+ const fn nz<const N: usize>() -> NonZero<usize> {
+ const { NonZero::new(N).unwrap() }
+ }
+
+ #[test]
+ fn chid_area() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(nz::<2048>()), GFP_KERNEL)?;
+ let unaligned = Alignment::new::<1>();
+
+ let first = pool.alloc_area(nz::<48>(), unaligned)?;
+ assert_eq!(0, first.start);
+ assert_eq!(48, first.len());
+ assert_eq!(48, first.end);
+
+ let second = pool.alloc_area(nz::<48>(), unaligned)?;
+ assert!(first.end <= second.start || second.end <= first.start);
+
+ let first_start = first.start;
+ drop(first);
+ assert_eq!(first_start, pool.alloc_area(nz::<48>(), unaligned)?.start);
+ Ok(())
+ }
+
+ #[test]
+ fn chid_area_drop() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(nz::<8>()), GFP_KERNEL)?;
+ let unaligned = Alignment::new::<1>();
+
+ let a = pool.alloc_area(nz::<3>(), unaligned)?;
+ let b = pool.alloc_area(nz::<3>(), unaligned)?;
+ let c = pool.alloc_area(nz::<2>(), unaligned)?;
+ assert_eq!(0, a.start);
+ assert_eq!(3, b.start);
+ assert_eq!(6, c.start);
+
+ drop(b);
+
+ // Only have space for 3 IDs right now.
+ assert_eq!(
+ Err(ENOSPC),
+ pool.alloc_area(nz::<4>(), unaligned).map(|_| ())
+ );
+ let b = pool.alloc_area(nz::<3>(), unaligned)?;
+ assert_eq!(3, b.start);
+
+ drop(a);
+ drop(c);
+ drop(b);
+
+ // Everything was dropped so the pool should be empty.
+ assert_eq!(0, pool.alloc_area(nz::<8>(), unaligned)?.start);
+ Ok(())
+ }
+
+ #[test]
+ fn chid_bounded_by_num_chids() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(nz::<4>()), GFP_KERNEL)?;
+ let unaligned = Alignment::new::<1>();
+
+ {
+ let a = pool.alloc_area(nz::<1>(), unaligned)?;
+ let b = pool.alloc_area(nz::<1>(), unaligned)?;
+ let c = pool.alloc_area(nz::<1>(), unaligned)?;
+ let d = pool.alloc_area(nz::<1>(), unaligned)?;
+ assert_eq!(0, a.start);
+ assert_eq!(1, b.start);
+ assert_eq!(2, c.start);
+ assert_eq!(3, d.start);
+ assert_eq!(
+ Err(ENOSPC),
+ pool.alloc_area(nz::<1>(), unaligned).map(|_| ())
+ );
+ }
+
+ assert_eq!(0, pool.alloc_area(nz::<4>(), unaligned)?.start);
+ assert_eq!(
+ Err(ENOSPC),
+ pool.alloc_area(nz::<5>(), unaligned).map(|_| ())
+ );
+
+ let head = pool.alloc_area(nz::<3>(), unaligned)?;
+ assert_eq!(0, head.start);
+ assert_eq!(
+ Err(ENOSPC),
+ pool.alloc_area(nz::<2>(), unaligned).map(|_| ())
+ );
+ assert_eq!(3, pool.alloc_area(nz::<1>(), unaligned)?.start);
+ Ok(())
+ }
+
+ #[test]
+ fn chid_area_aligned() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(nz::<16>()), GFP_KERNEL)?;
+ let unaligned = Alignment::new::<1>();
+ let align4 = Alignment::new::<4>();
+
+ // Alloc 0 so the first fit for the next area is unaligned.
+ let pad = pool.alloc_area(nz::<1>(), unaligned)?;
+ assert_eq!(0, pad.start);
+
+ let a = pool.alloc_area(nz::<4>(), align4)?;
+ assert_eq!(4, a.start);
+
+ // The area skipped over by the aligned allocation should still be available.
+ let b = pool.alloc_area(nz::<1>(), unaligned)?;
+ assert_eq!(1, b.start);
+
+ let c = pool.alloc_area(nz::<8>(), Alignment::new::<8>())?;
+ assert_eq!(8, c.start);
+
+ // Only 2 IDs left.
+ assert_eq!(Err(ENOSPC), pool.alloc_area(nz::<4>(), align4).map(|_| ()));
+ assert_eq!(
+ Err(ENOSPC),
+ pool.alloc_area(nz::<1>(), Alignment::new::<32>())
+ .map(|_| ())
+ );
+
+ assert_eq!(2, pool.alloc_area(nz::<2>(), unaligned)?.start);
+ Ok(())
+ }
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 8+ messages in thread
end of thread, other threads:[~2026-08-13 7:35 UTC | newest]
Thread overview: 8+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-13 7:34 [PATCH v6 0/7] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 1/7] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 2/7] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 3/7] rust: bitmap: add contiguous area operations Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 4/7] rust: id_pool: add contiguous area allocation Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 5/7] rust: id_pool: take a NonZero capacity in with_capacity Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 6/7] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
2026-08-13 7:34 ` [PATCH v6 7/7] gpu: nova-core: add ChannelIdPool Eliot Courtney
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox