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>,
"Erik Schilling" <erik.schilling@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 V9 03/17] rust: cpumask: Add initial abstractions
Date: Fri, 11 Apr 2025 11:54:06 -0400 [thread overview]
Message-ID: <Z_k7HtIZaSWeJvM4@yury> (raw)
In-Reply-To: <9a004e3dff5321dae3b96df2817799daa699ce01.1744366571.git.viresh.kumar@linaro.org>
On Fri, Apr 11, 2025 at 04:25:02PM +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>
> ---
> rust/kernel/cpumask.rs | 328 +++++++++++++++++++++++++++++++++++++++++
> rust/kernel/lib.rs | 1 +
> 2 files changed, 329 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..a9d22c1d7a5a
> --- /dev/null
> +++ b/rust/kernel/cpumask.rs
> @@ -0,0 +1,328 @@
> +// 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.
> +///
> +/// 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
This line is too long to me.
> +/// 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 _ as _
> + }
> +
> + /// 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_cpu`.
> + unsafe { bindings::__cpumask_set_cpu(cpu, self.as_raw()) };
> + }
> +
> + /// Clear `cpu` in the cpumask.
> + ///
> + /// Equivalent to the kernel's `__cpumask_clear_cpu` API.
Similarly to bitmaps, can you explain here that this is a non-atomic
operation?
> + #[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(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 an initialized instance of the [`CpumaskVar`].
> + 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 `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 uninitialized instance of the [`CpumaskVar`].
I would do this another way: introduce new() that calls
alloc_cpumask_var(), and new_zero() binded to zalloc() version. Your
statement here is simply wrong because I can pass GFP_ZERO and 'hack'
all your architecture.
> + ///
> + /// # Safety
> + ///
> + /// The caller must ensure that the returned [`CpumaskVar`] 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.
I'm not sure I understand this sentence. What's wrong with safety when
the alloc() function sleeps? Even if something is wrong. If you really
want to protect your users, you'd introduce new_sync() version that
returns error if user provides sleeping flags.
To that extend, once you write so many flavors of constructors, I bet
your users will be happy if you hide the 'flags' entirely:
new_gfp(flags);
new();
new_zero(); // or znew()?
new_sync();
> + //
> + // 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 from_raw_mut<'a>(ptr: *mut bindings::cpumask_var_t) -> &'a mut Self {
The 'from' (wrt cpumasks) has a special meaning: search for a cpu
starting from a given one. This 'from_raw' may confuse readers. Have
you any other name for it in mind?
> + // 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> {
Just clone(), I think.
> + // 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 [`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-04-11 15:54 UTC|newest]
Thread overview: 31+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-11 10:54 [PATCH V9 00/17] Rust abstractions for clk, cpumask, cpufreq, OPP Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 01/17] rust: cpumask: Use non-atomic helpers Viresh Kumar
2025-04-11 15:20 ` Yury Norov
2025-04-11 10:55 ` [PATCH V9 02/17] rust: cpumask: Add few more helpers Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 03/17] rust: cpumask: Add initial abstractions Viresh Kumar
2025-04-11 15:54 ` Yury Norov [this message]
2025-04-14 11:29 ` Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 04/17] MAINTAINERS: Add entry for Rust cpumask API Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 05/17] rust: clk: Add helpers for Rust code Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 06/17] rust: clk: Add initial abstractions Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 07/17] rust: macros: enable use of hyphens in module names Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 08/17] cpufreq: Use enum for cpufreq flags that use BIT() Viresh Kumar
2025-04-11 16:07 ` Yury Norov
2025-04-11 17:05 ` Miguel Ojeda
2025-04-11 10:55 ` [PATCH V9 09/17] rust: cpu: Add from_cpu() Viresh Kumar
2025-04-11 16:18 ` Yury Norov
2025-04-11 18:03 ` Danilo Krummrich
2025-04-11 10:55 ` [PATCH V9 10/17] rust: opp: Add initial abstractions for OPP framework Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 11/17] rust: opp: Add abstractions for the OPP table Viresh Kumar
2025-04-11 16:29 ` Yury Norov
2025-04-11 10:55 ` [PATCH V9 12/17] rust: opp: Add abstractions for the configuration options Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 13/17] rust: cpufreq: Add initial abstractions for cpufreq framework Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 14/17] rust: cpufreq: Extend abstractions for policy and driver ops Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 15/17] rust: cpufreq: Extend abstractions for driver registration Viresh Kumar
2025-04-11 11:58 ` Danilo Krummrich
2025-04-14 8:47 ` Viresh Kumar
2025-04-14 9:39 ` Danilo Krummrich
2025-04-14 10:52 ` Viresh Kumar
2025-04-14 11:51 ` Danilo Krummrich
2025-04-11 10:55 ` [PATCH V9 16/17] rust: opp: Extend OPP abstractions with cpufreq support Viresh Kumar
2025-04-11 10:55 ` [PATCH V9 17/17] cpufreq: Add Rust-based cpufreq-dt driver 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=Z_k7HtIZaSWeJvM4@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=erik.schilling@linaro.org \
--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.