From: Alexandre Courbot <acourbot@nvidia.com>
To: "Danilo Krummrich" <dakr@kernel.org>,
"Joel Fernandes" <joelagnelf@nvidia.com>,
"Yury Norov" <yury.norov@gmail.com>,
"Jesung Yang" <y.j3ms.n@gmail.com>,
"Miguel Ojeda" <ojeda@kernel.org>,
"Alex Gaynor" <alex.gaynor@gmail.com>,
"Boqun Feng" <boqun.feng@gmail.com>,
"Gary Guo" <gary@garyguo.net>,
"Björn Roy Baron" <bjorn3_gh@protonmail.com>,
"Benno Lossin" <lossin@kernel.org>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>
Cc: nouveau@lists.freedesktop.org, linux-kernel@vger.kernel.org,
rust-for-linux@vger.kernel.org,
Alexandre Courbot <acourbot@nvidia.com>
Subject: [PATCH RFC v2 2/3] rust: kernel: add bounded integer types
Date: Thu, 09 Oct 2025 21:37:09 +0900 [thread overview]
Message-ID: <20251009-bounded_ints-v2-2-ff3d7fee3ffd@nvidia.com> (raw)
In-Reply-To: <20251009-bounded_ints-v2-0-ff3d7fee3ffd@nvidia.com>
Add the BoundedInt type, which restricts the number of bits allowed to
be used in a given integer value. This is useful to carry guarantees
when setting bitfields.
Alongside this type, many `From` and `TryFrom` implementations are
provided to reduce friction when using with regular integer types. Proxy
implementations of common integer traits are also provided.
Signed-off-by: Alexandre Courbot <acourbot@nvidia.com>
---
rust/kernel/lib.rs | 1 +
rust/kernel/num.rs | 499 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 500 insertions(+)
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index fcffc3988a90..21c1f452ee6a 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -101,6 +101,7 @@
pub mod mm;
#[cfg(CONFIG_NET)]
pub mod net;
+pub mod num;
pub mod of;
#[cfg(CONFIG_PM_OPP)]
pub mod opp;
diff --git a/rust/kernel/num.rs b/rust/kernel/num.rs
new file mode 100644
index 000000000000..b2aad95ce51c
--- /dev/null
+++ b/rust/kernel/num.rs
@@ -0,0 +1,499 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! Numerical types for the kernel.
+
+use kernel::prelude::*;
+
+/// Integer type for which only the bits `0..NUM_BITS` are valid.
+///
+/// # Invariants
+///
+/// Stored values are represented with at most `NUM_BITS` bits.
+#[repr(transparent)]
+#[derive(Clone, Copy, Debug, Default, Hash)]
+pub struct BoundedInt<T, const NUM_BITS: u32>(T);
+
+/// Returns `true` if `$value` can be represented with at most `$NUM_BITS` on `$type`.
+macro_rules! is_in_bounds {
+ ($value:expr, $type:ty, $num_bits:expr) => {{
+ let v = $value;
+ v & <$type as Boundable<NUM_BITS>>::MASK == v
+ }};
+}
+
+/// Trait for primitive integer types that can be used with `BoundedInt`.
+pub trait Boundable<const NUM_BITS: u32>
+where
+ Self: Sized + Copy + core::ops::BitAnd<Output = Self> + core::cmp::PartialEq,
+ Self: TryInto<u8> + TryInto<u16> + TryInto<u32> + TryInto<u64>,
+{
+ /// Mask of the valid bits for this type.
+ const MASK: Self;
+
+ /// Returns `true` if `value` can be represented with at most `NUM_BITS`.
+ ///
+ /// TODO: post-RFC: replace this with a left-shift followed by right-shift operation. This will
+ /// allow us to handle signed values as well.
+ fn is_in_bounds(value: Self) -> bool {
+ is_in_bounds!(value, Self, NUM_BITS)
+ }
+}
+
+impl<const NUM_BITS: u32> Boundable<NUM_BITS> for u8 {
+ const MASK: u8 = crate::bits::genmask_u8(0..=(NUM_BITS - 1));
+}
+
+impl<const NUM_BITS: u32> Boundable<NUM_BITS> for u16 {
+ const MASK: u16 = crate::bits::genmask_u16(0..=(NUM_BITS - 1));
+}
+
+impl<const NUM_BITS: u32> Boundable<NUM_BITS> for u32 {
+ const MASK: u32 = crate::bits::genmask_u32(0..=(NUM_BITS - 1));
+}
+
+impl<const NUM_BITS: u32> Boundable<NUM_BITS> for u64 {
+ const MASK: u64 = crate::bits::genmask_u64(0..=(NUM_BITS - 1));
+}
+
+impl<T, const NUM_BITS: u32> BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+{
+ /// Checks that `value` is valid for this type at compile-time and build a new value.
+ ///
+ /// This relies on [`build_assert!`] to perform validation at compile-time. If `value` cannot
+ /// be inferred to be in bounds at compile-time, use the fallible [`Self::try_new`] instead.
+ ///
+ /// When possible, use one of the `new_const` methods instead of this method as it statically
+ /// validates `value` instead of relying on the compiler's optimizations.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// # fn some_number() -> u32 { 0xffffffff }
+ ///
+ /// assert_eq!(BoundedInt::<u8, 1>::new(1).get(), 1);
+ /// assert_eq!(BoundedInt::<u16, 8>::new(0xff).get(), 0xff);
+ ///
+ /// // Triggers a build error as `0x1ff` doesn't fit into 8 bits.
+ /// // assert_eq!(BoundedInt::<u32, 8>::new(0x1ff).get(), 0x1ff);
+ ///
+ /// let v: u32 = some_number();
+ /// // Triggers a build error as `v` cannot be asserted to fit within 4 bits...
+ /// // let _ = BoundedInt::<u32, 4>::new(v);
+ /// // ... but this works as the compiler can assert the range from the mask.
+ /// let _ = BoundedInt::<u32, 4>::new(v & 0xf);
+ /// ```
+ pub fn new(value: T) -> Self {
+ crate::build_assert!(
+ T::is_in_bounds(value),
+ "Provided parameter is larger than maximal supported value"
+ );
+
+ Self(value)
+ }
+
+ /// Attempts to convert `value` into a value bounded by `NUM_BITS`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// assert_eq!(BoundedInt::<u8, 1>::try_new(1).map(|v| v.get()), Ok(1));
+ /// assert_eq!(BoundedInt::<u16, 8>::try_new(0xff).map(|v| v.get()), Ok(0xff));
+ ///
+ /// // `0x1ff` doesn't fit into 8 bits.
+ /// assert_eq!(BoundedInt::<u32, 8>::try_new(0x1ff), Err(EOVERFLOW));
+ /// ```
+ pub fn try_new(value: T) -> Result<Self> {
+ if !T::is_in_bounds(value) {
+ Err(EOVERFLOW)
+ } else {
+ Ok(Self(value))
+ }
+ }
+
+ /// Returns the contained value as a primitive type.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// let v = BoundedInt::<u32, 4>::new_const::<7>();
+ /// assert_eq!(v.get(), 7u32);
+ /// ```
+ pub fn get(self) -> T {
+ if !T::is_in_bounds(self.0) {
+ // SAFETY: Per the invariants, `self.0` cannot have bits set outside of `MASK`, so
+ // this block will
+ // never be reached.
+ unsafe { core::hint::unreachable_unchecked() }
+ }
+ self.0
+ }
+
+ /// Increase the number of bits usable for `self`.
+ ///
+ /// This operation cannot fail.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// let v = BoundedInt::<u32, 4>::new_const::<7>();
+ /// let larger_v = v.enlarge::<12>();
+ /// // The contained values are equal even though `larger_v` has a bigger capacity.
+ /// assert_eq!(larger_v, v);
+ /// ```
+ pub const fn enlarge<const NEW_NUM_BITS: u32>(self) -> BoundedInt<T, NEW_NUM_BITS>
+ where
+ T: Boundable<NEW_NUM_BITS>,
+ T: Copy,
+ {
+ build_assert!(NEW_NUM_BITS >= NUM_BITS);
+
+ // INVARIANT: the value did fit within `NUM_BITS`, so it will all the more fit within
+ // `NEW_NUM_BITS` which is larger.
+ BoundedInt(self.0)
+ }
+
+ /// Shrink the number of bits usable for `self`.
+ ///
+ /// Returns `EOVERFLOW` if the value of `self` cannot be represented within `NEW_NUM_BITS`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// let v = BoundedInt::<u32, 12>::new_const::<7>();
+ /// let smaller_v = v.shrink::<4>()?;
+ /// // The contained values are equal even though `smaller_v` has a smaller capacity.
+ /// assert_eq!(smaller_v, v);
+ ///
+ /// # Ok::<(), Error>(())
+ /// ```
+ pub fn shrink<const NEW_NUM_BITS: u32>(self) -> Result<BoundedInt<T, NEW_NUM_BITS>>
+ where
+ T: Boundable<NEW_NUM_BITS>,
+ T: Copy,
+ {
+ BoundedInt::<T, NEW_NUM_BITS>::try_new(self.get())
+ }
+
+ /// Casts `self` into a `BoundedInt` using a different storage type, but using the same
+ /// number of bits for representation.
+ ///
+ /// This method cannot fail as the number of bits used for representation doesn't change.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ /// let v = BoundedInt::<u32, 4>::new_const::<7>();
+ /// let smaller_v: BoundedInt<u8, _> = v.cast();
+ /// // The contained values are equal even though `smaller_v` has a smaller storage type.
+ /// assert_eq!(u32::from(smaller_v.get()), v.get());
+ /// ```
+ pub fn cast<U>(self) -> BoundedInt<U, NUM_BITS>
+ where
+ U: TryFrom<T> + Boundable<NUM_BITS>,
+ {
+ // SAFETY: the contained value is represented using `NUM_BITS`, and `U` can be bounded to
+ // `NUM_BITS`, hence the conversion cannot fail.
+ let value = unsafe { U::try_from(self.0).unwrap_unchecked() };
+
+ // INVARIANT: although the storage type has changed, the value is still represented within
+ // `NUM_BITS`.
+ BoundedInt(value)
+ }
+}
+
+/// Validating the value as a const expression cannot be done as a regular method, as the
+/// arithmetic expressions we rely on to check the bounds are not const. Thus, implement
+/// `new_const` using a macro.
+macro_rules! impl_const_new {
+ ($($type:ty)*) => {
+ $(
+ impl<const NUM_BITS: u32> BoundedInt<$type, NUM_BITS> {
+ /// Creates a bounded value for `VALUE`, statically validated.
+ ///
+ /// This method should be used instead of [`Self::new`] when the value is a constant
+ /// expression.
+ ///
+ /// # Examples
+ /// ```
+ /// use kernel::num::BoundedInt;
+ ///
+ #[doc = ::core::concat!(
+ "let v = BoundedInt::<",
+ ::core::stringify!($type),
+ ", 4>::new_const::<7>();")]
+ /// assert_eq!(v.get(), 7);
+ /// ```
+ pub const fn new_const<const VALUE: $type>() -> Self {
+ build_assert!(is_in_bounds!(VALUE, $type, NUM_BITS));
+
+ Self(VALUE)
+ }
+ }
+ )*
+ };
+}
+
+impl_const_new!(u8 u16 u32 u64);
+
+/// Declares a new `$trait` and implements it for all bounded types represented using `$num_bits`.
+///
+/// This is used to declare properties as traits that we can use for later implementations.
+macro_rules! impl_size_rule {
+ ($trait:ident, $($num_bits:literal)*) => {
+ trait $trait {}
+
+ $(
+ impl<T> $trait for BoundedInt<T, $num_bits> where T: Boundable<$num_bits> {}
+ )*
+ };
+}
+
+// Bounds that are larger than a `u64`.
+impl_size_rule!(LargerThanU64, 64);
+
+// Bounds that are larger than a `u32`.
+impl_size_rule!(LargerThanU32,
+ 32 33 34 35 36 37 38 39
+ 40 41 42 43 44 45 46 47
+ 48 49 50 51 52 53 54 55
+ 56 57 58 59 60 61 62 63
+);
+// Anything larger than `u64` is also larger than `u32`.
+impl<T> LargerThanU32 for T where T: LargerThanU64 {}
+
+// Bounds that are larger than a `u16`.
+impl_size_rule!(LargerThanU16,
+ 16 17 18 19 20 21 22 23
+ 24 25 26 27 28 29 30 31
+);
+// Anything larger than `u32` is also larger than `u16`.
+impl<T> LargerThanU16 for T where T: LargerThanU32 {}
+
+// Bounds that are larger than a `u8`.
+impl_size_rule!(LargerThanU8, 8 9 10 11 12 13 14 15);
+// Anything larger than `u16` is also larger than `u8`.
+impl<T> LargerThanU8 for T where T: LargerThanU16 {}
+
+// Bounds that are larger than a boolean.
+impl_size_rule!(LargerThanBool, 1 2 3 4 5 6 7);
+// Anything larger than `u8` is also larger than `bool`.
+impl<T> LargerThanBool for T where T: LargerThanU8 {}
+
+/// Generates `From` implementations from a primitive type into a bounded integer that is
+/// guaranteed to being able to contain it.
+macro_rules! impl_from_primitive {
+ ($($type:ty => $trait:ident),*) => {
+ $(
+ impl<T, const NUM_BITS: u32> From<$type> for BoundedInt<T, NUM_BITS>
+ where
+ Self: $trait,
+ T: From<$type>,
+ {
+ fn from(value: $type) -> Self {
+ Self(T::from(value))
+ }
+ }
+ )*
+ }
+}
+
+impl_from_primitive!(
+ bool => LargerThanBool,
+ u8 => LargerThanU8,
+ u16 => LargerThanU16,
+ u32 => LargerThanU32,
+ u64 => LargerThanU64
+);
+
+impl_size_rule!(FitsIntoBool, 1);
+
+impl_size_rule!(FitsIntoU8, 2 3 4 5 6 7 8);
+
+// Anything that fits into a `bool` also fits into a `u8`.
+impl<T> FitsIntoU8 for T where T: FitsIntoBool {}
+
+impl_size_rule!(FitsIntoU16, 9 10 11 12 13 14 15 16);
+
+// Anything that fits into a `u8` also fits into a `u16`.
+impl<T> FitsIntoU16 for T where T: FitsIntoU8 {}
+
+impl_size_rule!(FitsIntoU32,
+ 17 18 19 20 21 22 23 24
+ 25 26 27 28 29 30 31 32
+);
+
+// Anything that fits into a `u16` also fits into a `u32`.
+impl<T> FitsIntoU32 for T where T: FitsIntoU16 {}
+
+impl_size_rule!(FitsIntoU64,
+ 33 34 35 36 37 38 39 40
+ 41 42 43 44 45 46 47 48
+ 49 50 51 52 53 54 55 56
+ 57 58 59 60 61 62 63 64
+);
+
+// Anything that fits into a `u32` also fits into a `u64`.
+impl<T> FitsIntoU64 for T where T: FitsIntoU32 {}
+
+/// Generates `From` implementations from a bounded integer into a primitive type that is
+/// guaranteed to being able to contain it.
+macro_rules! impl_into_primitive {
+ ($($trait:ident => $type:ty),*) => {
+ $(
+ impl<T, const NUM_BITS: u32> From<BoundedInt<T, NUM_BITS>> for $type
+ where
+ T: Boundable<NUM_BITS>,
+ BoundedInt<T, NUM_BITS>: $trait
+ {
+ fn from(value: BoundedInt<T, NUM_BITS>) -> Self {
+ // SAFETY: per the `BoundedInt` invariants, less than 8 bits are used to the conversion
+ // cannot fail.
+ unsafe { value.get().try_into().unwrap_unchecked() }
+ }
+ }
+ )*
+ }
+}
+
+impl_into_primitive!(
+ FitsIntoU8 => u8,
+ FitsIntoU16 => u16,
+ FitsIntoU32 => u32,
+ FitsIntoU64 => u64
+);
+
+// Conversion to boolean must be handled separately as it does not have `TryFrom` implementation
+// from integers.
+impl<T> From<BoundedInt<T, 1>> for bool
+where
+ T: Boundable<1>,
+ BoundedInt<T, 1>: FitsIntoBool,
+ T: PartialEq + Zeroable,
+{
+ fn from(value: BoundedInt<T, 1>) -> Self {
+ value.get() != Zeroable::zeroed()
+ }
+}
+
+/// Trait similar to `TryInto` to avoid conflicting implementations errors.
+pub trait TryIntoBounded<T: Boundable<NUM_BITS>, const NUM_BITS: u32> {
+ /// Attempts to convert `self` into a value bounded by `NUM_BITS`.
+ fn try_into(self) -> Result<BoundedInt<T, NUM_BITS>>;
+}
+
+/// Any value can be attempted to be converted into a bounded integer of any size.
+impl<T, U, const NUM_BITS: u32> TryIntoBounded<T, NUM_BITS> for U
+where
+ T: Boundable<NUM_BITS>,
+ U: TryInto<T>,
+{
+ fn try_into(self) -> Result<BoundedInt<T, NUM_BITS>> {
+ self.try_into()
+ .map_err(|_| EOVERFLOW)
+ .and_then(BoundedInt::try_new)
+ }
+}
+
+/// `BoundedInts` can be compared if their respective storage types can be.
+impl<T, U, const NUM_BITS: u32, const NUM_BITS_U: u32> PartialEq<BoundedInt<U, NUM_BITS_U>>
+ for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ U: Boundable<NUM_BITS_U>,
+ T: PartialEq<U>,
+{
+ fn eq(&self, other: &BoundedInt<U, NUM_BITS_U>) -> bool {
+ self.get() == other.get()
+ }
+}
+
+impl<T, const NUM_BITS: u32> Eq for BoundedInt<T, NUM_BITS> where T: Boundable<NUM_BITS> {}
+
+/// `BoundedInts` can be ordered if their respective storage types can be.
+impl<T, U, const NUM_BITS: u32, const NUM_BITS_U: u32> PartialOrd<BoundedInt<U, NUM_BITS_U>>
+ for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ U: Boundable<NUM_BITS_U>,
+ T: PartialOrd<U>,
+{
+ fn partial_cmp(&self, other: &BoundedInt<U, NUM_BITS_U>) -> Option<core::cmp::Ordering> {
+ self.get().partial_cmp(&other.get())
+ }
+}
+
+impl<T, const NUM_BITS: u32> Ord for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: Ord,
+{
+ fn cmp(&self, other: &Self) -> core::cmp::Ordering {
+ self.get().cmp(&other.get())
+ }
+}
+
+/// Allow comparison with non-bounded values.
+impl<T, const NUM_BITS: u32> PartialEq<T> for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: PartialEq,
+{
+ fn eq(&self, other: &T) -> bool {
+ self.get() == *other
+ }
+}
+
+/// Allow ordering with non-bounded values.
+impl<T, const NUM_BITS: u32> PartialOrd<T> for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: PartialOrd,
+{
+ fn partial_cmp(&self, other: &T) -> Option<core::cmp::Ordering> {
+ self.get().partial_cmp(other)
+ }
+}
+
+impl<T, const NUM_BITS: u32> core::fmt::Display for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: core::fmt::Display,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T, const NUM_BITS: u32> core::fmt::LowerHex for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: core::fmt::LowerHex,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ self.0.fmt(f)
+ }
+}
+
+impl<T, const NUM_BITS: u32> core::fmt::UpperHex for BoundedInt<T, NUM_BITS>
+where
+ T: Boundable<NUM_BITS>,
+ T: core::fmt::UpperHex,
+{
+ fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
+ self.0.fmt(f)
+ }
+}
--
2.51.0
next prev parent reply other threads:[~2025-10-09 12:37 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-10-09 12:37 [RFC PATCH v2 0/3] rust: bounded integer types and use in register macro Alexandre Courbot
2025-10-09 12:37 ` [PATCH RFC v2 1/3] gpu: nova-core: register: use field type for Into implementation Alexandre Courbot
2025-10-09 15:17 ` Joel Fernandes
2025-10-09 15:41 ` Joel Fernandes
2025-10-10 8:24 ` Alexandre Courbot
2025-10-10 8:26 ` Alexandre Courbot
2025-10-10 14:59 ` Joel Fernandes
2025-10-09 20:10 ` Edwin Peer
2025-10-16 14:51 ` Joel Fernandes
2025-10-09 12:37 ` Alexandre Courbot [this message]
2025-10-09 21:33 ` [PATCH RFC v2 2/3] rust: kernel: add bounded integer types Joel Fernandes
2025-10-10 8:49 ` Alexandre Courbot
2025-10-10 15:23 ` Joel Fernandes
2025-10-11 10:33 ` Alice Ryhl
2025-10-09 12:37 ` [PATCH RFC v2 3/3] gpu: nova-core: use BoundedInt Alexandre Courbot
2025-10-09 16:40 ` Yury Norov
2025-10-09 17:18 ` Danilo Krummrich
2025-10-09 18:28 ` Yury Norov
2025-10-09 18:37 ` Joel Fernandes
2025-10-09 19:19 ` Miguel Ojeda
2025-10-09 19:34 ` Danilo Krummrich
2025-10-09 18:29 ` Joel Fernandes
2025-10-10 9:19 ` Alexandre Courbot
2025-10-10 17:20 ` Yury Norov
2025-10-10 17:58 ` Danilo Krummrich
2025-10-13 13:58 ` Joel Fernandes
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20251009-bounded_ints-v2-2-ff3d7fee3ffd@nvidia.com \
--to=acourbot@nvidia.com \
--cc=a.hindborg@kernel.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=dakr@kernel.org \
--cc=gary@garyguo.net \
--cc=joelagnelf@nvidia.com \
--cc=linux-kernel@vger.kernel.org \
--cc=lossin@kernel.org \
--cc=nouveau@lists.freedesktop.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tmgross@umich.edu \
--cc=y.j3ms.n@gmail.com \
--cc=yury.norov@gmail.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.