rust: Add Per-CPU Variable API
Subsystems (4)
v2 cover rust: Add Per-CPU Variable API
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.
---
---
Changes in v2:
- Fix kernel test robot issues
- Fix documentation error
- Require T: Zeroable in the dynamic case
- Link to v1: https://lore.kernel.org/r/[email protected]
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/[email protected]
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/[email protected]
---
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 | 315 ++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++ rust/kernel/percpu/numeric.rs | 117 ++++++++++++++ scripts/generate_rust_analyzer.py | 2 +- 11 files changed, 673 insertions(+), 1 deletion(-)
---
base-commit: 19272b37aa4f83ca52bdf9c16d5d81bdd1354494
change-id: 20240813-rust-percpu-ea2f54b5da33
Best regards,
Signature · 1 line
Mitchell Levy <[email protected]>
PATCH v2 1/5 rust: percpu: introduce a rust API for 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.
--- rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 9 ++ rust/helpers/preempt.c | 14 ++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 308 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++ 6 files changed, 371 insertions(+)
rust/helpers/helpers.c +2 -0
@@ -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"
rust/helpers/percpu.c new file +9 -0
@@ -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); +} +
rust/helpers/preempt.c new file +14 -0
@@ -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 (); +} +
rust/kernel/lib.rs +3 -0
@@ -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;
rust/kernel/percpu.rs new file +308 -0
308 lines not on this page · raw message
rust/kernel/percpu/cpu_guard.rs new file +35 -0
@@ -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 (); + } + } +}
Signature · 1 line
2.34.1
PATCH v2 2/5 rust: rust-analyzer: add lib to dirs searched for crates
In reply to Mitchell LevyWhen generating rust-project.json, also include crates in lib/
--- scripts/generate_rust_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/generate_rust_analyzer.py +1 -1
@@ -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:
Signature · 1 line
2.34.1
PATCH v2 3/5 rust: percpu: add a rust per-CPU variable test
In reply to Mitchell LevyAdd a short exercise for Rust's per-CPU variable API, modelled after lib/percpu_test.c
--- lib/Kconfig.debug | 9 ++++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++ rust/helpers/percpu.c | 11 +++++ 4 files changed, 141 insertions(+)
lib/Kconfig.debug +9 -0
@@ -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
lib/Makefile +1 -0
@@ -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
lib/percpu_test_rust.rs new file +120 -0
120 lines not on this page · raw message
rust/helpers/percpu.c +11 -0
@@ -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 (); +} +
Signature · 1 line
2.34.1
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 8 lines · Mitchell Levy
Add a short exercise for Rust's per-CPU variable API, modelled after
lib/percpu_test.cSigned-off-by Mitchell Levy <[email protected]>--- lib/Kconfig.debug | 9 ++++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
I don't know if this is the correct place, the code looks much more like a sample, so why not place it there instead?
Quoted 32 lines
rust/helpers/percpu.c | 11 +++++ 4 files changed, 141 insertions(+)lib/percpu_test_rust.rs new file +24
-0@@ -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 ; +
lib/percpu_test_rust.rs line 34
Quoted 10 lines
lib/percpu_test_rust.rs new file +10
-0+define_per_cpu! (PERCPU :i64 =0 ); +define_per_cpu! (UPERCPU :u64 =0 ); + +impl kernel::Module for PerCpuTestModule { +fn init (_module: &'staticThisModule ) ->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 ) };
I don't understand why we need unsafe here, can't we just create something specially in the define_per_cpu macro that is then confirmed by the get_per_cpu! macro and thus it can be safe?
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {
Hmm I also don't like the unsafe part here...
Can't we use the same API that thread_local! in the standard library has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a Cell<i64> instead.
I'm not familiar with per CPU variables, but if you're usually storing Copy types, then this is much better wrt not having unsafe code everywhere.
If one also often stores !Copy types, then we might be able to get away with RefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to store UnsafeCell and use unsafe regardless. (or we invent something specifically for that case, eg tokens that are statically known to be unique etc)
---
Cheers,
Benno
Quoted 12 lines
lib/percpu_test_rust.rs new file +12
-0+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 ); + });
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 4 lines · Benno Lossin
Quoted 3 lines · Mitchell Levy
Add a short exercise for Rust's per-CPU variable API, modelled after
lib/percpu_test.c
Quoted 8 lines
Quoted 5 lines
Signed-off-by Mitchell Levy <[email protected]>--- lib/Kconfig.debug | 9 ++++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++I don't know if this is the correct place, the code looks much more like a sample, so why not place it there instead?
I don't feel particularly strongly either way --- I defaulted to lib/ since that's where the percpu_test.c I was working off of is located. Happy to change for v3
Quoted 38 lines
Quoted 38 lines
rust/helpers/percpu.c | 11 +++++ 4 files changed, 141 insertions(+)lib/percpu_test_rust.rs new file +30
-0@@ -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: &'staticThisModule ) ->Result <Self ,Error > { +pr_info! ("rust percpu test start\n" );
Quoted 8 lines
lib/percpu_test_rust.rs line 34
Quoted 4 lines
lib/percpu_test_rust.rs new file +4
-0+ +let mut native:i64 =0 ; +// SAFETY: PERCPU is properly defined +let mut pcpu:StaticPerCpu <i64 > =unsafe {unsafe_get_per_cpu! (PERCPU ) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?
As is, something like
define_per_cpu!(PERCPU: i32 = 0);
fn func() {
let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) };
}will compile, but any usage of pcpu will be UB. This is because unsafe_get_per_cpu! is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)
Quoted 16 lines
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard library has:https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.
Quoted 7 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent something specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for T: Copy in a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facing unsafe. But yes, I believe there is a significant amount of interest in having !Copy per-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)
I would definitely like to avoid *requiring* the use of RefCell since, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a &T (rather than a &mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can choose T to be a RefCell. Thoughts?
For UnsafeCell, if a user of the API were to have something like a PerCpu<UnsafeCell<T>> that safely spits out a &UnsafeCell<T>, my understanding is that mutating the underlying T would require the exact same safety guarantees as what's here, except now it'd need a much bigger unsafe block and would have to do all of its manipulations via pointers. That seems like a pretty big ergonomics burden without a clear (to me) benefit.
Quoted 16 lines
---
Cheers,
BennoQuoted 12 lines
lib/percpu_test_rust.rs new file +12
-0+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 ); + });
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 11 lines · Mitchell Levy
Quoted 10 lines · Benno Lossin
Quoted 8 lines · Mitchell Levy
Add a short exercise for Rust's per-CPU variable API, modelled after
lib/percpu_test.cSigned-off-by Mitchell Levy <[email protected]>--- lib/Kconfig.debug | 9 ++++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 120 ++++++++++++++++++++++++++++++++++++++++++++++++
Quoted 6 lines
Quoted 2 lines
I don't know if this is the correct place, the code looks much more like a sample, so why not place it there instead?
I don't feel particularly strongly either way --- I defaulted to
lib/since that's where thepercpu_test.cI was working off of is located. Happy to change for v3
Since we don't have Rust stuff in lib/ yet (and that the code looks much more like the samples we already have) I think putting it in samples/rust is better.
Quoted 53 lines
Quoted 38 lines
Quoted 38 lines
rust/helpers/percpu.c | 11 +++++ 4 files changed, 141 insertions(+)lib/percpu_test_rust.rs new file +30
-0@@ -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: &'staticThisModule ) ->Result <Self ,Error > { +pr_info! ("rust percpu test start\n" );Quoted 8 lines
lib/percpu_test_rust.rs line 34
Quoted 4 lines
lib/percpu_test_rust.rs new file +4
-0+ +let mut native:i64 =0 ; +// SAFETY: PERCPU is properly defined +let mut pcpu:StaticPerCpu <i64 > =unsafe {unsafe_get_per_cpu! (PERCPU ) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?As is, something like
define_per_cpu!(PERCPU: i32 = 0); fn func() { let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) }; }
Quoted 5 lines
will compile, but any usage of
pcpuwill be UB. This is becauseunsafe_get_per_cpu!is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)
I haven't taken a look at your implementation, but you do have the type declared in define_per_cpu!, so it's a bit of a mystery to me why you can't get that out in unsafe_get_per_cpu!...
Maybe in a few weeks I'll be able to take a closer look.
Quoted 24 lines
Quoted 16 lines
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard library has:https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.Quoted 7 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent something specifically for that case, eg tokens that are statically known to be unique etc)
Quoted 6 lines
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
Quoted 9 lines
I would definitely like to avoid *requiring* the use of
RefCellsince, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a&T(rather than a&mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can chooseTto be aRefCell. Thoughts?
So I think using an API similar to thread_local! will allow us to have multiple other APIs that slot into that. Cell<T> for T: Copy, RefCell<T> for cases where you don't care about the runtime overhead, plain T for cases where you only need &T. For the case where you need &mut T, we could have something like a TokenCell<T> that gives out a token that you need to mutably borrow in order to get &mut T. Finally for anything else that is too restricted by this, users can also use UnsafeCell<T> although that requires unsafe.
I think the advantage of this is that the common cases are all safe and very idiomatic. In the current design, you *always* have to use unsafe.
Quoted 7 lines
For
UnsafeCell, if a user of the API were to have something like aPerCpu<UnsafeCell<T>>that safely spits out a&UnsafeCell<T>, my understanding is that mutating the underlyingTwould require the exact same safety guarantees as what's here, except now it'd need a much bigger unsafe block and would have to do all of its manipulations via pointers. That seems like a pretty big ergonomics burden without a clear (to me) benefit.
It would require the same amount of unsafe & safety comments, but it wouldn't be bigger comments, since you can just as well create &mut T to the value.
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
On Tue, Jul 15, 2025 at 01:31:06PM +0200, Benno Lossin wrote:
[...]
Quoted 24 lines
Quoted 18 lines
Quoted 11 lines
lib/percpu_test_rust.rs line 34
Quoted 7 lines
lib/percpu_test_rust.rs new file +7
-0+impl kernel::Module for PerCpuTestModule { +fn init (_module: &'staticThisModule ) ->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 ) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?As is, something like
define_per_cpu!(PERCPU: i32 = 0); fn func() { let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) }; }Quoted 5 lines
will compile, but any usage of
pcpuwill be UB. This is becauseunsafe_get_per_cpu!is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)
Quoted 12 lines
I haven't taken a look at your implementation, but you do have the type declared in
define_per_cpu!, so it's a bit of a mystery to me why you can't get that out inunsafe_get_per_cpu!...Maybe in a few weeks I'll be able to take a closer look.
Quoted 6 lines
Quoted 6 lines
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard library
First of all, thread_local! has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's using pthread_key_t:
https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.html
what we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent something
This sounds reasonable to me.
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)
Quoted 3 lines
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
In the VMBus driver, there is a percpu work_struct.
Quoted 19 lines
Quoted 9 lines
I would definitely like to avoid *requiring* the use of
RefCellsince, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a&T(rather than a&mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can chooseTto be aRefCell. Thoughts?So I think using an API similar to
thread_local!will allow us to have multiple other APIs that slot into that.Cell<T>forT: Copy,RefCell<T>for cases where you don't care about the runtime overhead, plainTfor cases where you only need&T. For the case where you need&mut T, we could have something like aTokenCell<T>that gives out a token that you need to mutably borrow in order to get&mut T. Finally for anything else that is too restricted by this, users can also useUnsafeCell<T>although that requiresunsafe.
Quoted 3 lines
I think the advantage of this is that the common cases are all safe and very idiomatic. In the current design, you *always* have to use unsafe.
I agree, but like I said, we need to figure out the unsafe interface that C already uses and build API upon it. I think focusing on the unsafe mechanism may be the way to start: you cannot implement something that cannot be implemented, and we don't have the magic pthread_key here ;-)
Regards,
Boqun
Quoted 15 lines
Quoted 7 lines
For
UnsafeCell, if a user of the API were to have something like aPerCpu<UnsafeCell<T>>that safely spits out a&UnsafeCell<T>, my understanding is that mutating the underlyingTwould require the exact same safety guarantees as what's here, except now it'd need a much bigger unsafe block and would have to do all of its manipulations via pointers. That seems like a pretty big ergonomics burden without a clear (to me) benefit.It would require the same amount of unsafe & safety comments, but it wouldn't be bigger comments, since you can just as well create
&mut Tto the value.---
Cheers,
Benno
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 43 lines · Boqun Feng
On Tue, Jul 15, 2025 at 01:31:06PM +0200, Benno Lossin wrote:
[...]Quoted 24 lines
Quoted 18 lines
Quoted 11 lines
lib/percpu_test_rust.rs line 34
Quoted 7 lines
lib/percpu_test_rust.rs new file +7
-0+impl kernel::Module for PerCpuTestModule { +fn init (_module: &'staticThisModule ) ->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 ) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?As is, something like
define_per_cpu!(PERCPU: i32 = 0); fn func() { let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) }; }Quoted 5 lines
will compile, but any usage of
pcpuwill be UB. This is becauseunsafe_get_per_cpu!is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)Quoted 12 lines
I haven't taken a look at your implementation, but you do have the type declared in
define_per_cpu!, so it's a bit of a mystery to me why you can't get that out inunsafe_get_per_cpu!...Maybe in a few weeks I'll be able to take a closer look.
Quoted 6 lines
Quoted 6 lines
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard libraryFirst of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:
Quoted 6 lines
https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
But this is intended to be used by drivers, right? If so, then we should do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to thread_local!, but it's bound to the specific cpu instead of the thread.
That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it !Send & !Sync + rely on klint to detect context switches.
Quoted 28 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent somethingThis sounds reasonable to me.
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)
Quoted 5 lines
Quoted 3 lines
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
In the VMBus driver, there is a percpu
work_struct.
Do you have a link? Or better yet a Rust struct description of what you think it will look like :)
Quoted 23 lines
Quoted 19 lines
Quoted 9 lines
I would definitely like to avoid *requiring* the use of
RefCellsince, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a&T(rather than a&mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can chooseTto be aRefCell. Thoughts?So I think using an API similar to
thread_local!will allow us to have multiple other APIs that slot into that.Cell<T>forT: Copy,RefCell<T>for cases where you don't care about the runtime overhead, plainTfor cases where you only need&T. For the case where you need&mut T, we could have something like aTokenCell<T>that gives out a token that you need to mutably borrow in order to get&mut T. Finally for anything else that is too restricted by this, users can also useUnsafeCell<T>although that requiresunsafe.Quoted 3 lines
I think the advantage of this is that the common cases are all safe and very idiomatic. In the current design, you *always* have to use unsafe.
Quoted 5 lines
I agree, but like I said, we need to figure out the unsafe interface that C already uses and build API upon it. I think focusing on the unsafe mechanism may be the way to start: you cannot implement something that cannot be implemented, and we don't have the magic
pthread_keyhere ;-)
Sure we can do some experimentation, but I don't think we should put unsafe abstractions upstream that we intend to replace with a safe abstraction later. Otherwise people are going to depend on it and it's going to be a mess. Do the experimenting out of tree and learn there.
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 44 lines · Benno Lossin
Quoted 43 lines · Boqun Feng
On Tue, Jul 15, 2025 at 01:31:06PM +0200, Benno Lossin wrote:
[...]Quoted 24 lines
Quoted 18 lines
Quoted 11 lines
lib/percpu_test_rust.rs line 34
Quoted 7 lines
lib/percpu_test_rust.rs new file +7
-0+impl kernel::Module for PerCpuTestModule { +fn init (_module: &'staticThisModule ) ->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 ) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?As is, something like
define_per_cpu!(PERCPU: i32 = 0); fn func() { let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) }; }Quoted 5 lines
will compile, but any usage of
pcpuwill be UB. This is becauseunsafe_get_per_cpu!is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)Quoted 12 lines
I haven't taken a look at your implementation, but you do have the type declared in
define_per_cpu!, so it's a bit of a mystery to me why you can't get that out inunsafe_get_per_cpu!...Maybe in a few weeks I'll be able to take a closer look.
Quoted 6 lines
Quoted 6 lines
lib/percpu_test_rust.rs line 36
Quoted 2 lines
lib/percpu_test_rust.rs new file +2
-0+// SAFETY: We only have one PerCpu that points at PERCPU +unsafe { pcpu.get (CpuGuard ::new ()) }.with (|val: &mut i64 | {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard libraryFirst of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:
Quoted 8 lines
Quoted 6 lines
https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klint
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
If we would like to model it conceptually, it's more like an array that's index by CpuId to me.
Quoted 30 lines
to detect context switches.
Quoted 28 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent somethingThis sounds reasonable to me.
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)
Quoted 9 lines
Quoted 5 lines
Quoted 3 lines
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
In the VMBus driver, there is a percpu
work_struct.Do you have a link? Or better yet a Rust struct description of what you think it will look like :)
Not Rust code yet, but here is the corresponding C code:
https://github.com/Rust-for-Linux/linux/blob/rust-next/drivers/hv/vmbus_drv.c#L1396
But please note that we are not solely developing the abstraction for this usage, but more for generally understand how to wrap percpu functionality similar to the usage in C.
Quoted 29 lines
Quoted 23 lines
Quoted 19 lines
Quoted 9 lines
I would definitely like to avoid *requiring* the use of
RefCellsince, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a&T(rather than a&mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can chooseTto be aRefCell. Thoughts?So I think using an API similar to
thread_local!will allow us to have multiple other APIs that slot into that.Cell<T>forT: Copy,RefCell<T>for cases where you don't care about the runtime overhead, plainTfor cases where you only need&T. For the case where you need&mut T, we could have something like aTokenCell<T>that gives out a token that you need to mutably borrow in order to get&mut T. Finally for anything else that is too restricted by this, users can also useUnsafeCell<T>although that requiresunsafe.Quoted 3 lines
I think the advantage of this is that the common cases are all safe and very idiomatic. In the current design, you *always* have to use unsafe.
Quoted 5 lines
I agree, but like I said, we need to figure out the unsafe interface that C already uses and build API upon it. I think focusing on the unsafe mechanism may be the way to start: you cannot implement something that cannot be implemented, and we don't have the magic
pthread_keyhere ;-)
Quoted 3 lines
Sure we can do some experimentation, but I don't think we should put unsafe abstractions upstream that we intend to replace with a safe abstraction later. Otherwise people are going to depend on it and it's
I doubt we can replace the unsafe abstraction with a safe one, if users really care the performance then they would really need to use some unsafe API to build their safe abstraction.
Quoted 1 line
going to be a mess. Do the experimenting out of tree and learn there.
I disagree, Rust as a language its own should be able to do what C does including being able to implement the percpu functionality same as C, there is nothing wrong with a set of Rust primitives in the kernel that provides fundamental percpu functionality the other core facilities can rely on. The better part is that it will have all the safety requirement documented well.
Regards,
Boqun
Quoted 4 lines
---
Cheers,
Benno
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 52 lines · Boqun Feng
Quoted 51 lines · Benno Lossin
Quoted 49 lines · Boqun Feng
On Tue, Jul 15, 2025 at 01:31:06PM +0200, Benno Lossin wrote:
[...]Quoted 24 lines
Quoted 18 lines
Quoted 11 lines
Review
Quoted 7 lines
(quoted diff) +7
-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) };I don't understand why we need unsafe here, can't we just create something specially in the
define_per_cpumacro that is then confirmed by theget_per_cpu!macro and thus it can be safe?As is, something like
define_per_cpu!(PERCPU: i32 = 0); fn func() { let mut pcpu: StaticPerCpu<i64> = unsafe { unsafe_get_per_cpu!(PERCPU) }; }Quoted 5 lines
will compile, but any usage of
pcpuwill be UB. This is becauseunsafe_get_per_cpu!is just blindly casting pointers and, as far as I know, the compiler does not do any checking of pointer casts. If you have thoughts/ideas on how to get around this problem, I'd certainly *like* to provide a safe API here :)Quoted 12 lines
I haven't taken a look at your implementation, but you do have the type declared in
define_per_cpu!, so it's a bit of a mystery to me why you can't get that out inunsafe_get_per_cpu!...Maybe in a few weeks I'll be able to take a closer look.
Quoted 6 lines
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+ // SAFETY: We only have one PerCpu that points at PERCPU + unsafe { pcpu.get(CpuGuard::new()) }.with(|val: &mut i64| {Hmm I also don't like the unsafe part here...
Can't we use the same API that
thread_local!in the standard libraryFirst of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be pub(crate).
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Yeah. But then we should do that before merging :)
Quoted 11 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintNot really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Quoted 2 lines
If we would like to model it conceptually, it's more like an array that's index by CpuId to me.
Gotcha, but this model is missing the access control/synchronization. So I'm not so sure how useful it is.
(I think I asked this somewhere else, but the number of CPUs doesn't change, right?)
Quoted 36 lines
Quoted 36 lines
to detect context switches.
Quoted 33 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent somethingThis sounds reasonable to me.
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)Quoted 3 lines
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
In the VMBus driver, there is a percpu
work_struct.
Quoted 7 lines
Quoted 3 lines
Do you have a link? Or better yet a Rust struct description of what you think it will look like :)
Not Rust code yet, but here is the corresponding C code:
https://github.com/Rust-for-Linux/linux/blob/rust-next/drivers/hv/vmbus_drv.c#L1396
Thanks!
Quoted 3 lines
But please note that we are not solely developing the abstraction for this usage, but more for generally understand how to wrap percpu functionality similar to the usage in C.
Well, I have to start somewhere for looking at the use-cases :)
If you have more, just let me see. (probably won't have enough time to look at them now, but maybe in a couple weeks)
Quoted 29 lines
Quoted 29 lines
Quoted 28 lines
Quoted 19 lines
Quoted 9 lines
I would definitely like to avoid *requiring* the use of
RefCellsince, as you mention, it does have a runtime overhead. Per-CPU variables can be used for "logical" reasons rather than just as a performance optimization, so there might be some cases where paying the runtime overhead is ok. But that's certainly not true in all cases. That said, perhaps there could be a safely obtainable token type that only passes a&T(rather than a&mut T) to its closure, and then if a user doesn't mind the runtime overhead, they can chooseTto be aRefCell. Thoughts?So I think using an API similar to
thread_local!will allow us to have multiple other APIs that slot into that.Cell<T>forT: Copy,RefCell<T>for cases where you don't care about the runtime overhead, plainTfor cases where you only need&T. For the case where you need&mut T, we could have something like aTokenCell<T>that gives out a token that you need to mutably borrow in order to get&mut T. Finally for anything else that is too restricted by this, users can also useUnsafeCell<T>although that requiresunsafe.Quoted 3 lines
I think the advantage of this is that the common cases are all safe and very idiomatic. In the current design, you *always* have to use unsafe.
I agree, but like I said, we need to figure out the unsafe interface that C already uses and build API upon it. I think focusing on the unsafe mechanism may be the way to start: you cannot implement something that cannot be implemented, and we don't have the magic
pthread_keyhere ;-)
Quoted 7 lines
Quoted 3 lines
Sure we can do some experimentation, but I don't think we should put unsafe abstractions upstream that we intend to replace with a safe abstraction later. Otherwise people are going to depend on it and it's
I doubt we can replace the unsafe abstraction with a safe one, if users really care the performance then they would really need to use some unsafe API to build their safe abstraction.
That sounds pretty pessimistic, why do you think that?
Quoted 8 lines
Quoted 1 line
going to be a mess. Do the experimenting out of tree and learn there.
I disagree, Rust as a language its own should be able to do what C does including being able to implement the percpu functionality same as C, there is nothing wrong with a set of Rust primitives in the kernel that provides fundamental percpu functionality the other core facilities can rely on. The better part is that it will have all the safety requirement documented well.
Sure, but we haven't even tried to make it safe, so I don't think we should add them now in this state.
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]
Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).
Fine by me, but please see below.
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Yeah. But then we should do that before merging :)
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klint
Quoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Keyword find all these cases are per_cpu_ptr():
https://elixir.bootlin.com/linux/v6.15.6/A/ident/per_cpu_ptr
Quoted 9 lines
Quoted 2 lines
If we would like to model it conceptually, it's more like an array that's index by CpuId to me.
Gotcha, but this model is missing the access control/synchronization. So I'm not so sure how useful it is.
(I think I asked this somewhere else, but the number of CPUs doesn't change, right?)
In terms of percpu variable, yes. A percpu variable is even available for an offline CPU.
Quoted 46 lines
Quoted 36 lines
Quoted 36 lines
to detect context switches.
Quoted 33 lines
Quoted 10 lines
Quoted 10 lines
Quoted 10 lines
has:
https://doc.rust-lang.org/std/macro.thread_local.html
So in this example you would store a
Cell<i64>instead.I'm not familiar with per CPU variables, but if you're usually storing
Copytypes, then this is much better wrt not having unsafe code everywhere.Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
If one also often stores
!Copytypes, then we might be able to get away withRefCell, but that's a small runtime overhead -- which is probably bad given that per cpu variables are most likely used for performance reasons? In that case the user might just need to storeUnsafeCelland use unsafe regardless. (or we invent somethingThis sounds reasonable to me.
Quoted 10 lines
Quoted 9 lines
Quoted 2 lines
specifically for that case, eg tokens that are statically known to be unique etc)
I'm open to including a specialization for
T: Copyin a similar vein to what I have here for numeric types. Off the top of my head, that shouldn't require any user-facingunsafe. But yes, I believe there is a significant amount of interest in having!Copyper-CPU variables. (At least, I'm interested in having them around for experimenting with using Rust for HV drivers.)Quoted 3 lines
What kinds of types would you like to store? Allocations? Just integers in bigger structs? Mutexes?
In the VMBus driver, there is a percpu
work_struct.Quoted 7 lines
Quoted 3 lines
Do you have a link? Or better yet a Rust struct description of what you think it will look like :)
Not Rust code yet, but here is the corresponding C code:
https://github.com/Rust-for-Linux/linux/blob/rust-next/drivers/hv/vmbus_drv.c#L1396Thanks!
Quoted 9 lines
Quoted 3 lines
But please note that we are not solely developing the abstraction for this usage, but more for generally understand how to wrap percpu functionality similar to the usage in C.
Well, I have to start somewhere for looking at the use-cases :)
If you have more, just let me see. (probably won't have enough time to look at them now, but maybe in a couple weeks)
If you have time, feel free to take a look at hazard pointers;
https://lore.kernel.org/lkml/[email protected]/
https://lore.kernel.org/lkml/[email protected]/
You can also take a look at existing usage of percpu, e.g. SRCU uses to track how many readers are active:
https://elixir.bootlin.com/linux/v6.15.6/source/kernel/rcu/srcutree.c#L577
[...]
Regards,
Boqun
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 32 lines · Boqun Feng
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).Fine by me, but please see below.
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Quoted 5 lines
Quoted 2 lines
Yeah. But then we should do that before merging :)
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
I understand [PATCH] emails as "I want to merge this" and [RFC PATCH] as "I want to talk about merging this". It might be that I haven't seen the RFC patch series, because I often mute those.
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintQuoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Quoted 6 lines
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Sure, so we probably want direct atomics support. What about "extra synchronization"? Is that using locks or RCU or what else?
Quoted 3 lines
Keyword find all these cases are
per_cpu_ptr():https://elixir.bootlin.com/linux/v6.15.6/A/ident/per_cpu_ptr
Could you explain to me how to find them? I can either click on one of the files with horrible C preprocessor macros or the auto-completion in the search bar. But that one only shows 3 suggestions _hyp_sym, _nvhe_sym and _to_phys which doesn't really mean much to me.
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 33 lines · Benno Lossin
Quoted 32 lines · Boqun Feng
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).Fine by me, but please see below.
Quoted 8 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Quoted 7 lines
Quoted 5 lines
Quoted 2 lines
Yeah. But then we should do that before merging :)
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
I understand [PATCH] emails as "I want to merge this" and [RFC PATCH] as
But it doesn't mean "merge as it is", right? I don't think either I or Mitchell implied that, I'm surprised that you had to mention that, also based on "I often mute those" below, making it "[PATCH]" seems to be a practical way to get more attention if one wants to get some reviews.
Quoted 3 lines
"I want to talk about merging this". It might be that I haven't seen the RFC patch series, because I often mute those.
Well, then you cannot blame people to move from "RFC PATCH" to "PATCH" stage for more reviews, right? And you cannot make rules about what the difference between [PATCH] and [RFC PATCH] if you ignore one of them ;-)
Quoted 22 lines
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintQuoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Quoted 6 lines
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Quoted 3 lines
Sure, so we probably want direct atomics support. What about "extra synchronization"? Is that using locks or RCU or what else?
It's up to the users obviously, It could be some sort of locking or RCU, it's case by case.
Quoted 9 lines
Quoted 3 lines
Keyword find all these cases are
per_cpu_ptr():https://elixir.bootlin.com/linux/v6.15.6/A/ident/per_cpu_ptrCould you explain to me how to find them? I can either click on one of the files with horrible C preprocessor macros or the auto-completion in the search bar. But that one only shows 3 suggestions
_hyp_sym,_nvhe_symand_to_physwhich doesn't really mean much to me.
You need to find the usage of per_cpu_ptr(), which is a function that gives you a pointer to a percpu variable on the other CPU, and then that's usually the case where a "remote" read of percpu variable happens.
Regards,
Boqun
Quoted 3 lines
---
Cheers,
Benno
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 37 lines · Boqun Feng
Quoted 36 lines · Benno Lossin
Quoted 35 lines · Boqun Feng
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).Fine by me, but please see below.
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Yeah. But then we should do that before merging :)
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
I understand [PATCH] emails as "I want to merge this" and [RFC PATCH] as
But it doesn't mean "merge as it is", right? I don't think either I or Mitchell implied that, I'm surprised that you had to mention that,
Yeah that is true, but it at least shows the intention :)
Quoted 3 lines
also based on "I often mute those" below, making it "[PATCH]" seems to be a practical way to get more attention if one wants to get some reviews.
That is true, I do usually read the titles of RFC patches though and sometimes take a look eg your atomics series.
Quoted 7 lines
Quoted 3 lines
"I want to talk about merging this". It might be that I haven't seen the RFC patch series, because I often mute those.
Well, then you cannot blame people to move from "RFC PATCH" to "PATCH" stage for more reviews, right? And you cannot make rules about what the difference between [PATCH] and [RFC PATCH] if you ignore one of them ;-)
I'm not trying to blame anyone. I saw a lot of unsafe in the example and thought "we can do better" and since I haven't heard any sufficient arguments showing that it's impossible to improve, we should do some design work.
Quoted 22 lines
Quoted 22 lines
Quoted 21 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintQuoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Quoted 6 lines
Quoted 3 lines
Sure, so we probably want direct atomics support. What about "extra synchronization"? Is that using locks or RCU or what else?
It's up to the users obviously, It could be some sort of locking or RCU, it's case by case.
Makes sense, what do you need in the VMS driver?
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
Keyword find all these cases are
per_cpu_ptr():https://elixir.bootlin.com/linux/v6.15.6/A/ident/per_cpu_ptrCould you explain to me how to find them? I can either click on one of the files with horrible C preprocessor macros or the auto-completion in the search bar. But that one only shows 3 suggestions
_hyp_sym,_nvhe_symand_to_physwhich doesn't really mean much to me.
Quoted 4 lines
You need to find the usage of
per_cpu_ptr(), which is a function that gives you a pointer to a percpu variable on the other CPU, and then that's usually the case where a "remote" read of percpu variable happens.
Ahh gotcha, I thought you pointed me to some definitions of operations on percpu pointers.
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 59 lines · Benno Lossin
Quoted 37 lines · Boqun Feng
Quoted 36 lines · Benno Lossin
Quoted 35 lines · Boqun Feng
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).Fine by me, but please see below.
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Yeah. But then we should do that before merging :)
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
I understand [PATCH] emails as "I want to merge this" and [RFC PATCH] as
But it doesn't mean "merge as it is", right? I don't think either I or Mitchell implied that, I'm surprised that you had to mention that,
Yeah that is true, but it at least shows the intention :)
Quoted 3 lines
also based on "I often mute those" below, making it "[PATCH]" seems to be a practical way to get more attention if one wants to get some reviews.
That is true, I do usually read the titles of RFC patches though and sometimes take a look eg your atomics series.
Quoted 4 lines
Quoted 3 lines
"I want to talk about merging this". It might be that I haven't seen the RFC patch series, because I often mute those.
Quoted 9 lines
Quoted 3 lines
Well, then you cannot blame people to move from "RFC PATCH" to "PATCH" stage for more reviews, right? And you cannot make rules about what the difference between [PATCH] and [RFC PATCH] if you ignore one of them ;-)
I'm not trying to blame anyone. I saw a lot of unsafe in the example and thought "we can do better" and since I haven't heard any sufficient arguments showing that it's impossible to improve, we should do some design work.
I agree with you, and I like what you're proposing, but I think design work can be done at "PATCH" stage, right? And sometimes, it's also OK to do some design work even at some version like "v12" ;-)
Also I want to see more forward-progress actions about the design work improvement. For example, we can examine every case that makes unsafe_get_per_cpu!() unsafe, and see if we can improve that by typing or something else. We always can "do better", but the important part is how to get there ;-)
Quoted 22 lines
Quoted 22 lines
Quoted 22 lines
Quoted 21 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintQuoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Quoted 9 lines
Quoted 6 lines
Quoted 3 lines
Sure, so we probably want direct atomics support. What about "extra synchronization"? Is that using locks or RCU or what else?
It's up to the users obviously, It could be some sort of locking or RCU, it's case by case.
Makes sense, what do you need in the VMS driver?
In VMBus driver, it's actually isolate, i.e. each CPU only access it's own work_struct, so synchronization between CPUs is not needed.
Regards,
Boqun
Quoted 21 lines
Quoted 10 lines
Quoted 9 lines
Quoted 3 lines
Keyword find all these cases are
per_cpu_ptr():https://elixir.bootlin.com/linux/v6.15.6/A/ident/per_cpu_ptrCould you explain to me how to find them? I can either click on one of the files with horrible C preprocessor macros or the auto-completion in the search bar. But that one only shows 3 suggestions
_hyp_sym,_nvhe_symand_to_physwhich doesn't really mean much to me.Quoted 4 lines
You need to find the usage of
per_cpu_ptr(), which is a function that gives you a pointer to a percpu variable on the other CPU, and then that's usually the case where a "remote" read of percpu variable happens.Ahh gotcha, I thought you pointed me to some definitions of operations on percpu pointers.
---
Cheers,
Benno
Benno Lossin
reviewerRe: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
Quoted 70 lines · Boqun Feng
Quoted 59 lines · Benno Lossin
Quoted 37 lines · Boqun Feng
Quoted 36 lines · Benno Lossin
Quoted 35 lines · Boqun Feng
On Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]Quoted 12 lines
Quoted 12 lines
Quoted 12 lines
Quoted 11 lines
First of all,
thread_local!has to be implemented by some sys-specific unsafe mechanism, right? For example on unix, I think it's usingpthread_key_t:https://pubs.opengroup.org/onlinepubs/009695399/functions/pthread_key_create.htmlwhat we are implementing (or wrapping) is the very basic unsafe mechanism for percpu here. Surely we can explore the design for a safe API, but the unsafe mechanism is probably necessary to look into at first.
Quoted 7 lines
Quoted 4 lines
Quoted 1 line
But this is intended to be used by drivers, right? If so, then we should
Not necessarily only for drivers, we can also use it for implementing other safe abstraction (e.g. hazard pointers, percpu counters etc)
That's fair, but then it should be
pub(crate).Fine by me, but please see below.
Quoted 10 lines
Quoted 7 lines
Quoted 3 lines
do our usual due diligence and work out a safe abstraction. Only fall back to unsafe if it isn't possible.
All I'm saying is instead of figuring out a safe abstraction at first, we should probably focus on identifying how to implement it and which part is really unsafe and the safety requirement for that.
Yeah. But then we should do that before merging :)
Quoted 7 lines
Quoted 4 lines
Quoted 2 lines
Well, who's talknig about merging? ;-) I thought we just began reviewing here ;-)
I understand [PATCH] emails as "I want to merge this" and [RFC PATCH] as
But it doesn't mean "merge as it is", right? I don't think either I or Mitchell implied that, I'm surprised that you had to mention that,
Yeah that is true, but it at least shows the intention :)
Quoted 3 lines
also based on "I often mute those" below, making it "[PATCH]" seems to be a practical way to get more attention if one wants to get some reviews.
That is true, I do usually read the titles of RFC patches though and sometimes take a look eg your atomics series.
Quoted 4 lines
Quoted 3 lines
"I want to talk about merging this". It might be that I haven't seen the RFC patch series, because I often mute those.
Quoted 9 lines
Quoted 3 lines
Well, then you cannot blame people to move from "RFC PATCH" to "PATCH" stage for more reviews, right? And you cannot make rules about what the difference between [PATCH] and [RFC PATCH] if you ignore one of them ;-)
I'm not trying to blame anyone. I saw a lot of unsafe in the example and thought "we can do better" and since I haven't heard any sufficient arguments showing that it's impossible to improve, we should do some design work.
Quoted 3 lines
I agree with you, and I like what you're proposing, but I think design work can be done at "PATCH" stage, right? And sometimes, it's also OK to do some design work even at some version like "v12" ;-)
Yeah of course. The thing is just that nobody asked why there was unsafe and thus I got the impression that people thought this would be a good abstraction for percpu. (don't take from this that it's bad :)
Quoted 5 lines
Also I want to see more forward-progress actions about the design work improvement. For example, we can examine every case that makes unsafe_get_per_cpu!() unsafe, and see if we can improve that by typing or something else. We always can "do better", but the important part is how to get there ;-)
Yeah that would be a starting point :)
Quoted 26 lines
Quoted 26 lines
Quoted 26 lines
Quoted 25 lines
Quoted 15 lines
Quoted 8 lines
Quoted 8 lines
Quoted 7 lines
I'm not familiar with percpu, but from the name I assumed that it's "just a variable for each cpu" so similar to
thread_local!, but it's bound to the specific cpu instead of the thread.That in my mind should be rather easy to support in Rust at least with the thread_local-style API. You just need to ensure that no reference can escape the cpu, so we can make it
!Send&!Sync+ rely on klintQuoted 6 lines
Quoted 3 lines
Not really, in kernel, we have plenty of use cases that we read the other CPU's percpu variables. For example, each CPU keeps it's own counter and we sum them other in another CPU.
But then you need some sort of synchronization?
Quoted 6 lines
Right, but the synchronization can exist either in the percpu operations themselves or outside the percpu operations. Some cases, the data types are small enough to fit in atomic data types, and operations are just load/store/cmpxchg etc, then operations on the current cpu and remote read will be naturally synchronized. Sometimes extra synchronization is needed.
Sure, so we probably want direct atomics support. What about "extra synchronization"? Is that using locks or RCU or what else?
Quoted 8 lines
Quoted 5 lines
Quoted 2 lines
It's up to the users obviously, It could be some sort of locking or RCU, it's case by case.
Makes sense, what do you need in the VMS driver?
In VMBus driver, it's actually isolate, i.e. each CPU only access it's own
work_struct, so synchronization between CPUs is not needed.
I see, so we could either just start out with no sync support or -- which I would prefer -- get a list of the most common use-cases and implement those too (or at least design the first part compatibly with further extensions).
---
Cheers,
Benno
Re: [PATCH v2 3/5] rust: percpu: add a rust per-CPU variable test
In reply to Benno LossinOn Tue, Jul 15, 2025 at 07:44:01PM +0200, Benno Lossin wrote:
[...]
Quoted 11 lines
Quoted 8 lines
Quoted 4 lines
Sure we can do some experimentation, but I don't think we should put unsafe abstractions upstream that we intend to replace with a safe abstraction later. Otherwise people are going to depend on it and it's
I doubt we can replace the unsafe abstraction with a safe one, if users really care the performance then they would really need to use some unsafe API to build their safe abstraction.
That sounds pretty pessimistic, why do you think that?
I could ask you the similar, you barely know the implementation and usage the percpu, why do you think it's possible? ;-)
Regards,
Boqun
PATCH v2 4/5 rust: percpu: Add pin-hole optimizations for numerics
In reply to Mitchell LevyThe 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.
--- lib/percpu_test_rust.rs | 36 +++++++++++++ rust/kernel/percpu.rs | 1 + rust/kernel/percpu/numeric.rs | 117 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 154 insertions(+)
lib/percpu_test_rust.rs +36 -0
@@ -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: &'staticThisModule ) ->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 ) }
rust/kernel/percpu.rs +1 -0
@@ -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 DynamicPerCpu<T> type, as well as the PerCpu<T> trait. pub mod cpu_guard; +pub mod numeric;use bindings::{alloc_percpu, free_percpu};
rust/kernel/percpu/numeric.rs new file +117 -0
117 lines not on this page · raw message
Signature · 1 line
2.34.1
PATCH v2 5/5 rust: percpu: cache per-CPU pointers in the dynamic case
In reply to Mitchell LevyCurrently, 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.
--- rust/kernel/percpu.rs | 12 +++++++++--- rust/kernel/percpu/numeric.rs | 2 +- 2 files changed, 10 insertions(+), 4 deletions(-)
rust/kernel/percpu.rs +9 -3
@@ -26,7 +26,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.
@@ -204,9 +207,10 @@ impl<T: Zeroable> 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 }) } }
@@ -217,8 +221,9 @@ impl<T> DynamicPerCpu<T> {/// * `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 }) } }
@@ -226,7 +231,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 } }
@@ -234,6 +239,7 @@ impl<T> Clone for DynamicPerCpu<T> {fn clone (&self) ->Self {Self { alloc: self.alloc.clone (), + ptr: self.ptr, } } }
rust/kernel/percpu/numeric.rs +1 -1
@@ -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> {
Signature · 1 line
2.34.1