* [PATCH 0/5] rust: Add Per-CPU Variable API
@ 2025-06-24 22:10 Mitchell Levy
2025-06-24 22:10 ` [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
` (4 more replies)
0 siblings, 5 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
This series adds an API for declaring an using per-CPU variables from
Rust, and it also adds support for Rust access to C per-CPU variables
(subject to some soundness requirements). It also adds a small test
module, lib/percpu_test_rust.rs, in the vein of lib/percpu_test.c.
---
Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
---
Changes in RFC v2:
- Renamed PerCpuVariable to StaticPerCpuSymbol to be more descriptive
- Support dynamically allocated per-CPU variables via the
PerCpuAllocation type. Rework statically allocated variables to use
this new type.
- Make use of a token/closure-based API via the PerCpu and PerCpuToken
types, rather than an API based on PerCpuRef that automatically
Deref(Mut)'s into a &(mut) T.
- Rebased
- Link to RFC: https://lore.kernel.org/r/20241219-rust-percpu-v1-0-209117e822b1@gmail.com
---
Changes in v1:
- Use wrapping_add in `PerCpuPtr::get_ref` since overflow is expected.
- Separate the dynamic and static cases, with shared logic in a
`PerCpuPtr` type.
- Implement pin-hole optimizations for numeric types
- Don't assume `GFP_KERNEL` when allocating the `Arc` in the dynamic
case.
- Link to RFC v2: https://lore.kernel.org/r/20250414-rust-percpu-v2-0-5ea0d0de13a5@gmail.com
---
Mitchell Levy (5):
rust: percpu: introduce a rust API for per-CPU variables
rust: rust-analyzer: add lib to dirs searched for crates
rust: percpu: add a rust per-CPU variable test
rust: percpu: Add pin-hole optimizations for numerics
rust: percpu: cache per-CPU pointers in the dynamic case
lib/Kconfig.debug | 9 ++
lib/Makefile | 1 +
lib/percpu_test_rust.rs | 156 +++++++++++++++++++
rust/helpers/helpers.c | 2 +
rust/helpers/percpu.c | 20 +++
rust/helpers/preempt.c | 14 ++
rust/kernel/lib.rs | 3 +
rust/kernel/percpu.rs | 306 ++++++++++++++++++++++++++++++++++++++
rust/kernel/percpu/cpu_guard.rs | 35 +++++
rust/kernel/percpu/numeric.rs | 117 +++++++++++++++
scripts/generate_rust_analyzer.py | 2 +-
11 files changed, 664 insertions(+), 1 deletion(-)
---
base-commit: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
change-id: 20240813-rust-percpu-ea2f54b5da33
Best regards,
--
Mitchell Levy <levymitchell0@gmail.com>
^ permalink raw reply [flat|nested] 11+ messages in thread
* [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
@ 2025-06-24 22:10 ` Mitchell Levy
2025-06-26 14:05 ` kernel test robot
2025-06-24 22:10 ` [PATCH 2/5] rust: rust-analyzer: add lib to dirs searched for crates Mitchell Levy
` (3 subsequent siblings)
4 siblings, 1 reply; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
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 | 299 ++++++++++++++++++++++++++++++++++++++++
rust/kernel/percpu/cpu_guard.rs | 35 +++++
6 files changed, 362 insertions(+)
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 0f1b5d115985..d56bbe6334d3 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -29,7 +29,9 @@
#include "page.c"
#include "platform.c"
#include "pci.c"
+#include "percpu.c"
#include "pid_namespace.c"
+#include "preempt.c"
#include "rbtree.c"
#include "rcu.c"
#include "refcount.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 6b4774b2b1c3..733f9ff8b888 100644
--- a/rust/kernel/lib.rs
+++ b/rust/kernel/lib.rs
@@ -95,6 +95,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..a912f74349e0
--- /dev/null
+++ b/rust/kernel/percpu.rs
@@ -0,0 +1,299 @@
+// 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 PerCpu<T> type.
+pub mod cpu_guard;
+
+use bindings::{alloc_percpu, free_percpu};
+
+use crate::alloc::Flags;
+use crate::percpu::cpu_guard::CpuGuard;
+use crate::sync::Arc;
+
+use core::arch::asm;
+
+use ffi::c_void;
+
+/// A per-CPU pointer; that is, an offset into the per-CPU area.
+pub struct PerCpuPtr<T>(*mut T);
+
+/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when
+/// dropped.
+pub struct PerCpuAllocation<T>(PerCpuPtr<T>);
+
+/// Holds a dynamically-allocated per-CPU variable.
+pub struct DynamicPerCpu<T> {
+ alloc: Arc<PerCpuAllocation<T>>,
+}
+
+/// Holds a statically-allocated per-CPU variable.
+pub struct StaticPerCpu<T>(PerCpuPtr<T>);
+
+/// Represents exclusive access to the memory location pointed at by a particular PerCpu<T>.
+pub struct PerCpuToken<'a, T> {
+ _guard: CpuGuard,
+ ptr: &'a PerCpuPtr<T>,
+}
+
+/// 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.
+#[repr(transparent)]
+pub struct StaticPerCpuSymbol<T> {
+ _val: T, // generate a correctly sized type
+}
+
+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 T) -> Self {
+ Self(ptr)
+ }
+
+ /// Get a `&mut T` to the per-CPU variable 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 any PerCpu
+ /// on the same CPU holding a reference to the same PerCpuAllocation as `self` mut not call
+ /// `get_ref` 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 &mut T 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_ref(&self) -> &mut T {
+ let this_cpu_off_pcpu = core::ptr::addr_of!(this_cpu_off);
+ 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,
+ out = out(reg) this_cpu_area,
+ )
+ };
+ // SAFETY: 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.
+ //
+ // We know no-one else has a reference to the underlying pcpu variable because of the
+ // safety requirements of this function.
+ unsafe { &mut *((this_cpu_area).wrapping_add(self.0 as usize) as *mut T) }
+ }
+}
+
+impl<T> Clone for PerCpuPtr<T> {
+ fn clone(&self) -> Self {
+ *self
+ }
+}
+
+/// PerCpuPtr is just a pointer, so it's safe to copy.
+impl<T> Copy for PerCpuPtr<T> {}
+
+impl<T> PerCpuAllocation<T> {
+ /// Dynamically allocates a space in the per-CPU area suitably sized and aligned to hold a `T`.
+ ///
+ /// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`.
+ pub fn new() -> Option<PerCpuAllocation<T>> {
+ // SAFETY: No preconditions to call alloc_percpu
+ let ptr: *mut T = unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) } as *mut T;
+ 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 as *mut c_void) }
+ }
+}
+
+/// 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` to get a `PerCpuToken` that
+/// can be used to access the underlying per-CPU variable. See `PerCpuToken::with`.
+///
+/// # Safety
+/// The returned value from `ptr` must be valid for the lifetime of `&mut self`.
+pub unsafe trait PerCpu<T> {
+ /// Gets a `PerCpuPtr<T>` to the per-CPU variable represented by `&mut self`
+ ///
+ /// # Safety
+ /// `self` may be doing all sorts of things to track when the underlying per-CPU variable can
+ /// be deallocated. You almost certainly shouldn't be calling this function directly (it's
+ /// essentially an implementation detail of the trait), and you certainly shouldn't be making
+ /// copies of the returned `PerCpuPtr<T>` that may outlive `&mut self`.
+ ///
+ /// Implementers of this trait should ensure that the returned `PerCpuPtr<T>` is valid for
+ /// the lifetime of `&mut self`.
+ unsafe fn ptr(&mut self) -> &PerCpuPtr<T>;
+
+ /// Produces a token, asserting that the holder has exclusive access to the underlying memory
+ /// pointed to by `self`
+ ///
+ /// # Safety
+ /// `func` (or its callees that execute on the same CPU) may not call `get_ref` on another
+ /// `PerCpu<T>` that represents the same per-CPU variable as `&mut self` (that is, they must
+ /// not be `clone()`s of each other or, in the case of statically allocated variables,
+ /// additionally can't both have come from the same `define_per_cpu!`) for the lifetime of the
+ /// returned token.
+ ///
+ /// In particular, this requires that the underlying per-CPU variable cannot ever be mutated
+ /// from an interrupt context, unless irqs are disabled for the lifetime of the returned
+ /// `PerCpuToken`.
+ unsafe fn get(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {
+ PerCpuToken {
+ _guard: guard,
+ // SAFETY: The lifetime of the returned `PerCpuToken<'_, T>` is bounded by the lifetime
+ // of `&mut self`.
+ ptr: unsafe { self.ptr() },
+ }
+ }
+}
+
+impl<T> StaticPerCpu<T> {
+ /// Creates a new PerCpu<T> pointing to the statically allocated variable at `ptr`. End-users
+ /// should probably be using the `unsafe_get_per_cpu!` macro instead of calling this function.
+ ///
+ /// # Safety
+ /// `ptr` must be a valid pointer to a per-CPU variable. This means that it must be a valid
+ /// offset into the per-CPU area, and that the per-CPU area must be suitably sized and aligned
+ /// to hold a `T`.
+ pub unsafe fn new(ptr: *mut T) -> Self {
+ Self(PerCpuPtr(ptr))
+ }
+}
+
+// SAFETY: The `PerCpuPtr<T>` returned by `ptr` is valid for the lifetime of `self` (and in fact,
+// forever).
+unsafe impl<T> PerCpu<T> for StaticPerCpu<T> {
+ unsafe fn ptr(&mut self) -> &PerCpuPtr<T> {
+ &self.0
+ }
+}
+
+impl<T> Clone for StaticPerCpu<T> {
+ fn clone(&self) -> Self {
+ Self(self.0)
+ }
+}
+
+impl<T> 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(flags: Flags) -> Option<Self> {
+ let alloc: PerCpuAllocation<T> = PerCpuAllocation::new()?;
+
+ let arc = Arc::new(alloc, flags).ok()?;
+
+ Some(Self { alloc: arc })
+ }
+
+ /// Wraps a `PerCpuAllocation<T>` in a `PerCpu<T>`
+ ///
+ /// # Arguments
+ /// * `alloc` - The allocation to use
+ /// * `flags` - The flags used to allocate an `Arc` that keeps track of the `PerCpuAllocation`.
+ pub fn new_from_allocation(alloc: PerCpuAllocation<T>, flags: Flags) -> Option<Self> {
+ let arc = Arc::new(alloc, flags).ok()?;
+ Some(Self { alloc: arc })
+ }
+}
+
+// SAFETY: The `PerCpuPtr<T>` returned by `ptr` is valid for the lifetime of `self` because we
+// don't deallocate the underlying `PerCpuAllocation` until `self` is dropped.
+unsafe impl<T> PerCpu<T> for DynamicPerCpu<T> {
+ unsafe fn ptr(&mut self) -> &PerCpuPtr<T> {
+ &self.alloc.0
+ }
+}
+
+impl<T> Clone for DynamicPerCpu<T> {
+ fn clone(&self) -> Self {
+ Self {
+ alloc: self.alloc.clone(),
+ }
+ }
+}
+
+impl<T> PerCpuToken<'_, T> {
+ /// 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_ref are
+ // satisfied.
+ func(unsafe { self.ptr.get_ref() });
+ }
+}
+
+/// 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! {
+ // Expand $expr outside of the unsafe block to avoid silently allowing unsafe code to be
+ // used without a user-facing unsafe block
+ static [<__INIT_ $id>]: $ty = $expr;
+
+ // SAFETY: StaticPerCpuSymbol<T> is #[repr(transparent)], so we can freely convert from T
+ #[link_section = ".data..percpu"]
+ $vis static $id: StaticPerCpuSymbol<$ty> = unsafe {
+ core::mem::transmute::<$ty, StaticPerCpuSymbol<$ty>>([<__INIT_ $id>])
+ };
+ }
+ };
+}
+
+/// Gets a `PerCpu<T>` from a symbol declared with `define_per_cpu!` or `declare_extern_per_cpu!`.
+///
+/// # Arguments
+/// * `ident` - The identifier declared
+///
+/// # Safety
+/// `$id` must be declared with either `define_per_cpu!` or `declare_extern_per_cpu!`, and the
+/// returned value must be stored in a `PerCpu<T>` where `T` matches the declared type of `$id`.
+#[macro_export]
+macro_rules! unsafe_get_per_cpu {
+ ($id:ident) => {{
+ $crate::percpu::StaticPerCpu::new((&$id) as *const _ as *mut _)
+ }};
+}
+
+/// 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>;
+ }
+ };
+}
+
+declare_extern_per_cpu!(this_cpu_off: u64);
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();
+ }
+ }
+}
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 2/5] rust: rust-analyzer: add lib to dirs searched for crates
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
2025-06-24 22:10 ` [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
@ 2025-06-24 22:10 ` Mitchell Levy
2025-06-24 22:10 ` [PATCH 3/5] rust: percpu: add a rust per-CPU variable test Mitchell Levy
` (2 subsequent siblings)
4 siblings, 0 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
When generating rust-project.json, also include crates in lib/
Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
---
scripts/generate_rust_analyzer.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index 7c3ea2b55041..08e14ae9c1a0 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -152,7 +152,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edit
# Then, the rest outside of `rust/`.
#
# We explicitly mention the top-level folders we want to cover.
- extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers"))
+ extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers", "lib"))
if external_src is not None:
extra_dirs = [external_src]
for folder in extra_dirs:
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 3/5] rust: percpu: add a rust per-CPU variable test
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
2025-06-24 22:10 ` [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
2025-06-24 22:10 ` [PATCH 2/5] rust: rust-analyzer: add lib to dirs searched for crates Mitchell Levy
@ 2025-06-24 22:10 ` Mitchell Levy
2025-06-24 22:10 ` [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
2025-06-24 22:10 ` [PATCH 5/5] rust: percpu: cache per-CPU pointers in the dynamic case Mitchell Levy
4 siblings, 0 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
Add a short exercise for Rust's per-CPU variable API, modelled after
lib/percpu_test.c
Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
---
lib/Kconfig.debug | 9 ++++
lib/Makefile | 1 +
lib/percpu_test_rust.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
rust/helpers/percpu.c | 11 +++++
4 files changed, 141 insertions(+)
diff --git a/lib/Kconfig.debug b/lib/Kconfig.debug
index ebe33181b6e6..959ce156c601 100644
--- a/lib/Kconfig.debug
+++ b/lib/Kconfig.debug
@@ -2418,6 +2418,15 @@ config PERCPU_TEST
If unsure, say N.
+config PERCPU_TEST_RUST
+ tristate "Rust per cpu operations test"
+ depends on m && DEBUG_KERNEL && RUST
+ help
+ Enable this option to build a test module which validates Rust per-cpu
+ operations.
+
+ If unsure, say N.
+
config ATOMIC64_SELFTEST
tristate "Perform an atomic64_t self-test"
help
diff --git a/lib/Makefile b/lib/Makefile
index c38582f187dd..ab19106cc22c 100644
--- a/lib/Makefile
+++ b/lib/Makefile
@@ -288,6 +288,7 @@ obj-$(CONFIG_RBTREE_TEST) += rbtree_test.o
obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o
obj-$(CONFIG_PERCPU_TEST) += percpu_test.o
+obj-$(CONFIG_PERCPU_TEST_RUST) += percpu_test_rust.o
obj-$(CONFIG_ASN1) += asn1_decoder.o
obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
diff --git a/lib/percpu_test_rust.rs b/lib/percpu_test_rust.rs
new file mode 100644
index 000000000000..a9652e6ece08
--- /dev/null
+++ b/lib/percpu_test_rust.rs
@@ -0,0 +1,120 @@
+// SPDX-License-Identifier: GPL-2.0
+//! A simple self test for the rust per-CPU API.
+
+use core::ffi::c_void;
+
+use kernel::{
+ bindings::{on_each_cpu, smp_processor_id},
+ define_per_cpu,
+ percpu::{cpu_guard::*, *},
+ pr_info,
+ prelude::*,
+ unsafe_get_per_cpu,
+};
+
+module! {
+ type: PerCpuTestModule,
+ name: "percpu_test_rust",
+ author: "Mitchell Levy",
+ description: "Test code to exercise the Rust Per CPU variable API",
+ license: "GPL v2",
+}
+
+struct PerCpuTestModule;
+
+define_per_cpu!(PERCPU: i64 = 0);
+define_per_cpu!(UPERCPU: u64 = 0);
+
+impl kernel::Module for PerCpuTestModule {
+ fn init(_module: &'static ThisModule) -> Result<Self, Error> {
+ pr_info!("rust percpu test start\n");
+
+ let mut native: i64 = 0;
+ // SAFETY: PERCPU is properly defined
+ let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) };
+ // SAFETY: We only have one PerCpu that points at PERCPU
+ unsafe { pcpu.get(CpuGuard::new()) }.with(|val: &mut i64| {
+ pr_info!("The contents of pcpu are {}\n", val);
+
+ native += -1;
+ *val += -1;
+ pr_info!("Native: {}, *pcpu: {}\n", native, val);
+ assert!(native == *val && native == -1);
+
+ native += 1;
+ *val += 1;
+ pr_info!("Native: {}, *pcpu: {}\n", native, val);
+ assert!(native == *val && native == 0);
+ });
+
+ let mut unative: u64 = 0;
+ // SAFETY: UPERCPU is properly defined
+ let mut upcpu: StaticPerCpu<u64> = unsafe { unsafe_get_per_cpu!(UPERCPU) };
+
+ // SAFETY: We only have one PerCpu pointing at UPERCPU
+ unsafe { upcpu.get(CpuGuard::new()) }.with(|val: &mut u64| {
+ unative += 1;
+ *val += 1;
+ pr_info!("Unative: {}, *upcpu: {}\n", unative, val);
+ assert!(unative == *val && unative == 1);
+
+ unative = unative.wrapping_add((-1i64) as u64);
+ *val = val.wrapping_add((-1i64) as u64);
+ pr_info!("Unative: {}, *upcpu: {}\n", unative, val);
+ assert!(unative == *val && unative == 0);
+
+ unative = unative.wrapping_add((-1i64) as u64);
+ *val = val.wrapping_add((-1i64) as u64);
+ pr_info!("Unative: {}, *upcpu: {}\n", unative, val);
+ assert!(unative == *val && unative == (-1i64) as u64);
+
+ unative = 0;
+ *val = 0;
+
+ unative = unative.wrapping_sub(1);
+ *val = val.wrapping_sub(1);
+ pr_info!("Unative: {}, *upcpu: {}\n", unative, val);
+ assert!(unative == *val && unative == (-1i64) as u64);
+ assert!(unative == *val && unative == u64::MAX);
+ });
+
+ pr_info!("rust static percpu test done\n");
+
+ pr_info!("rust dynamic percpu test start\n");
+ let mut test: DynamicPerCpu<u64> = DynamicPerCpu::new(GFP_KERNEL).unwrap();
+
+ // SAFETY: No prerequisites for on_each_cpu.
+ unsafe {
+ on_each_cpu(Some(inc_percpu), (&raw mut test) as *mut c_void, 0);
+ on_each_cpu(Some(inc_percpu), (&raw mut test) as *mut c_void, 0);
+ on_each_cpu(Some(inc_percpu), (&raw mut test) as *mut c_void, 0);
+ on_each_cpu(Some(inc_percpu), (&raw mut test) as *mut c_void, 1);
+ on_each_cpu(Some(check_percpu), (&raw mut test) as *mut c_void, 1);
+ }
+
+ pr_info!("rust dynamic percpu test done\n");
+
+ // Return Err to unload the module
+ Result::Err(EINVAL)
+ }
+}
+
+extern "C" fn inc_percpu(info: *mut c_void) {
+ // SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send.
+ let mut pcpu = unsafe { (*(info as *const DynamicPerCpu<u64>)).clone() };
+ // SAFETY: smp_processor_id has no preconditions
+ pr_info!("Incrementing on {}\n", unsafe { smp_processor_id() });
+
+ // SAFETY: We don't have multiple clones of pcpu in scope
+ unsafe { pcpu.get(CpuGuard::new()) }.with(|val: &mut u64| *val += 1);
+}
+
+extern "C" fn check_percpu(info: *mut c_void) {
+ // SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send.
+ let mut pcpu = unsafe { (*(info as *const DynamicPerCpu<u64>)).clone() };
+ // SAFETY: smp_processor_id has no preconditions
+ pr_info!("Asserting on {}\n", unsafe { smp_processor_id() });
+
+ // SAFETY: We don't have multiple clones of pcpu in scope
+ unsafe { pcpu.get(CpuGuard::new()) }.with(|val: &mut u64| assert!(*val == 4));
+}
diff --git a/rust/helpers/percpu.c b/rust/helpers/percpu.c
index a091389f730f..0e9b2fed3ebd 100644
--- a/rust/helpers/percpu.c
+++ b/rust/helpers/percpu.c
@@ -1,9 +1,20 @@
// SPDX-License-Identifier: GPL-2.0
#include <linux/percpu.h>
+#include <linux/smp.h>
void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align)
{
return __alloc_percpu(sz, align);
}
+void rust_helper_on_each_cpu(smp_call_func_t func, void *info, int wait)
+{
+ on_each_cpu(func, info, wait);
+}
+
+int rust_helper_smp_processor_id(void)
+{
+ return smp_processor_id();
+}
+
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
` (2 preceding siblings ...)
2025-06-24 22:10 ` [PATCH 3/5] rust: percpu: add a rust per-CPU variable test Mitchell Levy
@ 2025-06-24 22:10 ` Mitchell Levy
2025-06-25 17:21 ` Christoph Lameter (Ampere)
2025-06-25 17:23 ` Christoph Lameter (Ampere)
2025-06-24 22:10 ` [PATCH 5/5] rust: percpu: cache per-CPU pointers in the dynamic case Mitchell Levy
4 siblings, 2 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
The C implementations of `this_cpu_add`, `this_cpu_sub`, etc., are
optimized to save an instruction by avoiding having to compute
`this_cpu_ptr(&x)` for some per-CPU variable `x`. For example, rather
than
u64 *x_ptr = this_cpu_ptr(&x);
*x_ptr += 5;
the implementation of `this_cpu_add` is clever enough to make use of the
fact that per-CPU variables are implemented on x86 via segment
registers, and so we can use only a single instruction (where we assume
`&x` is already in `rax`)
add gs:[rax], 5
Add this optimization via a `PerCpuNumeric` type to enable code-reuse
between `DynamicPerCpu` and `StaticPerCpu`.
Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
---
lib/percpu_test_rust.rs | 36 +++++++++++++
rust/kernel/percpu.rs | 1 +
rust/kernel/percpu/numeric.rs | 117 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 154 insertions(+)
diff --git a/lib/percpu_test_rust.rs b/lib/percpu_test_rust.rs
index a9652e6ece08..114015435a85 100644
--- a/lib/percpu_test_rust.rs
+++ b/lib/percpu_test_rust.rs
@@ -25,6 +25,26 @@
define_per_cpu!(PERCPU: i64 = 0);
define_per_cpu!(UPERCPU: u64 = 0);
+macro_rules! make_optimization_test {
+ ($ty:ty) => {
+ let mut test: DynamicPerCpu<$ty> = DynamicPerCpu::new(GFP_KERNEL).unwrap();
+ {
+ let _ = CpuGuard::new();
+ // SAFETY: No other usage of `test`
+ unsafe { test.get(CpuGuard::new()) }.with(|val: &mut $ty| *val = 10);
+ test.num().add(1);
+ // SAFETY: No other usage of `test`
+ unsafe { test.get(CpuGuard::new()) }.with(|val: &mut $ty| assert_eq!(*val, 11));
+ test.num().add(10);
+ // SAFETY: No other usage of `test`
+ unsafe { test.get(CpuGuard::new()) }.with(|val: &mut $ty| assert_eq!(*val, 21));
+ test.num().sub(5);
+ // SAFETY: No other usage of `test`
+ unsafe { test.get(CpuGuard::new()) }.with(|val: &mut $ty| assert_eq!(*val, 16));
+ }
+ };
+}
+
impl kernel::Module for PerCpuTestModule {
fn init(_module: &'static ThisModule) -> Result<Self, Error> {
pr_info!("rust percpu test start\n");
@@ -94,6 +114,22 @@ fn init(_module: &'static ThisModule) -> Result<Self, Error> {
pr_info!("rust dynamic percpu test done\n");
+ pr_info!("rust numeric optimizations test start\n");
+
+ make_optimization_test!(u8);
+ make_optimization_test!(u16);
+ make_optimization_test!(u32);
+ make_optimization_test!(u64);
+ make_optimization_test!(usize);
+
+ make_optimization_test!(i8);
+ make_optimization_test!(i16);
+ make_optimization_test!(i32);
+ make_optimization_test!(i64);
+ make_optimization_test!(isize);
+
+ pr_info!("rust numeric optimizations test done\n");
+
// Return Err to unload the module
Result::Err(EINVAL)
}
diff --git a/rust/kernel/percpu.rs b/rust/kernel/percpu.rs
index a912f74349e0..9659bff2d889 100644
--- a/rust/kernel/percpu.rs
+++ b/rust/kernel/percpu.rs
@@ -2,6 +2,7 @@
//! This module contains abstractions for creating and using per-CPU variables from Rust.
//! See the define_per_cpu! macro and the PerCpu<T> type.
pub mod cpu_guard;
+pub mod numeric;
use bindings::{alloc_percpu, free_percpu};
diff --git a/rust/kernel/percpu/numeric.rs b/rust/kernel/percpu/numeric.rs
new file mode 100644
index 000000000000..e4008f872af1
--- /dev/null
+++ b/rust/kernel/percpu/numeric.rs
@@ -0,0 +1,117 @@
+// SPDX-License-Identifier: GPL-2.0
+//! Pin-hole optimizations for PerCpu<T> where T is a numeric type.
+
+use crate::percpu::*;
+use core::arch::asm;
+
+/// Represents a per-CPU variable that can be manipulated with machine-intrinsic numeric
+/// operations.
+pub struct PerCpuNumeric<'a, T> {
+ ptr: &'a PerCpuPtr<T>,
+}
+
+macro_rules! impl_ops {
+ ($ty:ty, $reg:tt) => {
+ impl DynamicPerCpu<$ty> {
+ /// Returns a `PerCpuNumeric` that can be used to manipulate the underlying per-CPU variable.
+ pub fn num(&self) -> PerCpuNumeric<'_, $ty> {
+ PerCpuNumeric { ptr: &self.alloc.0 }
+ }
+ }
+ impl StaticPerCpu<$ty> {
+ /// Returns a `PerCpuNumeric` that can be used to manipulate the underlying per-CPU variable.
+ pub fn num(&self) -> PerCpuNumeric<'_, $ty> {
+ PerCpuNumeric { ptr: &self.0 }
+ }
+ }
+
+ impl PerCpuNumeric<'_, $ty> {
+ /// Adds `rhs` to the per-CPU variable.
+ pub fn add(&mut self, rhs: $ty) {
+ // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
+ // pointer relative to the `gs` segment register) by the invariants of PerCpu.
+ unsafe {
+ asm!(
+ concat!("add gs:[{off}], {val:", $reg, "}"),
+ off = in(reg) self.ptr.0 as *mut $ty,
+ val = in(reg) rhs,
+ );
+ }
+ }
+ }
+ impl PerCpuNumeric<'_, $ty> {
+ /// Subtracts `rhs` from the per-CPU variable.
+ pub fn sub(&mut self, rhs: $ty) {
+ // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
+ // pointer relative to the `gs` segment register) by the invariants of PerCpu.
+ unsafe {
+ asm!(
+ concat!("sub gs:[{off}], {val:", $reg, "}"),
+ off = in(reg) self.ptr.0 as *mut $ty,
+ val = in(reg) rhs,
+ );
+ }
+ }
+ }
+ };
+}
+
+macro_rules! impl_ops_byte {
+ ($ty:ty) => {
+ impl DynamicPerCpu<$ty> {
+ /// Returns a `PerCpuNumeric` that can be used to manipulate the underlying per-CPU
+ /// variable.
+ pub fn num(&self) -> PerCpuNumeric<'_, $ty> {
+ PerCpuNumeric { ptr: &self.alloc.0 }
+ }
+ }
+ impl StaticPerCpu<$ty> {
+ /// Returns a `PerCpuNumeric` that can be used to manipulate the underlying per-CPU
+ /// variable.
+ pub fn num(&self) -> PerCpuNumeric<'_, $ty> {
+ PerCpuNumeric { ptr: &self.0 }
+ }
+ }
+
+ impl PerCpuNumeric<'_, $ty> {
+ /// Adds `rhs` to the per-CPU variable.
+ pub fn add(&mut self, rhs: $ty) {
+ // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
+ // pointer relative to the `gs` segment register) by the invariants of PerCpu.
+ unsafe {
+ asm!(
+ concat!("add gs:[{off}], {val}"),
+ off = in(reg) self.ptr.0 as *mut $ty,
+ val = in(reg_byte) rhs,
+ );
+ }
+ }
+ }
+ impl PerCpuNumeric<'_, $ty> {
+ /// Subtracts `rhs` from the per-CPU variable.
+ pub fn sub(&mut self, rhs: $ty) {
+ // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
+ // pointer relative to the `gs` segment register) by the invariants of PerCpu.
+ unsafe {
+ asm!(
+ concat!("sub gs:[{off}], {val}"),
+ off = in(reg) self.ptr.0 as *mut $ty,
+ val = in(reg_byte) rhs,
+ );
+ }
+ }
+ }
+ };
+}
+
+impl_ops_byte!(i8);
+impl_ops!(i16, "x");
+impl_ops!(i32, "e");
+impl_ops!(i64, "r");
+impl_ops!(isize, "r");
+
+impl_ops_byte!(u8);
+impl_ops!(u16, "x");
+impl_ops!(u32, "e");
+impl_ops!(u64, "r");
+impl_ops!(usize, "r");
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* [PATCH 5/5] rust: percpu: cache per-CPU pointers in the dynamic case
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
` (3 preceding siblings ...)
2025-06-24 22:10 ` [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
@ 2025-06-24 22:10 ` Mitchell Levy
4 siblings, 0 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-24 22:10 UTC (permalink / raw)
To: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: linux-kernel, rust-for-linux, linux-mm, Mitchell Levy
Currently, the creation of a `PerCpuNumeric` requires a memory read via
the `Arc` managing the dynamic allocation. While the compiler might be
clever enough to consolidate these reads in some cases, the read must
happen *somewhere*, which, when we're concerning ourselves with
individual instructions, is a very high burden.
Instead, cache the `PerCpuPointer` inside the `DynamicPerCpu` structure;
then, the `Arc` is used solely to manage the allocation.
Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
---
rust/kernel/percpu.rs | 12 +++++++++---
rust/kernel/percpu/numeric.rs | 2 +-
2 files changed, 10 insertions(+), 4 deletions(-)
diff --git a/rust/kernel/percpu.rs b/rust/kernel/percpu.rs
index 9659bff2d889..ffcfe02124ab 100644
--- a/rust/kernel/percpu.rs
+++ b/rust/kernel/percpu.rs
@@ -23,7 +23,10 @@
/// Holds a dynamically-allocated per-CPU variable.
pub struct DynamicPerCpu<T> {
+ // INVARIANT: `ptr` is managed by `alloc` and the value of `ptr` does not change for the
+ // lifetime of `self`.
alloc: Arc<PerCpuAllocation<T>>,
+ ptr: PerCpuPtr<T>,
}
/// Holds a statically-allocated per-CPU variable.
@@ -199,9 +202,10 @@ impl<T> DynamicPerCpu<T> {
pub fn new(flags: Flags) -> Option<Self> {
let alloc: PerCpuAllocation<T> = PerCpuAllocation::new()?;
+ let ptr = alloc.0;
let arc = Arc::new(alloc, flags).ok()?;
- Some(Self { alloc: arc })
+ Some(Self { alloc: arc, ptr })
}
/// Wraps a `PerCpuAllocation<T>` in a `PerCpu<T>`
@@ -210,8 +214,9 @@ pub fn new(flags: Flags) -> Option<Self> {
/// * `alloc` - The allocation to use
/// * `flags` - The flags used to allocate an `Arc` that keeps track of the `PerCpuAllocation`.
pub fn new_from_allocation(alloc: PerCpuAllocation<T>, flags: Flags) -> Option<Self> {
+ let ptr = alloc.0;
let arc = Arc::new(alloc, flags).ok()?;
- Some(Self { alloc: arc })
+ Some(Self { alloc: arc, ptr })
}
}
@@ -219,7 +224,7 @@ pub fn new_from_allocation(alloc: PerCpuAllocation<T>, flags: Flags) -> Option<S
// don't deallocate the underlying `PerCpuAllocation` until `self` is dropped.
unsafe impl<T> PerCpu<T> for DynamicPerCpu<T> {
unsafe fn ptr(&mut self) -> &PerCpuPtr<T> {
- &self.alloc.0
+ &self.ptr
}
}
@@ -227,6 +232,7 @@ impl<T> Clone for DynamicPerCpu<T> {
fn clone(&self) -> Self {
Self {
alloc: self.alloc.clone(),
+ ptr: self.ptr,
}
}
}
diff --git a/rust/kernel/percpu/numeric.rs b/rust/kernel/percpu/numeric.rs
index e4008f872af1..1b37cc7e5c19 100644
--- a/rust/kernel/percpu/numeric.rs
+++ b/rust/kernel/percpu/numeric.rs
@@ -62,7 +62,7 @@ impl DynamicPerCpu<$ty> {
/// Returns a `PerCpuNumeric` that can be used to manipulate the underlying per-CPU
/// variable.
pub fn num(&self) -> PerCpuNumeric<'_, $ty> {
- PerCpuNumeric { ptr: &self.alloc.0 }
+ PerCpuNumeric { ptr: &self.ptr }
}
}
impl StaticPerCpu<$ty> {
--
2.34.1
^ permalink raw reply related [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics
2025-06-24 22:10 ` [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
@ 2025-06-25 17:21 ` Christoph Lameter (Ampere)
2025-06-26 18:50 ` Mitchell Levy
2025-06-25 17:23 ` Christoph Lameter (Ampere)
1 sibling, 1 reply; 11+ messages in thread
From: Christoph Lameter (Ampere) @ 2025-06-25 17:21 UTC (permalink / raw)
To: Mitchell Levy
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Danilo Krummrich,
Benno Lossin, linux-kernel, rust-for-linux, linux-mm
On Tue, 24 Jun 2025, Mitchell Levy wrote:
> The C implementations of `this_cpu_add`, `this_cpu_sub`, etc., are
> optimized to save an instruction by avoiding having to compute
> `this_cpu_ptr(&x)` for some per-CPU variable `x`. For example, rather
> than
Cool. Great progress for Rust support. Maybe we can switch the SLUB
allocator over or come up with SLRB for the Slab Rust allocator ;-)
> + impl PerCpuNumeric<'_, $ty> {
> + /// Adds `rhs` to the per-CPU variable.
> + pub fn add(&mut self, rhs: $ty) {
> + // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
> + // pointer relative to the `gs` segment register) by the invariants of PerCpu.
> + unsafe {
> + asm!(
> + concat!("add gs:[{off}], {val}"),
> + off = in(reg) self.ptr.0 as *mut $ty,
> + val = in(reg_byte) rhs,
That looks arch specific to x86? What about ARM and other platforms?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics
2025-06-24 22:10 ` [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
2025-06-25 17:21 ` Christoph Lameter (Ampere)
@ 2025-06-25 17:23 ` Christoph Lameter (Ampere)
2025-06-26 18:55 ` Mitchell Levy
1 sibling, 1 reply; 11+ messages in thread
From: Christoph Lameter (Ampere) @ 2025-06-25 17:23 UTC (permalink / raw)
To: Mitchell Levy
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Danilo Krummrich,
Benno Lossin, linux-kernel, rust-for-linux, linux-mm
On Tue, 24 Jun 2025, Mitchell Levy wrote:
> + concat!("add gs:[{off}], {val:", $reg, "}"),
> + concat!("sub gs:[{off}], {val:", $reg, "}"),
> + concat!("sub gs:[{off}], {val}"),
Where are the other RMV instructions like this_cpu_xchg and
this_cpu_cmpxchg, this_cpu_cmpxchg_double etc?
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables
2025-06-24 22:10 ` [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
@ 2025-06-26 14:05 ` kernel test robot
0 siblings, 0 replies; 11+ messages in thread
From: kernel test robot @ 2025-06-26 14:05 UTC (permalink / raw)
To: Mitchell Levy, Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Christoph Lameter,
Danilo Krummrich, Benno Lossin
Cc: llvm, oe-kbuild-all, Linux Memory Management List, linux-kernel,
rust-for-linux, Mitchell Levy
Hi Mitchell,
kernel test robot noticed the following build errors:
[auto build test ERROR on 19272b37aa4f83ca52bdf9c16d5d81bdd1354494]
url: https://github.com/intel-lab-lkp/linux/commits/Mitchell-Levy/rust-percpu-introduce-a-rust-API-for-per-CPU-variables/20250625-061219
base: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
patch link: https://lore.kernel.org/r/20250624-rust-percpu-v1-1-9c59b07d2a9c%40gmail.com
patch subject: [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables
config: x86_64-buildonly-randconfig-004-20250626 (https://download.01.org/0day-ci/archive/20250626/202506262153.vo9sMbM8-lkp@intel.com/config)
compiler: clang version 20.1.7 (https://github.com/llvm/llvm-project 6146a88f60492b520a36f8f8f3231e15f3cc6082)
rustc: rustc 1.78.0 (9b00956e5 2024-04-29)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20250626/202506262153.vo9sMbM8-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202506262153.vo9sMbM8-lkp@intel.com/
All errors (new ones prefixed by >>):
>> error[E0425]: cannot find function `align_of` in this scope
--> rust/kernel/percpu.rs:106:65
|
106 | let ptr: *mut T = unsafe { alloc_percpu(size_of::<T>(), align_of::<T>()) } as *mut T;
| ^^^^^^^^ not found in this scope
|
help: consider importing this function
|
6 + use core::mem::align_of;
|
--
>> error: use of extern static is unsafe and requires unsafe block (error E0133)
--> rust/kernel/percpu.rs:71:53
|
71 | let this_cpu_off_pcpu = core::ptr::addr_of!(this_cpu_off);
| ^^^^^^^^^^^^ use of extern static
|
= note: for more information, see issue #71668 <https://github.com/rust-lang/rust/issues/71668>
= note: extern statics are not controlled by the Rust type system: invalid data, aliasing violations or data races will cause undefined behavior
note: an unsafe function restricts its caller, but its body is safe by default
--> rust/kernel/percpu.rs:70:5
|
70 | pub unsafe fn get_ref(&self) -> &mut T {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: requested on the command line with `-D unsafe-op-in-unsafe-fn`
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics
2025-06-25 17:21 ` Christoph Lameter (Ampere)
@ 2025-06-26 18:50 ` Mitchell Levy
0 siblings, 0 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-26 18:50 UTC (permalink / raw)
To: Christoph Lameter (Ampere)
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Danilo Krummrich,
Benno Lossin, linux-kernel, rust-for-linux, linux-mm
On Wed, Jun 25, 2025 at 10:21:17AM -0700, Christoph Lameter (Ampere) wrote:
> On Tue, 24 Jun 2025, Mitchell Levy wrote:
>
> > The C implementations of `this_cpu_add`, `this_cpu_sub`, etc., are
> > optimized to save an instruction by avoiding having to compute
> > `this_cpu_ptr(&x)` for some per-CPU variable `x`. For example, rather
> > than
>
> Cool. Great progress for Rust support. Maybe we can switch the SLUB
> allocator over or come up with SLRB for the Slab Rust allocator ;-)
Thank you!
I'm certainly very excited about the prospect of more Rust :)
>
> > + impl PerCpuNumeric<'_, $ty> {
> > + /// Adds `rhs` to the per-CPU variable.
> > + pub fn add(&mut self, rhs: $ty) {
> > + // SAFETY: `self.ptr.0` is a valid offset into the per-CPU area (i.e., valid as a
> > + // pointer relative to the `gs` segment register) by the invariants of PerCpu.
> > + unsafe {
> > + asm!(
> > + concat!("add gs:[{off}], {val}"),
> > + off = in(reg) self.ptr.0 as *mut $ty,
> > + val = in(reg_byte) rhs,
>
> That looks arch specific to x86? What about ARM and other platforms?
Yes; pretty much everything added by this series is x86_64 specific. In
`rust/kernel/lib.rs` the whole percpu module is gated behind
`#[cfg(CONFIG_X86_64)]`.
I'm certainly interested in adding support for ARM and other
architectures. That said, x86 is where I started, and since it's in a
workable state, I wanted to get some input from the list.
^ permalink raw reply [flat|nested] 11+ messages in thread
* Re: [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics
2025-06-25 17:23 ` Christoph Lameter (Ampere)
@ 2025-06-26 18:55 ` Mitchell Levy
0 siblings, 0 replies; 11+ messages in thread
From: Mitchell Levy @ 2025-06-26 18:55 UTC (permalink / raw)
To: Christoph Lameter (Ampere)
Cc: Miguel Ojeda, Alex Gaynor, Boqun Feng, Gary Guo,
Björn Roy Baron, Andreas Hindborg, Alice Ryhl, Trevor Gross,
Andrew Morton, Dennis Zhou, Tejun Heo, Danilo Krummrich,
Benno Lossin, linux-kernel, rust-for-linux, linux-mm
On Wed, Jun 25, 2025 at 10:23:42AM -0700, Christoph Lameter (Ampere) wrote:
> On Tue, 24 Jun 2025, Mitchell Levy wrote:
>
> > + concat!("add gs:[{off}], {val:", $reg, "}"),
>
> > + concat!("sub gs:[{off}], {val:", $reg, "}"),
>
> > + concat!("sub gs:[{off}], {val}"),
>
> Where are the other RMV instructions like this_cpu_xchg and
> this_cpu_cmpxchg, this_cpu_cmpxchg_double etc?
I wanted to stick with (what I saw as) the most basic operations for the
first submission. That said, I tried to design things such that adding
more operations should be straightforward. Happy to build on this
further for a v2 if folks are interested in that.
^ permalink raw reply [flat|nested] 11+ messages in thread
end of thread, other threads:[~2025-06-26 18:55 UTC | newest]
Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2025-06-24 22:10 [PATCH 0/5] rust: Add Per-CPU Variable API Mitchell Levy
2025-06-24 22:10 ` [PATCH 1/5] rust: percpu: introduce a rust API for per-CPU variables Mitchell Levy
2025-06-26 14:05 ` kernel test robot
2025-06-24 22:10 ` [PATCH 2/5] rust: rust-analyzer: add lib to dirs searched for crates Mitchell Levy
2025-06-24 22:10 ` [PATCH 3/5] rust: percpu: add a rust per-CPU variable test Mitchell Levy
2025-06-24 22:10 ` [PATCH 4/5] rust: percpu: Add pin-hole optimizations for numerics Mitchell Levy
2025-06-25 17:21 ` Christoph Lameter (Ampere)
2025-06-26 18:50 ` Mitchell Levy
2025-06-25 17:23 ` Christoph Lameter (Ampere)
2025-06-26 18:55 ` Mitchell Levy
2025-06-24 22:10 ` [PATCH 5/5] rust: percpu: cache per-CPU pointers in the dynamic case Mitchell Levy
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).