* [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs
@ 2026-09-30 2:42 Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 1/9] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
` (9 more replies)
0 siblings, 10 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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 and depends on the cv! series [4].
[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/
[4]: https://lore.kernel.org/all/20260917-cv-v4-0-547ef5727451@nvidia.com/
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
Changes in v9:
- Rebase on drm-rust-next
- Moved cv! to a separate series which this now depends on
- Reference Link: tag from the text + indent code in patch #4 commit
message (Miguel)
- Add reviewed-by tags
- Link to v8: https://patch.msgid.link/20260827-chid-v8-0-bc74c77d0214@nvidia.com
Changes in v8:
- Clarify overflow cases in patch #2 (Alex)
- Replace nz! with generalized cv! macro (Gary)
- Add cv! macro to prelude
- Split SizeConstants and adding new constants patch (Alex)
- Move alignment size constants conersions patch (Alex)
- Drop NonZero on IdPool, fix grow_request instead (Alice, Alex)
- Add shrink/grow doctests (Burak)
- Link to v7: https://patch.msgid.link/20260817-chid-v7-0-a5872e64d8f4@nvidia.com
Changes in v7:
- Add nz! macro for compile time NonZero
- Implement SizeConstants for Alignment + add sub 1k size constants
- Use Alignment constants + nz! in this series
- Add final patch converting existing callers to use Alignment constants
- Rename alloc_area/release_area -> reserve_ids / release_ids
- Link to v6: https://patch.msgid.link/20260813-chid-v6-0-160be5dfb5bd@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 (9):
rust: bitmap: use function-level cfg on kunit test
rust: bitmap: restrict bitmap length to at most i32::MAX
rust: sizes: add sub-1K size constants
rust: sizes: implement SizeConstants for Alignment
rust: use Alignment size constants
rust: bitmap: add contiguous area operations
rust: id_pool: add contiguous ID reservation
rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
gpu: nova-core: add ChannelIdPool
drivers/gpu/nova-core/fb.rs | 10 +-
drivers/gpu/nova-core/fb/hal/gb100.rs | 2 +-
drivers/gpu/nova-core/fsp.rs | 4 +-
drivers/gpu/nova-core/gpu.rs | 2 +
drivers/gpu/nova-core/gpu/channel.rs | 198 ++++++++++++++++++++
drivers/gpu/nova-core/gsp/fw.rs | 12 +-
drivers/gpu/nova-core/vbios.rs | 7 +-
rust/kernel/bitmap.rs | 336 ++++++++++++++++++++++++++++++----
rust/kernel/gpu/buddy.rs | 26 +--
rust/kernel/id_pool.rs | 70 ++++++-
rust/kernel/io.rs | 5 +-
rust/kernel/sizes.rs | 47 ++++-
12 files changed, 641 insertions(+), 78 deletions(-)
---
base-commit: 10a6623a24a85708650efad7be15182289403cd7
change-id: 20260608-chid-18fa943c6d6c
prerequisite-change-id: 20260828-cv-8d4e952fc14c:v4
prerequisite-patch-id: b913a24359e679cb354298102cf5de13760a3c27
prerequisite-patch-id: 01ec2e5a44b393a7ce1ee820654750dd4743fcb5
Best regards,
--
Eliot Courtney <ecourtney@nvidia.com>
^ permalink raw reply [flat|nested] 26+ messages in thread
* [PATCH v9 1/9] rust: bitmap: use function-level cfg on kunit test
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 2/9] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
` (8 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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>
Reviewed-by: Burak Emir <burak.emir@gmail.com>
Reviewed-by: Gary Guo <gary@garyguo.net>
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] 26+ messages in thread
* [PATCH v9 2/9] rust: bitmap: restrict bitmap length to at most i32::MAX
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 1/9] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 3/9] rust: sizes: add sub-1K size constants Eliot Courtney
` (7 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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`. For example,
`copy_and_extend` truncates `len` to u32, which is wrong for > u32::MAX
size. `__bitmap_set` and `__bitmap_clear` need i32 for `size` and u32
for `start` - so they can't be run on a `Bitmap` with a > i32::MAX size.
Rather than adding runtime checks to account for the case of a non
`BitmapVec` backed `Bitmap`, just include that in the requirements for
`Bitmap`.
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>
Reviewed-by: Burak Emir <burak.emir@gmail.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/bitmap.rs | 70 +++++++++++++++++++++++++++++++++++----------------
1 file changed, 49 insertions(+), 21 deletions(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index a43bfe0ec3dc..df5505ec7a96 100644
--- a/rust/kernel/bitmap.rs
+++ b/rust/kernel/bitmap.rs
@@ -17,24 +17,59 @@
/// # Invariants
///
/// Must reference a `[c_ulong]` long enough to fit `data.len()` bits.
+/// Must not be longer than `i32::MAX` bits, so offsets and lengths used with
+/// `Bitmap` functions fit in the int and unsigned int arguments of the C bitmap API.
+/// This also matches [`BitmapVec::MAX_LEN`].
#[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 +86,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 +143,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 +442,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] 26+ messages in thread
* [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 1/9] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 2/9] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 9:12 ` Miguel Ojeda
2026-09-30 2:42 ` [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
` (6 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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 some more size constants, mirroring include/linux/sizes.h. This is
useful for making `Alignment` implement `SizeConstants` in a following
patch, because these are more common alignment values.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/sizes.rs | 20 ++++++++++++++++++++
1 file changed, 20 insertions(+)
diff --git a/rust/kernel/sizes.rs b/rust/kernel/sizes.rs
index 521b2b38bfe7..7d03361eae3d 100644
--- a/rust/kernel/sizes.rs
+++ b/rust/kernel/sizes.rs
@@ -35,6 +35,26 @@
macro_rules! define_sizes {
($($type:ty),* $(,)?) => {
define_sizes!(@internal [$($type),*]
+ /// `0x0000_0001`.
+ SZ_1,
+ /// `0x0000_0002`.
+ SZ_2,
+ /// `0x0000_0004`.
+ SZ_4,
+ /// `0x0000_0008`.
+ SZ_8,
+ /// `0x0000_0010`.
+ SZ_16,
+ /// `0x0000_0020`.
+ SZ_32,
+ /// `0x0000_0040`.
+ SZ_64,
+ /// `0x0000_0080`.
+ SZ_128,
+ /// `0x0000_0100`.
+ SZ_256,
+ /// `0x0000_0200`.
+ SZ_512,
/// `0x0000_0400`.
SZ_1K,
/// `0x0000_0800`.
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (2 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 3/9] rust: sizes: add sub-1K size constants Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 9:12 ` Miguel Ojeda
2026-09-30 2:42 ` [PATCH v9 5/9] rust: use Alignment size constants Eliot Courtney
` (5 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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
Currently, constructing an alignment is quite verbose [1]:
Alignment::new::<8>()
It's unfortunate because it disincentivizes using it at interface
boundaries. Implement `SizeConstants` for `Alignment` so we can write
e.g. `Alignment::SZ_8` instead.
Link: https://lore.kernel.org/an4xDp29VX8Am0uR@yury [1]
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/sizes.rs | 27 ++++++++++++++++++++++++++-
1 file changed, 26 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/sizes.rs b/rust/kernel/sizes.rs
index 7d03361eae3d..188e00c2b8b4 100644
--- a/rust/kernel/sizes.rs
+++ b/rust/kernel/sizes.rs
@@ -13,6 +13,11 @@
//! these constants as [`u64`] (or [`u32`]) rather than [`usize`], because
//! device address spaces are sized independently of the CPU pointer width.
//!
+//! The trait is also implemented for [`Alignment`], providing each size as a
+//! compile-time validated alignment.
+//!
+//! [`Alignment`]: crate::ptr::Alignment
+//!
//! # Examples
//!
//! ```
@@ -105,6 +110,7 @@ macro_rules! define_sizes {
(@internal [$($type:ty),*] $($names_and_metas:tt)*) => {
define_sizes!(@consts_and_trait $($names_and_metas)*);
define_sizes!(@impls [$($type),*] $($names_and_metas)*);
+ define_sizes!(@impl_alignment $($names_and_metas)*);
};
(@consts_and_trait $($(#[$meta:meta])* $name:ident,)*) => {
@@ -119,13 +125,22 @@ macro_rules! define_sizes {
/// choose the width that matches their hardware. All `SZ_*` values fit
/// in a [`u32`], so all implementations are lossless.
///
+ /// Also implemented for [`Alignment`], providing each size as a
+ /// compile-time validated alignment.
+ ///
+ /// [`Alignment`]: crate::ptr::Alignment
+ ///
/// # Examples
///
/// ```
- /// use kernel::sizes::SizeConstants;
+ /// use kernel::{
+ /// ptr::Alignment,
+ /// sizes::SizeConstants, //
+ /// };
///
/// let gpu_heap = 14 * u64::SZ_1M;
/// let mmio_window = u32::SZ_16M;
+ /// let page_align = Alignment::SZ_4K;
/// ```
pub trait SizeConstants {
$(
@@ -137,6 +152,16 @@ pub trait SizeConstants {
(@impls [] $($(#[$meta:meta])* $name:ident,)*) => {};
+ (@impl_alignment $($(#[$meta:meta])* $name:ident,)*) => {
+ impl SizeConstants for crate::ptr::Alignment {
+ $(
+ $(#[$meta])*
+ // A non-power-of-two constant will fail the build here if used.
+ const $name: Self = crate::ptr::Alignment::new::<{ self::$name }>();
+ )*
+ }
+ };
+
(@impls [$first:ty $(, $rest:ty)*] $($(#[$meta:meta])* $name:ident,)*) => {
impl SizeConstants for $first {
$(
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v9 5/9] rust: use Alignment size constants
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (3 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 6/9] rust: bitmap: add contiguous area operations Eliot Courtney
` (4 subsequent siblings)
9 siblings, 0 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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
Use `SizeConstants` for Alignment that are implemented now.
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/fb.rs | 10 +++++-----
drivers/gpu/nova-core/fb/hal/gb100.rs | 2 +-
drivers/gpu/nova-core/fsp.rs | 4 ++--
drivers/gpu/nova-core/gsp/fw.rs | 12 +++---------
drivers/gpu/nova-core/vbios.rs | 7 +++++--
rust/kernel/gpu/buddy.rs | 26 +++++++++++++-------------
rust/kernel/io.rs | 5 +++--
7 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index b0c0b8fe6008..5ceb7760c78e 100644
--- a/drivers/gpu/nova-core/fb.rs
+++ b/drivers/gpu/nova-core/fb.rs
@@ -219,7 +219,7 @@ pub(crate) fn new(
};
let frts = {
- const FRTS_DOWN_ALIGN: Alignment = Alignment::new::<SZ_128K>();
+ const FRTS_DOWN_ALIGN: Alignment = Alignment::SZ_128K;
let frts_size: u64 = hal.frts_size();
let frts_base = vga_workspace.start.align_down(FRTS_DOWN_ALIGN) - frts_size;
@@ -227,7 +227,7 @@ pub(crate) fn new(
};
let boot = {
- const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::new::<SZ_4K>();
+ const BOOTLOADER_DOWN_ALIGN: Alignment = Alignment::SZ_4K;
let bootloader_size = u64::from_safe_cast(gsp_fw.bootloader.ucode.size());
let bootloader_base = (frts.start - bootloader_size).align_down(BOOTLOADER_DOWN_ALIGN);
@@ -235,7 +235,7 @@ pub(crate) fn new(
};
let fw_image = {
- const FW_IMAGE_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+ const FW_IMAGE_DOWN_ALIGN: Alignment = Alignment::SZ_64K;
let fw_image_size = u64::from_safe_cast(gsp_fw.size());
let fw_image_addr = (boot.start - fw_image_size).align_down(FW_IMAGE_DOWN_ALIGN);
@@ -245,7 +245,7 @@ pub(crate) fn new(
let (vf_partition_count, wpr2_heap_size) = wpr2_heap_params(chipset, vgpu_state, fb.end)?;
let wpr2_heap = {
- const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ const WPR2_HEAP_DOWN_ALIGN: Alignment = Alignment::SZ_1M;
let wpr2_heap_addr = fw_image
.start
.checked_sub(wpr2_heap_size)
@@ -256,7 +256,7 @@ pub(crate) fn new(
};
let wpr2 = {
- const WPR2_DOWN_ALIGN: Alignment = Alignment::new::<SZ_1M>();
+ const WPR2_DOWN_ALIGN: Alignment = Alignment::SZ_1M;
let wpr2_addr = (wpr2_heap.start - u64::from_safe_cast(size_of::<gsp::GspFwWprMeta>()))
.align_down(WPR2_DOWN_ALIGN);
diff --git a/drivers/gpu/nova-core/fb/hal/gb100.rs b/drivers/gpu/nova-core/fb/hal/gb100.rs
index 612f70333c11..37b2ae762df4 100644
--- a/drivers/gpu/nova-core/fb/hal/gb100.rs
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -80,7 +80,7 @@ fn write_sysmem_flush_page_gb100(hshub0: Mmio<'_, regs::Hshub0Registers>, addr:
// This PMU reservation size is r570-specific.
pub(super) const fn pmu_reserved_size_gb100() -> u32 {
- cv!(const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::new::<SZ_128K>()).unwrap())
+ cv!(const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::SZ_128K).unwrap())
}
impl FbHal for Gb100 {
diff --git a/drivers/gpu/nova-core/fsp.rs b/drivers/gpu/nova-core/fsp.rs
index 3a4a10487f33..637a00b2c951 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -20,7 +20,7 @@
Alignable,
Alignment, //
},
- sizes::SZ_2M,
+ sizes::SizeConstants,
time::Delta,
transmute::{
AsBytes,
@@ -259,7 +259,7 @@ fn frts_vidmem_offset(hal: &dyn hal::FspHal, fb_info: &FbSizes) -> Result<u64> {
if fb_info.pmu_reserved_size != 0 {
offset = (offset + u64::from(fb_info.pmu_reserved_size))
// The 2 MiB alignment is r570-specific.
- .align_up(Alignment::new::<SZ_2M>())
+ .align_up(Alignment::SZ_2M)
.ok_or(EINVAL)?;
}
diff --git a/drivers/gpu/nova-core/gsp/fw.rs b/drivers/gpu/nova-core/gsp/fw.rs
index 1b548bbf146e..b4866ad21f6e 100644
--- a/drivers/gpu/nova-core/gsp/fw.rs
+++ b/drivers/gpu/nova-core/gsp/fw.rs
@@ -25,10 +25,7 @@
Alignment,
KnownSize, //
},
- sizes::{
- SizeConstants,
- SZ_128K, //
- },
+ sizes::SizeConstants,
transmute::{
AsBytes,
FromBytes, //
@@ -63,7 +60,7 @@
enum GspFwHeapParams {}
/// Minimum required alignment for the GSP heap.
-const GSP_HEAP_ALIGNMENT: Alignment = Alignment::new::<{ 1 << 20 }>();
+const GSP_HEAP_ALIGNMENT: Alignment = Alignment::SZ_1M;
impl GspFwHeapParams {
/// Returns the amount of GSP-RM heap memory used during GSP-RM boot and initialization (up to
@@ -207,10 +204,7 @@ pub(crate) fn from_ranges<'a>(
bootBinOffset: ranges.boot.start,
frtsOffset: ranges.frts.start,
frtsSize: ranges.frts.len(),
- gspFwWprEnd: ranges
- .vga_workspace
- .start
- .align_down(Alignment::new::<SZ_128K>()),
+ gspFwWprEnd: ranges.vga_workspace.start.align_down(Alignment::SZ_128K),
gspFwHeapVfPartitionCount: ranges.vf_partition_count,
fbSize: ranges.fb.len(),
vgaWorkspaceOffset: ranges.vga_workspace.start,
diff --git a/drivers/gpu/nova-core/vbios.rs b/drivers/gpu/nova-core/vbios.rs
index 9c214b9f4dd9..a2d2d170b501 100644
--- a/drivers/gpu/nova-core/vbios.rs
+++ b/drivers/gpu/nova-core/vbios.rs
@@ -11,7 +11,10 @@
Alignment, //
},
register,
- sizes::SZ_4K,
+ sizes::{
+ SizeConstants,
+ SZ_4K, //
+ },
sync::aref::ARef,
};
@@ -291,7 +294,7 @@ fn next(&mut self) -> Option<Self::Item> {
// Advance to next image (aligned to 512 bytes).
self.current_offset += image_size;
- self.current_offset = self.current_offset.align_up(Alignment::new::<512>())?;
+ self.current_offset = self.current_offset.align_up(Alignment::SZ_512)?;
Some(Ok(full_image))
}
diff --git a/rust/kernel/gpu/buddy.rs b/rust/kernel/gpu/buddy.rs
index d502ada6ebbd..691bb40629d4 100644
--- a/rust/kernel/gpu/buddy.rs
+++ b/rust/kernel/gpu/buddy.rs
@@ -31,11 +31,11 @@
//! let buddy = GpuBuddy::new(GpuBuddyParams {
//! base_offset: 0,
//! size: SZ_1G as u64,
-//! chunk_size: Alignment::new::<SZ_4K>(),
+//! chunk_size: Alignment::SZ_4K,
//! })?;
//!
//! assert_eq!(buddy.size(), SZ_1G as u64);
-//! assert_eq!(buddy.chunk_size(), Alignment::new::<SZ_4K>());
+//! assert_eq!(buddy.chunk_size(), Alignment::SZ_4K);
//! let initial_free = buddy.avail();
//!
//! // Allocate 16MB. Block lands at the top of the address range.
@@ -43,7 +43,7 @@
//! buddy.alloc_blocks(
//! GpuBuddyAllocMode::Simple,
//! SZ_16M as u64,
-//! Alignment::new::<SZ_16M>(),
+//! Alignment::SZ_16M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -74,14 +74,14 @@
//! # let buddy = GpuBuddy::new(GpuBuddyParams {
//! # base_offset: 0,
//! # size: SZ_1G as u64,
-//! # chunk_size: Alignment::new::<SZ_4K>(),
+//! # chunk_size: Alignment::SZ_4K,
//! # })?;
//! # let initial_free = buddy.avail();
//! let topdown = KBox::pin_init(
//! buddy.alloc_blocks(
//! GpuBuddyAllocMode::TopDown,
//! SZ_16M as u64,
-//! Alignment::new::<SZ_16M>(),
+//! Alignment::SZ_16M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -114,7 +114,7 @@
//! # let buddy = GpuBuddy::new(GpuBuddyParams {
//! # base_offset: 0,
//! # size: SZ_1G as u64,
-//! # chunk_size: Alignment::new::<SZ_4K>(),
+//! # chunk_size: Alignment::SZ_4K,
//! # })?;
//! # let initial_free = buddy.avail();
//! // Create fragmentation by allocating 4MB blocks at [0,4M) and [8M,12M).
@@ -122,7 +122,7 @@
//! buddy.alloc_blocks(
//! GpuBuddyAllocMode::Range(0..SZ_4M as u64),
//! SZ_4M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -133,7 +133,7 @@
//! buddy.alloc_blocks(
//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64),
//! SZ_4M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -145,7 +145,7 @@
//! buddy.alloc_blocks(
//! GpuBuddyAllocMode::Range(0..SZ_16M as u64),
//! SZ_8M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -178,14 +178,14 @@
//! let small = GpuBuddy::new(GpuBuddyParams {
//! base_offset: 0,
//! size: SZ_16M as u64,
-//! chunk_size: Alignment::new::<SZ_4K>(),
+//! chunk_size: Alignment::SZ_4K,
//! })?;
//!
//! let _hole1 = KBox::pin_init(
//! small.alloc_blocks(
//! GpuBuddyAllocMode::Range(0..SZ_4M as u64),
//! SZ_4M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -195,7 +195,7 @@
//! small.alloc_blocks(
//! GpuBuddyAllocMode::Range(SZ_8M as u64..(SZ_8M + SZ_4M) as u64),
//! SZ_4M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlags::default(),
//! ),
//! GFP_KERNEL,
@@ -206,7 +206,7 @@
//! small.alloc_blocks(
//! GpuBuddyAllocMode::Simple,
//! SZ_8M as u64,
-//! Alignment::new::<SZ_4M>(),
+//! Alignment::SZ_4M,
//! GpuBuddyAllocFlag::Contiguous,
//! ),
//! GFP_KERNEL,
diff --git a/rust/kernel/io.rs b/rust/kernel/io.rs
index de8ef8e2aec4..5712478a5461 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -19,7 +19,8 @@
ptr::{
Alignment,
KnownSize, //
- }, //
+ },
+ sizes::SizeConstants, //
};
#[cfg(CONFIG_HAS_IOMEM)]
@@ -90,7 +91,7 @@ pub fn ptr_try_from_raw_parts_mut(base: *mut u8, size: usize) -> Result<*mut Sel
impl<const SIZE: usize> KnownSize for Region<SIZE> {
const MIN_SIZE: usize = SIZE;
// Alignment of 4 is the most common; different base types can be added once required.
- const MIN_ALIGN: Alignment = Alignment::new::<4>();
+ const MIN_ALIGN: Alignment = Alignment::SZ_4;
#[inline(always)]
fn size(p: *const Self) -> usize {
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v9 6/9] rust: bitmap: add contiguous area operations
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (4 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 5/9] rust: use Alignment size constants Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 4:41 ` Yury Norov
2026-09-30 2:42 ` [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation Eliot Courtney
` (3 subsequent siblings)
9 siblings, 1 reply; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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.
Reviewed-by: Burak Emir <burak.emir@gmail.com>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/bitmap.rs | 241 +++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 239 insertions(+), 2 deletions(-)
diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
index df5505ec7a96..23c2b43a98ac 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.
///
@@ -525,13 +529,159 @@ 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 kernel::{
+ /// alloc::{AllocError, flags::GFP_KERNEL},
+ /// bitmap::BitmapVec,
+ /// ptr::Alignment,
+ /// sizes::SizeConstants, //
+ /// };
+ ///
+ /// let mut b = BitmapVec::new(64, GFP_KERNEL)?;
+ ///
+ /// assert_eq!(Some(0), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
+ /// b.set(0, cv!(5));
+ /// assert_eq!(Some(5), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
+ /// assert_eq!(Some(8), b.next_zero_area(0, cv!(8), Alignment::SZ_8));
+ /// assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
+ /// # 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)]
#[macros::kunit_tests(rust_kernel_bitmap)]
mod tests {
use super::*;
- use kernel::alloc::flags::GFP_KERNEL;
+ use kernel::{
+ alloc::flags::GFP_KERNEL,
+ num::cv,
+ sizes::SizeConstants, //
+ };
#[test]
fn bitmap_borrow() {
@@ -642,4 +792,91 @@ 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)?;
+
+ assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
+ b.set(0, cv!(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, cv!(5), Alignment::SZ_1));
+ assert_eq!(Some(8), b.next_zero_area(0, cv!(5), Alignment::SZ_8));
+
+ b.set(8, cv!(8)); // Now contains {[0, 5), [8, 16)}.
+ assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_16));
+ assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_1));
+
+ b.clear(0, cv!(5)); // Now contains {[8, 16)}.
+ assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
+ assert_eq!(Some(8), b.next_bit(0));
+ assert_eq!(Some(15), b.last_bit());
+
+ b.set(60, cv!(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, cv!(40), Alignment::SZ_1));
+ assert_eq!(Some(70), b.next_zero_area(0, cv!(45), Alignment::SZ_1));
+
+ b.clear(62, cv!(6)); // Now contains {[8, 16), [60, 62), [68, 70)}.
+ assert_eq!(Some(62), b.next_zero_area(60, cv!(6), Alignment::SZ_1));
+ 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)?;
+
+ assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
+ assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
+ assert_eq!(None, b.next_zero_area(1, cv!(usize::MAX), Alignment::SZ_1));
+
+ b.set_bit(0); // Now contains {[0, 1)}.
+ assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
+
+ b.set(0, cv!(61)); // Now contains {[0, 61)}.
+ assert_eq!(None, b.next_zero_area(0, cv!(4), Alignment::SZ_1));
+ assert_eq!(Some(61), b.next_zero_area(0, cv!(3), Alignment::SZ_1));
+ assert_eq!(None, b.next_zero_area(0, cv!(1), Alignment::SZ_64));
+ Ok(())
+ }
+
+ #[test]
+ fn bitmap_area_off() -> Result<(), AllocError> {
+ let mut b = BitmapVec::new(64, GFP_KERNEL)?;
+
+ b.set(0, cv!(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, cv!(8), Alignment::SZ_8, 1));
+ assert_eq!(Some(5), b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 3));
+
+ // A zero offset behaves like next_zero_area().
+ assert_eq!(
+ b.next_zero_area(0, cv!(8), Alignment::SZ_8),
+ b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 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, cv!(4));
+ b.set(62, cv!(8));
+ b.set(usize::MAX, cv!(1));
+ b.clear(usize::MAX, cv!(1));
+ b.clear(2048, cv!(8));
+ assert_eq!(None, b.next_bit(0));
+ assert_eq!(None, b.next_zero_area(64, cv!(1), Alignment::SZ_1));
+ Ok(())
+ }
}
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (5 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 6/9] rust: bitmap: add contiguous area operations Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 2:51 ` sashiko-bot
2026-09-30 4:50 ` Yury Norov
2026-09-30 2:42 ` [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
` (2 subsequent siblings)
9 siblings, 2 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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::reserve_ids` which allocates a contiguous range with the
given offset, count, and alignment.
Reviewed-by: Burak Emir <burak.emir@gmail.com>
Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
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..06a4c71c4c6c 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);
}
+
+ /// Reserves a contiguous area of `count` IDs at or after `offset`.
+ ///
+ /// The start of the returned area is a multiple of `align`.
+ ///
+ /// Returns the reserved range upon success, or [`None`] if no such area could be found.
+ #[inline]
+ #[must_use]
+ pub fn reserve_ids(
+ &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_ids(&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] 26+ messages in thread
* [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (6 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-09-30 2:52 ` sashiko-bot
2026-09-30 5:05 ` Yury Norov
2026-09-30 2:42 ` [PATCH v9 9/9] gpu: nova-core: add ChannelIdPool Eliot Courtney
2026-10-08 15:04 ` [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Alexandre Courbot
9 siblings, 2 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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.
This allows specifying a real capacity of 0, which was not previously
possible. This breaks `grow_request` in this case, so change it to grow
to at least `BitmapVec::MAX_INLINE_LEN`, mirroring the capacity floor in
`shrink_request`.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/id_pool.rs | 38 ++++++++++++++++++++++++++++++++------
1 file changed, 32 insertions(+), 6 deletions(-)
diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
index 06a4c71c4c6c..4f329249df9d 100644
--- a/rust/kernel/id_pool.rs
+++ b/rust/kernel/id_pool.rs
@@ -112,13 +112,8 @@ 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: usize, flags: Flags) -> Result<Self, AllocError> {
- let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
let map = BitmapVec::new(num_ids, flags)?;
Ok(Self { map })
}
@@ -152,6 +147,13 @@ pub fn capacity(&self) -> usize {
/// let resizer = alloc_request.realloc(GFP_KERNEL)?;
/// pool.shrink(resizer);
/// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
+ ///
+ /// // A pool at the `MAX_INLINE_LEN` floor cannot shrink further.
+ /// assert!(pool.shrink_request().is_none());
+ ///
+ /// // Neither can a pool with a capacity below `MAX_INLINE_LEN`.
+ /// let small = IdPool::with_capacity(8, GFP_KERNEL)?;
+ /// assert!(small.shrink_request().is_none());
/// # Ok::<(), AllocError>(())
/// ```
#[inline]
@@ -198,12 +200,36 @@ pub fn shrink(&mut self, mut resizer: PoolResizer) {
/// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.
///
+ /// Grows to at least [`MAX_INLINE_LEN`].
/// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].
///
+ /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
/// [`MAX_LEN`]: BitmapVec::MAX_LEN
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::{
+ /// alloc::AllocError,
+ /// bitmap::BitmapVec,
+ /// id_pool::IdPool, //
+ /// };
+ ///
+ /// // Grow goes to at least BitmapVec::MAX_INLINE_LEN.
+ /// let mut pool = IdPool::with_capacity(0, GFP_KERNEL)?;
+ /// let resizer = pool.grow_request().ok_or(AllocError)?.realloc(GFP_KERNEL)?;
+ /// pool.grow(resizer);
+ /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
+ ///
+ /// // Grow doubles if at least BitmapVec::MAX_INLINE_LEN.
+ /// let resizer = pool.grow_request().ok_or(AllocError)?.realloc(GFP_KERNEL)?;
+ /// pool.grow(resizer);
+ /// assert_eq!(pool.capacity(), 2 * BitmapVec::MAX_INLINE_LEN);
+ /// # Ok::<(), AllocError>(())
+ /// ```
#[inline]
pub fn grow_request(&self) -> Option<ReallocRequest> {
- let num_ids = self.capacity() * 2;
+ let num_ids = usize::max(BitmapVec::MAX_INLINE_LEN, self.capacity() * 2);
if num_ids > BitmapVec::MAX_LEN {
return None;
}
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* [PATCH v9 9/9] gpu: nova-core: add ChannelIdPool
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (7 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
@ 2026-09-30 2:42 ` Eliot Courtney
2026-10-08 15:04 ` [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Alexandre Courbot
9 siblings, 0 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-09-30 2:42 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 | 198 +++++++++++++++++++++++++++++++++++
2 files changed, 200 insertions(+)
diff --git a/drivers/gpu/nova-core/gpu.rs b/drivers/gpu/nova-core/gpu.rs
index fb6f8a86a503..0571d7c9fcd3 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -47,6 +47,8 @@
vgpu::VgpuManager, //
};
+#[cfg_attr(not(CONFIG_KUNIT = "y"), expect(dead_code))]
+mod channel;
mod hal;
mod regs;
diff --git a/drivers/gpu/nova-core/gpu/channel.rs b/drivers/gpu/nova-core/gpu/channel.rs
new file mode 100644
index 000000000000..485efaba059d
--- /dev/null
+++ b/drivers/gpu/nova-core/gpu/channel.rs
@@ -0,0 +1,198 @@
+// 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.get(), 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 reserve_ids(
+ &self,
+ count: NonZero<usize>,
+ align: Alignment,
+ ) -> Result<ChannelIdReservation<'_>> {
+ let mut ids = self.inner.lock();
+ let range = ids.reserve_ids(0, count, align).ok_or(ENOSPC)?;
+ Ok(ChannelIdReservation { 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 reservation is released immediately when unused"]
+pub(crate) struct ChannelIdReservation<'a> {
+ pool: &'a ChannelIdPool,
+ range: Range<usize>,
+}
+
+impl Drop for ChannelIdReservation<'_> {
+ fn drop(&mut self) {
+ self.pool.inner.lock().release_ids(&self.range);
+ }
+}
+
+impl Deref for ChannelIdReservation<'_> {
+ type Target = Range<usize>;
+
+ fn deref(&self) -> &Self::Target {
+ &self.range
+ }
+}
+
+#[kunit_tests(nova_core_channel)]
+mod tests {
+ use super::*;
+ use kernel::sizes::SizeConstants;
+
+ #[test]
+ fn chid_reservation() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(cv!(2048)), GFP_KERNEL)?;
+
+ let first = pool.reserve_ids(cv!(48), Alignment::SZ_1)?;
+ assert_eq!(0, first.start);
+ assert_eq!(48, first.len());
+ assert_eq!(48, first.end);
+
+ let second = pool.reserve_ids(cv!(48), Alignment::SZ_1)?;
+ assert!(first.end <= second.start || second.end <= first.start);
+
+ let first_start = first.start;
+ drop(first);
+ assert_eq!(
+ first_start,
+ pool.reserve_ids(cv!(48), Alignment::SZ_1)?.start
+ );
+ Ok(())
+ }
+
+ #[test]
+ fn chid_reservation_drop() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(cv!(8)), GFP_KERNEL)?;
+
+ let a = pool.reserve_ids(cv!(3), Alignment::SZ_1)?;
+ let b = pool.reserve_ids(cv!(3), Alignment::SZ_1)?;
+ let c = pool.reserve_ids(cv!(2), Alignment::SZ_1)?;
+ 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.reserve_ids(cv!(4), Alignment::SZ_1).map(|_| ())
+ );
+ let b = pool.reserve_ids(cv!(3), Alignment::SZ_1)?;
+ assert_eq!(3, b.start);
+
+ drop(a);
+ drop(c);
+ drop(b);
+
+ // Everything was dropped so the pool should be empty.
+ assert_eq!(0, pool.reserve_ids(cv!(8), Alignment::SZ_1)?.start);
+ Ok(())
+ }
+
+ #[test]
+ fn chid_bounded_by_num_chids() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(cv!(4)), GFP_KERNEL)?;
+
+ {
+ let a = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ let b = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ let c = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ let d = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ 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.reserve_ids(cv!(1), Alignment::SZ_1).map(|_| ())
+ );
+ }
+
+ assert_eq!(0, pool.reserve_ids(cv!(4), Alignment::SZ_1)?.start);
+ assert_eq!(
+ Err(ENOSPC),
+ pool.reserve_ids(cv!(5), Alignment::SZ_1).map(|_| ())
+ );
+
+ let head = pool.reserve_ids(cv!(3), Alignment::SZ_1)?;
+ assert_eq!(0, head.start);
+ assert_eq!(
+ Err(ENOSPC),
+ pool.reserve_ids(cv!(2), Alignment::SZ_1).map(|_| ())
+ );
+ assert_eq!(3, pool.reserve_ids(cv!(1), Alignment::SZ_1)?.start);
+ Ok(())
+ }
+
+ #[test]
+ fn chid_reservation_aligned() -> Result {
+ let pool = KBox::pin_init(ChannelIdPool::new(cv!(16)), GFP_KERNEL)?;
+
+ // Alloc 0 so the first fit for the next area is unaligned.
+ let pad = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ assert_eq!(0, pad.start);
+
+ let a = pool.reserve_ids(cv!(4), Alignment::SZ_4)?;
+ assert_eq!(4, a.start);
+
+ // The area skipped over by the aligned allocation should still be available.
+ let b = pool.reserve_ids(cv!(1), Alignment::SZ_1)?;
+ assert_eq!(1, b.start);
+
+ let c = pool.reserve_ids(cv!(8), Alignment::SZ_8)?;
+ assert_eq!(8, c.start);
+
+ // Only 2 IDs left.
+ assert_eq!(
+ Err(ENOSPC),
+ pool.reserve_ids(cv!(4), Alignment::SZ_4).map(|_| ())
+ );
+ assert_eq!(
+ Err(ENOSPC),
+ pool.reserve_ids(cv!(1), Alignment::SZ_32).map(|_| ())
+ );
+
+ assert_eq!(2, pool.reserve_ids(cv!(2), Alignment::SZ_1)?.start);
+ Ok(())
+ }
+}
--
2.55.0
^ permalink raw reply related [flat|nested] 26+ messages in thread
* Re: [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation
2026-09-30 2:42 ` [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation Eliot Courtney
@ 2026-09-30 2:51 ` sashiko-bot
2026-09-30 4:50 ` Yury Norov
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-30 2:51 UTC (permalink / raw)
To: Eliot Courtney
Cc: dri-devel, ojeda, Danilo Krummrich, Alexandre Courbot, gary
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] rust: id_pool: reserve_ids() and find_unused_id() panic when offset >= capacity
--
commit aa26fdd2bd4ce814afc1e6755fd4a71b9333439f
Author: Eliot Courtney <ecourtney@nvidia.com>
rust: id_pool: add contiguous ID reservation
Add `IdPool::reserve_ids` which allocates a contiguous range with the
given offset, count, and alignment.
> diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
> index 384753fe0e44a..06a4c71c4c6c9 100644
> --- a/rust/kernel/id_pool.rs
> +++ b/rust/kernel/id_pool.rs
[ ... ]
> @@ -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);
> }
> +
> + /// Reserves a contiguous area of `count` IDs at or after `offset`.
> + ///
> + /// The start of the returned area is a multiple of `align`.
> + ///
> + /// Returns the reserved range upon success, or [`None`] if no such area could be found.
> + #[inline]
> + #[must_use]
> + pub fn reserve_ids(
> + &mut self,
> + offset: usize,
> + count: NonZero<usize>,
> + align: Alignment,
> + ) -> Option<Range<usize>> {
> + let start = self.map.next_zero_area(offset, count, align)?;
[Severity: High]
Does this code panic when offset is greater than or equal to pool.capacity()?
If a caller attempts to reserve an ID in an IdPool where the requested
offset is greater than or equal to pool.capacity(), IdPool::reserve_ids()
passes offset directly to BitmapVec::next_zero_area() without bounds validation.
This can trivially occur if a caller instantiates an empty pool
(IdPool::with_capacity(0)) or resumes searching for an ID by passing
offset = last_id + 1 when last_id was the last available ID.
The underlying BitmapVec::next_zero_area() function calls
next_zero_area_off() in rust/kernel/bitmap.rs, which contains a
bitmap_assert!(start < self.len()) that expands to a hard assert! under
CONFIG_RUST_BITMAP_HARDENED, leading to a kernel panic (denial of service).
Since IdPool is documented to return None to prompt a grow_request
when no IDs are available, failing to handle offset >= self.capacity()
gracefully breaks the resizing API and causes panics on legitimate resource
pool usage patterns.
It appears IdPool::find_unused_id() also shares this behavior when calling
self.map.next_zero_bit(offset)? without bounds checking.
> + self.map.set(start, count);
> + Some(start..start + count.get())
> + }
> +
> + /// Releases a contiguous area of IDs.
> + #[inline]
> + pub fn release_ids(&mut self, range: &Range<usize>) {
> + if let Some(nbits) = NonZero::new(range.len()) {
> + self.map.clear(range.start, nbits);
> + }
> + }
> }
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260930-chid-v9-0-0d6cca376cff@nvidia.com?part=7
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-09-30 2:42 ` [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
@ 2026-09-30 2:52 ` sashiko-bot
2026-09-30 5:05 ` Yury Norov
1 sibling, 0 replies; 26+ messages in thread
From: sashiko-bot @ 2026-09-30 2:52 UTC (permalink / raw)
To: Eliot Courtney
Cc: ojeda, Alexandre Courbot, gary, dri-devel, Danilo Krummrich
Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:
- [High] rust: id_pool: panic on find_unused_id with 0-capacity pool
--
commit 424a508011ab0d65530fe9badebd792ee11ae3f5
Author: Eliot Courtney <ecourtney@nvidia.com>
rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
This patch modifies IdPool::with_capacity to no longer round up the initial
capacity to BitmapVec::MAX_INLINE_LEN, allowing for a capacity of 0. It also
adjusts grow_request to ensure that a 0-capacity pool grows to at least
BitmapVec::MAX_INLINE_LEN instead of remaining 0.
> diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
> index 06a4c71c4c6c9..4f329249df9d0 100644
> --- a/rust/kernel/id_pool.rs
> +++ b/rust/kernel/id_pool.rs
> @@ -112,13 +112,8 @@ 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: usize, flags: Flags) -> Result<Self, AllocError> {
> - let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
> let map = BitmapVec::new(num_ids, flags)?;
> Ok(Self { map })
> }
[Severity: High]
Can allowing a capacity of 0 here introduce a regression where finding unused
IDs triggers a kernel panic?
If a user initializes a 0-capacity pool and calls find_unused_id() or
reserve_ids(), the offset is passed directly to the underlying Bitmap
functions.
For example, in IdPool::find_unused_id():
rust/kernel/id_pool.rs:IdPool::find_unused_id() {
...
Some(UnusedId {
id: self.map.next_zero_bit(offset)?,
pool: self,
})
...
}
The underlying Bitmap::next_zero_bit() function enforces a bounds check
via bitmap_assert!(start < self.len()).
For a 0-capacity pool with start=0, this evaluates to 0 < 0, triggering an
unconditional panic when the CONFIG_RUST_BITMAP_HARDENED configuration is
enabled.
Should we handle 0-capacity gracefully and return None to signal the need for
a grow_request() instead?
--
Sashiko AI review · https://sashiko.dev/#/patchset/20260930-chid-v9-0-0d6cca376cff@nvidia.com?part=8
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 6/9] rust: bitmap: add contiguous area operations
2026-09-30 2:42 ` [PATCH v9 6/9] rust: bitmap: add contiguous area operations Eliot Courtney
@ 2026-09-30 4:41 ` Yury Norov
0 siblings, 0 replies; 26+ messages in thread
From: Yury Norov @ 2026-09-30 4:41 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 11:42:55AM +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.
>
> Add tests demonstrating the edge cases.
>
> Reviewed-by: Burak Emir <burak.emir@gmail.com>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> ---
> rust/kernel/bitmap.rs | 241 +++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 239 insertions(+), 2 deletions(-)
>
> diff --git a/rust/kernel/bitmap.rs b/rust/kernel/bitmap.rs
> index df5505ec7a96..23c2b43a98ac 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.
> ///
> @@ -525,13 +529,159 @@ 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 kernel::{
> + /// alloc::{AllocError, flags::GFP_KERNEL},
> + /// bitmap::BitmapVec,
> + /// ptr::Alignment,
> + /// sizes::SizeConstants, //
> + /// };
> + ///
> + /// let mut b = BitmapVec::new(64, GFP_KERNEL)?;
> + ///
> + /// assert_eq!(Some(0), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
> + /// b.set(0, cv!(5));
> + /// assert_eq!(Some(5), b.next_zero_area(0, cv!(8), Alignment::SZ_1));
> + /// assert_eq!(Some(8), b.next_zero_area(0, cv!(8), Alignment::SZ_8));
> + /// assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
> + /// # 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>) {
I'm still not convinced about having this non-standard parameter types
in function declaration, but ... let's give it a try.
The rest looks OK, so
Reviewed-by: Yury Norov <yury.norov@gmail.com>
> + 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)]
> #[macros::kunit_tests(rust_kernel_bitmap)]
> mod tests {
> use super::*;
> - use kernel::alloc::flags::GFP_KERNEL;
> + use kernel::{
> + alloc::flags::GFP_KERNEL,
> + num::cv,
> + sizes::SizeConstants, //
> + };
>
> #[test]
> fn bitmap_borrow() {
> @@ -642,4 +792,91 @@ 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)?;
> +
> + assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
> + b.set(0, cv!(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, cv!(5), Alignment::SZ_1));
> + assert_eq!(Some(8), b.next_zero_area(0, cv!(5), Alignment::SZ_8));
> +
> + b.set(8, cv!(8)); // Now contains {[0, 5), [8, 16)}.
> + assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_16));
> + assert_eq!(Some(16), b.next_zero_area(0, cv!(4), Alignment::SZ_1));
> +
> + b.clear(0, cv!(5)); // Now contains {[8, 16)}.
> + assert_eq!(Some(0), b.next_zero_area(0, cv!(5), Alignment::SZ_1));
> + assert_eq!(Some(8), b.next_bit(0));
> + assert_eq!(Some(15), b.last_bit());
> +
> + b.set(60, cv!(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, cv!(40), Alignment::SZ_1));
> + assert_eq!(Some(70), b.next_zero_area(0, cv!(45), Alignment::SZ_1));
> +
> + b.clear(62, cv!(6)); // Now contains {[8, 16), [60, 62), [68, 70)}.
> + assert_eq!(Some(62), b.next_zero_area(60, cv!(6), Alignment::SZ_1));
> + 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)?;
> +
> + assert_eq!(None, b.next_zero_area(0, cv!(65), Alignment::SZ_1));
> + assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
> + assert_eq!(None, b.next_zero_area(1, cv!(usize::MAX), Alignment::SZ_1));
> +
> + b.set_bit(0); // Now contains {[0, 1)}.
> + assert_eq!(None, b.next_zero_area(0, cv!(usize::MAX), Alignment::SZ_1));
> +
> + b.set(0, cv!(61)); // Now contains {[0, 61)}.
> + assert_eq!(None, b.next_zero_area(0, cv!(4), Alignment::SZ_1));
> + assert_eq!(Some(61), b.next_zero_area(0, cv!(3), Alignment::SZ_1));
> + assert_eq!(None, b.next_zero_area(0, cv!(1), Alignment::SZ_64));
> + Ok(())
> + }
> +
> + #[test]
> + fn bitmap_area_off() -> Result<(), AllocError> {
> + let mut b = BitmapVec::new(64, GFP_KERNEL)?;
> +
> + b.set(0, cv!(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, cv!(8), Alignment::SZ_8, 1));
> + assert_eq!(Some(5), b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 3));
> +
> + // A zero offset behaves like next_zero_area().
> + assert_eq!(
> + b.next_zero_area(0, cv!(8), Alignment::SZ_8),
> + b.next_zero_area_off(0, cv!(8), Alignment::SZ_8, 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, cv!(4));
> + b.set(62, cv!(8));
> + b.set(usize::MAX, cv!(1));
> + b.clear(usize::MAX, cv!(1));
> + b.clear(2048, cv!(8));
> + assert_eq!(None, b.next_bit(0));
> + assert_eq!(None, b.next_zero_area(64, cv!(1), Alignment::SZ_1));
> + Ok(())
> + }
> }
>
> --
> 2.55.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation
2026-09-30 2:42 ` [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation Eliot Courtney
2026-09-30 2:51 ` sashiko-bot
@ 2026-09-30 4:50 ` Yury Norov
1 sibling, 0 replies; 26+ messages in thread
From: Yury Norov @ 2026-09-30 4:50 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 11:42:56AM +0900, Eliot Courtney wrote:
> Add `IdPool::reserve_ids` which allocates a contiguous range with the
> given offset, count, and alignment.
>
> Reviewed-by: Burak Emir <burak.emir@gmail.com>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
Reviewed-by: Yury Norov <ynorov@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..06a4c71c4c6c 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);
> }
> +
> + /// Reserves a contiguous area of `count` IDs at or after `offset`.
> + ///
> + /// The start of the returned area is a multiple of `align`.
> + ///
> + /// Returns the reserved range upon success, or [`None`] if no such area could be found.
> + #[inline]
> + #[must_use]
> + pub fn reserve_ids(
> + &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_ids(&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 [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-09-30 2:42 ` [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
2026-09-30 2:52 ` sashiko-bot
@ 2026-09-30 5:05 ` Yury Norov
2026-10-01 6:20 ` Alexandre Courbot
1 sibling, 1 reply; 26+ messages in thread
From: Yury Norov @ 2026-09-30 5:05 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 11:42:57AM +0900, Eliot Courtney wrote:
> 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.
>
> This allows specifying a real capacity of 0, which was not previously
> possible. This breaks `grow_request` in this case, so change it to grow
> to at least `BitmapVec::MAX_INLINE_LEN`, mirroring the capacity floor in
> `shrink_request`.
>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> ---
> rust/kernel/id_pool.rs | 38 ++++++++++++++++++++++++++++++++------
> 1 file changed, 32 insertions(+), 6 deletions(-)
>
> diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
> index 06a4c71c4c6c..4f329249df9d 100644
> --- a/rust/kernel/id_pool.rs
> +++ b/rust/kernel/id_pool.rs
> @@ -112,13 +112,8 @@ 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: usize, flags: Flags) -> Result<Self, AllocError> {
> - let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
> let map = BitmapVec::new(num_ids, flags)?;
> Ok(Self { map })
> }
> @@ -152,6 +147,13 @@ pub fn capacity(&self) -> usize {
> /// let resizer = alloc_request.realloc(GFP_KERNEL)?;
> /// pool.shrink(resizer);
> /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
> + ///
> + /// // A pool at the `MAX_INLINE_LEN` floor cannot shrink further.
> + /// assert!(pool.shrink_request().is_none());
> + ///
> + /// // Neither can a pool with a capacity below `MAX_INLINE_LEN`.
> + /// let small = IdPool::with_capacity(8, GFP_KERNEL)?;
> + /// assert!(small.shrink_request().is_none());
> /// # Ok::<(), AllocError>(())
> /// ```
> #[inline]
> @@ -198,12 +200,36 @@ pub fn shrink(&mut self, mut resizer: PoolResizer) {
>
> /// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.
> ///
> + /// Grows to at least [`MAX_INLINE_LEN`].
> /// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].
> ///
> + /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
> /// [`MAX_LEN`]: BitmapVec::MAX_LEN
> + ///
> + /// # Examples
> + ///
> + /// ```
> + /// use kernel::{
> + /// alloc::AllocError,
> + /// bitmap::BitmapVec,
> + /// id_pool::IdPool, //
> + /// };
> + ///
> + /// // Grow goes to at least BitmapVec::MAX_INLINE_LEN.
> + /// let mut pool = IdPool::with_capacity(0, GFP_KERNEL)?;
Allocating a pool with 0-bit capacity is wrong. Please don't put it
in the examples. I recall I pointed that this object would panic the
kernel if, for example, you call pool.next_zero_bit(0) immediately
after this. Sorry, but NAK.
This .with_capacity() should take num_ids: NonZero, after all...
> + /// let resizer = pool.grow_request().ok_or(AllocError)?.realloc(GFP_KERNEL)?;
> + /// pool.grow(resizer);
> + /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
> + ///
> + /// // Grow doubles if at least BitmapVec::MAX_INLINE_LEN.
> + /// let resizer = pool.grow_request().ok_or(AllocError)?.realloc(GFP_KERNEL)?;
> + /// pool.grow(resizer);
> + /// assert_eq!(pool.capacity(), 2 * BitmapVec::MAX_INLINE_LEN);
> + /// # Ok::<(), AllocError>(())
> + /// ```
> #[inline]
> pub fn grow_request(&self) -> Option<ReallocRequest> {
> - let num_ids = self.capacity() * 2;
> + let num_ids = usize::max(BitmapVec::MAX_INLINE_LEN, self.capacity() * 2);
> if num_ids > BitmapVec::MAX_LEN {
> return None;
> }
>
> --
> 2.55.0
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment
2026-09-30 2:42 ` [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
@ 2026-09-30 9:12 ` Miguel Ojeda
0 siblings, 0 replies; 26+ messages in thread
From: Miguel Ojeda @ 2026-09-30 9:12 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 4:44 AM Eliot Courtney <ecourtney@nvidia.com> wrote:
>
> Currently, constructing an alignment is quite verbose [1]:
>
> Alignment::new::<8>()
>
> It's unfortunate because it disincentivizes using it at interface
> boundaries. Implement `SizeConstants` for `Alignment` so we can write
> e.g. `Alignment::SZ_8` instead.
I don't find the former disincentivizing -- it is how I would expect
to have to write such a call in general. We do have the other size
constants already, and `SZ_` is well-known, so the short form is fine
too here, but I just wanted to mention that I don't think we should be
scared of seeing colons and angle brackets. :)
If this ends up going through bitmap or DRM or similar this cycle:
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Otherwise, I can pick this one up this cycle.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-09-30 2:42 ` [PATCH v9 3/9] rust: sizes: add sub-1K size constants Eliot Courtney
@ 2026-09-30 9:12 ` Miguel Ojeda
2026-09-30 11:04 ` Alexandre Courbot
0 siblings, 1 reply; 26+ messages in thread
From: Miguel Ojeda @ 2026-09-30 9:12 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 4:44 AM Eliot Courtney <ecourtney@nvidia.com> wrote:
>
> Add some more size constants, mirroring include/linux/sizes.h. This is
> useful for making `Alignment` implement `SizeConstants` in a following
> patch, because these are more common alignment values.
>
> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
If this ends up going through bitmap or DRM or similar this cycle:
Acked-by: Miguel Ojeda <ojeda@kernel.org>
Otherwise, I can pick this one up this cycle.
Thanks!
Cheers,
Miguel
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-09-30 9:12 ` Miguel Ojeda
@ 2026-09-30 11:04 ` Alexandre Courbot
2026-09-30 11:22 ` Miguel Ojeda
0 siblings, 1 reply; 26+ messages in thread
From: Alexandre Courbot @ 2026-09-30 11:04 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
Hi Miguel,
On Wed Sep 30, 2026 at 6:12 PM JST, Miguel Ojeda wrote:
> On Wed, Sep 30, 2026 at 4:44 AM Eliot Courtney <ecourtney@nvidia.com> wrote:
>>
>> Add some more size constants, mirroring include/linux/sizes.h. This is
>> useful for making `Alignment` implement `SizeConstants` in a following
>> patch, because these are more common alignment values.
>>
>> Reviewed-by: Alexandre Courbot <acourbot@nvidia.com>
>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>
> If this ends up going through bitmap or DRM or similar this cycle:
>
> Acked-by: Miguel Ojeda <ojeda@kernel.org>
>
> Otherwise, I can pick this one up this cycle.
I am considering picking the series through DRM, but it also has a
dependency on the `cv!` macro [1].
Is this ok if we take the patch introducing `cv!` through DRM as well?
[1] https://lore.kernel.org/all/20260917-cv-v4-1-547ef5727451@nvidia.com/
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-09-30 11:04 ` Alexandre Courbot
@ 2026-09-30 11:22 ` Miguel Ojeda
2026-10-01 0:40 ` Gary Guo
0 siblings, 1 reply; 26+ messages in thread
From: Miguel Ojeda @ 2026-09-30 11:22 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed, Sep 30, 2026 at 1:04 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>
> I am considering picking the series through DRM, but it also has a
> dependency on the `cv!` macro [1].
>
> Is this ok if we take the patch introducing `cv!` through DRM as well?
>
> [1] https://lore.kernel.org/all/20260917-cv-v4-1-547ef5727451@nvidia.com/
I asked Gary yesterday if there was any more recent discussion on the
tag thing from v3. Does anyone want to do some cleanups this cycle
through rust-next with `cv!`?
Cheers,
Miguel
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-09-30 11:22 ` Miguel Ojeda
@ 2026-10-01 0:40 ` Gary Guo
2026-10-01 5:23 ` Eliot Courtney
0 siblings, 1 reply; 26+ messages in thread
From: Gary Guo @ 2026-10-01 0:40 UTC (permalink / raw)
To: Miguel Ojeda, Alexandre Courbot
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed Sep 30, 2026 at 12:22 PM BST, Miguel Ojeda wrote:
> On Wed, Sep 30, 2026 at 1:04 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>>
>> I am considering picking the series through DRM, but it also has a
>> dependency on the `cv!` macro [1].
>>
>> Is this ok if we take the patch introducing `cv!` through DRM as well?
>>
>> [1] https://lore.kernel.org/all/20260917-cv-v4-1-547ef5727451@nvidia.com/
>
> I asked Gary yesterday if there was any more recent discussion on the
> tag thing from v3. Does anyone want to do some cleanups this cycle
> through rust-next with `cv!`?
Given close proximity to LPC probably not.
Best,
Gary
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 3/9] rust: sizes: add sub-1K size constants
2026-10-01 0:40 ` Gary Guo
@ 2026-10-01 5:23 ` Eliot Courtney
0 siblings, 0 replies; 26+ messages in thread
From: Eliot Courtney @ 2026-10-01 5:23 UTC (permalink / raw)
To: Gary Guo, Miguel Ojeda, Alexandre Courbot
Cc: Eliot Courtney, Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda,
Boqun Feng, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Onur Özkan, David Airlie, Simona Vetter, Greg Kroah-Hartman,
John Hubbard, Alistair Popple, Timur Tabi, Zhi Wang,
rust-for-linux, linux-kernel, nova-gpu, dri-devel, dri-devel
On Thu Oct 1, 2026 at 9:40 AM JST, Gary Guo wrote:
> On Wed Sep 30, 2026 at 12:22 PM BST, Miguel Ojeda wrote:
>> On Wed, Sep 30, 2026 at 1:04 PM Alexandre Courbot <acourbot@nvidia.com> wrote:
>>>
>>> I am considering picking the series through DRM, but it also has a
>>> dependency on the `cv!` macro [1].
>>>
>>> Is this ok if we take the patch introducing `cv!` through DRM as well?
>>>
>>> [1] https://lore.kernel.org/all/20260917-cv-v4-1-547ef5727451@nvidia.com/
>>
>> I asked Gary yesterday if there was any more recent discussion on the
>> tag thing from v3. Does anyone want to do some cleanups this cycle
>> through rust-next with `cv!`?
>
> Given close proximity to LPC probably not.
>
> Best,
> Gary
I have a cleanup series locally already, I was planning to send it next
cycle.
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-09-30 5:05 ` Yury Norov
@ 2026-10-01 6:20 ` Alexandre Courbot
2026-10-08 15:47 ` Yury Norov
0 siblings, 1 reply; 26+ messages in thread
From: Alexandre Courbot @ 2026-10-01 6:20 UTC (permalink / raw)
To: Yury Norov
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Wed Sep 30, 2026 at 2:05 PM JST, Yury Norov wrote:
> On Wed, Sep 30, 2026 at 11:42:57AM +0900, Eliot Courtney wrote:
>> 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.
>>
>> This allows specifying a real capacity of 0, which was not previously
>> possible. This breaks `grow_request` in this case, so change it to grow
>> to at least `BitmapVec::MAX_INLINE_LEN`, mirroring the capacity floor in
>> `shrink_request`.
>>
>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>> ---
>> rust/kernel/id_pool.rs | 38 ++++++++++++++++++++++++++++++++------
>> 1 file changed, 32 insertions(+), 6 deletions(-)
>>
>> diff --git a/rust/kernel/id_pool.rs b/rust/kernel/id_pool.rs
>> index 06a4c71c4c6c..4f329249df9d 100644
>> --- a/rust/kernel/id_pool.rs
>> +++ b/rust/kernel/id_pool.rs
>> @@ -112,13 +112,8 @@ 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: usize, flags: Flags) -> Result<Self, AllocError> {
>> - let num_ids = usize::max(num_ids, BitmapVec::MAX_INLINE_LEN);
>> let map = BitmapVec::new(num_ids, flags)?;
>> Ok(Self { map })
>> }
>> @@ -152,6 +147,13 @@ pub fn capacity(&self) -> usize {
>> /// let resizer = alloc_request.realloc(GFP_KERNEL)?;
>> /// pool.shrink(resizer);
>> /// assert_eq!(pool.capacity(), BitmapVec::MAX_INLINE_LEN);
>> + ///
>> + /// // A pool at the `MAX_INLINE_LEN` floor cannot shrink further.
>> + /// assert!(pool.shrink_request().is_none());
>> + ///
>> + /// // Neither can a pool with a capacity below `MAX_INLINE_LEN`.
>> + /// let small = IdPool::with_capacity(8, GFP_KERNEL)?;
>> + /// assert!(small.shrink_request().is_none());
>> /// # Ok::<(), AllocError>(())
>> /// ```
>> #[inline]
>> @@ -198,12 +200,36 @@ pub fn shrink(&mut self, mut resizer: PoolResizer) {
>>
>> /// Returns a [`ReallocRequest`] for growing this [`IdPool`], if possible.
>> ///
>> + /// Grows to at least [`MAX_INLINE_LEN`].
>> /// The capacity of an [`IdPool`] cannot be grown above [`MAX_LEN`].
>> ///
>> + /// [`MAX_INLINE_LEN`]: BitmapVec::MAX_INLINE_LEN
>> /// [`MAX_LEN`]: BitmapVec::MAX_LEN
>> + ///
>> + /// # Examples
>> + ///
>> + /// ```
>> + /// use kernel::{
>> + /// alloc::AllocError,
>> + /// bitmap::BitmapVec,
>> + /// id_pool::IdPool, //
>> + /// };
>> + ///
>> + /// // Grow goes to at least BitmapVec::MAX_INLINE_LEN.
>> + /// let mut pool = IdPool::with_capacity(0, GFP_KERNEL)?;
>
> Allocating a pool with 0-bit capacity is wrong. Please don't put it
> in the examples. I recall I pointed that this object would panic the
> kernel if, for example, you call pool.next_zero_bit(0) immediately
> after this. Sorry, but NAK.
>
> This .with_capacity() should take num_ids: NonZero, after all...
This panic is not specific to the size zero, any size triggers the same
behavior when accessed out of bounds. A size of zero has nothing special
in that respect, so why make an exception and forbid it? We had this
discussion some time ago [1][2], and I'd recommend instead making e.g.
`next_zero_bit` return `None` on out-of-bounds accesses, which is
semantically correct.
[1] https://lore.kernel.org/all/ao2GHqop_Z_9bsyl@google.com/
[2] https://lore.kernel.org/all/ao2U1jNaT7waibJW@google.com/
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (8 preceding siblings ...)
2026-09-30 2:42 ` [PATCH v9 9/9] gpu: nova-core: add ChannelIdPool Eliot Courtney
@ 2026-10-08 15:04 ` Alexandre Courbot
9 siblings, 0 replies; 26+ messages in thread
From: Alexandre Courbot @ 2026-10-08 15:04 UTC (permalink / raw)
To: Eliot Courtney, Miguel Ojeda
Cc: 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,
Onur Özkan, David Airlie, Simona Vetter, Greg Kroah-Hartman,
John Hubbard, Alistair Popple, Timur Tabi, Zhi Wang,
rust-for-linux, linux-kernel, nova-gpu, dri-devel, Yury Norov
On Wed Sep 30, 2026 at 11:42 AM JST, Eliot Courtney wrote:
<...>
> Eliot Courtney (9):
> rust: bitmap: use function-level cfg on kunit test
> rust: bitmap: restrict bitmap length to at most i32::MAX
> rust: sizes: add sub-1K size constants
> rust: sizes: implement SizeConstants for Alignment
> rust: use Alignment size constants
> rust: bitmap: add contiguous area operations
> rust: id_pool: add contiguous ID reservation
> rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
> gpu: nova-core: add ChannelIdPool
I am wondering about the merge path for this series (and the `cv` one).
We would like to use it for 7.4 in `drm-rust-next`, but given the
current timing we can either merge it there after `-rc1` is tagged, or
take the bits that apply on `nust-next` now.
The bits that apply being:
- Patch 1 of the `cv!` series [1],
- Patches 1-4 and 6-7 of this series.
(patch 5 touches mostly nova-core and doesn't apply on `rust-next`,
patch 8 lacks the required tags, and patch 9 is a nova-core patch)
Since that's quite a bit of code outside of nova-core, maybe it is
better to merge the non-nova bits mentioned above through the Rust tree
in time for the 7.4 merge window. All the patches I have listed should
have the required tags, but the `cv!` patch needs one last respin to
address the remaining comments and add a "Pitfalls" section at the very
least. Miguel, does that sound doable?
[1] https://lore.kernel.org/all/20260917-cv-v4-1-547ef5727451@nvidia.com/
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-10-01 6:20 ` Alexandre Courbot
@ 2026-10-08 15:47 ` Yury Norov
2026-10-08 16:04 ` Gary Guo
2026-10-09 14:07 ` Alexandre Courbot
0 siblings, 2 replies; 26+ messages in thread
From: Yury Norov @ 2026-10-08 15:47 UTC (permalink / raw)
To: Alexandre Courbot
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Thu, Oct 01, 2026 at 03:20:40PM +0900, Alexandre Courbot wrote:
> On Wed Sep 30, 2026 at 2:05 PM JST, Yury Norov wrote:
...
> > Allocating a pool with 0-bit capacity is wrong. Please don't put it
> > in the examples. I recall I pointed that this object would panic the
> > kernel if, for example, you call pool.next_zero_bit(0) immediately
> > after this. Sorry, but NAK.
> >
> > This .with_capacity() should take num_ids: NonZero, after all...
>
> This panic is not specific to the size zero, any size triggers the same
> behavior when accessed out of bounds.
In C, malloc(0) is implementation defined behavior, i.e. it can return
a pointer valid for free(), or NULL (which is also valid for free).
This is a very old legacy coming from K&R implementation, then rejected
in C89, and later this all became an impl-def, mostly for compatibility
reasons. See 7.20.3 in
https://www.open-std.org/jtc1/sc22/wg14/www/docs/n937.pdf
Rust adopted C bitmaps, thus creating 0-bit bitmap may go through, and
hit that questionable behavior. You add this example without any
discussion about all that possible complications, and with no
protection for users.
Interestingly, you're doing it for the reason that has been considered
a bad practice for over 30 years ago - malloc(0) with the immediate
realloc(). See the above link for details.
To me it looks like pulling legacy with a potential of undefined behavior
into Rust.
Bitmaps is a way more simple case than the generic malloc(). There's the
only user of bitmaps - the Linux kernel, so we know exactly all users and
their user patterns. I'm not aware of any in-tree user allocating 0-length
bitmap for whatever reason, and such a coding style is highly unwelcome
nowadays (30+ years).
When it comes to rust, things are even simpler. Rust has much stricter
memory policy - no undefined behavior, no implementation-defined behavior
is allowed, no 50-years old legacy has to be considered.
Rust community decided to take the existing in-kernel implementation of
bitmaps written in C, for a reason. But with that it pulls all undefined
and poorly defined behavior associate to C language. We did quite well
spotting such places and fencing them with safety checks.
The 0-length bitmaps is just another case that should be resolved.
If you still think that you need 0-length bitmaps in Rust, can you please
give the clear and thorough explanation why rust needs those 0-length
bitmaps. Are there any in-kernel examples? Any language concepts requiring
it? If not, it's still a NAK.
> A size of zero has nothing special
> in that respect, so why make an exception and forbid it? We had this
> discussion some time ago [1][2], and I'd recommend instead making e.g.
> `next_zero_bit` return `None` on out-of-bounds accesses, which is
> semantically correct.
No. out-of-bound access should panic because every caller of bitmap
API knows the length of that bitmap.
But if you make that 0-length bitmap a valid case, we need to revisit
every function and make sure it returns ENOENT or something instead of
panicking. That, again, must be very well explained and justified, and
all this has to be done before adding 0-length bitmap support in code
and examples.
Thanks,
Yury
> [1] https://lore.kernel.org/all/ao2GHqop_Z_9bsyl@google.com/
> [2] https://lore.kernel.org/all/ao2U1jNaT7waibJW@google.com/
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-10-08 15:47 ` Yury Norov
@ 2026-10-08 16:04 ` Gary Guo
2026-10-09 14:07 ` Alexandre Courbot
1 sibling, 0 replies; 26+ messages in thread
From: Gary Guo @ 2026-10-08 16:04 UTC (permalink / raw)
To: Yury Norov, Alexandre Courbot
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Thu Oct 8, 2026 at 4:47 PM BST, Yury Norov wrote:
> On Thu, Oct 01, 2026 at 03:20:40PM +0900, Alexandre Courbot wrote:
>> On Wed Sep 30, 2026 at 2:05 PM JST, Yury Norov wrote:
>
> ...
>
>> > Allocating a pool with 0-bit capacity is wrong. Please don't put it
>> > in the examples. I recall I pointed that this object would panic the
>> > kernel if, for example, you call pool.next_zero_bit(0) immediately
>> > after this. Sorry, but NAK.
>> >
>> > This .with_capacity() should take num_ids: NonZero, after all...
>>
>> This panic is not specific to the size zero, any size triggers the same
>> behavior when accessed out of bounds.
>
> In C, malloc(0) is implementation defined behavior, i.e. it can return
> a pointer valid for free(), or NULL (which is also valid for free).
>
> This is a very old legacy coming from K&R implementation, then rejected
> in C89, and later this all became an impl-def, mostly for compatibility
> reasons. See 7.20.3 in
>
> https://www.open-std.org/jtc1/sc22/wg14/www/docs/n937.pdf
>
> Rust adopted C bitmaps, thus creating 0-bit bitmap may go through, and
> hit that questionable behavior. You add this example without any
> discussion about all that possible complications, and with no
> protection for users.
>
> Interestingly, you're doing it for the reason that has been considered
> a bad practice for over 30 years ago - malloc(0) with the immediate
> realloc(). See the above link for details.
>
> To me it looks like pulling legacy with a potential of undefined behavior
> into Rust.
A bad historical design in C standard shouldn't be affecting a modern API
design.
Special casing 0 as a size is bad, because all code would then need to care if
something is 0 or not. Imagine that I have a code that want unconditionally put
a generic piece of data to heap via `KBox`, I should be able to write
`KBox::new(data)` directly without having to do `if size_of::<T>() == 0`.
Special casing 0 break the nice code composition properties.
Rust standard library does not repeat the mistake in its allocation
implementations (and same for our in-kernel allocator abstraction). All
zero-sized allocation are well-defined to return a pointer that is valid for,
well, 0 bytes.
In fact, in kernel we also did the sensible thing -- make 0 byte allocation
defined. `kmalloc(0)` is not a bug and it'll return you `ZERO_SIZE_PTR` which
you can then give to `kfree`. For `krealloc`, both reallocating from 0 to
non-zero or the other way is defined.
Best,
Gary
^ permalink raw reply [flat|nested] 26+ messages in thread
* Re: [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-10-08 15:47 ` Yury Norov
2026-10-08 16:04 ` Gary Guo
@ 2026-10-09 14:07 ` Alexandre Courbot
1 sibling, 0 replies; 26+ messages in thread
From: Alexandre Courbot @ 2026-10-09 14:07 UTC (permalink / raw)
To: Yury Norov
Cc: Eliot Courtney, 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, Onur Özkan, David Airlie, Simona Vetter,
Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel
On Fri Oct 9, 2026 at 12:47 AM JST, Yury Norov wrote:
> On Thu, Oct 01, 2026 at 03:20:40PM +0900, Alexandre Courbot wrote:
>> On Wed Sep 30, 2026 at 2:05 PM JST, Yury Norov wrote:
>
> ...
>
>> > Allocating a pool with 0-bit capacity is wrong. Please don't put it
>> > in the examples. I recall I pointed that this object would panic the
>> > kernel if, for example, you call pool.next_zero_bit(0) immediately
>> > after this. Sorry, but NAK.
>> >
>> > This .with_capacity() should take num_ids: NonZero, after all...
>>
>> This panic is not specific to the size zero, any size triggers the same
>> behavior when accessed out of bounds.
>
> In C, malloc(0) is implementation defined behavior, i.e. it can return
> a pointer valid for free(), or NULL (which is also valid for free).
>
> This is a very old legacy coming from K&R implementation, then rejected
> in C89, and later this all became an impl-def, mostly for compatibility
> reasons. See 7.20.3 in
>
> https://www.open-std.org/jtc1/sc22/wg14/www/docs/n937.pdf
>
> Rust adopted C bitmaps, thus creating 0-bit bitmap may go through, and
> hit that questionable behavior. You add this example without any
> discussion about all that possible complications, and with no
> protection for users.
>
> Interestingly, you're doing it for the reason that has been considered
> a bad practice for over 30 years ago - malloc(0) with the immediate
> realloc(). See the above link for details.
>
> To me it looks like pulling legacy with a potential of undefined behavior
> into Rust.
>
> Bitmaps is a way more simple case than the generic malloc(). There's the
> only user of bitmaps - the Linux kernel, so we know exactly all users and
> their user patterns. I'm not aware of any in-tree user allocating 0-length
> bitmap for whatever reason, and such a coding style is highly unwelcome
> nowadays (30+ years).
>
> When it comes to rust, things are even simpler. Rust has much stricter
> memory policy - no undefined behavior, no implementation-defined behavior
> is allowed, no 50-years old legacy has to be considered.
>
> Rust community decided to take the existing in-kernel implementation of
> bitmaps written in C, for a reason. But with that it pulls all undefined
> and poorly defined behavior associate to C language. We did quite well
> spotting such places and fencing them with safety checks.
>
> The 0-length bitmaps is just another case that should be resolved.
As it turns out we would never perform a zero-sized malloc, even for a
zero-sized bitmap. `IdPool` is backed by a `BitmapVec`, which up to
`usize::BITS` uses an inline member as backing storage. So we would
never call `bitmap_zalloc` with a value of 0, making the safety concern
moot.
>
> If you still think that you need 0-length bitmaps in Rust, can you please
> give the clear and thorough explanation why rust needs those 0-length
> bitmaps. Are there any in-kernel examples? Any language concepts requiring
> it? If not, it's still a NAK.
I don't know of an in-kernel example, but please look at the
`bitmap_vec_new` test which has been here since the API was initially
merged last year: the first thing it does is create a zero-sized bitmap.
It is also easy to imagine a user starting with an empty pool and
growing it on-demand. Having the ability to create a zero-sized pool is
convenient to avoid special-casing user code, and in this case I'd say
expected, just like you can create a zero-sized vector. Again a size of
zero does not trigger anything that a larger capacity cannot trigger, so
I don't see a reason to forbid it.
>
>> A size of zero has nothing special
>> in that respect, so why make an exception and forbid it? We had this
>> discussion some time ago [1][2], and I'd recommend instead making e.g.
>> `next_zero_bit` return `None` on out-of-bounds accesses, which is
>> semantically correct.
>
> No. out-of-bound access should panic because every caller of bitmap
> API knows the length of that bitmap.
Right now out-of-bound accesses are allowed if
`CONFIG_RUST_BITMAP_HARDENED` is not set. The Kconfig documentation for
that option even says "if unsure, say N", which suggests that not
panicking (and thus the behavior I described above for `next_zero_bit`)
is the default. If out-of-bound accesses should panic, then the Kconfig
does not reflect that.
>
> But if you make that 0-length bitmap a valid case, we need to revisit
> every function and make sure it returns ENOENT or something instead of
> panicking. That, again, must be very well explained and justified, and
> all this has to be done before adding 0-length bitmap support in code
> and examples.
With hardening off, every function already behaves in that way, and
existing user code is already written to not trigger that condition
anyway (because of the possibility of a panic), so that would be a
pretty innocuous change.
^ permalink raw reply [flat|nested] 26+ messages in thread
end of thread, other threads:[~2026-10-09 14:10 UTC | newest]
Thread overview: 26+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-30 2:42 [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 1/9] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 2/9] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 3/9] rust: sizes: add sub-1K size constants Eliot Courtney
2026-09-30 9:12 ` Miguel Ojeda
2026-09-30 11:04 ` Alexandre Courbot
2026-09-30 11:22 ` Miguel Ojeda
2026-10-01 0:40 ` Gary Guo
2026-10-01 5:23 ` Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 4/9] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
2026-09-30 9:12 ` Miguel Ojeda
2026-09-30 2:42 ` [PATCH v9 5/9] rust: use Alignment size constants Eliot Courtney
2026-09-30 2:42 ` [PATCH v9 6/9] rust: bitmap: add contiguous area operations Eliot Courtney
2026-09-30 4:41 ` Yury Norov
2026-09-30 2:42 ` [PATCH v9 7/9] rust: id_pool: add contiguous ID reservation Eliot Courtney
2026-09-30 2:51 ` sashiko-bot
2026-09-30 4:50 ` Yury Norov
2026-09-30 2:42 ` [PATCH v9 8/9] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
2026-09-30 2:52 ` sashiko-bot
2026-09-30 5:05 ` Yury Norov
2026-10-01 6:20 ` Alexandre Courbot
2026-10-08 15:47 ` Yury Norov
2026-10-08 16:04 ` Gary Guo
2026-10-09 14:07 ` Alexandre Courbot
2026-09-30 2:42 ` [PATCH v9 9/9] gpu: nova-core: add ChannelIdPool Eliot Courtney
2026-10-08 15:04 ` [PATCH v9 0/9] rust: Add support for reserving of ranges of IDs Alexandre Courbot
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox