From: Yury Norov <yury.norov@gmail.com>
To: Viresh Kumar <viresh.kumar@linaro.org>
Cc: "Rafael J. Wysocki" <rafael@kernel.org>,
"Miguel Ojeda" <miguel.ojeda.sandonis@gmail.com>,
"Danilo Krummrich" <dakr@redhat.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" <benno.lossin@proton.me>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Danilo Krummrich" <dakr@kernel.org>,
linux-pm@vger.kernel.org,
"Vincent Guittot" <vincent.guittot@linaro.org>,
"Stephen Boyd" <sboyd@kernel.org>, "Nishanth Menon" <nm@ti.com>,
rust-for-linux@vger.kernel.org,
"Manos Pitsidianakis" <manos.pitsidianakis@linaro.org>,
"Alex Bennée" <alex.bennee@linaro.org>,
"Joakim Bech" <joakim.bech@linaro.org>,
"Rob Herring" <robh@kernel.org>, "Burak Emir" <bqe@google.com>,
"Rasmus Villemoes" <linux@rasmusvillemoes.dk>,
"Russell King" <linux@armlinux.org.uk>,
linux-clk@vger.kernel.org,
"Michael Turquette" <mturquette@baylibre.com>,
linux-kernel@vger.kernel.org
Subject: Re: [PATCH V11 02/15] rust: cpumask: Add initial abstractions
Date: Fri, 2 May 2025 13:01:12 -0400 [thread overview]
Message-ID: <aBT6WK9vcX1oZGmT@yury> (raw)
In-Reply-To: <4a8f23fd447897cb76ccc46d03cc30a2086a0e90.1745218975.git.viresh.kumar@linaro.org>
On Mon, Apr 21, 2025 at 12:52:09PM +0530, Viresh Kumar wrote:
> 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>
Reviewed-by: Yury Norov [NVIDIA] <yury.norov@gmail.com>
> ---
> rust/kernel/cpumask.rs | 330 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 2 files changed, 331 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..c90bfac9346a
> --- /dev/null
> +++ b/rust/kernel/cpumask.rs
> @@ -0,0 +1,330 @@
> +// 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},
> + 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.
> +///
> +/// 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::as_mut_ref(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 as_mut_ref<'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 as_ref<'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 {
> + let this: *const Self = self;
> + this.cast_mut().cast()
> + }
> +
> + /// Set `cpu` in the cpumask.
> + ///
> + /// ATTENTION: Contrary to C, this Rust `set()` method is non-atomic.
> + /// This mismatches kernel naming convention and corresponds to the C
> + /// function `__cpumask_set_cpu()`.
> + #[inline]
> + pub fn set(&mut self, cpu: u32) {
> + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `__cpumask_set_cpu`.
> + unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) };
> + }
> +
> + /// Clear `cpu` in the cpumask.
> + ///
> + /// ATTENTION: Contrary to C, this Rust `clear()` method is non-atomic.
> + /// This mismatches kernel naming convention and corresponds to the C
> + /// function `__cpumask_clear_cpu()`.
> + #[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()) };
> + }
> +
> + /// Test `cpu` in the cpumask.
> + ///
> + /// Equivalent to the kernel's `cpumask_test_cpu` API.
> + #[inline]
> + pub fn test(&self, cpu: i32) -> bool {
> + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_test_cpu`.
> + unsafe { bindings::cpumask_test_cpu(cpu, self.as_raw()) }
> + }
> +
> + /// Set all CPUs in the cpumask.
> + ///
> + /// Equivalent to the kernel's `cpumask_setall` API.
> + #[inline]
> + pub fn setall(&mut self) {
> + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_setall`.
> + unsafe { bindings::cpumask_setall(self.as_raw()) };
> + }
> +
> + /// Checks if cpumask is empty.
> + ///
> + /// Equivalent to the kernel's `cpumask_empty` API.
> + #[inline]
> + pub fn empty(&self) -> bool {
> + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_empty`.
> + unsafe { bindings::cpumask_empty(self.as_raw()) }
> + }
> +
> + /// Checks if cpumask is full.
> + ///
> + /// Equivalent to the kernel's `cpumask_full` API.
> + #[inline]
> + pub fn full(&self) -> bool {
> + // SAFETY: By the type invariant, `self.as_raw` is a valid argument to `cpumask_full`.
> + unsafe { bindings::cpumask_full(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.
> +///
> +/// Rust abstraction for the C `struct cpumask_var_t`.
> +///
> +/// # Invariants
> +///
> +/// A [`CpumaskVar`] 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 [`CpumaskVar`].
> +///
> +/// ## Examples
> +///
> +/// The following example demonstrates how to create and update a [`CpumaskVar`].
> +///
> +/// ```
> +/// use kernel::cpumask::CpumaskVar;
> +///
> +/// let mut mask = CpumaskVar::new_zero(GFP_KERNEL).unwrap();
> +///
> +/// assert!(mask.empty());
> +/// mask.set(2);
> +/// assert!(mask.test(2));
> +/// mask.set(3);
> +/// assert!(mask.test(3));
> +/// assert_eq!(mask.weight(), 2);
> +///
> +/// let mask2 = CpumaskVar::try_clone(&mask).unwrap();
> +/// assert!(mask2.test(2));
> +/// assert!(mask2.test(3));
> +/// assert_eq!(mask2.weight(), 2);
> +/// ```
> +pub struct CpumaskVar {
> + #[cfg(CONFIG_CPUMASK_OFFSTACK)]
> + ptr: NonNull<Cpumask>,
> + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
> + mask: Cpumask,
> +}
> +
> +impl CpumaskVar {
> + /// Creates a zero-initialized instance of the [`CpumaskVar`].
> + pub fn new_zero(_flags: Flags) -> Result<Self, AllocError> {
> + Ok(Self {
> + #[cfg(CONFIG_CPUMASK_OFFSTACK)]
> + ptr: {
> + let mut ptr: *mut bindings::cpumask = ptr::null_mut();
> +
> + // SAFETY: It is safe to call this method as the reference to `ptr` is valid.
> + //
> + // INVARIANT: The associated memory is freed when the `CpumaskVar` 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 `CpumaskVar` goes out of scope.
> + mask: unsafe { core::mem::zeroed() },
> + })
> + }
> +
> + /// Creates an instance of the [`CpumaskVar`].
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that the returned [`CpumaskVar`] is properly initialized before
> + /// getting used.
> + pub unsafe fn new(_flags: Flags) -> Result<Self, AllocError> {
> + Ok(Self {
> + #[cfg(CONFIG_CPUMASK_OFFSTACK)]
> + ptr: {
> + let mut ptr: *mut bindings::cpumask = ptr::null_mut();
> +
> + // SAFETY: It is safe to call this method as the reference to `ptr` is valid.
> + //
> + // INVARIANT: The associated memory is freed when the `CpumaskVar` 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 `CpumaskVar` 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 as_mut_ref<'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 as_ref<'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_var is initialized right after this call.
> + let mut cpumask_var = unsafe { Self::new(GFP_KERNEL) }?;
> +
> + cpumask.copy(&mut cpumask_var);
> + Ok(cpumask_var)
> + }
> +}
> +
> +// Make [`CpumaskVar`] behave like a pointer to [`Cpumask`].
> +impl Deref for CpumaskVar {
> + type Target = Cpumask;
> +
> + #[cfg(CONFIG_CPUMASK_OFFSTACK)]
> + fn deref(&self) -> &Self::Target {
> + // SAFETY: The caller owns CpumaskVar, so it is safe to deref the cpumask.
> + unsafe { &*self.ptr.as_ptr() }
> + }
> +
> + #[cfg(not(CONFIG_CPUMASK_OFFSTACK))]
> + fn deref(&self) -> &Self::Target {
> + &self.mask
> + }
> +}
> +
> +impl DerefMut for CpumaskVar {
> + #[cfg(CONFIG_CPUMASK_OFFSTACK)]
> + fn deref_mut(&mut self) -> &mut Cpumask {
> + // SAFETY: The caller owns CpumaskVar, 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 CpumaskVar {
> + 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 de07aadd1ff5..75f78f6bfaa6 100644
> --- a/rust/kernel/lib.rs
> +++ b/rust/kernel/lib.rs
> @@ -42,6 +42,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
next prev parent reply other threads:[~2025-05-02 17:01 UTC|newest]
Thread overview: 33+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-21 7:22 [PATCH V11 00/15] Rust abstractions for clk, cpumask, cpufreq, OPP Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 01/15] rust: cpumask: Add few more helpers Viresh Kumar
2025-05-02 16:56 ` Yury Norov
2025-04-21 7:22 ` [PATCH V11 02/15] rust: cpumask: Add initial abstractions Viresh Kumar
2025-05-02 17:01 ` Yury Norov [this message]
2025-04-21 7:22 ` [PATCH V11 03/15] MAINTAINERS: Add entry for Rust cpumask API Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 04/15] rust: clk: Add helpers for Rust code Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 05/15] rust: clk: Add initial abstractions Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 06/15] rust: macros: enable use of hyphens in module names Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 07/15] rust: cpu: Add from_cpu() Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 08/15] rust: opp: Add initial abstractions for OPP framework Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 09/15] rust: opp: Add abstractions for the OPP table Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 10/15] rust: opp: Add abstractions for the configuration options Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 11/15] rust: cpufreq: Add initial abstractions for cpufreq framework Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 12/15] rust: cpufreq: Extend abstractions for policy and driver ops Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 13/15] rust: cpufreq: Extend abstractions for driver registration Viresh Kumar
2025-04-23 12:05 ` Danilo Krummrich
2025-04-24 6:01 ` Viresh Kumar
2025-04-23 12:08 ` Danilo Krummrich
2025-04-24 6:29 ` Viresh Kumar
2025-04-24 8:03 ` Danilo Krummrich
2025-04-24 10:51 ` Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 14/15] rust: opp: Extend OPP abstractions with cpufreq support Viresh Kumar
2025-04-21 7:22 ` [PATCH V11 15/15] cpufreq: Add Rust-based cpufreq-dt driver Viresh Kumar
2025-05-02 7:01 ` [PATCH V11 00/15] Rust abstractions for clk, cpumask, cpufreq, OPP Viresh Kumar
2025-05-06 10:13 ` Andrew Ballance
2025-05-06 10:25 ` Miguel Ojeda
2025-05-08 2:10 ` Viresh Kumar
2025-05-08 2:52 ` Philip Li
2025-05-19 5:12 ` Viresh Kumar
2025-05-09 8:43 ` Miguel Ojeda
2025-05-13 11:15 ` Viresh Kumar
2025-05-19 5:00 ` Viresh Kumar
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=aBT6WK9vcX1oZGmT@yury \
--to=yury.norov@gmail.com \
--cc=a.hindborg@kernel.org \
--cc=alex.bennee@linaro.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=benno.lossin@proton.me \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=bqe@google.com \
--cc=dakr@kernel.org \
--cc=dakr@redhat.com \
--cc=gary@garyguo.net \
--cc=joakim.bech@linaro.org \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pm@vger.kernel.org \
--cc=linux@armlinux.org.uk \
--cc=linux@rasmusvillemoes.dk \
--cc=manos.pitsidianakis@linaro.org \
--cc=miguel.ojeda.sandonis@gmail.com \
--cc=mturquette@baylibre.com \
--cc=nm@ti.com \
--cc=ojeda@kernel.org \
--cc=rafael@kernel.org \
--cc=robh@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=sboyd@kernel.org \
--cc=tmgross@umich.edu \
--cc=vincent.guittot@linaro.org \
--cc=viresh.kumar@linaro.org \
/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.