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(+)
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 7cf7fe95e41d..2fc8d26cfe66 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -31,9 +31,11 @@
#include "of.c"
#include "page.c"
#include "pci.c"
+#include "percpu.c"
#include "pid_namespace.c"
#include "platform.c"
#include "poll.c"
+#include "preempt.c"
#include "property.c"
#include "rbtree.c"
#include "rcu.c"
diff --git a/rust/helpers/percpu.c b/rust/helpers/percpu.c
new file mode 100644
index 000000000000..a091389f730f
--- /dev/null
+++ b/rust/helpers/percpu.c
@@ -0,0 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/percpu.h>
+
+void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align)
+{
+ return __alloc_percpu(sz, align);
+}
+
diff --git a/rust/helpers/preempt.c b/rust/helpers/preempt.c
new file mode 100644
index 000000000000..2c7529528ddd
--- /dev/null
+++ b/rust/helpers/preempt.c
@@ -0,0 +1,14 @@
+// SPDX-License-Identifier: GPL-2.0
+
+#include <linux/preempt.h>
+
+void rust_helper_preempt_disable(void)
+{
+ preempt_disable();
+}
+
+void rust_helper_preempt_enable(void)
+{
+ preempt_enable();
+}
+
diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs
index ed53169e795c..ed0d5756dc55 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -106,6 +106,9 @@
pub mod page;
#[cfg(CONFIG_PCI)]
pub mod pci;
+// Only x86_64 is supported by percpu for now
+#[cfg(CONFIG_X86_64)]
+pub mod percpu;
pub mod pid_namespace;
pub mod platform;
pub mod prelude;
diff --git a/rust/kernel/percpu.rs b/rust/kernel/percpu.rs
new file mode 100644
index 000000000000..35afcdba3ccd
--- /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`
+ /// 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()) }
+ }
+
+ /// Get a `&MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self`
+ ///
+ /// # Safety
+ /// The returned `&T` must follow Rust's aliasing rules. That is, no `&mut T` may exist that
+ /// points to the same location in memory. In practice, this means that `get_mut_ref` 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.
+ pub unsafe fn get_ref(&self) -> &MaybeUninit<T> {
+ // SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract.
+ // The safety requirements of this function ensure that the returned reference isn't
+ // aliased by a `&mut MaybeUninit<T>`.
+ unsafe { &*self.get_ptr() }
+ }
+
+ /// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by
+ /// `&self`. Note that if CPU preemption is not disabled before calling this function, use of
+ /// the returned pointer may cause a data race without some other synchronization mechanism.
+ /// Buyer beware!
+ pub fn get_ptr(&self) -> *mut MaybeUninit<T> {
+ let this_cpu_off_pcpu: PerCpuPtr<*mut c_void> = get_static_per_cpu!(this_cpu_off).0;
+ let mut this_cpu_area: *mut c_void;
+ // SAFETY: gs + this_cpu_off_pcpu is guaranteed to be a valid pointer because `gs` points
+ // to the per-CPU area and this_cpu_off_pcpu is a valid per-CPU allocation.
+ unsafe {
+ asm!(
+ "mov {out}, gs:[{off_val}]",
+ off_val = in(reg) this_cpu_off_pcpu.0,
+ out = out(reg) this_cpu_area,
+ )
+ };
+
+ // This_cpu_area + self.0 is guaranteed to be a valid pointer by the per-CPU subsystem and
+ // the invariant that self.0 is a valid offset into the per-CPU area.
+ (this_cpu_area).wrapping_add(self.0 as usize).cast()
+ }
+}
+
+// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the
+// pointer is logically referring to a different place in memory in the other CPU's per-CPU area.
+// In particular, this means that there are no restrictions on the type `T`.
+unsafe impl<T> Send for PerCpuPtr<T> {}
+
+// SAFETY: Two threads concurrently making use of a `PerCpuPtr<T>` will each see the `T` in their
+// own per-CPU area, so there's no potential for a data race (regardless of whether `T` is itself
+// `Sync`).
+unsafe impl<T> Sync for PerCpuPtr<T> {}
+
+impl<T> Clone for PerCpuPtr<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+/// `PerCpuPtr` is just a wrapper around a pointer.
+impl<T> Copy for PerCpuPtr<T> {}
+
+/// A trait representing a per-CPU variable. This is implemented for both `StaticPerCpu<T>` and
+/// `DynamicPerCpu<T>`. The main usage of this trait is to call `get_mut` to get a `PerCpuToken`
+/// that can be used to access the underlying per-CPU variable. See `PerCpuToken::with`.
+pub trait PerCpu<T> {
+ /// Produces a token, asserting that the holder has exclusive access to the underlying memory
+ /// pointed to by `self`
+ ///
+ /// # Safety
+ /// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a
+ /// sensible notion, since we keep a `CpuGuard` around) that is derived from the same
+ /// `PerCpu<T>` or a clone thereof.
+ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T>;
+}
+
+/// A marker trait for types that are interior mutable. Types that implement this trait can be used
+/// to create "checked" per-CPU variables. See `CheckedPerCpu<T>`.
+pub trait InteriorMutable {}
+
+impl<T> InteriorMutable for Cell<T> {}
+impl<T> InteriorMutable for RefCell<T> {}
+impl<T> InteriorMutable for UnsafeCell<T> {}
+impl<T> InteriorMutable for Opaque<T> {}
+
+/// A trait representing a per-CPU variable that is usable via a `&T`. The unsafety of `PerCpu<T>`
+/// stems from the fact that the holder of a `PerCpuToken` can use it to get a `&mut T` to the
+/// underlying per-CPU variable. This is problematic because the existence of aliaising `&mut T` is
+/// undefined behavior in Rust. This type avoids that issue by only allowing access via a `&T`,
+/// with the tradeoff that then `T` must be interior mutable or the underlying per-CPU variable
+/// must be a constant for the lifetime of the corresponding `CheckedPerCpu<T>`.
+///
+/// Currently, only the case where `T` is interior mutable has first-class support, though a custom
+/// implementation of `PerCpu<T>`/`CheckedPerCpu<T>` could be created for the const case.
+pub trait CheckedPerCpu<T>: PerCpu<T> {
+ /// Produces a token via which the holder can access the underlying per-CPU variable.
+ fn get(&mut self, guard: CpuGuard) -> CheckedPerCpuToken<'_, T>;
+}
+
+impl<'a, T> PerCpuToken<'a, T> {
+ /// # Safety
+ /// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a
+ /// sensible notion, since we keep a `CpuGuard` around) that uses the same `PerCpuPtr<T>`.
+ ///
+ /// 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 this CPU) must be initialized.
+ pub unsafe fn new(guard: CpuGuard, ptr: &'a PerCpuPtr<T>) -> PerCpuToken<'a, T> {
+ Self { _guard: guard, ptr }
+ }
+
+ /// Immediately invokes `func` with a `&mut T` that points at the underlying per-CPU variable
+ /// that `&mut self` represents.
+ pub fn with<U>(&mut self, func: U)
+ where
+ U: FnOnce(&mut T),
+ {
+ // SAFETY: The existence of a PerCpuToken means that the requirements for get_mut_ref are
+ // satisfied. Likewise, the requirements for assume_init_mut are satisfied because the
+ // invariants of this type ensure that on the current CPU (which is a sensible notion
+ // because we have a CpuGuard), the memory location pointed to by `ptr` is initialized.
+ func(unsafe { self.ptr.get_mut_ref().assume_init_mut() });
+ }
+}
+
+impl<'a, T> CheckedPerCpuToken<'a, T> {
+ /// # Safety
+ /// 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 this CPU) must be initialized.
+ pub unsafe fn new(guard: CpuGuard, ptr: &'a PerCpuPtr<T>) -> CheckedPerCpuToken<'a, T> {
+ Self { _guard: guard, ptr }
+ }
+
+ /// Immediately invokes `func` with a `&T` that points at the underlying per-CPU variable that
+ /// `&mut self` represents.
+ pub fn with<U>(&mut self, func: U)
+ where
+ U: FnOnce(&T),
+ {
+ // SAFETY: The existence of a CheckedPerCpuToken means that the requirements for get_ref
+ // are satisfied. Likewise, the requirements for assume_init_ref are satisfied because the
+ // invariants of this type ensure that on the current CPU (which is a sensible notion
+ // because we have a CpuGuard), the memory location pointed to by `ptr` is initialized.
+ func(unsafe { self.ptr.get_ref().assume_init_ref() });
+ }
+}
+
+declare_extern_per_cpu!(this_cpu_off: *mut c_void);
diff --git a/rust/kernel/percpu/cpu_guard.rs b/rust/kernel/percpu/cpu_guard.rs
new file mode 100644
index 000000000000..14c04b12e7f0
--- /dev/null
+++ b/rust/kernel/percpu/cpu_guard.rs
@@ -0,0 +1,35 @@
+// SPDX-License-Identifier: GPL-2.0
+//! Contains abstractions for disabling CPU preemption. See `CpuGuard`.
+
+/// A RAII guard for bindings::preempt_disable and bindings::preempt_enable. Guarantees preemption
+/// is disabled for as long as this object exists.
+pub struct CpuGuard {
+ // Don't make one without using new()
+ _phantom: (),
+}
+
+impl CpuGuard {
+ /// Create a new CpuGuard. Disables preemption for its lifetime.
+ pub fn new() -> Self {
+ // SAFETY: There are no preconditions required to call preempt_disable
+ unsafe {
+ bindings::preempt_disable();
+ }
+ CpuGuard { _phantom: () }
+ }
+}
+
+impl Default for CpuGuard {
+ fn default() -> Self {
+ Self::new()
+ }
+}
+
+impl Drop for CpuGuard {
+ fn drop(&mut self) {
+ // SAFETY: There are no preconditions required to call preempt_enable
+ unsafe {
+ bindings::preempt_enable();
+ }
+ }
+}
diff --git a/rust/kernel/percpu/dynamic.rs b/rust/kernel/percpu/dynamic.rs
new file mode 100644
index 000000000000..ce95e420f943
--- /dev/null
+++ b/rust/kernel/percpu/dynamic.rs
@@ -0,0 +1,83 @@
+// SPDX-License-Identifier: GPL-2.0
+//! Dynamically allocated per-CPU variables.
+
+use super::*;
+
+/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when
+/// dropped.
+pub struct PerCpuAllocation<T>(PerCpuPtr<T>);
+
+impl<T: Zeroable> PerCpuAllocation<T> {
+ /// Dynamically allocates a space in the per-CPU area suitably sized and aligned to hold a `T`,
+ /// initially filled with the zero value for `T`.
+ ///
+ /// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`.
+ pub fn new_zero() -> Option<PerCpuAllocation<T>> {
+ let ptr: *mut MaybeUninit<T> =
+ // SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is
+ // `#[repr(transparent)]`, so we can cast a `*mut T` to it.
+ unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) }.cast();
+ if ptr.is_null() {
+ return None;
+ }
+
+ // alloc_percpu returns zero'ed memory
+ Some(Self(PerCpuPtr(ptr)))
+ }
+}
+
+impl<T> PerCpuAllocation<T> {
+ /// Makes a per-CPU allocation sized and aligned to hold a `T`.
+ ///
+ /// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`.
+ pub fn new_uninit() -> Option<PerCpuAllocation<T>> {
+ let ptr: *mut MaybeUninit<T> =
+ // SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is
+ // `#[repr(transparent)]`, so we can cast a `*mut T` to it.
+ unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) }.cast();
+ if ptr.is_null() {
+ return None;
+ }
+
+ Some(Self(PerCpuPtr(ptr)))
+ }
+}
+
+impl<T> Drop for PerCpuAllocation<T> {
+ fn drop(&mut self) {
+ // SAFETY: self.0.0 was returned by alloc_percpu, and so was a valid pointer into
+ // the percpu area, and has remained valid by the invariants of PerCpuAllocation<T>.
+ unsafe { free_percpu(self.0 .0.cast()) }
+ }
+}
+
+/// Holds a dynamically-allocated per-CPU variable.
+#[derive(Clone)]
+pub struct DynamicPerCpu<T> {
+ // INVARIANT: The memory location in each CPU's per-CPU area pointed at by `alloc.0` has been
+ // initialized.
+ pub(super) alloc: Arc<PerCpuAllocation<T>>,
+}
+
+impl<T: Zeroable> DynamicPerCpu<T> {
+ /// Allocates a new per-CPU variable
+ ///
+ /// # Arguments
+ /// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying
+ /// `PerCpuAllocation`.
+ pub fn new_zero(flags: Flags) -> Option<Self> {
+ let alloc: PerCpuAllocation<T> = PerCpuAllocation::new_zero()?;
+
+ let arc = Arc::new(alloc, flags).ok()?;
+
+ Some(Self { alloc: arc })
+ }
+}
+
+impl<T> PerCpu<T> for DynamicPerCpu<T> {
+ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {
+ // SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the
+ // requirements of `PerCpuToken::new` are met.
+ unsafe { PerCpuToken::new(guard, &self.alloc.0) }
+ }
+}
diff --git a/rust/kernel/percpu/static_.rs b/rust/kernel/percpu/static_.rs
new file mode 100644
index 000000000000..be226dd2c3aa
--- /dev/null
+++ b/rust/kernel/percpu/static_.rs
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+//! Statically allocated per-CPU variables.
+
+use super::*;
+
+/// A wrapper used for declaring static per-CPU variables. These symbols are "virtual" in that the
+/// linker uses them to generate offsets into each CPU's per-CPU area, but shouldn't be read
+/// from/written to directly. The fact that the statics are immutable prevents them being written
+/// to (generally), this struct having _val be non-public prevents reading from them.
+///
+/// The end-user of the per-CPU API should make use of the define_per_cpu! macro instead of
+/// declaring variables of this type directly. All instances of this type must be `static` and
+/// `#[link_section = ".data..percpu"]` (which the macro handles).
+#[repr(transparent)]
+pub struct StaticPerCpuSymbol<T> {
+ _val: T, // generate a correctly sized type
+}
+
+/// Holds a statically-allocated per-CPU variable.
+#[derive(Clone)]
+pub struct StaticPerCpu<T>(pub(super) PerCpuPtr<T>);
+
+impl<T> StaticPerCpu<T> {
+ /// Creates a `StaticPerCpu<T>` from a `StaticPerCpuSymbol<T>`. You should probably be using
+ /// `get_static_per_cpu!` instead.
+ pub fn new(ptr: *const StaticPerCpuSymbol<T>) -> StaticPerCpu<T> {
+ // SAFETY: `StaticPerCpuSymbol<T>` is `#[repr(transparent)]`, so we can safely cast a
+ // pointer to it into a pointer to `MaybeUninit<T>`. The validity of it as a per-CPU
+ // pointer is guaranteed by the per-CPU subsystem and invariants of the StaticPerCpuSymbol
+ // type.
+ let pcpu_ptr = unsafe { PerCpuPtr::new(ptr.cast_mut().cast()) };
+ Self(pcpu_ptr)
+ }
+}
+
+impl<T> PerCpu<T> for StaticPerCpu<T> {
+ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {
+ // SAFETY: The requirements of `PerCpu::get_mut` and the fact that statically-allocated
+ // per-CPU variables are initialized by the per-CPU subsystem ensure that the requirements
+ // of `PerCpuToken::new` are met.
+ unsafe { PerCpuToken::new(guard, &self.0) }
+ }
+}
+
+impl<T: InteriorMutable> CheckedPerCpu<T> for StaticPerCpu<T> {
+ fn get(&mut self, guard: CpuGuard) -> CheckedPerCpuToken<'_, T> {
+ // SAFETY: The per-CPU subsystem guarantees that each CPU's instance of a
+ // statically allocated variable begins with a copy of the contents of the
+ // corresponding symbol in `.data..percpu`. Thus, the requirements of
+ // `CheckedPerCpuToken::new` are met.
+ unsafe { CheckedPerCpuToken::new(guard, &self.0) }
+ }
+}
+
+/// Gets a `StaticPerCpu<T>` from a symbol declared with `define_per_cpu!` or
+/// `declare_extern_per_cpu!`.
+///
+/// # Arguments
+/// * `ident` - The identifier declared
+#[macro_export]
+macro_rules! get_static_per_cpu {
+ ($id:ident) => {
+ $crate::percpu::StaticPerCpu::new((&raw const $id).cast())
+ };
+}
+
+/// Declares a StaticPerCpuSymbol corresponding to a per-CPU variable defined in C. Be sure to read
+/// the safety requirements of `PerCpu::get`.
+#[macro_export]
+macro_rules! declare_extern_per_cpu {
+ ($id:ident: $ty:ty) => {
+ extern "C" {
+ static $id: StaticPerCpuSymbol<$ty>;
+ }
+ };
+}
+
+/// define_per_cpu! is analogous to the C DEFINE_PER_CPU macro in that it lets you create a
+/// statically allocated per-CPU variable.
+///
+/// # Example
+/// ```
+/// use kernel::define_per_cpu;
+/// use kernel::percpu::StaticPerCpuSymbol;
+///
+/// define_per_cpu!(pub MY_PERCPU: u64 = 0);
+/// ```
+#[macro_export]
+macro_rules! define_per_cpu {
+ ($vis:vis $id:ident: $ty:ty = $expr:expr) => {
+ $crate::macros::paste! {
+ // We might want to have a per-CPU variable that doesn't implement `Sync` (not paying
+ // sync overhead costs is part of the point), but Rust won't let us declare a static of
+ // a `!Sync` type. Of course, we don't actually have any synchronization issues, since
+ // each CPU will see its own copy of the variable, so we cheat a little bit and tell
+ // Rust it's fine.
+ #[doc(hidden)]
+ #[allow(non_camel_case_types)]
+ #[repr(transparent)] // It needs to be the same size as $ty
+ struct [<__PRIVATE_TYPE_ $id>]($ty);
+
+ impl [<__PRIVATE_TYPE_ $id>] {
+ #[doc(hidden)]
+ const fn new(val: $ty) -> Self {
+ Self(val)
+ }
+ }
+
+ // Expand $expr outside of the unsafe block to avoid silently allowing unsafe code to be
+ // used without a user-facing unsafe block
+ #[doc(hidden)]
+ static [<__INIT_ $id>]: [<__PRIVATE_TYPE_ $id>] = [<__PRIVATE_TYPE_ $id>]::new($expr);
+
+ // SAFETY: This type will ONLY ever be used to declare a `StaticPerCpuSymbol`
+ // (which we then only ever use as input to `&raw`). Reading from the symbol is
+ // already UB, so we won't ever actually have any variables of this type where
+ // synchronization is a concern.
+ #[doc(hidden)]
+ unsafe impl Sync for [<__PRIVATE_TYPE_ $id>] {}
+
+ // SAFETY: StaticPerCpuSymbol<T> is #[repr(transparent)], so we can freely convert from
+ // [<__PRIVATE_TYPE_ $id>], which is also `#[repr(transparent)]` (i.e., everything is
+ // just a `$ty` from a memory layout perspective).
+ #[link_section = ".data..percpu"]
+ $vis static $id: StaticPerCpuSymbol<[<__PRIVATE_TYPE_ $id>]> = unsafe {
+ core::mem::transmute_copy::<
+ [<__PRIVATE_TYPE_ $id>], StaticPerCpuSymbol<[<__PRIVATE_TYPE_ $id>]>
+ >(&[<__INIT_ $id>])
+ };
+ }
+ };
+}
--
2.34.1
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? > diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c > index 7cf7fe95e41d..2fc8d26cfe66 100644 > --- a/rust/helpers/helpers.c > +++ b/rust/helpers/helpers.c > @@ -31,9 +31,11 @@ > #include "of.c" > #include "page.c" > #include "pci.c" > +#include "percpu.c" > #include "pid_namespace.c" > #include "platform.c" > #include "poll.c" > +#include "preempt.c" > #include "property.c" > #include "rbtree.c" > #include "rcu.c" > diff --git a/rust/helpers/percpu.c b/rust/helpers/percpu.c > new file mode 100644 > index 000000000000..a091389f730f > --- /dev/null > +++ b/rust/helpers/percpu.c > @@ -0,0 +1,9 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/percpu.h> > + > +void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align) > +{ > + return __alloc_percpu(sz, align); > +} > + > diff --git a/rust/helpers/preempt.c b/rust/helpers/preempt.c > new file mode 100644 > index 000000000000..2c7529528ddd > --- /dev/null > +++ b/rust/helpers/preempt.c > @@ -0,0 +1,14 @@ > +// SPDX-License-Identifier: GPL-2.0 > + > +#include <linux/preempt.h> > + > +void rust_helper_preempt_disable(void) > +{ > + preempt_disable(); > +} > + > +void rust_helper_preempt_enable(void) > +{ > + preempt_enable(); > +} > + > diff --git a/rust/kernel/lib.rs b/rust/kernel/lib.rs > index ed53169e795c..ed0d5756dc55 100644 > --- a/rust/kernel/lib.rs > +++ b/rust/kernel/lib.rs > @@ -106,6 +106,9 @@ > pub mod page; > #[cfg(CONFIG_PCI)] > pub mod pci; > +// Only x86_64 is supported by percpu for now > +#[cfg(CONFIG_X86_64)] > +pub mod percpu; > pub mod pid_namespace; > pub mod platform; > pub mod prelude; > diff --git a/rust/kernel/percpu.rs b/rust/kernel/percpu.rs > new file mode 100644 > index 000000000000..35afcdba3ccd > --- /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? > + /// 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? Thanks, Yury > + > + /// Get a `&MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` > + /// > + /// # Safety > + /// The returned `&T` must follow Rust's aliasing rules. That is, no `&mut T` may exist that > + /// points to the same location in memory. In practice, this means that `get_mut_ref` 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. > + pub unsafe fn get_ref(&self) -> &MaybeUninit<T> { > + // SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract. > + // The safety requirements of this function ensure that the returned reference isn't > + // aliased by a `&mut MaybeUninit<T>`. > + unsafe { &*self.get_ptr() } > + } > + > + /// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by > + /// `&self`. Note that if CPU preemption is not disabled before calling this function, use of > + /// the returned pointer may cause a data race without some other synchronization mechanism. > + /// Buyer beware! > + pub fn get_ptr(&self) -> *mut MaybeUninit<T> { > + let this_cpu_off_pcpu: PerCpuPtr<*mut c_void> = get_static_per_cpu!(this_cpu_off).0; > + let mut this_cpu_area: *mut c_void; > + // SAFETY: gs + this_cpu_off_pcpu is guaranteed to be a valid pointer because `gs` points > + // to the per-CPU area and this_cpu_off_pcpu is a valid per-CPU allocation. > + unsafe { > + asm!( > + "mov {out}, gs:[{off_val}]", > + off_val = in(reg) this_cpu_off_pcpu.0, > + out = out(reg) this_cpu_area, > + ) > + }; > + > + // This_cpu_area + self.0 is guaranteed to be a valid pointer by the per-CPU subsystem and > + // the invariant that self.0 is a valid offset into the per-CPU area. > + (this_cpu_area).wrapping_add(self.0 as usize).cast() > + } > +} > + > +// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the > +// pointer is logically referring to a different place in memory in the other CPU's per-CPU area. > +// In particular, this means that there are no restrictions on the type `T`. > +unsafe impl<T> Send for PerCpuPtr<T> {} > + > +// SAFETY: Two threads concurrently making use of a `PerCpuPtr<T>` will each see the `T` in their > +// own per-CPU area, so there's no potential for a data race (regardless of whether `T` is itself > +// `Sync`). > +unsafe impl<T> Sync for PerCpuPtr<T> {} > + > +impl<T> Clone for PerCpuPtr<T> { > + fn clone(&self) -> Self { > + *self > + } > +} > + > +/// `PerCpuPtr` is just a wrapper around a pointer. > +impl<T> Copy for PerCpuPtr<T> {} > + > +/// A trait representing a per-CPU variable. This is implemented for both `StaticPerCpu<T>` and > +/// `DynamicPerCpu<T>`. The main usage of this trait is to call `get_mut` to get a `PerCpuToken` > +/// that can be used to access the underlying per-CPU variable. See `PerCpuToken::with`. > +pub trait PerCpu<T> { > + /// Produces a token, asserting that the holder has exclusive access to the underlying memory > + /// pointed to by `self` > + /// > + /// # Safety > + /// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a > + /// sensible notion, since we keep a `CpuGuard` around) that is derived from the same > + /// `PerCpu<T>` or a clone thereof. > + unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T>; > +} > + > +/// A marker trait for types that are interior mutable. Types that implement this trait can be used > +/// to create "checked" per-CPU variables. See `CheckedPerCpu<T>`. > +pub trait InteriorMutable {} > + > +impl<T> InteriorMutable for Cell<T> {} > +impl<T> InteriorMutable for RefCell<T> {} > +impl<T> InteriorMutable for UnsafeCell<T> {} > +impl<T> InteriorMutable for Opaque<T> {} > + > +/// A trait representing a per-CPU variable that is usable via a `&T`. The unsafety of `PerCpu<T>` > +/// stems from the fact that the holder of a `PerCpuToken` can use it to get a `&mut T` to the > +/// underlying per-CPU variable. This is problematic because the existence of aliaising `&mut T` is > +/// undefined behavior in Rust. This type avoids that issue by only allowing access via a `&T`, > +/// with the tradeoff that then `T` must be interior mutable or the underlying per-CPU variable > +/// must be a constant for the lifetime of the corresponding `CheckedPerCpu<T>`. > +/// > +/// Currently, only the case where `T` is interior mutable has first-class support, though a custom > +/// implementation of `PerCpu<T>`/`CheckedPerCpu<T>` could be created for the const case. > +pub trait CheckedPerCpu<T>: PerCpu<T> { > + /// Produces a token via which the holder can access the underlying per-CPU variable. > + fn get(&mut self, guard: CpuGuard) -> CheckedPerCpuToken<'_, T>; > +} > + > +impl<'a, T> PerCpuToken<'a, T> { > + /// # Safety > + /// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a > + /// sensible notion, since we keep a `CpuGuard` around) that uses the same `PerCpuPtr<T>`. > + /// > + /// 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 this CPU) must be initialized. > + pub unsafe fn new(guard: CpuGuard, ptr: &'a PerCpuPtr<T>) -> PerCpuToken<'a, T> { > + Self { _guard: guard, ptr } > + } > + > + /// Immediately invokes `func` with a `&mut T` that points at the underlying per-CPU variable > + /// that `&mut self` represents. > + pub fn with<U>(&mut self, func: U) > + where > + U: FnOnce(&mut T), > + { > + // SAFETY: The existence of a PerCpuToken means that the requirements for get_mut_ref are > + // satisfied. Likewise, the requirements for assume_init_mut are satisfied because the > + // invariants of this type ensure that on the current CPU (which is a sensible notion > + // because we have a CpuGuard), the memory location pointed to by `ptr` is initialized. > + func(unsafe { self.ptr.get_mut_ref().assume_init_mut() }); > + } > +} > + > +impl<'a, T> CheckedPerCpuToken<'a, T> { > + /// # Safety > + /// 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 this CPU) must be initialized. > + pub unsafe fn new(guard: CpuGuard, ptr: &'a PerCpuPtr<T>) -> CheckedPerCpuToken<'a, T> { > + Self { _guard: guard, ptr } > + } > + > + /// Immediately invokes `func` with a `&T` that points at the underlying per-CPU variable that > + /// `&mut self` represents. > + pub fn with<U>(&mut self, func: U) > + where > + U: FnOnce(&T), > + { > + // SAFETY: The existence of a CheckedPerCpuToken means that the requirements for get_ref > + // are satisfied. Likewise, the requirements for assume_init_ref are satisfied because the > + // invariants of this type ensure that on the current CPU (which is a sensible notion > + // because we have a CpuGuard), the memory location pointed to by `ptr` is initialized. > + func(unsafe { self.ptr.get_ref().assume_init_ref() }); > + } > +} > + > +declare_extern_per_cpu!(this_cpu_off: *mut c_void); > diff --git a/rust/kernel/percpu/cpu_guard.rs b/rust/kernel/percpu/cpu_guard.rs > new file mode 100644 > index 000000000000..14c04b12e7f0 > --- /dev/null > +++ b/rust/kernel/percpu/cpu_guard.rs > @@ -0,0 +1,35 @@ > +// SPDX-License-Identifier: GPL-2.0 > +//! Contains abstractions for disabling CPU preemption. See `CpuGuard`. > + > +/// A RAII guard for bindings::preempt_disable and bindings::preempt_enable. Guarantees preemption > +/// is disabled for as long as this object exists. > +pub struct CpuGuard { > + // Don't make one without using new() > + _phantom: (), > +} > + > +impl CpuGuard { > + /// Create a new CpuGuard. Disables preemption for its lifetime. > + pub fn new() -> Self { > + // SAFETY: There are no preconditions required to call preempt_disable > + unsafe { > + bindings::preempt_disable(); > + } > + CpuGuard { _phantom: () } > + } > +} > + > +impl Default for CpuGuard { > + fn default() -> Self { > + Self::new() > + } > +} > + > +impl Drop for CpuGuard { > + fn drop(&mut self) { > + // SAFETY: There are no preconditions required to call preempt_enable > + unsafe { > + bindings::preempt_enable(); > + } > + } > +} > diff --git a/rust/kernel/percpu/dynamic.rs b/rust/kernel/percpu/dynamic.rs > new file mode 100644 > index 000000000000..ce95e420f943 > --- /dev/null > +++ b/rust/kernel/percpu/dynamic.rs > @@ -0,0 +1,83 @@ > +// SPDX-License-Identifier: GPL-2.0 > +//! Dynamically allocated per-CPU variables. > + > +use super::*; > + > +/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when > +/// dropped. > +pub struct PerCpuAllocation<T>(PerCpuPtr<T>); > + > +impl<T: Zeroable> PerCpuAllocation<T> { > + /// Dynamically allocates a space in the per-CPU area suitably sized and aligned to hold a `T`, > + /// initially filled with the zero value for `T`. > + /// > + /// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`. > + pub fn new_zero() -> Option<PerCpuAllocation<T>> { > + let ptr: *mut MaybeUninit<T> = > + // SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is > + // `#[repr(transparent)]`, so we can cast a `*mut T` to it. > + unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) }.cast(); > + if ptr.is_null() { > + return None; > + } > + > + // alloc_percpu returns zero'ed memory > + Some(Self(PerCpuPtr(ptr))) > + } > +} > + > +impl<T> PerCpuAllocation<T> { > + /// Makes a per-CPU allocation sized and aligned to hold a `T`. > + /// > + /// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`. > + pub fn new_uninit() -> Option<PerCpuAllocation<T>> { > + let ptr: *mut MaybeUninit<T> = > + // SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is > + // `#[repr(transparent)]`, so we can cast a `*mut T` to it. > + unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) }.cast(); > + if ptr.is_null() { > + return None; > + } > + > + Some(Self(PerCpuPtr(ptr))) > + } > +} > + > +impl<T> Drop for PerCpuAllocation<T> { > + fn drop(&mut self) { > + // SAFETY: self.0.0 was returned by alloc_percpu, and so was a valid pointer into > + // the percpu area, and has remained valid by the invariants of PerCpuAllocation<T>. > + unsafe { free_percpu(self.0 .0.cast()) } > + } > +} > + > +/// Holds a dynamically-allocated per-CPU variable. > +#[derive(Clone)] > +pub struct DynamicPerCpu<T> { > + // INVARIANT: The memory location in each CPU's per-CPU area pointed at by `alloc.0` has been > + // initialized. > + pub(super) alloc: Arc<PerCpuAllocation<T>>, > +} > + > +impl<T: Zeroable> DynamicPerCpu<T> { > + /// Allocates a new per-CPU variable > + /// > + /// # Arguments > + /// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying > + /// `PerCpuAllocation`. > + pub fn new_zero(flags: Flags) -> Option<Self> { > + let alloc: PerCpuAllocation<T> = PerCpuAllocation::new_zero()?; > + > + let arc = Arc::new(alloc, flags).ok()?; > + > + Some(Self { alloc: arc }) > + } > +} > + > +impl<T> PerCpu<T> for DynamicPerCpu<T> { > + unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> { > + // SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the > + // requirements of `PerCpuToken::new` are met. > + unsafe { PerCpuToken::new(guard, &self.alloc.0) } > + } > +} > diff --git a/rust/kernel/percpu/static_.rs b/rust/kernel/percpu/static_.rs > new file mode 100644 > index 000000000000..be226dd2c3aa > --- /dev/null > +++ b/rust/kernel/percpu/static_.rs > @@ -0,0 +1,132 @@ > +// SPDX-License-Identifier: GPL-2.0 > +//! Statically allocated per-CPU variables. > + > +use super::*; > + > +/// A wrapper used for declaring static per-CPU variables. These symbols are "virtual" in that the > +/// linker uses them to generate offsets into each CPU's per-CPU area, but shouldn't be read > +/// from/written to directly. The fact that the statics are immutable prevents them being written > +/// to (generally), this struct having _val be non-public prevents reading from them. > +/// > +/// The end-user of the per-CPU API should make use of the define_per_cpu! macro instead of > +/// declaring variables of this type directly. All instances of this type must be `static` and > +/// `#[link_section = ".data..percpu"]` (which the macro handles). > +#[repr(transparent)] > +pub struct StaticPerCpuSymbol<T> { > + _val: T, // generate a correctly sized type > +} > + > +/// Holds a statically-allocated per-CPU variable. > +#[derive(Clone)] > +pub struct StaticPerCpu<T>(pub(super) PerCpuPtr<T>); > + > +impl<T> StaticPerCpu<T> { > + /// Creates a `StaticPerCpu<T>` from a `StaticPerCpuSymbol<T>`. You should probably be using > + /// `get_static_per_cpu!` instead. > + pub fn new(ptr: *const StaticPerCpuSymbol<T>) -> StaticPerCpu<T> { > + // SAFETY: `StaticPerCpuSymbol<T>` is `#[repr(transparent)]`, so we can safely cast a > + // pointer to it into a pointer to `MaybeUninit<T>`. The validity of it as a per-CPU > + // pointer is guaranteed by the per-CPU subsystem and invariants of the StaticPerCpuSymbol > + // type. > + let pcpu_ptr = unsafe { PerCpuPtr::new(ptr.cast_mut().cast()) }; > + Self(pcpu_ptr) > + } > +} > + > +impl<T> PerCpu<T> for StaticPerCpu<T> { > + unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> { > + // SAFETY: The requirements of `PerCpu::get_mut` and the fact that statically-allocated > + // per-CPU variables are initialized by the per-CPU subsystem ensure that the requirements > + // of `PerCpuToken::new` are met. > + unsafe { PerCpuToken::new(guard, &self.0) } > + } > +} > + > +impl<T: InteriorMutable> CheckedPerCpu<T> for StaticPerCpu<T> { > + fn get(&mut self, guard: CpuGuard) -> CheckedPerCpuToken<'_, T> { > + // SAFETY: The per-CPU subsystem guarantees that each CPU's instance of a > + // statically allocated variable begins with a copy of the contents of the > + // corresponding symbol in `.data..percpu`. Thus, the requirements of > + // `CheckedPerCpuToken::new` are met. > + unsafe { CheckedPerCpuToken::new(guard, &self.0) } > + } > +} > + > +/// Gets a `StaticPerCpu<T>` from a symbol declared with `define_per_cpu!` or > +/// `declare_extern_per_cpu!`. > +/// > +/// # Arguments > +/// * `ident` - The identifier declared > +#[macro_export] > +macro_rules! get_static_per_cpu { > + ($id:ident) => { > + $crate::percpu::StaticPerCpu::new((&raw const $id).cast()) > + }; > +} > + > +/// Declares a StaticPerCpuSymbol corresponding to a per-CPU variable defined in C. Be sure to read > +/// the safety requirements of `PerCpu::get`. > +#[macro_export] > +macro_rules! declare_extern_per_cpu { > + ($id:ident: $ty:ty) => { > + extern "C" { > + static $id: StaticPerCpuSymbol<$ty>; > + } > + }; > +} > + > +/// define_per_cpu! is analogous to the C DEFINE_PER_CPU macro in that it lets you create a > +/// statically allocated per-CPU variable. > +/// > +/// # Example > +/// ``` > +/// use kernel::define_per_cpu; > +/// use kernel::percpu::StaticPerCpuSymbol; > +/// > +/// define_per_cpu!(pub MY_PERCPU: u64 = 0); > +/// ``` > +#[macro_export] > +macro_rules! define_per_cpu { > + ($vis:vis $id:ident: $ty:ty = $expr:expr) => { > + $crate::macros::paste! { > + // We might want to have a per-CPU variable that doesn't implement `Sync` (not paying > + // sync overhead costs is part of the point), but Rust won't let us declare a static of > + // a `!Sync` type. Of course, we don't actually have any synchronization issues, since > + // each CPU will see its own copy of the variable, so we cheat a little bit and tell > + // Rust it's fine. > + #[doc(hidden)] > + #[allow(non_camel_case_types)] > + #[repr(transparent)] // It needs to be the same size as $ty > + struct [<__PRIVATE_TYPE_ $id>]($ty); > + > + impl [<__PRIVATE_TYPE_ $id>] { > + #[doc(hidden)] > + const fn new(val: $ty) -> Self { > + Self(val) > + } > + } > + > + // Expand $expr outside of the unsafe block to avoid silently allowing unsafe code to be > + // used without a user-facing unsafe block > + #[doc(hidden)] > + static [<__INIT_ $id>]: [<__PRIVATE_TYPE_ $id>] = [<__PRIVATE_TYPE_ $id>]::new($expr); > + > + // SAFETY: This type will ONLY ever be used to declare a `StaticPerCpuSymbol` > + // (which we then only ever use as input to `&raw`). Reading from the symbol is > + // already UB, so we won't ever actually have any variables of this type where > + // synchronization is a concern. > + #[doc(hidden)] > + unsafe impl Sync for [<__PRIVATE_TYPE_ $id>] {} > + > + // SAFETY: StaticPerCpuSymbol<T> is #[repr(transparent)], so we can freely convert from > + // [<__PRIVATE_TYPE_ $id>], which is also `#[repr(transparent)]` (i.e., everything is > + // just a `$ty` from a memory layout perspective). > + #[link_section = ".data..percpu"] > + $vis static $id: StaticPerCpuSymbol<[<__PRIVATE_TYPE_ $id>]> = unsafe { > + core::mem::transmute_copy::< > + [<__PRIVATE_TYPE_ $id>], StaticPerCpuSymbol<[<__PRIVATE_TYPE_ $id>]> > + >(&[<__INIT_ $id>]) > + }; > + } > + }; > +} > > -- > 2.34.1
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
On Thu, Sep 04, 2025 at 12:53:59PM -0700, Mitchell Levy wrote: > 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: ... > > > + /// 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. Then don't use that tool - it's broken. In kernel we used to have 80-chars limit for the lines, recently relaxed to 100. > > > + /// 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 :) If you think that in future there will be a user who will not need to disable preemption before dereferencing a percpu pointer, then you can add another less restricted flavor of the helper. > 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. I don't want you to panic just because of invlid user call, but whatever you call in comment must be enforced in code, right? You can use the guard, if it guarantees the preemption disabled; or you can return None; you can create CONFIG_RUST_PERCPU_HARDENED for panics. Please refer the recent bitmap API wrapper, and how erroneous request is handled there. https://lore.kernel.org/all/20250904165015.3791895-4-bqe@google.com/
On Thu, Sep 04, 2025 at 04:27:43PM -0400, Yury Norov wrote: > On Thu, Sep 04, 2025 at 12:53:59PM -0700, Mitchell Levy wrote: > > 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: > > ... > > > > > + /// 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. > > Then don't use that tool - it's broken. In kernel we used to have 80-chars > limit for the lines, recently relaxed to 100. Sure, will fix. > > > > + /// 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 :) > > If you think that in future there will be a user who will not need to > disable preemption before dereferencing a percpu pointer, then you can > add another less restricted flavor of the helper. Yeah, that's fair. > > 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. > > I don't want you to panic just because of invlid user call, but > whatever you call in comment must be enforced in code, right? > > > You can use the guard, if it guarantees the preemption disabled; or > you can return None; you can create CONFIG_RUST_PERCPU_HARDENED for > panics. Yes, the existence of a `CpuGuard` guarantees that preemption is disabled. > Please refer the recent bitmap API wrapper, and how erroneous request > is handled there. > > https://lore.kernel.org/all/20250904165015.3791895-4-bqe@google.com/ Thanks, Mitchell
© 2016 - 2025 Red Hat, Inc.