* [PATCH V3 1/2] rust: Add initial cpumask abstractions
2025-03-18 11:51 [PATCH V3 0/2] Rust: Add cpumask abstractions Viresh Kumar
@ 2025-03-18 11:51 ` Viresh Kumar
2025-03-18 11:51 ` [PATCH V3 2/2] MAINTAINERS: Add entry for Rust bitmap API Viresh Kumar
1 sibling, 0 replies; 5+ messages in thread
From: Viresh Kumar @ 2025-03-18 11:51 UTC (permalink / raw)
To: Yury Norov, Rasmus Villemoes, Viresh Kumar, Miguel Ojeda,
Alex Gaynor, Boqun Feng, Gary Guo, Björn Roy Baron,
Benno Lossin, Andreas Hindborg, Alice Ryhl, Trevor Gross
Cc: linux-kernel, Danilo Krummrich, rust-for-linux, Vincent Guittot,
Burak Emir
Add initial Rust abstractions for struct cpumask, covering a subset of
its APIs. Additional APIs can be added as needed.
These abstractions will be used in upcoming Rust support for cpufreq and
OPP frameworks.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
---
rust/kernel/cpumask.rs | 301 +++++++++++++++++++++++++++++++++++++++++
rust/kernel/lib.rs | 1 +
2 files changed, 302 insertions(+)
create mode 100644 rust/kernel/cpumask.rs
diff --git a/rust/kernel/cpumask.rs b/rust/kernel/cpumask.rs
new file mode 100644
index 000000000000..792210a77770
--- /dev/null
+++ b/rust/kernel/cpumask.rs
@@ -0,0 +1,301 @@
+// SPDX-License-Identifier: GPL-2.0
+
+//! CPU Mask abstractions.
+//!
+//! C header: [`include/linux/cpumask.h`](srctree/include/linux/cpumask.h)
+
+use crate::{
+ alloc::{AllocError, Flags},
+ bindings,
+ prelude::*,
+ types::Opaque,
+};
+
+#[cfg(CONFIG_CPUMASK_OFFSTACK)]
+use core::ptr::{self, NonNull};
+
+#[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+use core::mem::MaybeUninit;
+
+use core::ops::{Deref, DerefMut};
+
+/// A CPU Mask.
+///
+/// This represents the Rust abstraction for the C `struct cpumask`.
+///
+/// # Invariants
+///
+/// A [`Cpumask`] instance always corresponds to a valid C `struct cpumask`.
+///
+/// The callers must ensure that the `struct cpumask` is valid for access and remains valid for the
+/// lifetime of the returned reference.
+///
+/// ## Examples
+///
+/// The following example demonstrates how to update a [`Cpumask`].
+///
+/// ```
+/// use kernel::bindings;
+/// use kernel::cpumask::Cpumask;
+///
+/// fn set_clear_cpu(ptr: *mut bindings::cpumask, set_cpu: u32, clear_cpu: i32) {
+/// // SAFETY: The `ptr` is valid for writing and remains valid for the lifetime of the
+/// // returned reference.
+/// let mask = unsafe { Cpumask::from_raw_mut(ptr) };
+/// mask.set(set_cpu);
+/// mask.clear(clear_cpu);
+/// }
+/// ```
+#[repr(transparent)]
+pub struct Cpumask(Opaque<bindings::cpumask>);
+
+impl Cpumask {
+ /// Creates a mutable reference to an existing `struct cpumask` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime
+ /// of the returned reference.
+ pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpumask) -> &'a mut Self {
+ // SAFETY: Guaranteed by the safety requirements of the function.
+ //
+ // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the
+ // lifetime of the returned reference.
+ unsafe { &mut *ptr.cast() }
+ }
+
+ /// Creates a reference to an existing `struct cpumask` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime
+ /// of the returned reference.
+ pub unsafe fn from_raw<'a>(ptr: *const bindings::cpumask) -> &'a Self {
+ // SAFETY: Guaranteed by the safety requirements of the function.
+ //
+ // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the
+ // lifetime of the returned reference.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Obtain the raw `struct cpumask` pointer.
+ pub fn as_raw(&self) -> *mut bindings::cpumask {
+ self as *const Cpumask as *mut bindings::cpumask
+ }
+
+ /// Set `cpu` in the cpumask.
+ ///
+ /// Equivalent to the kernel's `cpumask_set_cpu` API.
+ #[inline]
+ pub fn set(&mut self, cpu: u32) {
+ // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_set_cpus`.
+ unsafe { bindings::cpumask_set_cpu(cpu, self.as_raw()) };
+ }
+
+ /// Clear `cpu` in the cpumask.
+ ///
+ /// Equivalent to the kernel's `cpumask_clear_cpu` API.
+ #[inline]
+ pub fn clear(&mut self, cpu: i32) {
+ // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_clear_cpu`.
+ unsafe { bindings::cpumask_clear_cpu(cpu, self.as_raw()) };
+ }
+
+ /// Set all CPUs in the cpumask.
+ ///
+ /// Equivalent to the kernel's `cpumask_setall` API.
+ #[inline]
+ pub fn set_all(&mut self) {
+ // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_setall`.
+ unsafe { bindings::cpumask_setall(self.as_raw()) };
+ }
+
+ /// Get weight of the cpumask.
+ ///
+ /// Equivalent to the kernel's `cpumask_weight` API.
+ #[inline]
+ pub fn weight(&self) -> u32 {
+ // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_weight`.
+ unsafe { bindings::cpumask_weight(self.as_raw()) }
+ }
+
+ /// Copy cpumask.
+ ///
+ /// Equivalent to the kernel's `cpumask_copy` API.
+ #[inline]
+ pub fn copy(&self, dstp: &mut Self) {
+ // SAFETY: By the type invariant, `Self::as_raw` is a valid argument to `cpumask_copy`.
+ unsafe { bindings::cpumask_copy(dstp.as_raw(), self.as_raw()) };
+ }
+}
+
+/// A CPU Mask pointer.
+///
+/// This represents the Rust abstraction for the C `struct cpumask_var_t`.
+///
+/// # Invariants
+///
+/// A [`CpumaskBox`] instance always corresponds to a valid C `struct cpumask_var_t`.
+///
+/// The callers must ensure that the `struct cpumask_var_t` is valid for access and remains valid
+/// for the lifetime of [`CpumaskBox`].
+///
+/// ## Examples
+///
+/// The following example demonstrates how to create and update a [`CpumaskBox`].
+///
+/// ```
+/// use kernel::cpumask::CpumaskBox;
+/// use kernel::error::Result;
+///
+/// fn cpumask_foo() -> Result {
+/// let mut mask = CpumaskBox::new(GFP_KERNEL)?;
+///
+/// assert_eq!(mask.weight(), 0);
+/// mask.set(2);
+/// assert_eq!(mask.weight(), 1);
+/// mask.set(3);
+/// assert_eq!(mask.weight(), 2);
+///
+/// let mask2 = CpumaskBox::try_clone(&mask)?;
+/// assert_eq!(mask2.weight(), 2);
+///
+/// Ok(())
+/// }
+/// ```
+pub struct CpumaskBox {
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ ptr: NonNull<Cpumask>,
+ #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+ mask: Cpumask,
+}
+
+impl CpumaskBox {
+ /// Creates an initialized instance of the [`CpumaskBox`].
+ pub fn new(_flags: Flags) -> Result<Self, AllocError> {
+ Ok(Self {
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ ptr: {
+ let mut ptr: *mut bindings::cpumask = ptr::null_mut();
+
+ // SAFETY: Depending on the value of `_flags`, this call may sleep. Other than
+ // that, it is always safe to call this method.
+ //
+ // INVARIANT: The associated memory is freed when the `CpumaskBox` goes out of
+ // scope.
+ unsafe { bindings::zalloc_cpumask_var(&mut ptr, _flags.as_raw()) };
+ NonNull::new(ptr.cast()).ok_or(AllocError)?
+ },
+
+ #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+ // SAFETY: FFI type is valid to be zero-initialized.
+ //
+ // INVARIANT: The associated memory is freed when the `CpumaskBox` goes out of scope.
+ mask: unsafe { core::mem::zeroed() },
+ })
+ }
+
+ /// Creates an uninitialized instance of the [`CpumaskBox`].
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that the returned [`CpumaskBox`] is properly initialized before
+ /// getting used.
+ unsafe fn new_uninit(_flags: Flags) -> Result<Self, AllocError> {
+ Ok(Self {
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ ptr: {
+ let mut ptr: *mut bindings::cpumask = ptr::null_mut();
+
+ // SAFETY: Depending on the value of `_flags`, this call may sleep. Other than
+ // that, it is always safe to call this method.
+ //
+ // INVARIANT: The associated memory is freed when the `CpumaskBox` goes out of
+ // scope.
+ unsafe { bindings::alloc_cpumask_var(&mut ptr, _flags.as_raw()) };
+ NonNull::new(ptr.cast()).ok_or(AllocError)?
+ },
+ #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+ // SAFETY: Guaranteed by the safety requirements of the function.
+ //
+ // INVARIANT: The associated memory is freed when the `CpumaskBox` goes out of scope.
+ mask: unsafe { MaybeUninit::uninit().assume_init() },
+ })
+ }
+
+ /// Creates a mutable reference to an existing `struct cpumask_var_t` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid for writing and remains valid for the lifetime
+ /// of the returned reference.
+ pub unsafe fn from_raw_mut<'a>(ptr: *mut bindings::cpumask_var_t) -> &'a mut Self {
+ // SAFETY: Guaranteed by the safety requirements of the function.
+ //
+ // INVARIANT: The caller ensures that `ptr` is valid for writing and remains valid for the
+ // lifetime of the returned reference.
+ unsafe { &mut *ptr.cast() }
+ }
+
+ /// Creates a reference to an existing `struct cpumask_var_t` pointer.
+ ///
+ /// # Safety
+ ///
+ /// The caller must ensure that `ptr` is valid for reading and remains valid for the lifetime
+ /// of the returned reference.
+ pub unsafe fn from_raw<'a>(ptr: *const bindings::cpumask_var_t) -> &'a Self {
+ // SAFETY: Guaranteed by the safety requirements of the function.
+ //
+ // INVARIANT: The caller ensures that `ptr` is valid for reading and remains valid for the
+ // lifetime of the returned reference.
+ unsafe { &*ptr.cast() }
+ }
+
+ /// Clones cpumask.
+ pub fn try_clone(cpumask: &Cpumask) -> Result<Self> {
+ // SAFETY: The returned cpumask_box is initialized right after this call.
+ let mut cpumask_box = unsafe { Self::new_uninit(GFP_KERNEL) }?;
+
+ cpumask.copy(&mut cpumask_box);
+ Ok(cpumask_box)
+ }
+}
+
+// Make [`CpumaskBox`] behave like a pointer to [`Cpumask`].
+impl Deref for CpumaskBox {
+ type Target = Cpumask;
+
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ fn deref(&self) -> &Cpumask {
+ // SAFETY: The caller owns CpumaskBox, so it is safe to deref the cpumask.
+ unsafe { &*self.ptr.as_ptr() }
+ }
+
+ #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+ fn deref(&self) -> &Cpumask {
+ &self.mask
+ }
+}
+
+impl DerefMut for CpumaskBox {
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ fn deref_mut(&mut self) -> &mut Cpumask {
+ // SAFETY: The caller owns CpumaskBox, so it is safe to deref the cpumask.
+ unsafe { self.ptr.as_mut() }
+ }
+
+ #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
+ fn deref_mut(&mut self) -> &mut Cpumask {
+ &mut self.mask
+ }
+}
+
+impl Drop for CpumaskBox {
+ fn drop(&mut self) {
+ #[cfg(CONFIG_CPUMASK_OFFSTACK)]
+ // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `free_cpumask_var`.
+ unsafe {
+ bindings::free_cpumask_var(self.as_raw())
+ };
+ }
+}
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index 398242f92a96..dbed774ea9f7 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -40,6 +40,7 @@
pub mod block;
#[doc(hidden)]
pub mod build_assert;
+pub mod cpumask;
pub mod cred;
pub mod device;
pub mod device_id;
--
2.31.1.272.g89b43f80a514
^ permalink raw reply related [flat|nested] 5+ messages in thread
* [PATCH V3 2/2] MAINTAINERS: Add entry for Rust bitmap API
2025-03-18 11:51 [PATCH V3 0/2] Rust: Add cpumask abstractions Viresh Kumar
2025-03-18 11:51 ` [PATCH V3 1/2] rust: Add initial " Viresh Kumar
@ 2025-03-18 11:51 ` Viresh Kumar
2025-04-01 11:23 ` Viresh Kumar
1 sibling, 1 reply; 5+ messages in thread
From: Viresh Kumar @ 2025-03-18 11:51 UTC (permalink / raw)
To: Yury Norov, Rasmus Villemoes, Miguel Ojeda, Alex Gaynor,
Boqun Feng, Gary Guo, Björn Roy Baron, Benno Lossin,
Andreas Hindborg, Alice Ryhl, Trevor Gross
Cc: Viresh Kumar, linux-kernel, Danilo Krummrich, rust-for-linux,
Vincent Guittot, Burak Emir
Update the MAINTAINERS file to include the Rust abstractions for bitmap
API.
Yury has indicated that he does not wish to maintain the Rust code but
would like to be listed as a reviewer.
Signed-off-by: Viresh Kumar <viresh.kumar@linaro.org>
Reviewed-by: Yury Norov <yury.norov@gmail.com>
---
MAINTAINERS | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 315cff76df29..c55db52590cb 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -4026,6 +4026,12 @@ M: Yury Norov <yury.norov@gmail.com>
S: Maintained
F: rust/helpers/cpumask.c
+BITMAP API [RUST]
+M: Viresh Kumar <viresh.kumar@linaro.org> (cpumask)
+R: Yury Norov <yury.norov@gmail.com>
+S: Maintained
+F: rust/kernel/cpumask.rs
+
BITOPS API
M: Yury Norov <yury.norov@gmail.com>
R: Rasmus Villemoes <linux@rasmusvillemoes.dk>
--
2.31.1.272.g89b43f80a514
^ permalink raw reply related [flat|nested] 5+ messages in thread