From: Mitchell Levy <levymitchell0@gmail.com>
To: Yury Norov <yury.norov@gmail.com>
Cc: "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>,
"Andreas Hindborg" <a.hindborg@kernel.org>,
"Alice Ryhl" <aliceryhl@google.com>,
"Trevor Gross" <tmgross@umich.edu>,
"Andrew Morton" <akpm@linux-foundation.org>,
"Dennis Zhou" <dennis@kernel.org>, "Tejun Heo" <tj@kernel.org>,
"Christoph Lameter" <cl@linux.com>,
"Danilo Krummrich" <dakr@kernel.org>,
"Benno Lossin" <lossin@kernel.org>,
"Viresh Kumar" <viresh.kumar@linaro.org>,
"Tyler Hicks" <code@tyhicks.com>,
linux-kernel@vger.kernel.org, rust-for-linux@vger.kernel.org,
linux-mm@kvack.org
Subject: Re: [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables
Date: Thu, 4 Sep 2025 12:53:59 -0700 [thread overview]
Message-ID: <68b9ee59.170a0220.a7a31.675c@mx.google.com> (raw)
In-Reply-To: <aLi2MBAwoD65tokv@yury>
On Wed, Sep 03, 2025 at 05:42:08PM -0400, Yury Norov wrote:
> On Thu, Aug 28, 2025 at 12:00:08PM -0700, Mitchell Levy wrote:
> > Per-CPU variables are an important tool for reducing lock contention,
> > especially in systems with many processors. They also provide a
> > convenient way to handle data that are logically associated with a
> > particular CPU (e.g., the currently running task). Therefore, add a Rust
> > API to make use of per-CPU variables.
> >
> > Add a `CpuGuard` type that disables preemption for its lifetime. Add a
> > `PerCpuAllocation` type used to track dynamic allocations. Add a
> > `define_per_cpu!` macro to create static per-CPU allocations. Add
> > `DynamicPerCpu` and `StaticPerCpu` to provide a high-level API. Add a
> > `PerCpu` trait to unify the dynamic and static cases.
> >
> > Co-developed-by: Boqun Feng <boqun.feng@gmail.com>
> > Signed-off-by: Boqun Feng <boqun.feng@gmail.com>
> > Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
> > ---
> > rust/helpers/helpers.c | 2 +
> > rust/helpers/percpu.c | 9 ++
> > rust/helpers/preempt.c | 14 +++
> > rust/kernel/lib.rs | 3 +
> > rust/kernel/percpu.rs | 223 ++++++++++++++++++++++++++++++++++++++++
> > rust/kernel/percpu/cpu_guard.rs | 35 +++++++
> > rust/kernel/percpu/dynamic.rs | 83 +++++++++++++++
> > rust/kernel/percpu/static_.rs | 132 ++++++++++++++++++++++++
> > 8 files changed, 501 insertions(+)
>
> That's quite a massive patch. Can you please split-out C-binders, and
> maybe make separate patches for each .rs component?
Sure, will do so for v4.
> > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
> > index 7cf7fe95e41d..2fc8d26cfe66 100644
[...]
> > --- /dev/null
> > +++ b/rust/kernel/percpu.rs
> > @@ -0,0 +1,223 @@
> > +// SPDX-License-Identifier: GPL-2.0
> > +//! This module contains abstractions for creating and using per-CPU variables from Rust.
> > +//! See the define_per_cpu! macro and the DynamicPerCpu<T> type, as well as the PerCpu<T> trait.
> > +
> > +pub mod cpu_guard;
> > +mod dynamic;
> > +mod static_;
> > +
> > +#[doc(inline)]
> > +pub use dynamic::*;
> > +#[doc(inline)]
> > +pub use static_::*;
> > +
> > +use bindings::{alloc_percpu, free_percpu};
> > +
> > +use crate::alloc::Flags;
> > +use crate::percpu::cpu_guard::CpuGuard;
> > +use crate::prelude::*;
> > +use crate::sync::Arc;
> > +use crate::types::Opaque;
> > +use crate::{declare_extern_per_cpu, get_static_per_cpu};
> > +
> > +use core::arch::asm;
> > +use core::cell::{Cell, RefCell, UnsafeCell};
> > +use core::mem::{align_of, size_of, MaybeUninit};
> > +
> > +use ffi::c_void;
> > +
> > +/// A per-CPU pointer; that is, an offset into the per-CPU area. Note that this type is NOT a smart
> > +/// pointer, it does not manage the allocation.
> > +pub struct PerCpuPtr<T>(*mut MaybeUninit<T>);
> > +
> > +/// Represents exclusive access to the memory location pointed at by a particular PerCpu<T>.
> > +pub struct PerCpuToken<'a, T> {
> > + // INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at
> > + // by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized.
> > + _guard: CpuGuard,
> > + ptr: &'a PerCpuPtr<T>,
> > +}
> > +
> > +/// Represents access to the memory location pointed at by a particular PerCpu<T> where the type
> > +/// `T` manages access to the underlying memory to avoid aliaising troubles. (For example, `T`
> > +/// might be a `Cell` or `RefCell`.)
> > +pub struct CheckedPerCpuToken<'a, T> {
> > + // INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at
> > + // by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized.
> > + _guard: CpuGuard,
> > + ptr: &'a PerCpuPtr<T>,
> > +}
> > +
> > +impl<T> PerCpuPtr<T> {
> > + /// Makes a new PerCpuPtr from a raw per-CPU pointer.
> > + ///
> > + /// # Safety
> > + /// `ptr` must be a valid per-CPU pointer.
> > + pub unsafe fn new(ptr: *mut MaybeUninit<T>) -> Self {
> > + Self(ptr)
> > + }
> > +
> > + /// Get a `&mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self`
> > + ///
> > + /// # Safety
> > + /// The returned `&mut T` must follow Rust's aliasing rules. That is, no other `&(mut) T` may
> > + /// exist that points to the same location in memory. In practice, this means that `get_(mut_)ref`
>
> How long is this line?
102 chars, or 103 if you include the newline. `rustfmt` doesn't break
the line, so I left it as-is for this patch. Happy to change it if it
poses a problem, though.
> > + /// must not be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as
> > + /// the returned reference lives.
> > + ///
> > + /// CPU preemption must be disabled before calling this function and for the lifetime of the
> > + /// returned reference. Otherwise, the returned reference might end up being a reference to a
> > + /// different CPU's per-CPU area, causing the potential for a data race.
> > + #[allow(clippy::mut_from_ref)] // Safety requirements prevent aliasing issues
> > + pub unsafe fn get_mut_ref(&self) -> &mut MaybeUninit<T> {
> > + // SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract,
> > + // and the safety requirements of this function ensure that the returned reference is
> > + // exclusive.
> > + unsafe { &mut *(self.get_ptr()) }
> > + }
>
> Here and everywhere: would it make sense to enforce it by testing
> the CPU with preemptible() before returning a reference?
The only thing we could do would be to panic, which I don't 100% love.
Another alternative would be to take a &'a CpuGuard and bound the
lifetime of the returned reference to 'a, and then we don't need to do
any run-time checking at all.
Originally, I had left this to the caller because it might make sense
down the line for some complex behavior based on per-CPU (e.g., per-CPU
refcount) to do all its own management of per-CPU variables using
`PerCpuPtr` as a core primitive. In these cases, I believe there are
some times where being non-preemptible wouldn't actually be required
(that said, my thoughts on this aren't well reflected in the safety
comment, since I said it must be disabled... gah). But, the more I think
about it, the more I think these use cases would be better served by
just using `get_ptr` --- conjuring `&mut` references seems like it would
be a big footgun. And the safety comment already actually reflects these
thoughts somewhat :)
For v4 I will probably have this function take a &'a CpuGuard and use
that to bound the liftetime of the returned reference, unless there are
other thoughts on this point.
Thanks,
Mitchell
> Thanks,
> Yury
next prev parent reply other threads:[~2025-09-04 19:54 UTC|newest]
Thread overview: 26+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-08-28 19:00 [PATCH v3 0/7] rust: Add Per-CPU Variable API Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
2025-09-03 21:42 ` Yury Norov
2025-09-04 19:53 ` Mitchell Levy [this message]
2025-09-04 20:27 ` Yury Norov
2025-09-04 21:17 ` Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 2/7] rust: percpu: add a rust per-CPU variable sample Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 3/7] rust: cpumask: Add a `Cpumask` iterator Mitchell Levy
2025-08-29 5:19 ` Viresh Kumar
2025-08-28 19:00 ` [PATCH v3 4/7] rust: cpumask: Add getters for globally defined cpumasks Mitchell Levy
2025-08-29 5:20 ` Viresh Kumar
2025-09-03 22:03 ` Yury Norov
2025-09-04 19:55 ` Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu Mitchell Levy
2025-09-03 22:19 ` Yury Norov
2025-09-04 20:26 ` Mitchell Levy
2025-09-04 20:37 ` Yury Norov
2025-09-04 21:05 ` Mitchell Levy
2025-09-04 21:46 ` Yury Norov
2025-09-04 21:57 ` Miguel Ojeda
2025-09-03 23:05 ` Miguel Ojeda
2025-09-04 20:17 ` Mitchell Levy
2025-09-04 20:37 ` Miguel Ojeda
2025-09-04 21:50 ` Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 6/7] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
2025-08-28 19:00 ` [PATCH v3 7/7] rust: percpu: cache per-CPU pointers in the dynamic case Mitchell Levy
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=68b9ee59.170a0220.a7a31.675c@mx.google.com \
--to=levymitchell0@gmail.com \
--cc=a.hindborg@kernel.org \
--cc=akpm@linux-foundation.org \
--cc=alex.gaynor@gmail.com \
--cc=aliceryhl@google.com \
--cc=bjorn3_gh@protonmail.com \
--cc=boqun.feng@gmail.com \
--cc=cl@linux.com \
--cc=code@tyhicks.com \
--cc=dakr@kernel.org \
--cc=dennis@kernel.org \
--cc=gary@garyguo.net \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=lossin@kernel.org \
--cc=ojeda@kernel.org \
--cc=rust-for-linux@vger.kernel.org \
--cc=tj@kernel.org \
--cc=tmgross@umich.edu \
--cc=viresh.kumar@linaro.org \
--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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®