* [PATCH v8 01/12] rust: bitmap: use function-level cfg on kunit test
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 02/12] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
` (10 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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] 21+ messages in thread* [PATCH v8 02/12] rust: bitmap: restrict bitmap length to at most i32::MAX
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 01/12] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
` (9 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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] 21+ messages in thread* [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 01/12] rust: bitmap: use function-level cfg on kunit test Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 02/12] rust: bitmap: restrict bitmap length to at most i32::MAX Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 9:32 ` Alice Ryhl
2026-08-27 7:28 ` [PATCH v8 04/12] rust: prelude: add `num::cv` Eliot Courtney
` (8 subsequent siblings)
11 siblings, 1 reply; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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, using NonZero/Bounded constants is quite verbose. It's
unfortunate because it disincentivizes using it in interface boundaries.
Introduce a macro to make it nicer to use. The macro `cv!` (for constant
value) takes a const integer expression and widens it to i128 (at build
time only) before passing it as a const generic value to a new trait
function `FromConst::from_const`. The trait is implemented by NonZero,
Bounded, and Alignment and lets values of each be constructed from
constants without a verbose turbofish syntax. For example,
`const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
Suggested-by: Gary Guo <gary@garyguo.net>
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/num.rs | 89 ++++++++++++++++++++++++++++++++++++++++++++++
rust/kernel/num/bounded.rs | 21 ++++++++++-
rust/kernel/ptr.rs | 13 +++++++
3 files changed, 122 insertions(+), 1 deletion(-)
diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
index 8532b511384c..29bf903a4a74 100644
--- a/rust/kernel/num.rs
+++ b/rust/kernel/num.rs
@@ -2,11 +2,100 @@
//! Additional numerical features for the kernel.
+use crate::const_assert;
use core::ops;
pub mod bounded;
pub use bounded::*;
+/// Creates a value from an integer constant expression, with validity checked at build time.
+///
+/// This works for any type that implements [`FromConst`], with the target type inferred from
+/// the context.
+///
+/// # Examples
+///
+/// ```
+/// use core::num::NonZero;
+/// use kernel::num::Bounded;
+/// use kernel::num::cv;
+/// use kernel::ptr::Alignment;
+///
+/// let v: NonZero<usize> = cv!(8);
+/// assert_eq!(v.get(), 8);
+///
+/// // Any integer constant expression works, not only literals.
+/// let m: NonZero<usize> = cv!(usize::MAX);
+/// assert_eq!(m.get(), usize::MAX);
+///
+/// let b: Bounded<u32, 4> = cv!(15);
+/// assert_eq!(b.get(), 15);
+///
+/// let a: Alignment = cv!(4096);
+/// assert_eq!(a.as_usize(), 4096);
+/// ```
+#[macro_export]
+#[doc(hidden)]
+macro_rules! cv {
+ ($v:expr) => {
+ $crate::num::FromConst::from_const::<
+ {
+ #[allow(unused_comparisons, unused_assignments, clippy::as_underscore)]
+ {
+ let v = $v;
+ let r = v as i128;
+ // Pin `back` to `v`'s type so `as _` casts back to the source type.
+ let mut back = v;
+ back = r as _;
+
+ ::core::assert!(
+ back == v && (v < 0) == (r < 0),
+ "value cannot be losslessly widened to `i128`"
+ );
+
+ r
+ }
+ },
+ >()
+ };
+}
+#[doc(inline)]
+pub use cv;
+
+/// Types that can be created from an integer constant expression validated at build time.
+// TODO: make this a `const` trait once they are stable. This will let cv! be used in const
+// contexts.
+pub trait FromConst: Sized {
+ /// Creates the value that corresponds to the constant `V`.
+ ///
+ /// Fails the build if `V` is not a valid value for `Self`.
+ fn from_const<const V: i128>() -> Self;
+}
+
+/// Implements [`FromConst`] for [`NonZero`](core::num::NonZero).
+macro_rules! impl_from_const_nonzero {
+ ($($type:ty)*) => {
+ $(
+ impl FromConst for core::num::NonZero<$type> {
+ #[inline]
+ fn from_const<const V: i128>() -> Self {
+ const_assert!(
+ V >= <$type>::MIN as i128 && V <= <$type>::MAX as i128,
+ "Constant cannot be represented by the underlying type."
+ );
+
+ const { core::num::NonZero::new(V as $type).unwrap() }
+ }
+ }
+ )*
+ };
+}
+
+impl_from_const_nonzero!(
+ u8 u16 u32 u64 usize
+ i8 i16 i32 i64 isize
+);
+
/// Designates unsigned primitive types.
pub enum Unsigned {}
diff --git a/rust/kernel/num/bounded.rs b/rust/kernel/num/bounded.rs
index dafe77782d79..b04bba3fa0cc 100644
--- a/rust/kernel/num/bounded.rs
+++ b/rust/kernel/num/bounded.rs
@@ -13,7 +13,10 @@
};
use kernel::{
- num::Integer,
+ num::{
+ FromConst,
+ Integer, //
+ },
prelude::*, //
};
@@ -262,6 +265,22 @@ pub const fn new<const VALUE: $type>() -> Self {
unsafe { Self::__new(VALUE) }
}
}
+
+ impl<const N: u32> FromConst for Bounded<$type, N> {
+ #[inline]
+ fn from_const<const V: i128>() -> Self {
+ const_assert!(
+ V >= <$type>::MIN as i128 && V <= <$type>::MAX as i128,
+ "Constant cannot be represented by the underlying type."
+ );
+ // Statically assert that `V` fits within the set number of bits.
+ const_assert!(fits_within!(V as $type, $type, N));
+
+ // SAFETY: the asserts above confirmed that `V` can be represented within `N`
+ // bits.
+ unsafe { Self::__new(V as $type) }
+ }
+ }
)*
};
}
diff --git a/rust/kernel/ptr.rs b/rust/kernel/ptr.rs
index 82acb531b17b..3dcf415bcec3 100644
--- a/rust/kernel/ptr.rs
+++ b/rust/kernel/ptr.rs
@@ -166,6 +166,19 @@ pub const fn mask(self) -> usize {
}
}
+impl crate::num::FromConst for Alignment {
+ #[inline]
+ fn from_const<const V: i128>() -> Self {
+ const_assert!(
+ V > 0 && V <= usize::MAX as i128,
+ "Constant cannot be represented as an Alignment."
+ );
+
+ // The unwrap fails the build if `V` is not a power of two.
+ const { Alignment::new_checked(V as usize).unwrap() }
+ }
+}
+
/// Trait for items that can be aligned against an [`Alignment`].
pub trait Alignable: Sized {
/// Aligns `self` down to `alignment`.
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 7:28 ` [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
@ 2026-08-27 9:32 ` Alice Ryhl
2026-08-27 10:42 ` Alexandre Courbot
0 siblings, 1 reply; 21+ messages in thread
From: Alice Ryhl @ 2026-08-27 9:32 UTC (permalink / raw)
To: Eliot Courtney
Cc: 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 Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
> Currently, using NonZero/Bounded constants is quite verbose. It's
> unfortunate because it disincentivizes using it in interface boundaries.
> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
> value) takes a const integer expression and widens it to i128 (at build
> time only) before passing it as a const generic value to a new trait
> function `FromConst::from_const`. The trait is implemented by NonZero,
> Bounded, and Alignment and lets values of each be constructed from
> constants without a verbose turbofish syntax. For example,
> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>
> Suggested-by: Gary Guo <gary@garyguo.net>
> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
This doesn't work in const context, so I don't think this is a great
strategy.
I would want to use it for cases like this:
drivers/android/binder/netlink.rs
const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
Alice
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 9:32 ` Alice Ryhl
@ 2026-08-27 10:42 ` Alexandre Courbot
2026-08-27 11:12 ` Alexandre Courbot
0 siblings, 1 reply; 21+ messages in thread
From: Alexandre Courbot @ 2026-08-27 10:42 UTC (permalink / raw)
To: Alice Ryhl
Cc: Eliot Courtney, 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 Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>> Currently, using NonZero/Bounded constants is quite verbose. It's
>> unfortunate because it disincentivizes using it in interface boundaries.
>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>> value) takes a const integer expression and widens it to i128 (at build
>> time only) before passing it as a const generic value to a new trait
>> function `FromConst::from_const`. The trait is implemented by NonZero,
>> Bounded, and Alignment and lets values of each be constructed from
>> constants without a verbose turbofish syntax. For example,
>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>
>> Suggested-by: Gary Guo <gary@garyguo.net>
>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>
> This doesn't work in const context, so I don't think this is a great
> strategy.
>
> I would want to use it for cases like this:
>
> drivers/android/binder/netlink.rs
> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
`const_as!` [1] should do the trick for this, provided you don't need to
create a const `NonZero`.
[1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 10:42 ` Alexandre Courbot
@ 2026-08-27 11:12 ` Alexandre Courbot
2026-08-27 13:48 ` Eliot Courtney
0 siblings, 1 reply; 21+ messages in thread
From: Alexandre Courbot @ 2026-08-27 11:12 UTC (permalink / raw)
To: Alice Ryhl
Cc: Eliot Courtney, 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 Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>>> Currently, using NonZero/Bounded constants is quite verbose. It's
>>> unfortunate because it disincentivizes using it in interface boundaries.
>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>>> value) takes a const integer expression and widens it to i128 (at build
>>> time only) before passing it as a const generic value to a new trait
>>> function `FromConst::from_const`. The trait is implemented by NonZero,
>>> Bounded, and Alignment and lets values of each be constructed from
>>> constants without a verbose turbofish syntax. For example,
>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>>
>>> Suggested-by: Gary Guo <gary@garyguo.net>
>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>>
>> This doesn't work in const context, so I don't think this is a great
>> strategy.
>>
>> I would want to use it for cases like this:
>>
>> drivers/android/binder/netlink.rs
>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
>
> `const_as!` [1] should do the trick for this, provided you don't need to
> create a const `NonZero`.
>
> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
... but I agree it would be nice to be able to use this in const
context. And there is an overlap with `const_as!` that becomes more
obvious the more I look at it.
In for a penny, in for a pound of macro code as they say. Since we
agreed on using macros, how about unifying both under the same `cv!`
macro, with as many branches as we have types we want to initialize from
a constant value? For instance:
// Does what `const_as!` currently does under the hood.
const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
// Calls `NonZero::new().unwrap()` under the hood.
const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
// Calls `Bounded::new::<{ ...}>()` under the hood.
const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
I.e. we would have one extra matching arm in `cv!` per type it handles
instead of implementing a trait. The syntax of the macro would look more
natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
limitations of such a semantic dispatch.
Even the name `const_as!` wasn't really accurate to begin with: what it
really emulates is a const `try_from`, and we even discussed
implementing it in these terms in the future.
I'm sure the idea needs more polishing but I think there's something to
explore here.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 11:12 ` Alexandre Courbot
@ 2026-08-27 13:48 ` Eliot Courtney
2026-08-27 13:59 ` Gary Guo
2026-08-27 14:29 ` Alexandre Courbot
0 siblings, 2 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 13:48 UTC (permalink / raw)
To: Alexandre Courbot, Alice Ryhl
Cc: Eliot Courtney, 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, dri-devel
On Thu Aug 27, 2026 at 8:12 PM JST, Alexandre Courbot wrote:
> On Thu Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
>> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
>>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>>>> Currently, using NonZero/Bounded constants is quite verbose. It's
>>>> unfortunate because it disincentivizes using it in interface boundaries.
>>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>>>> value) takes a const integer expression and widens it to i128 (at build
>>>> time only) before passing it as a const generic value to a new trait
>>>> function `FromConst::from_const`. The trait is implemented by NonZero,
>>>> Bounded, and Alignment and lets values of each be constructed from
>>>> constants without a verbose turbofish syntax. For example,
>>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>>>
>>>> Suggested-by: Gary Guo <gary@garyguo.net>
>>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>>>
>>> This doesn't work in const context, so I don't think this is a great
>>> strategy.
>>>
>>> I would want to use it for cases like this:
>>>
>>> drivers/android/binder/netlink.rs
>>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
>>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
>>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
>>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
>>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
>>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
>>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
>>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
>>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
>>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
>>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
>>
>> `const_as!` [1] should do the trick for this, provided you don't need to
>> create a const `NonZero`.
>>
>> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
>
> ... but I agree it would be nice to be able to use this in const
> context. And there is an overlap with `const_as!` that becomes more
> obvious the more I look at it.
>
> In for a penny, in for a pound of macro code as they say. Since we
> agreed on using macros, how about unifying both under the same `cv!`
> macro, with as many branches as we have types we want to initialize from
> a constant value? For instance:
>
> // Does what `const_as!` currently does under the hood.
> const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
> // Calls `NonZero::new().unwrap()` under the hood.
> const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
> // Calls `Bounded::new::<{ ...}>()` under the hood.
> const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
>
> I.e. we would have one extra matching arm in `cv!` per type it handles
> instead of implementing a trait. The syntax of the macro would look more
> natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
> limitations of such a semantic dispatch.
>
> Even the name `const_as!` wasn't really accurate to begin with: what it
> really emulates is a const `try_from`, and we even discussed
> implementing it in these terms in the future.
>
> I'm sure the idea needs more polishing but I think there's something to
> explore here.
Yeah I agree that const_as! is similar and if we had const traits we
could fully merge them and have it always work in a const context for
both duties (which are really a const tryfrom as you said).
I am not sure about the suggested syntax (e.g.
cv!(Bounded::new(kernel::uapi::SMALL_VALUE))), since it seems very
verbose.
Alternatively, what about just directly merging them so you use
cv!(5) (the non const context FromConst::from_const dispatcher in this
series) or cv!(value => u8) (exactly const_as!), with the =>
distinguishing between the two?
So this is what would work for Alice's example (const_as! but pushed
inside cv!):
```
const BINDER_CMD_REPORT: u8 = cv!(kernel::uapi::BINDER_CMD_REPORT => u8);
```
When we have const traits then we could remove the => syntax I think.
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 13:48 ` Eliot Courtney
@ 2026-08-27 13:59 ` Gary Guo
2026-08-27 14:29 ` Alexandre Courbot
1 sibling, 0 replies; 21+ messages in thread
From: Gary Guo @ 2026-08-27 13:59 UTC (permalink / raw)
To: Eliot Courtney, Alexandre Courbot, Alice Ryhl
Cc: 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, dri-devel
On Thu Aug 27, 2026 at 2:48 PM BST, Eliot Courtney wrote:
> On Thu Aug 27, 2026 at 8:12 PM JST, Alexandre Courbot wrote:
>> On Thu Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
>>> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
>>>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>>>>> Currently, using NonZero/Bounded constants is quite verbose. It's
>>>>> unfortunate because it disincentivizes using it in interface boundaries.
>>>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>>>>> value) takes a const integer expression and widens it to i128 (at build
>>>>> time only) before passing it as a const generic value to a new trait
>>>>> function `FromConst::from_const`. The trait is implemented by NonZero,
>>>>> Bounded, and Alignment and lets values of each be constructed from
>>>>> constants without a verbose turbofish syntax. For example,
>>>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>>>>
>>>>> Suggested-by: Gary Guo <gary@garyguo.net>
>>>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>>>>
>>>> This doesn't work in const context, so I don't think this is a great
>>>> strategy.
>>>>
>>>> I would want to use it for cases like this:
>>>>
>>>> drivers/android/binder/netlink.rs
>>>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
>>>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
>>>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
>>>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
>>>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
>>>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
>>>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
>>>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
>>>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
>>>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
>>>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
>>>
>>> `const_as!` [1] should do the trick for this, provided you don't need to
>>> create a const `NonZero`.
>>>
>>> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
>>
>> ... but I agree it would be nice to be able to use this in const
>> context. And there is an overlap with `const_as!` that becomes more
>> obvious the more I look at it.
>>
>> In for a penny, in for a pound of macro code as they say. Since we
>> agreed on using macros, how about unifying both under the same `cv!`
>> macro, with as many branches as we have types we want to initialize from
>> a constant value? For instance:
>>
>> // Does what `const_as!` currently does under the hood.
>> const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
>> // Calls `NonZero::new().unwrap()` under the hood.
>> const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
>> // Calls `Bounded::new::<{ ...}>()` under the hood.
>> const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
>>
>> I.e. we would have one extra matching arm in `cv!` per type it handles
>> instead of implementing a trait. The syntax of the macro would look more
>> natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
>> limitations of such a semantic dispatch.
>>
>> Even the name `const_as!` wasn't really accurate to begin with: what it
>> really emulates is a const `try_from`, and we even discussed
>> implementing it in these terms in the future.
>>
>> I'm sure the idea needs more polishing but I think there's something to
>> explore here.
>
> Yeah I agree that const_as! is similar and if we had const traits we
> could fully merge them and have it always work in a const context for
> both duties (which are really a const tryfrom as you said).
>
> I am not sure about the suggested syntax (e.g.
> cv!(Bounded::new(kernel::uapi::SMALL_VALUE))), since it seems very
> verbose.
>
> Alternatively, what about just directly merging them so you use
> cv!(5) (the non const context FromConst::from_const dispatcher in this
> series) or cv!(value => u8) (exactly const_as!), with the =>
> distinguishing between the two?
>
> So this is what would work for Alice's example (const_as! but pushed
> inside cv!):
> ```
> const BINDER_CMD_REPORT: u8 = cv!(kernel::uapi::BINDER_CMD_REPORT => u8);
> ```
>
> When we have const traits then we could remove the => syntax I think.
It'd still be useful to allow explicit specification if user prefers. I think it
makes sense to (ultimately) translate
cv!(val)
to
const { FromConst::from_const(val as i128) }
and
cv!(val => ty)
to
const { ty::from_const(val as i128) }
I think there might be some tricks possible to get it working without const
trait, e.g. for `Bounded` and `Alignment` we could simply add an inherent method
#[doc(hidden)]
#[inline]
const fn __from_const(val: i128) -> Self { ... }
but I'll need to think about how to make it work with core types.
Best,
Gary
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 13:48 ` Eliot Courtney
2026-08-27 13:59 ` Gary Guo
@ 2026-08-27 14:29 ` Alexandre Courbot
2026-08-27 14:37 ` Gary Guo
1 sibling, 1 reply; 21+ messages in thread
From: Alexandre Courbot @ 2026-08-27 14:29 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,
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 Aug 27, 2026 at 10:48 PM JST, Eliot Courtney wrote:
> On Thu Aug 27, 2026 at 8:12 PM JST, Alexandre Courbot wrote:
>> On Thu Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
>>> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
>>>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>>>>> Currently, using NonZero/Bounded constants is quite verbose. It's
>>>>> unfortunate because it disincentivizes using it in interface boundaries.
>>>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>>>>> value) takes a const integer expression and widens it to i128 (at build
>>>>> time only) before passing it as a const generic value to a new trait
>>>>> function `FromConst::from_const`. The trait is implemented by NonZero,
>>>>> Bounded, and Alignment and lets values of each be constructed from
>>>>> constants without a verbose turbofish syntax. For example,
>>>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>>>>
>>>>> Suggested-by: Gary Guo <gary@garyguo.net>
>>>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>>>>
>>>> This doesn't work in const context, so I don't think this is a great
>>>> strategy.
>>>>
>>>> I would want to use it for cases like this:
>>>>
>>>> drivers/android/binder/netlink.rs
>>>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
>>>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
>>>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
>>>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
>>>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
>>>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
>>>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
>>>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
>>>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
>>>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
>>>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
>>>
>>> `const_as!` [1] should do the trick for this, provided you don't need to
>>> create a const `NonZero`.
>>>
>>> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
>>
>> ... but I agree it would be nice to be able to use this in const
>> context. And there is an overlap with `const_as!` that becomes more
>> obvious the more I look at it.
>>
>> In for a penny, in for a pound of macro code as they say. Since we
>> agreed on using macros, how about unifying both under the same `cv!`
>> macro, with as many branches as we have types we want to initialize from
>> a constant value? For instance:
>>
>> // Does what `const_as!` currently does under the hood.
>> const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
>> // Calls `NonZero::new().unwrap()` under the hood.
>> const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
>> // Calls `Bounded::new::<{ ...}>()` under the hood.
>> const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
>>
>> I.e. we would have one extra matching arm in `cv!` per type it handles
>> instead of implementing a trait. The syntax of the macro would look more
>> natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
>> limitations of such a semantic dispatch.
>>
>> Even the name `const_as!` wasn't really accurate to begin with: what it
>> really emulates is a const `try_from`, and we even discussed
>> implementing it in these terms in the future.
>>
>> I'm sure the idea needs more polishing but I think there's something to
>> explore here.
>
> Yeah I agree that const_as! is similar and if we had const traits we
> could fully merge them and have it always work in a const context for
> both duties (which are really a const tryfrom as you said).
>
> I am not sure about the suggested syntax (e.g.
> cv!(Bounded::new(kernel::uapi::SMALL_VALUE))), since it seems very
> verbose.
A bit, but what I like is that it looks very close to what you would
naturally write if you had const traits (minus the unwraps), so you
don't have to learn a new syntax. As long as it's not *more* verbose
than natural Rust, I think it's fine.
It also has the benefit of relying less on type inference, i.e. `cv!(5)`
requires the caller to specify the type even with a `let` statement,
whereas you could do `let v = cv!(NonZero::new(5));` and it would work
as expected.
I also feel that we should be able to support const items with `cv!`
today, which is not going to be possible with the trait-based approach,
so if we switch to macro dispatch we need differentiating syntax to
decide the arm.
>
> Alternatively, what about just directly merging them so you use
> cv!(5) (the non const context FromConst::from_const dispatcher in this
> series) or cv!(value => u8) (exactly const_as!), with the =>
> distinguishing between the two?
>
> So this is what would work for Alice's example (const_as! but pushed
> inside cv!):
> ```
> const BINDER_CMD_REPORT: u8 = cv!(kernel::uapi::BINDER_CMD_REPORT => u8);
> ```
>
> When we have const traits then we could remove the => syntax I think.
Agreed that they should be merged in any case, even if we keep the
current syntax of `const_as`, the macro itself is hard to justify in a
world where `cv` exists. And `cv` also corresponds better to the actual
purpose of `const_as` (which is not, as I initially thought, a const
`as`, but rather a `try_from`).
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 14:29 ` Alexandre Courbot
@ 2026-08-27 14:37 ` Gary Guo
2026-08-27 14:55 ` Alice Ryhl
0 siblings, 1 reply; 21+ messages in thread
From: Gary Guo @ 2026-08-27 14:37 UTC (permalink / raw)
To: Alexandre Courbot, 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,
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 Aug 27, 2026 at 3:29 PM BST, Alexandre Courbot wrote:
> On Thu Aug 27, 2026 at 10:48 PM JST, Eliot Courtney wrote:
>> On Thu Aug 27, 2026 at 8:12 PM JST, Alexandre Courbot wrote:
>>> On Thu Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
>>>> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
>>>>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
>>>>>> Currently, using NonZero/Bounded constants is quite verbose. It's
>>>>>> unfortunate because it disincentivizes using it in interface boundaries.
>>>>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
>>>>>> value) takes a const integer expression and widens it to i128 (at build
>>>>>> time only) before passing it as a const generic value to a new trait
>>>>>> function `FromConst::from_const`. The trait is implemented by NonZero,
>>>>>> Bounded, and Alignment and lets values of each be constructed from
>>>>>> constants without a verbose turbofish syntax. For example,
>>>>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
>>>>>>
>>>>>> Suggested-by: Gary Guo <gary@garyguo.net>
>>>>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
>>>>>
>>>>> This doesn't work in const context, so I don't think this is a great
>>>>> strategy.
>>>>>
>>>>> I would want to use it for cases like this:
>>>>>
>>>>> drivers/android/binder/netlink.rs
>>>>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
>>>>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
>>>>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
>>>>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
>>>>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
>>>>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
>>>>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
>>>>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
>>>>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
>>>>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
>>>>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
>>>>
>>>> `const_as!` [1] should do the trick for this, provided you don't need to
>>>> create a const `NonZero`.
>>>>
>>>> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
>>>
>>> ... but I agree it would be nice to be able to use this in const
>>> context. And there is an overlap with `const_as!` that becomes more
>>> obvious the more I look at it.
>>>
>>> In for a penny, in for a pound of macro code as they say. Since we
>>> agreed on using macros, how about unifying both under the same `cv!`
>>> macro, with as many branches as we have types we want to initialize from
>>> a constant value? For instance:
>>>
>>> // Does what `const_as!` currently does under the hood.
>>> const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
>>> // Calls `NonZero::new().unwrap()` under the hood.
>>> const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
>>> // Calls `Bounded::new::<{ ...}>()` under the hood.
>>> const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
>>>
>>> I.e. we would have one extra matching arm in `cv!` per type it handles
>>> instead of implementing a trait. The syntax of the macro would look more
>>> natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
>>> limitations of such a semantic dispatch.
>>>
>>> Even the name `const_as!` wasn't really accurate to begin with: what it
>>> really emulates is a const `try_from`, and we even discussed
>>> implementing it in these terms in the future.
>>>
>>> I'm sure the idea needs more polishing but I think there's something to
>>> explore here.
>>
>> Yeah I agree that const_as! is similar and if we had const traits we
>> could fully merge them and have it always work in a const context for
>> both duties (which are really a const tryfrom as you said).
>>
>> I am not sure about the suggested syntax (e.g.
>> cv!(Bounded::new(kernel::uapi::SMALL_VALUE))), since it seems very
>> verbose.
>
> A bit, but what I like is that it looks very close to what you would
> naturally write if you had const traits (minus the unwraps), so you
> don't have to learn a new syntax. As long as it's not *more* verbose
> than natural Rust, I think it's fine.
>
> It also has the benefit of relying less on type inference, i.e. `cv!(5)`
> requires the caller to specify the type even with a `let` statement,
> whereas you could do `let v = cv!(NonZero::new(5));` and it would work
> as expected.
Even with const try_from we'd still want `cv!()` to avoid having to write
const { Type::try_from(...).unwrap() }
I think having `=>` syntax is great because it is a good place to *optionally*
require type annotation.
For enum repr for example, I think it'd be great that
const BINDER_CMD_REPORT: u8 = cv!(kernel::uapi::BINDER_CMD_REPORT);
would work directly. It might need some tricks, which I have hard time coming up
as I'm not feeling very well today, but I'll give it a shot over the weekend...
Best,
Gary
^ permalink raw reply [flat|nested] 21+ messages in thread* Re: [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions
2026-08-27 14:37 ` Gary Guo
@ 2026-08-27 14:55 ` Alice Ryhl
0 siblings, 0 replies; 21+ messages in thread
From: Alice Ryhl @ 2026-08-27 14:55 UTC (permalink / raw)
To: Gary Guo
Cc: Alexandre Courbot, Eliot Courtney, 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, Aug 27, 2026 at 4:37 PM Gary Guo <gary@garyguo.net> wrote:
>
> On Thu Aug 27, 2026 at 3:29 PM BST, Alexandre Courbot wrote:
> > On Thu Aug 27, 2026 at 10:48 PM JST, Eliot Courtney wrote:
> >> On Thu Aug 27, 2026 at 8:12 PM JST, Alexandre Courbot wrote:
> >>> On Thu Aug 27, 2026 at 7:42 PM JST, Alexandre Courbot wrote:
> >>>> On Thu Aug 27, 2026 at 6:32 PM JST, Alice Ryhl wrote:
> >>>>> On Thu, Aug 27, 2026 at 04:28:31PM +0900, Eliot Courtney wrote:
> >>>>>> Currently, using NonZero/Bounded constants is quite verbose. It's
> >>>>>> unfortunate because it disincentivizes using it in interface boundaries.
> >>>>>> Introduce a macro to make it nicer to use. The macro `cv!` (for constant
> >>>>>> value) takes a const integer expression and widens it to i128 (at build
> >>>>>> time only) before passing it as a const generic value to a new trait
> >>>>>> function `FromConst::from_const`. The trait is implemented by NonZero,
> >>>>>> Bounded, and Alignment and lets values of each be constructed from
> >>>>>> constants without a verbose turbofish syntax. For example,
> >>>>>> `const { NonZero::new(1).unwrap() }` can be written as `cv!(1)`.
> >>>>>>
> >>>>>> Suggested-by: Gary Guo <gary@garyguo.net>
> >>>>>> Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
> >>>>>
> >>>>> This doesn't work in const context, so I don't think this is a great
> >>>>> strategy.
> >>>>>
> >>>>> I would want to use it for cases like this:
> >>>>>
> >>>>> drivers/android/binder/netlink.rs
> >>>>> const BINDER_CMD_REPORT: u8 = kernel::uapi::BINDER_CMD_REPORT as u8;
> >>>>> const BINDER_A_REPORT_ERROR: c_int = kernel::uapi::BINDER_A_REPORT_ERROR as c_int;
> >>>>> const BINDER_A_REPORT_CONTEXT: c_int = kernel::uapi::BINDER_A_REPORT_CONTEXT as c_int;
> >>>>> const BINDER_A_REPORT_FROM_PID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_PID as c_int;
> >>>>> const BINDER_A_REPORT_FROM_TID: c_int = kernel::uapi::BINDER_A_REPORT_FROM_TID as c_int;
> >>>>> const BINDER_A_REPORT_TO_PID: c_int = kernel::uapi::BINDER_A_REPORT_TO_PID as c_int;
> >>>>> const BINDER_A_REPORT_TO_TID: c_int = kernel::uapi::BINDER_A_REPORT_TO_TID as c_int;
> >>>>> const BINDER_A_REPORT_IS_REPLY: c_int = kernel::uapi::BINDER_A_REPORT_IS_REPLY as c_int;
> >>>>> const BINDER_A_REPORT_FLAGS: c_int = kernel::uapi::BINDER_A_REPORT_FLAGS as c_int;
> >>>>> const BINDER_A_REPORT_CODE: c_int = kernel::uapi::BINDER_A_REPORT_CODE as c_int;
> >>>>> const BINDER_A_REPORT_DATA_SIZE: c_int = kernel::uapi::BINDER_A_REPORT_DATA_SIZE as c_int;
> >>>>
> >>>> `const_as!` [1] should do the trick for this, provided you don't need to
> >>>> create a const `NonZero`.
> >>>>
> >>>> [1] https://lore.kernel.org/all/20260825-const_as-v1-1-1ce712225fe2@nvidia.com/
> >>>
> >>> ... but I agree it would be nice to be able to use this in const
> >>> context. And there is an overlap with `const_as!` that becomes more
> >>> obvious the more I look at it.
> >>>
> >>> In for a penny, in for a pound of macro code as they say. Since we
> >>> agreed on using macros, how about unifying both under the same `cv!`
> >>> macro, with as many branches as we have types we want to initialize from
> >>> a constant value? For instance:
> >>>
> >>> // Does what `const_as!` currently does under the hood.
> >>> const BINDER_CMD_REPORT: u8 = cv!(u8::from(kernel::uapi::BINDER_CMD_REPORT));
> >>> // Calls `NonZero::new().unwrap()` under the hood.
> >>> const SOME_NONZERO: NonZero<u8> = cv!(NonZero::new(kernel::uapi::NONZERO_VALUE));
> >>> // Calls `Bounded::new::<{ ...}>()` under the hood.
> >>> const SOME_BOUNDED: Bounded<u32, 2> = cv!(Bounded::new(kernel::uapi::SMALL_VALUE));
> >>>
> >>> I.e. we would have one extra matching arm in `cv!` per type it handles
> >>> instead of implementing a trait. The syntax of the macro would look more
> >>> natural (bye bye `const_as`'s awkward `=>`), albeit it would have the
> >>> limitations of such a semantic dispatch.
> >>>
> >>> Even the name `const_as!` wasn't really accurate to begin with: what it
> >>> really emulates is a const `try_from`, and we even discussed
> >>> implementing it in these terms in the future.
> >>>
> >>> I'm sure the idea needs more polishing but I think there's something to
> >>> explore here.
> >>
> >> Yeah I agree that const_as! is similar and if we had const traits we
> >> could fully merge them and have it always work in a const context for
> >> both duties (which are really a const tryfrom as you said).
> >>
> >> I am not sure about the suggested syntax (e.g.
> >> cv!(Bounded::new(kernel::uapi::SMALL_VALUE))), since it seems very
> >> verbose.
> >
> > A bit, but what I like is that it looks very close to what you would
> > naturally write if you had const traits (minus the unwraps), so you
> > don't have to learn a new syntax. As long as it's not *more* verbose
> > than natural Rust, I think it's fine.
> >
> > It also has the benefit of relying less on type inference, i.e. `cv!(5)`
> > requires the caller to specify the type even with a `let` statement,
> > whereas you could do `let v = cv!(NonZero::new(5));` and it would work
> > as expected.
>
> Even with const try_from we'd still want `cv!()` to avoid having to write
>
> const { Type::try_from(...).unwrap() }
>
> I think having `=>` syntax is great because it is a good place to *optionally*
> require type annotation.
>
> For enum repr for example, I think it'd be great that
>
> const BINDER_CMD_REPORT: u8 = cv!(kernel::uapi::BINDER_CMD_REPORT);
>
> would work directly. It might need some tricks, which I have hard time coming up
> as I'm not feeling very well today, but I'll give it a shot over the weekend...
One could potentially define a trait with a MIN and MAX value
constant, and then implement cv! like this:
1. Verify that the value lies between MIN and MAX.
2. Cast the value to uNN of the same size as the target type.
3. Transmute the uNN to the target type.
Since the trait has no methods, this works in const eval.
Alice
^ permalink raw reply [flat|nested] 21+ messages in thread
* [PATCH v8 04/12] rust: prelude: add `num::cv`
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (2 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 03/12] rust: num: add cv! macro to create values from constant expressions Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 05/12] rust: use cv! to build Bounded values from constants Eliot Courtney
` (7 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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 the `cv!` macro to the prelude so it doesn't need to be imported
explicitly to use it.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
rust/kernel/num.rs | 1 -
rust/kernel/prelude.rs | 1 +
2 files changed, 1 insertion(+), 1 deletion(-)
diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
index 29bf903a4a74..a20b8937d4b5 100644
--- a/rust/kernel/num.rs
+++ b/rust/kernel/num.rs
@@ -18,7 +18,6 @@
/// ```
/// use core::num::NonZero;
/// use kernel::num::Bounded;
-/// use kernel::num::cv;
/// use kernel::ptr::Alignment;
///
/// let v: NonZero<usize> = cv!(8);
diff --git a/rust/kernel/prelude.rs b/rust/kernel/prelude.rs
index ca396f1f78a6..5facf8f7af90 100644
--- a/rust/kernel/prelude.rs
+++ b/rust/kernel/prelude.rs
@@ -106,6 +106,7 @@
Result, //
},
init::InPlaceInit,
+ num::cv,
pr_alert,
pr_crit,
pr_debug,
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v8 05/12] rust: use cv! to build Bounded values from constants
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (3 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 04/12] rust: prelude: add `num::cv` Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 06/12] rust: sizes: add sub-1K size constants Eliot Courtney
` (6 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 UTC (permalink / raw)
To: Alice Ryhl, Burak Emir, Yury Norov, Miguel Ojeda, Boqun Feng,
Gary Guo, Björn Roy Baron, Benno Lossin, Andreas Hindborg,
Trevor Gross, Danilo Krummrich, Daniel Almeida, Tamir Duberstein,
Alexandre Courbot, Onur Özkan, David Airlie, Simona Vetter
Cc: Greg Kroah-Hartman, John Hubbard, Alistair Popple, Timur Tabi,
Zhi Wang, rust-for-linux, linux-kernel, nova-gpu, dri-devel,
Eliot Courtney
There are some locations which use Bounded::new() which can use the new
cv! macro, so convert them.
Signed-off-by: Eliot Courtney <ecourtney@nvidia.com>
---
drivers/gpu/nova-core/num.rs | 3 +--
rust/kernel/bitfield.rs | 8 ++++----
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/drivers/gpu/nova-core/num.rs b/drivers/gpu/nova-core/num.rs
index 6eb174d136ab..03a7e7b3ff8e 100644
--- a/drivers/gpu/nova-core/num.rs
+++ b/drivers/gpu/nova-core/num.rs
@@ -240,8 +240,7 @@ macro_rules! bounded_enum {
impl core::convert::From<$enum_type> for kernel::num::Bounded<$width, $length> {
fn from(value: $enum_type) -> Self {
match value {
- $($enum_type::$variant =>
- kernel::num::Bounded::<$width, _>::new::<{ $value }>()),*
+ $($enum_type::$variant => kernel::num::cv!($value)),*
}
}
}
diff --git a/rust/kernel/bitfield.rs b/rust/kernel/bitfield.rs
index 35ede53f2b8e..5331df986579 100644
--- a/rust/kernel/bitfield.rs
+++ b/rust/kernel/bitfield.rs
@@ -21,7 +21,7 @@
//! }
//!
//! // Valid value for the `blue` field.
-//! let blue = Bounded::<u16, 5>::new::<0x18>();
+//! let blue: Bounded<u16, 5> = cv!(0x18);
//!
//! // Setters can be chained. Values ranges are checked at compile-time.
//! let color = Rgb::zeroed()
@@ -229,9 +229,9 @@
//! impl From<Mode> for Bounded<u32, 2> {
//! fn from(m: Mode) -> Self {
//! match m {
-//! Mode::Low => Bounded::<u32, _>::new::<0>(),
-//! Mode::High => Bounded::<u32, _>::new::<1>(),
-//! Mode::Auto => Bounded::<u32, _>::new::<2>(),
+//! Mode::Low => cv!(0),
+//! Mode::High => cv!(1),
+//! Mode::Auto => cv!(2),
//! }
//! }
//! }
--
2.55.0
^ permalink raw reply related [flat|nested] 21+ messages in thread* [PATCH v8 06/12] rust: sizes: add sub-1K size constants
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (4 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 05/12] rust: use cv! to build Bounded values from constants Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 07/12] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
` (5 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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.
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] 21+ messages in thread* [PATCH v8 07/12] rust: sizes: implement SizeConstants for Alignment
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (5 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 06/12] rust: sizes: add sub-1K size constants Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 08/12] rust: use Alignment size constants Eliot Courtney
` (4 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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:
`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
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] 21+ messages in thread* [PATCH v8 08/12] rust: use Alignment size constants
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (6 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 07/12] rust: sizes: implement SizeConstants for Alignment Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 09/12] rust: bitmap: add contiguous area operations Eliot Courtney
` (3 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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 | 3 +--
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(+), 35 deletions(-)
diff --git a/drivers/gpu/nova-core/fb.rs b/drivers/gpu/nova-core/fb.rs
index 1576399389b1..70493c4a07b7 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 elf = {
- const ELF_DOWN_ALIGN: Alignment = Alignment::new::<SZ_64K>();
+ const ELF_DOWN_ALIGN: Alignment = Alignment::SZ_64K;
let elf_size = u64::from_safe_cast(gsp_fw.size);
let elf_addr = (boot.start - elf_size).align_down(ELF_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 = elf
.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 d9e4d62ae632..b37f11c9a891 100644
--- a/drivers/gpu/nova-core/fb/hal/gb100.rs
+++ b/drivers/gpu/nova-core/fb/hal/gb100.rs
@@ -82,8 +82,7 @@ fn write_sysmem_flush_page_gb100(bar: Bar0<'_>, addr: Bounded<u64, 52>) {
// This PMU reservation size is r570-specific.
pub(super) const fn pmu_reserved_size_gb100() -> u32 {
- usize_into_u32::<{ const_align_up(SZ_8M + SZ_16M + SZ_4K, Alignment::new::<SZ_128K>()).unwrap() }>(
- )
+ usize_into_u32::<{ 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 ab685fb4168f..6bac5206a836 100644
--- a/drivers/gpu/nova-core/fsp.rs
+++ b/drivers/gpu/nova-core/fsp.rs
@@ -17,7 +17,7 @@
Alignable,
Alignment, //
},
- sizes::SZ_2M,
+ sizes::SizeConstants,
time::Delta,
transmute::{
AsBytes,
@@ -257,7 +257,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 05f54fee6186..6b92e0e0a2a9 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
@@ -209,10 +206,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 c03650ee5226..de46e3399ff8 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,
};
@@ -282,7 +285,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 95f46bb75f9e..10fbb27a1998 100644
--- a/rust/kernel/io.rs
+++ b/rust/kernel/io.rs
@@ -15,7 +15,8 @@
ptr::{
Alignment,
KnownSize, //
- }, //
+ },
+ sizes::SizeConstants, //
};
pub mod mem;
@@ -85,7 +86,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] 21+ messages in thread* [PATCH v8 09/12] rust: bitmap: add contiguous area operations
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (7 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 08/12] rust: use Alignment size constants Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 10/12] rust: id_pool: add contiguous ID reservation Eliot Courtney
` (2 subsequent siblings)
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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] 21+ messages in thread* [PATCH v8 10/12] rust: id_pool: add contiguous ID reservation
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (8 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 09/12] rust: bitmap: add contiguous area operations Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 11/12] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 12/12] gpu: nova-core: add ChannelIdPool Eliot Courtney
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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] 21+ messages in thread* [PATCH v8 11/12] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (9 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 10/12] rust: id_pool: add contiguous ID reservation Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
2026-08-27 7:28 ` [PATCH v8 12/12] gpu: nova-core: add ChannelIdPool Eliot Courtney
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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] 21+ messages in thread* [PATCH v8 12/12] gpu: nova-core: add ChannelIdPool
2026-08-27 7:28 [PATCH v8 00/12] rust: Add support for reserving of ranges of IDs Eliot Courtney
` (10 preceding siblings ...)
2026-08-27 7:28 ` [PATCH v8 11/12] rust: id_pool: do not round capacity up to BitmapVec::MAX_INLINE_LEN Eliot Courtney
@ 2026-08-27 7:28 ` Eliot Courtney
11 siblings, 0 replies; 21+ messages in thread
From: Eliot Courtney @ 2026-08-27 7:28 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 42a4cd7971fa..66ea697a89f8 100644
--- a/drivers/gpu/nova-core/gpu.rs
+++ b/drivers/gpu/nova-core/gpu.rs
@@ -33,6 +33,8 @@
vgpu::VgpuManager, //
};
+#[cfg_attr(not(CONFIG_KUNIT = "y"), expect(dead_code))]
+mod channel;
mod hal;
macro_rules! define_chipset {
diff --git a/drivers/gpu/nova-core/gpu/channel.rs b/drivers/gpu/nova-core/gpu/channel.rs
new file mode 100644
index 000000000000..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] 21+ messages in thread