rust: Add Per-CPU Variable API
Subsystems (4)
v3 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 sample module, samples/rust/rust_percpu.rs, in the vein of lib/percpu_test.c.
---
---
Changes in v3:
- Add a CheckedPerCpuToken that enables usage of per-CPU variables via
a &T, allowing for a wholly safe interface when T allows for interior mutability (Thanks Benno Lossin)
- Add support for non-zeroable types to be used in a DynamicPerCpu.
- Remove necessity for unsafe to get a StaticPerCpu from its declaration (Thanks Benno Lossin)
- Allow the declaration of static per-CPU variables of types that are !Sync.
- Implement PerCpuPtr in terms of MaybeUninit<T> rather than T so
as to keep all invariants in the DynamicPerCpu and StaticPerCpu types --- this would also enable PerCpuPtr to be used in a per-CPU type that does lazy initialization.
- Link to v2: https://lore.kernel.org/r/[email protected]
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 (7):
rust: percpu: introduce a rust API for per-CPU variables
rust: percpu: add a rust per-CPU variable sample
rust: cpumask: Add a Cpumask iterator
rust: cpumask: Add getters for globally defined cpumasks
rust: percpu: Support non-zeroable types for DynamicPerCpu
rust: percpu: Add pin-hole optimizations for numerics
rust: percpu: cache per-CPU pointers in the dynamic case
rust/helpers/cpumask.c | 5 + rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 20 +++ rust/helpers/preempt.c | 14 ++ rust/kernel/cpumask.rs | 94 ++++++++++++- rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 239 +++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++ rust/kernel/percpu/dynamic.rs | 127 ++++++++++++++++++ rust/kernel/percpu/numeric.rs | 128 ++++++++++++++++++ rust/kernel/percpu/static_.rs | 132 +++++++++++++++++++ samples/rust/Kconfig | 9 ++ samples/rust/Makefile | 1 + samples/rust/rust_percpu.rs | 284 ++++++++++++++++++++++++++++++++++++++++ 14 files changed, 1092 insertions(+), 1 deletion(-)
---
base-commit: 8f5ae30d69d7543eee0d70083daf4de8fe15d585
change-id: 20240813-rust-percpu-ea2f54b5da33
Best regards,
Signature · 1 line
Mitchell Levy <[email protected]>
PATCH v3 1/7 rust: percpu: introduce a rust API for per-CPU variables
Per-CPU variables are an important tool for reducing lock contention, especially in systems with many processors. They also provide a convenient way to handle data that are logically associated with a particular CPU (e.g., the currently running task). Therefore, add a Rust API to make use of per-CPU variables.
Add a CpuGuard type that disables preemption for its lifetime. Add a PerCpuAllocation type used to track dynamic allocations. Add a define_per_cpu! macro to create static per-CPU allocations. Add DynamicPerCpu and StaticPerCpu to provide a high-level API. Add a PerCpu trait to unify the dynamic and static cases.
--- rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 9 ++ rust/helpers/preempt.c | 14 +++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 223 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++++ rust/kernel/percpu/dynamic.rs | 83 +++++++++++++++ rust/kernel/percpu/static_.rs | 132 ++++++++++++++++++++++++ 8 files changed, 501 insertions(+)
rust/helpers/helpers.c +2 -0
@@ -31,9 +31,11 @@#include "of.c" #include "page.c" #include "pci.c" +#include "percpu.c" #include "pid_namespace.c" #include "platform.c" #include "poll.c" +#include "preempt.c" #include "property.c" #include "rbtree.c" #include "rcu.c"
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
@@ -106,6 +106,9 @@pub mod page;#[cfg( CONFIG_PCI )] pub mod pci; +// Only x86_64 is supported by percpu for now +#[cfg( CONFIG_X86_64 )] +pub mod percpu;pub mod pid_namespace;pub mod platform;pub mod prelude;
rust/kernel/percpu.rs new file +223 -0
223 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 (); + } + } +}
rust/kernel/percpu/dynamic.rs new file +83 -0
83 lines not on this page · raw message
rust/kernel/percpu/static_.rs new file +132 -0
132 lines not on this page · raw message
Signature · 1 line
2.34.1
Yury Norov
maintainerRe: [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables
Quoted 15 lines · Mitchell Levy
Per-CPU variables are an important tool for reducing lock contention, especially in systems with many processors. They also provide a convenient way to handle data that are logically associated with a particular CPU (e.g., the currently running task). Therefore, add a Rust API to make use of per-CPU variables.
Add a
CpuGuardtype that disables preemption for its lifetime. Add aPerCpuAllocationtype used to track dynamic allocations. Add adefine_per_cpu!macro to create static per-CPU allocations. AddDynamicPerCpuandStaticPerCputo provide a high-level API. Add aPerCputrait to unify the dynamic and static cases.Co-developed-by Boqun Feng <[email protected]>Signed-off-by Boqun Feng <[email protected]>Signed-off-by Mitchell Levy <[email protected]>
Quoted 10 lines
--- rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 9 ++ rust/helpers/preempt.c | 14 +++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 223 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++++ rust/kernel/percpu/dynamic.rs | 83 +++++++++++++++ rust/kernel/percpu/static_.rs | 132 ++++++++++++++++++++++++ 8 files changed, 501 insertions(+)
That's quite a massive patch. Can you please split-out C-binders, and maybe make separate patches for each .rs component?
Quoted 125 lines
rust/helpers/helpers.c +2
-0@@ -31,9 +31,11 @@#include "of.c" #include "page.c" #include "pci.c" +#include "percpu.c" #include "pid_namespace.c" #include "platform.c" #include "poll.c" +#include "preempt.c" #include "property.c" #include "rbtree.c" #include "rcu.c" 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@@ -106,6 +106,9 @@pub mod page;#[cfg( CONFIG_PCI )] pub mod pci; +// Only x86_64 is supported by percpu for now +#[cfg( CONFIG_X86_64 )] +pub mod percpu;pub mod pid_namespace;pub mod platform;pub mod prelude;rust/kernel/percpu.rs new file +54
-0@@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +//! This module contains abstractions for creating and using per-CPU variables from Rust. +//! See the define_per_cpu! macro and the DynamicPerCpu<T> type, as well as the PerCpu<T> trait. + +pub mod cpu_guard; +mod dynamic; +mod static_; + +#[doc(inline)] +pub use dynamic::*; +#[doc(inline)] +pub use static_::*; + +use bindings::{alloc_percpu, free_percpu}; + +use crate ::alloc::Flags ; +use crate ::percpu::cpu_guard::CpuGuard ; +use crate ::prelude::*; +use crate ::sync::Arc ; +use crate ::types::Opaque ; +use crate ::{declare_extern_per_cpu, get_static_per_cpu}; + +use core::arch::asm; +use core::cell::{Cell ,RefCell ,UnsafeCell }; +use core::mem::{align_of, size_of,MaybeUninit }; + +use ffi::c_void; + +/// A per-CPU pointer; that is, an offset into the per-CPU area. Note that this type is NOT a smart +/// pointer, it does not manage the allocation. +pub struct PerCpuPtr <T >(*mut MaybeUninit <T >); + +/// Represents exclusive access to the memory location pointed at by a particular PerCpu<T>. +pub struct PerCpuToken <'a,T > { +// INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at +// by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized. + _guard:CpuGuard , + ptr: &'aPerCpuPtr <T >, +} + +/// Represents access to the memory location pointed at by a particular PerCpu<T> where the type +/// `T` manages access to the underlying memory to avoid aliaising troubles. (For example, `T` +/// might be a `Cell` or `RefCell`.) +pub struct CheckedPerCpuToken <'a,T > { +// INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at +// by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized. + _guard:CpuGuard , + ptr: &'aPerCpuPtr <T >, +} + +impl <T >PerCpuPtr <T > { +/// Makes a new PerCpuPtr from a raw per-CPU pointer. +/// +/// # Safety
rust/kernel/percpu.rs line 64
Quoted 10 lines
rust/kernel/percpu.rs new file +10
-0+/// `ptr` must be a valid per-CPU pointer. +pub unsafe fn new (ptr: *mut MaybeUninit <T >) ->Self { +Self (ptr) + } + +/// Get a `&mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` +/// +/// # Safety +/// The returned `&mut T` must follow Rust's aliasing rules. That is, no other `&(mut) T` may +/// exist that points to the same location in memory. In practice, this means that `get_(mut_)ref`
How long is this line?
rust/kernel/percpu.rs line 77
Quoted 13 lines
rust/kernel/percpu.rs new file +13
-0+/// must not be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as +/// the returned reference lives. +/// +/// CPU preemption must be disabled before calling this function and for the lifetime of the +/// returned reference. Otherwise, the returned reference might end up being a reference to a +/// different CPU's per-CPU area, causing the potential for a data race. +#[allow(clippy::mut_from_ref)] // Safety requirements prevent aliasing issues +pub unsafe fn get_mut_ref (&self) -> &mut MaybeUninit <T > { +// SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract, +// and the safety requirements of this function ensure that the returned reference is +// exclusive. +unsafe { &mut *(self.get_ptr ()) } + }
Here and everywhere: would it make sense to enforce it by testing the CPU with preemptible() before returning a reference?
Thanks,
Yury
Quoted 417 lines
rust/kernel/percpu.rs new file +146
-0+ +/// Get a `&MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` +/// +/// # Safety +/// The returned `&T` must follow Rust's aliasing rules. That is, no `&mut T` may exist that +/// points to the same location in memory. In practice, this means that `get_mut_ref` must not +/// be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as the +/// returned reference lives. +/// +/// CPU preemption must be disabled before calling this function and for the lifetime of the +/// returned reference. Otherwise, the returned reference might end up being a reference to a +/// different CPU's per-CPU area, causing the potential for a data race. +pub unsafe fn get_ref (&self) -> &MaybeUninit <T > { +// SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract. +// The safety requirements of this function ensure that the returned reference isn't +// aliased by a `&mut MaybeUninit<T>`. +unsafe { &*self.get_ptr () } + } + +/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by +/// `&self`. Note that if CPU preemption is not disabled before calling this function, use of +/// the returned pointer may cause a data race without some other synchronization mechanism. +/// Buyer beware! +pub fn get_ptr (&self) -> *mut MaybeUninit <T > { +let this_cpu_off_pcpu:PerCpuPtr <*mut c_void > =get_static_per_cpu! (this_cpu_off).0 ; +let mut this_cpu_area: *mut c_void ; +// SAFETY: gs + this_cpu_off_pcpu is guaranteed to be a valid pointer because `gs` points +// to the per-CPU area and this_cpu_off_pcpu is a valid per-CPU allocation. +unsafe { +asm! ( +"mov {out}, gs:[{off_val}]" , + off_val = in(reg) this_cpu_off_pcpu.0, + out =out (reg) this_cpu_area, + ) + }; + +// This_cpu_area + self.0 is guaranteed to be a valid pointer by the per-CPU subsystem and +// the invariant that self.0 is a valid offset into the per-CPU area. + (this_cpu_area).wrapping_add (self.0 as usize ).cast () + } +} + +// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the +// pointer is logically referring to a different place in memory in the other CPU's per-CPU area. +// In particular, this means that there are no restrictions on the type `T`. +unsafe impl <T >Send for PerCpuPtr <T > {} + +// SAFETY: Two threads concurrently making use of a `PerCpuPtr<T>` will each see the `T` in their +// own per-CPU area, so there's no potential for a data race (regardless of whether `T` is itself +// `Sync`). +unsafe impl <T >Sync for PerCpuPtr <T > {} + +impl <T >Clone for PerCpuPtr <T > { +fn clone (&self) ->Self { + *self + } +} + +/// `PerCpuPtr` is just a wrapper around a pointer. +impl <T >Copy for PerCpuPtr <T > {} + +/// A trait representing a per-CPU variable. This is implemented for both `StaticPerCpu<T>` and +/// `DynamicPerCpu<T>`. The main usage of this trait is to call `get_mut` to get a `PerCpuToken` +/// that can be used to access the underlying per-CPU variable. See `PerCpuToken::with`. +pub trait PerCpu <T > { +/// Produces a token, asserting that the holder has exclusive access to the underlying memory +/// pointed to by `self` +/// +/// # Safety +/// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a +/// sensible notion, since we keep a `CpuGuard` around) that is derived from the same +/// `PerCpu<T>` or a clone thereof. +unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T >; +} + +/// A marker trait for types that are interior mutable. Types that implement this trait can be used +/// to create "checked" per-CPU variables. See `CheckedPerCpu<T>`. +pub trait InteriorMutable {} + +impl <T >InteriorMutable for Cell <T > {} +impl <T >InteriorMutable for RefCell <T > {} +impl <T >InteriorMutable for UnsafeCell <T > {} +impl <T >InteriorMutable for Opaque <T > {} + +/// A trait representing a per-CPU variable that is usable via a `&T`. The unsafety of `PerCpu<T>` +/// stems from the fact that the holder of a `PerCpuToken` can use it to get a `&mut T` to the +/// underlying per-CPU variable. This is problematic because the existence of aliaising `&mut T` is +/// undefined behavior in Rust. This type avoids that issue by only allowing access via a `&T`, +/// with the tradeoff that then `T` must be interior mutable or the underlying per-CPU variable +/// must be a constant for the lifetime of the corresponding `CheckedPerCpu<T>`. +/// +/// Currently, only the case where `T` is interior mutable has first-class support, though a custom +/// implementation of `PerCpu<T>`/`CheckedPerCpu<T>` could be created for the const case. +pub trait CheckedPerCpu <T >:PerCpu <T > { +/// Produces a token via which the holder can access the underlying per-CPU variable. +fn get (&mut self, guard:CpuGuard ) ->CheckedPerCpuToken <'_,T >; +} + +impl <'a,T >PerCpuToken <'a,T > { +/// # Safety +/// No other `PerCpuToken` or `CheckedPerCpuToken` may exist on the current CPU (which is a +/// sensible notion, since we keep a `CpuGuard` around) that uses the same `PerCpuPtr<T>`. +/// +/// The current CPU's memory location associated with the per-CPU variable pointed at by `ptr` +/// (i.e., the entry in the per-CPU area on this CPU) must be initialized. +pub unsafe fn new (guard:CpuGuard , ptr: &'aPerCpuPtr <T >) ->PerCpuToken <'a,T > { +Self { _guard: guard, ptr } + } + +/// Immediately invokes `func` with a `&mut T` that points at the underlying per-CPU variable +/// that `&mut self` represents. +pub fn with <U >(&mut self, func:U ) +where +U :FnOnce (&mut T ), + { +// SAFETY: The existence of a PerCpuToken means that the requirements for get_mut_ref are +// satisfied. Likewise, the requirements for assume_init_mut are satisfied because the +// invariants of this type ensure that on the current CPU (which is a sensible notion +// because we have a CpuGuard), the memory location pointed to by `ptr` is initialized. +func (unsafe { self.ptr.get_mut_ref ().assume_init_mut () }); + } +} + +impl <'a,T >CheckedPerCpuToken <'a,T > { +/// # Safety +/// The current CPU's memory location associated with the per-CPU variable pointed at by `ptr` +/// (i.e., the entry in the per-CPU area on this CPU) must be initialized. +pub unsafe fn new (guard:CpuGuard , ptr: &'aPerCpuPtr <T >) ->CheckedPerCpuToken <'a,T > { +Self { _guard: guard, ptr } + } + +/// Immediately invokes `func` with a `&T` that points at the underlying per-CPU variable that +/// `&mut self` represents. +pub fn with <U >(&mut self, func:U ) +where +U :FnOnce (&T ), + { +// SAFETY: The existence of a CheckedPerCpuToken means that the requirements for get_ref +// are satisfied. Likewise, the requirements for assume_init_ref are satisfied because the +// invariants of this type ensure that on the current CPU (which is a sensible notion +// because we have a CpuGuard), the memory location pointed to by `ptr` is initialized. +func (unsafe { self.ptr.get_ref ().assume_init_ref () }); + } +} + +declare_extern_per_cpu! (this_cpu_off: *mut c_void);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 (); + } + } +}rust/kernel/percpu/dynamic.rs new file +83
-0@@ -0,0 +1,83 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Dynamically allocated per-CPU variables. + +use super ::*; + +/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when +/// dropped. +pub struct PerCpuAllocation <T >(PerCpuPtr <T >); + +impl <T :Zeroable >PerCpuAllocation <T > { +/// Dynamically allocates a space in the per-CPU area suitably sized and aligned to hold a `T`, +/// initially filled with the zero value for `T`. +/// +/// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`. +pub fn new_zero () ->Option <PerCpuAllocation <T >> { +let ptr: *mut MaybeUninit <T > = +// SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is +// `#[repr(transparent)]`, so we can cast a `*mut T` to it. +unsafe {alloc_percpu (size_of ::<T >(),align_of ::<T >()) }.cast (); +if ptr.is_null () { +return None ; + } + +// alloc_percpu returns zero'ed memory +Some (Self (PerCpuPtr (ptr))) + } +} + +impl <T >PerCpuAllocation <T > { +/// Makes a per-CPU allocation sized and aligned to hold a `T`. +/// +/// Returns `None` under the same circumstances the C function `alloc_percpu` returns `NULL`. +pub fn new_uninit () ->Option <PerCpuAllocation <T >> { +let ptr: *mut MaybeUninit <T > = +// SAFETY: No preconditions to call alloc_percpu; MaybeUninit<T> is +// `#[repr(transparent)]`, so we can cast a `*mut T` to it. +unsafe {alloc_percpu (size_of ::<T >(),align_of ::<T >()) }.cast (); +if ptr.is_null () { +return None ; + } + +Some (Self (PerCpuPtr (ptr))) + } +} + +impl <T >Drop for PerCpuAllocation <T > { +fn drop (&mut self) { +// SAFETY: self.0.0 was returned by alloc_percpu, and so was a valid pointer into +// the percpu area, and has remained valid by the invariants of PerCpuAllocation<T>. +unsafe {free_percpu (self.0 .0 .cast ()) } + } +} + +/// Holds a dynamically-allocated per-CPU variable. +#[derive( Clone )] +pub struct DynamicPerCpu <T > { +// INVARIANT: The memory location in each CPU's per-CPU area pointed at by `alloc.0` has been +// initialized. +pub (super ) alloc:Arc <PerCpuAllocation <T >>, +} + +impl <T :Zeroable >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable +/// +/// # Arguments +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_zero (flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_zero ()?; + +let arc =Arc ::new (alloc, flags).ok ()?; + +Some (Self { alloc: arc }) + } +} + +impl <T >PerCpu <T >for DynamicPerCpu <T > { +unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T > { +// SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the +// requirements of `PerCpuToken::new` are met. +unsafe {PerCpuToken ::new (guard, &self.alloc.0 ) } + } +}rust/kernel/percpu/static_.rs new file +132
-0@@ -0,0 +1,132 @@ +// SPDX-License-Identifier: GPL-2.0 +//! Statically allocated per-CPU variables. + +use super ::*; + +/// A wrapper used for declaring static per-CPU variables. These symbols are "virtual" in that the +/// linker uses them to generate offsets into each CPU's per-CPU area, but shouldn't be read +/// from/written to directly. The fact that the statics are immutable prevents them being written +/// to (generally), this struct having _val be non-public prevents reading from them. +/// +/// The end-user of the per-CPU API should make use of the define_per_cpu! macro instead of +/// declaring variables of this type directly. All instances of this type must be `static` and +/// `#[link_section = ".data..percpu"]` (which the macro handles). +#[repr(transparent)] +pub struct StaticPerCpuSymbol <T > { + _val:T ,// generate a correctly sized type +} + +/// Holds a statically-allocated per-CPU variable. +#[derive( Clone )] +pub struct StaticPerCpu <T >(pub (super )PerCpuPtr <T >); + +impl <T >StaticPerCpu <T > { +/// Creates a `StaticPerCpu<T>` from a `StaticPerCpuSymbol<T>`. You should probably be using +/// `get_static_per_cpu!` instead. +pub fn new (ptr: *const StaticPerCpuSymbol <T >) ->StaticPerCpu <T > { +// SAFETY: `StaticPerCpuSymbol<T>` is `#[repr(transparent)]`, so we can safely cast a +// pointer to it into a pointer to `MaybeUninit<T>`. The validity of it as a per-CPU +// pointer is guaranteed by the per-CPU subsystem and invariants of the StaticPerCpuSymbol +// type. +let pcpu_ptr =unsafe {PerCpuPtr ::new (ptr.cast_mut ().cast ()) }; +Self (pcpu_ptr) + } +} + +impl <T >PerCpu <T >for StaticPerCpu <T > { +unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T > { +// SAFETY: The requirements of `PerCpu::get_mut` and the fact that statically-allocated +// per-CPU variables are initialized by the per-CPU subsystem ensure that the requirements +// of `PerCpuToken::new` are met. +unsafe {PerCpuToken ::new (guard, &self.0 ) } + } +} + +impl <T :InteriorMutable >CheckedPerCpu <T >for StaticPerCpu <T > { +fn get (&mut self, guard:CpuGuard ) ->CheckedPerCpuToken <'_,T > { +// SAFETY: The per-CPU subsystem guarantees that each CPU's instance of a +// statically allocated variable begins with a copy of the contents of the +// corresponding symbol in `.data..percpu`. Thus, the requirements of +// `CheckedPerCpuToken::new` are met. +unsafe {CheckedPerCpuToken ::new (guard, &self.0 ) } + } +} + +/// Gets a `StaticPerCpu<T>` from a symbol declared with `define_per_cpu!` or +/// `declare_extern_per_cpu!`. +/// +/// # Arguments +/// * `ident` - The identifier declared +#[macro_export] +macro_rules! get_static_per_cpu { + ($id:ident) => { + $crate::percpu::StaticPerCpu ::new ((&raw const $id).cast ()) + }; +} + +/// Declares a StaticPerCpuSymbol corresponding to a per-CPU variable defined in C. Be sure to read +/// the safety requirements of `PerCpu::get`. +#[macro_export] +macro_rules! declare_extern_per_cpu { + ($id:ident: $ty:ty) => { +extern "C" { +static $id:StaticPerCpuSymbol <$ty>; + } + }; +} + +/// define_per_cpu! is analogous to the C DEFINE_PER_CPU macro in that it lets you create a +/// statically allocated per-CPU variable. +/// +/// # Example +/// ``` +/// use kernel::define_per_cpu; +/// use kernel::percpu::StaticPerCpuSymbol; +/// +/// define_per_cpu!(pub MY_PERCPU: u64 = 0); +/// ``` +#[macro_export] +macro_rules! define_per_cpu { + ($vis:vis $id:ident: $ty:ty = $expr:expr) => { + $crate::macros::paste! { +// We might want to have a per-CPU variable that doesn't implement `Sync` (not paying +// sync overhead costs is part of the point), but Rust won't let us declare a static of +// a `!Sync` type. Of course, we don't actually have any synchronization issues, since +// each CPU will see its own copy of the variable, so we cheat a little bit and tell +// Rust it's fine. +#[doc(hidden)] +#[allow(non_camel_case_types)] +#[repr(transparent)] // It needs to be the same size as $ty +struct [<__PRIVATE_TYPE_ $id>]($ty); + +impl [<__PRIVATE_TYPE_ $id>] { +#[doc(hidden)] +const fn new (val: $ty) ->Self { +Self (val) + } + } + +// Expand $expr outside of the unsafe block to avoid silently allowing unsafe code to be +// used without a user-facing unsafe block +#[doc(hidden)] +static [<__INIT_ $id>]: [<__PRIVATE_TYPE_ $id>] = [<__PRIVATE_TYPE_ $id>]::new ($expr); + +// SAFETY: This type will ONLY ever be used to declare a `StaticPerCpuSymbol` +// (which we then only ever use as input to `&raw`). Reading from the symbol is +// already UB, so we won't ever actually have any variables of this type where +// synchronization is a concern. +#[doc(hidden)] +unsafe impl Sync for [<__PRIVATE_TYPE_ $id>] {} + +// SAFETY: StaticPerCpuSymbol<T> is #[repr(transparent)], so we can freely convert from +// [<__PRIVATE_TYPE_ $id>], which is also `#[repr(transparent)]` (i.e., everything is +// just a `$ty` from a memory layout perspective). +#[link_section = ".data..percpu" ] + $visstatic $id:StaticPerCpuSymbol <[<__PRIVATE_TYPE_ $id>]> =unsafe { + core::mem::transmute_copy ::< + [<__PRIVATE_TYPE_ $id>],StaticPerCpuSymbol <[<__PRIVATE_TYPE_ $id>]> + >(&[<__INIT_ $id>]) + }; + } + }; +}Signature · 2 lines
-- 2.34.1
Re: [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables
Quoted 27 lines · Yury Norov
Quoted 15 lines · Mitchell Levy
Per-CPU variables are an important tool for reducing lock contention, especially in systems with many processors. They also provide a convenient way to handle data that are logically associated with a particular CPU (e.g., the currently running task). Therefore, add a Rust API to make use of per-CPU variables.
Add a
CpuGuardtype that disables preemption for its lifetime. Add aPerCpuAllocationtype used to track dynamic allocations. Add adefine_per_cpu!macro to create static per-CPU allocations. AddDynamicPerCpuandStaticPerCputo provide a high-level API. Add aPerCputrait to unify the dynamic and static cases.Co-developed-by Boqun Feng <[email protected]>Signed-off-by Boqun Feng <[email protected]>Signed-off-by Mitchell Levy <[email protected]>Quoted 10 lines
--- rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 9 ++ rust/helpers/preempt.c | 14 +++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 223 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 35 +++++++ rust/kernel/percpu/dynamic.rs | 83 +++++++++++++++ rust/kernel/percpu/static_.rs | 132 ++++++++++++++++++++++++ 8 files changed, 501 insertions(+)
Quoted 2 lines
That's quite a massive patch. Can you please split-out C-binders, and maybe make separate patches for each .rs component?
Sure, will do so for v4.
Quoted 2 lines
Quoted 2 lines
rust/helpers/helpers.c +0
-0
[...]
Quoted 61 lines
Quoted 61 lines
rust/kernel/percpu.rs new file +58
-0@@ -0,0 +1,223 @@ +// SPDX-License-Identifier: GPL-2.0 +//! This module contains abstractions for creating and using per-CPU variables from Rust. +//! See the define_per_cpu! macro and the DynamicPerCpu<T> type, as well as the PerCpu<T> trait. + +pub mod cpu_guard; +mod dynamic; +mod static_; + +#[doc(inline)] +pub use dynamic::*; +#[doc(inline)] +pub use static_::*; + +use bindings::{alloc_percpu, free_percpu}; + +use crate ::alloc::Flags ; +use crate ::percpu::cpu_guard::CpuGuard ; +use crate ::prelude::*; +use crate ::sync::Arc ; +use crate ::types::Opaque ; +use crate ::{declare_extern_per_cpu, get_static_per_cpu}; + +use core::arch::asm; +use core::cell::{Cell ,RefCell ,UnsafeCell }; +use core::mem::{align_of, size_of,MaybeUninit }; + +use ffi::c_void; + +/// A per-CPU pointer; that is, an offset into the per-CPU area. Note that this type is NOT a smart +/// pointer, it does not manage the allocation. +pub struct PerCpuPtr <T >(*mut MaybeUninit <T >); + +/// Represents exclusive access to the memory location pointed at by a particular PerCpu<T>. +pub struct PerCpuToken <'a,T > { +// INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at +// by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized. + _guard:CpuGuard , + ptr: &'aPerCpuPtr <T >, +} + +/// Represents access to the memory location pointed at by a particular PerCpu<T> where the type +/// `T` manages access to the underlying memory to avoid aliaising troubles. (For example, `T` +/// might be a `Cell` or `RefCell`.) +pub struct CheckedPerCpuToken <'a,T > { +// INVARIANT: the current CPU's memory location associated with the per-CPU variable pointed at +// by `ptr` (i.e., the entry in the per-CPU area on the current CPU) has been initialized. + _guard:CpuGuard , + ptr: &'aPerCpuPtr <T >, +} + +impl <T >PerCpuPtr <T > { +/// Makes a new PerCpuPtr from a raw per-CPU pointer. +/// +/// # Safety +/// `ptr` must be a valid per-CPU pointer. +pub unsafe fn new (ptr: *mut MaybeUninit <T >) ->Self { +Self (ptr) + }
Quoted 8 lines
rust/kernel/percpu.rs line 64
Quoted 6 lines
rust/kernel/percpu.rs new file +6
-0+ +/// Get a `&mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` +/// +/// # Safety +/// The returned `&mut T` must follow Rust's aliasing rules. That is, no other `&(mut) T` may +/// exist that points to the same location in memory. In practice, this means that `get_(mut_)ref` How long is this line?
102 chars, or 103 if you include the newline. rustfmt doesn't break the line, so I left it as-is for this patch. Happy to change it if it poses a problem, though.
Quoted 14 lines
rust/kernel/percpu.rs line 77
Quoted 13 lines
rust/kernel/percpu.rs new file +13
-0+/// must not be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as +/// the returned reference lives. +/// +/// CPU preemption must be disabled before calling this function and for the lifetime of the +/// returned reference. Otherwise, the returned reference might end up being a reference to a +/// different CPU's per-CPU area, causing the potential for a data race. +#[allow(clippy::mut_from_ref)] // Safety requirements prevent aliasing issues +pub unsafe fn get_mut_ref (&self) -> &mut MaybeUninit <T > { +// SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract, +// and the safety requirements of this function ensure that the returned reference is +// exclusive. +unsafe { &mut *(self.get_ptr ()) } + }
Quoted 2 lines
Here and everywhere: would it make sense to enforce it by testing the CPU with
preemptible()before returning a reference?
The only thing we could do would be to panic, which I don't 100% love. Another alternative would be to take a &'a CpuGuard and bound the lifetime of the returned reference to 'a, and then we don't need to do any run-time checking at all.
Originally, I had left this to the caller because it might make sense down the line for some complex behavior based on per-CPU (e.g., per-CPU refcount) to do all its own management of per-CPU variables using PerCpuPtr as a core primitive. In these cases, I believe there are some times where being non-preemptible wouldn't actually be required (that said, my thoughts on this aren't well reflected in the safety comment, since I said it must be disabled... gah). But, the more I think about it, the more I think these use cases would be better served by just using get_ptr --- conjuring &mut references seems like it would be a big footgun. And the safety comment already actually reflects these thoughts somewhat :)
For v4 I will probably have this function take a &'a CpuGuard and use that to bound the liftetime of the returned reference, unless there are other thoughts on this point.
Thanks,
Mitchell
Quoted 2 lines
Thanks,
Yury
Yury Norov
maintainerRe: [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables
Quoted 2 lines · Mitchell Levy
Quoted 1 line · Yury Norov
On Thu, Aug 28, 2025 at 12:00:08PM -0700, Mitchell Levy wrote:
...
Quoted 11 lines
Quoted 7 lines
rust/kernel/percpu.rs line 64
Quoted 5 lines
rust/kernel/percpu.rs new file +5
-0+/// Get a `&mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` +/// +/// # Safety +/// The returned `&mut T` must follow Rust's aliasing rules. That is, no other `&(mut) T` may +/// exist that points to the same location in memory. In practice, this means that `get_(mut_)ref` How long is this line?
102 chars, or 103 if you include the newline.
rustfmtdoesn't break the line, so I left it as-is for this patch. Happy to change it if it poses a problem, though.
Then don't use that tool - it's broken. In kernel we used to have 80-chars limit for the lines, recently relaxed to 100.
Quoted 22 lines
Quoted 14 lines
rust/kernel/percpu.rs line 77
Quoted 13 lines
rust/kernel/percpu.rs new file +13
-0+/// must not be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as +/// the returned reference lives. +/// +/// CPU preemption must be disabled before calling this function and for the lifetime of the +/// returned reference. Otherwise, the returned reference might end up being a reference to a +/// different CPU's per-CPU area, causing the potential for a data race. +#[allow(clippy::mut_from_ref)] // Safety requirements prevent aliasing issues +pub unsafe fn get_mut_ref (&self) -> &mut MaybeUninit <T > { +// SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract, +// and the safety requirements of this function ensure that the returned reference is +// exclusive. +unsafe { &mut *(self.get_ptr ()) } + }Quoted 2 lines
Here and everywhere: would it make sense to enforce it by testing the CPU with
preemptible()before returning a reference?The only thing we could do would be to panic, which I don't 100% love. Another alternative would be to take a &'a CpuGuard and bound the lifetime of the returned reference to 'a, and then we don't need to do any run-time checking at all.
Quoted 11 lines
Originally, I had left this to the caller because it might make sense down the line for some complex behavior based on per-CPU (e.g., per-CPU refcount) to do all its own management of per-CPU variables using
PerCpuPtras a core primitive. In these cases, I believe there are some times where being non-preemptible wouldn't actually be required (that said, my thoughts on this aren't well reflected in the safety comment, since I said it must be disabled... gah). But, the more I think about it, the more I think these use cases would be better served by just usingget_ptr--- conjuring&mutreferences seems like it would be a big footgun. And the safety comment already actually reflects these thoughts somewhat :)
If you think that in future there will be a user who will not need to disable preemption before dereferencing a percpu pointer, then you can add another less restricted flavor of the helper.
Quoted 3 lines
For v4 I will probably have this function take a &'a CpuGuard and use that to bound the liftetime of the returned reference, unless there are other thoughts on this point.
I don't want you to panic just because of invlid user call, but whatever you call in comment must be enforced in code, right?
You can use the guard, if it guarantees the preemption disabled; or you can return None; you can create CONFIG_RUST_PERCPU_HARDENED for panics.
Please refer the recent bitmap API wrapper, and how erroneous request is handled there.
Re: [PATCH v3 1/7] rust: percpu: introduce a rust API for per-CPU variables
Quoted 12 lines · Yury Norov
Quoted 2 lines · Mitchell Levy
Quoted 1 line · Yury Norov
On Thu, Aug 28, 2025 at 12:00:08PM -0700, Mitchell Levy wrote:
...
Quoted 6 lines
Quoted 6 lines
Quoted 5 lines
rust/kernel/percpu.rs new file +5
-0+/// Get a `&mut MaybeUninit<T>` to the per-CPU variable on the current CPU represented by `&self` +/// +/// # Safety +/// The returned `&mut T` must follow Rust's aliasing rules. That is, no other `&(mut) T` may +/// exist that points to the same location in memory. In practice, this means that `get_(mut_)ref`
Quoted 8 lines
Quoted 5 lines
Quoted 1 line
How long is this line?
102 chars, or 103 if you include the newline.
rustfmtdoesn't break the line, so I left it as-is for this patch. Happy to change it if it poses a problem, though.Then don't use that tool - it's broken. In kernel we used to have 80-chars limit for the lines, recently relaxed to 100.
Sure, will fix.
Quoted 34 lines
Quoted 22 lines
Quoted 14 lines
rust/kernel/percpu.rs line 77
Quoted 13 lines
rust/kernel/percpu.rs new file +13
-0+/// must not be called on another `PerCpuPtr<T>` that is a copy/clone of `&self` for as long as +/// the returned reference lives. +/// +/// CPU preemption must be disabled before calling this function and for the lifetime of the +/// returned reference. Otherwise, the returned reference might end up being a reference to a +/// different CPU's per-CPU area, causing the potential for a data race. +#[allow(clippy::mut_from_ref)] // Safety requirements prevent aliasing issues +pub unsafe fn get_mut_ref (&self) -> &mut MaybeUninit <T > { +// SAFETY: `self.get_ptr()` returns a valid pointer to a `MaybeUninit<T>` by its contract, +// and the safety requirements of this function ensure that the returned reference is +// exclusive. +unsafe { &mut *(self.get_ptr ()) } + }Quoted 2 lines
Here and everywhere: would it make sense to enforce it by testing the CPU with
preemptible()before returning a reference?The only thing we could do would be to panic, which I don't 100% love. Another alternative would be to take a &'a CpuGuard and bound the lifetime of the returned reference to 'a, and then we don't need to do any run-time checking at all.
Quoted 11 lines
Originally, I had left this to the caller because it might make sense down the line for some complex behavior based on per-CPU (e.g., per-CPU refcount) to do all its own management of per-CPU variables using
PerCpuPtras a core primitive. In these cases, I believe there are some times where being non-preemptible wouldn't actually be required (that said, my thoughts on this aren't well reflected in the safety comment, since I said it must be disabled... gah). But, the more I think about it, the more I think these use cases would be better served by just usingget_ptr--- conjuring&mutreferences seems like it would be a big footgun. And the safety comment already actually reflects these thoughts somewhat :)
Quoted 3 lines
If you think that in future there will be a user who will not need to disable preemption before dereferencing a percpu pointer, then you can add another less restricted flavor of the helper.
Yeah, that's fair.
Quoted 11 lines
Quoted 3 lines
For v4 I will probably have this function take a &'a CpuGuard and use that to bound the liftetime of the returned reference, unless there are other thoughts on this point.
I don't want you to panic just because of invlid user call, but whatever you call in comment must be enforced in code, right?
You can use the guard, if it guarantees the preemption disabled; or you can return None; you can create
CONFIG_RUST_PERCPU_HARDENEDfor panics.
Yes, the existence of a CpuGuard guarantees that preemption is disabled.
Quoted 4 lines
Please refer the recent bitmap API wrapper, and how erroneous request is handled there.
Thanks,
Mitchell
PATCH v3 2/7 rust: percpu: add a rust per-CPU variable sample
In reply to Mitchell LevyAdd a short exercise for Rust's per-CPU variable API, modelled after lib/percpu_test.c
--- rust/helpers/percpu.c | 6 ++ samples/rust/Kconfig | 9 +++ samples/rust/Makefile | 1 + samples/rust/rust_percpu.rs | 163 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 179 insertions(+)
rust/helpers/percpu.c +6 -0
@@ -1,9 +1,15 @@// 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); +} +
samples/rust/Kconfig +9 -0
@@ -105,6 +105,15 @@ config SAMPLE_RUST_DRIVER_AUXILIARY If unsure, say N. +config SAMPLE_RUST_PERCPU + tristate "Per-CPU support" + depends on m + help + Enable this option to build a module which demonstrates Rust per-CPU + operations. + + If unsure, say N. + config SAMPLE_RUST_HOSTPROGS bool "Host programs" help
samples/rust/Makefile +1 -0
@@ -10,6 +10,7 @@ obj-$(CONFIG_SAMPLE_RUST_DRIVER_PLATFORM) += rust_driver_platform.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_FAUX) += rust_driver_faux.o obj-$(CONFIG_SAMPLE_RUST_DRIVER_AUXILIARY) += rust_driver_auxiliary.o obj-$(CONFIG_SAMPLE_RUST_CONFIGFS) += rust_configfs.o +obj-$(CONFIG_SAMPLE_RUST_PERCPU) += rust_percpu.o rust_print-y := rust_print_main.o rust_print_events.o
samples/rust/rust_percpu.rs new file +163 -0
163 lines not on this page · raw message
Signature · 1 line
2.34.1
PATCH v3 3/7 rust: cpumask: Add a `Cpumask` iterator
In reply to Mitchell LevyAdd an iterator for Cpumask making use of C's cpumask_next.
--- rust/helpers/cpumask.c | 5 +++++ rust/kernel/cpumask.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-)
rust/helpers/cpumask.c +5 -0
@@ -42,6 +42,11 @@ bool rust_helper_cpumask_full(struct cpumask *srcp)return cpumask_full (srcp); } +unsigned int rust_helper_cpumask_next (int n,struct cpumask *srcp) +{ +return cpumask_next (n, srcp); +} +unsigned int rust_helper_cpumask_weight (struct cpumask *srcp) {return cpumask_weight (srcp);
rust/kernel/cpumask.rs +47 -1
60 lines not on this page · raw message
Signature · 1 line
2.34.1
Re: [PATCH v3 3/7] rust: cpumask: Add a `Cpumask` iterator
Quoted 7 lines · Mitchell Levy
Add an iterator for
Cpumaskmaking use of C'scpumask_next.Signed-off-by Mitchell Levy <[email protected]>--- rust/helpers/cpumask.c | 5 +++++ rust/kernel/cpumask.rs | 48 +++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-)
Signature · 1 line
viresh
PATCH v3 4/7 rust: cpumask: Add getters for globally defined cpumasks
In reply to Mitchell LevyAdd getters for the global cpumasks documented ininclude/linux/cpumask.h, specifically:
- cpu_possible_mask
- cpu_online_mask
- cpu_enabled_mask
- cpu_present_mask
- cpu_active_mask
--- rust/kernel/cpumask.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
rust/kernel/cpumask.rs +46 -0
@@ -77,6 +77,52 @@ pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self {unsafe { &*ptr.cast () } } +/// Get a CPU mask representing possible CPUs; has bit `cpu` set iff cpu is populatable +#[inline] +pub fn possible () -> &'staticSelf { +// SAFETY: `__cpu_possible_mask` is a valid global provided by the kernel that lives +// forever. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_possible_mask) } + } + +/// Get a CPU mask representing online CPUs; has bit `cpu` set iff cpu available to the +/// scheduler +#[inline] +pub fn online () -> &'staticSelf { +// SAFETY: `__cpu_online_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_online_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_online_mask) } + } + +/// Get a CPU mask representing enabled CPUs; has bit `cpu` set iff cpu can be brought online +#[inline] +pub fn enabled () -> &'staticSelf { +// SAFETY: `__cpu_enabled_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_enabled_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_enabled_mask) } + } + +/// Get a CPU mask representing present CPUs; has bit `cpu` set iff cpu is populated +#[inline] +pub fn present () -> &'staticSelf { +// SAFETY: `__cpu_present_mask` is a valid global provided by the kernel that lives +// forever. Since we wrap the returned pointer in an `Opaque`, it's ok that +// `__cpu_present_mask` may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_present_mask) } + } + +/// Get a CPU mask representing active CPUs; has bit `cpu` set iff cpu is available to +/// migration. +#[inline] +pub fn active () -> &'staticSelf { +// SAFETY: `__cpu_active_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_active_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_active_mask) } + } +/// Obtain the raw `struct cpumask` pointer. pub fn as_raw (&self) -> *mut bindings::cpumask {let this: *const Self = self;
Signature · 1 line
2.34.1
Re: [PATCH v3 4/7] rust: cpumask: Add getters for globally defined cpumasks
Quoted 8 lines · Mitchell Levy
Add getters for the global cpumasks documented in
include/linux/cpumask.h, specifically:
-cpu_possible_mask
-cpu_online_mask
-cpu_enabled_mask
-cpu_present_mask
-cpu_active_mask
Quoted 4 lines
Signed-off-by Mitchell Levy <[email protected]>--- rust/kernel/cpumask.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)
Signature · 1 line
viresh
Yury Norov
maintainerRe: [PATCH v3 4/7] rust: cpumask: Add getters for globally defined cpumasks
In reply to Mitchell LevyQuoted 18 lines · Mitchell Levy
Add getters for the global cpumasks documented in
include/linux/cpumask.h, specifically:
-cpu_possible_mask
-cpu_online_mask
-cpu_enabled_mask
-cpu_present_mask
-cpu_active_maskSigned-off-by Mitchell Levy <[email protected]>--- rust/kernel/cpumask.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)rust/kernel/cpumask.rs +0
-0
rust/kernel/cpumask.rs pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self { · line 86
Quoted 10 lines
rust/kernel/cpumask.rs +7
-0@@ -77,6 +77,52 @@ pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self {unsafe { &*ptr.cast () } } +/// Get a CPU mask representing possible CPUs; has bit `cpu` set iff cpu is populatable +#[inline] +pub fn possible () -> &'staticSelf { +// SAFETY: `__cpu_possible_mask` is a valid global provided by the kernel that lives +// forever. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_possible_mask) } + }
This function and the following look like returning a boolean. Maybe possible_cpus()?
The general rule is to keep the rust naming as close to C as possible, but in this case Cpumask::cpu_possible_mask is, OK, somewhat excessive.
Thanks,
Yury
Quoted 45 lines
rust/kernel/cpumask.rs +39
-0+ +/// Get a CPU mask representing online CPUs; has bit `cpu` set iff cpu available to the +/// scheduler +#[inline] +pub fn online () -> &'staticSelf { +// SAFETY: `__cpu_online_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_online_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_online_mask) } + } + +/// Get a CPU mask representing enabled CPUs; has bit `cpu` set iff cpu can be brought online +#[inline] +pub fn enabled () -> &'staticSelf { +// SAFETY: `__cpu_enabled_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_enabled_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_enabled_mask) } + } + +/// Get a CPU mask representing present CPUs; has bit `cpu` set iff cpu is populated +#[inline] +pub fn present () -> &'staticSelf { +// SAFETY: `__cpu_present_mask` is a valid global provided by the kernel that lives +// forever. Since we wrap the returned pointer in an `Opaque`, it's ok that +// `__cpu_present_mask` may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_present_mask) } + } + +/// Get a CPU mask representing active CPUs; has bit `cpu` set iff cpu is available to +/// migration. +#[inline] +pub fn active () -> &'staticSelf { +// SAFETY: `__cpu_active_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_active_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_active_mask) } + } +/// Obtain the raw `struct cpumask` pointer. pub fn as_raw (&self) -> *mut bindings::cpumask {let this: *const Self = self;Signature · 2 lines
-- 2.34.1
Re: [PATCH v3 4/7] rust: cpumask: Add getters for globally defined cpumasks
Quoted 22 lines · Yury Norov
Quoted 21 lines · Mitchell Levy
Add getters for the global cpumasks documented in
include/linux/cpumask.h, specifically:
-cpu_possible_mask
-cpu_online_mask
-cpu_enabled_mask
-cpu_present_mask
-cpu_active_maskSigned-off-by Mitchell Levy <[email protected]>--- rust/kernel/cpumask.rs | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 46 insertions(+)rust/kernel/cpumask.rs +0
-0@@ -77,6 +77,52 @@ pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self {unsafe { &*ptr.cast () } }
Quoted 10 lines
rust/kernel/cpumask.rs pub unsafe fn as_ref<'a>(ptr: *const bindings::cpumask) -> &'a Self { · line 86
Quoted 7 lines
rust/kernel/cpumask.rs +7
-0+/// Get a CPU mask representing possible CPUs; has bit `cpu` set iff cpu is populatable +#[inline] +pub fn possible () -> &'staticSelf { +// SAFETY: `__cpu_possible_mask` is a valid global provided by the kernel that lives +// forever. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_possible_mask) } + }This function and the following look like returning a boolean. Maybe
possible_cpus()?
Agreed, I think that's definitely better. Will do for v4.
Quoted 3 lines
The general rule is to keep the rust naming as close to C as possible, but in this case
Cpumask::cpu_possible_maskis, OK, somewhat excessive.
Agreed :)
Thanks,
Mitchell
Quoted 48 lines
Thanks,
YuryQuoted 45 lines
rust/kernel/cpumask.rs +39
-0+ +/// Get a CPU mask representing online CPUs; has bit `cpu` set iff cpu available to the +/// scheduler +#[inline] +pub fn online () -> &'staticSelf { +// SAFETY: `__cpu_online_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_online_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_online_mask) } + } + +/// Get a CPU mask representing enabled CPUs; has bit `cpu` set iff cpu can be brought online +#[inline] +pub fn enabled () -> &'staticSelf { +// SAFETY: `__cpu_enabled_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_enabled_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_enabled_mask) } + } + +/// Get a CPU mask representing present CPUs; has bit `cpu` set iff cpu is populated +#[inline] +pub fn present () -> &'staticSelf { +// SAFETY: `__cpu_present_mask` is a valid global provided by the kernel that lives +// forever. Since we wrap the returned pointer in an `Opaque`, it's ok that +// `__cpu_present_mask` may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_present_mask) } + } + +/// Get a CPU mask representing active CPUs; has bit `cpu` set iff cpu is available to +/// migration. +#[inline] +pub fn active () -> &'staticSelf { +// SAFETY: `__cpu_active_mask` is a valid global provided by the kernel that lives forever. +// Since we wrap the returned pointer in an `Opaque`, it's ok that `__cpu_active_mask` +// may change its value. +unsafe {Cpumask ::as_ref (&raw const bindings::__cpu_active_mask) } + } +/// Obtain the raw `struct cpumask` pointer. pub fn as_raw (&self) -> *mut bindings::cpumask {let this: *const Self = self;Signature · 2 lines
-- 2.34.1
PATCH v3 5/7 rust: percpu: Support non-zeroable types for DynamicPerCpu
In reply to Mitchell LevyAdd functionality to PerCpuPtr to compute pointers to per-CPU variable slots on other CPUs. Use this facility to initialize per-CPU variables on all possible CPUs when a dynamic per-CPU variable is created with a non-zeroable type. Since RefCell and other Cell-like types fall into this category, impl CheckedPerCpu on DynamicPerCpu for these InteriorMutable types since they can now be used. Add examples of these usages to samples/rust/rust_percpu.rs.
--- rust/helpers/percpu.c | 5 +++ rust/kernel/percpu.rs | 15 +++++++ rust/kernel/percpu/dynamic.rs | 40 +++++++++++++++++ samples/rust/rust_percpu.rs | 99 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 152 insertions(+), 7 deletions(-)
rust/helpers/percpu.c +5 -0
@@ -8,6 +8,11 @@ void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align)return __alloc_percpu (sz, align); } +void *rust_helper_per_cpu_ptr (void __percpu *ptr,unsigned int cpu) +{ +return per_cpu_ptr (ptr, cpu); +} +void rust_helper_on_each_cpu (smp_call_func_t func,void *info,int wait) {on_each_cpu (func, info, wait);
rust/kernel/percpu.rs +15 -0
@@ -14,6 +14,7 @@use bindings::{alloc_percpu, free_percpu};use crate ::alloc::Flags ; +use crate ::cpu::CpuId ;use crate ::percpu::cpu_guard::CpuGuard ;use crate ::prelude::*;use crate ::sync::Arc ;
@@ -115,6 +116,20 @@ pub fn get_ptr(&self) -> *mut MaybeUninit<T> {// the invariant that self.0 is a valid offset into the per-CPU area. (this_cpu_area).wrapping_add (self.0 as usize ).cast () } + +/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the CPU represented by `cpu`. Note +/// that without some kind of synchronization, use of the returned pointer may cause a data +/// race. It is the caller's responsibility to use the returned pointer in a reasonable way. +/// +/// # Safety +/// - The returned pointer is valid only if `self` is (that is, it points to a live allocation +/// correctly sized and aligned to hold a `T`) +/// - The returned pointer is valid only if the bit corresponding to `cpu` is set in +/// `Cpumask::possible()`. +pub unsafe fn get_remote_ptr (&self, cpu:CpuId ) -> *mut MaybeUninit <T > { +// SAFETY: The requirements of this function ensure this call is safe. +unsafe { bindings::per_cpu_ptr (self.0 .cast (), cpu.as_u32 ()) }.cast () + } }// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the
rust/kernel/percpu/dynamic.rs +40 -0
@@ -3,6 +3,8 @@use super ::*; +use crate ::cpumask::Cpumask ; +/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when /// dropped. pub struct PerCpuAllocation <T >(PerCpuPtr <T >);
@@ -74,6 +76,36 @@ pub fn new_zero(flags: Flags) -> Option<Self> { } } +impl <T :Clone >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable +/// +/// # Arguments +/// * `val` - The initial value of the per-CPU variable on all CPUs. +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_with (val:T , flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_uninit ()?; +let ptr = alloc.0 ; + +for cpuin Cpumask ::possible ().iter () { +// SAFETY: `ptr` is a valid allocation, and `cpu` appears in `Cpumask::possible()` +let remote_ptr =unsafe { ptr.get_remote_ptr (cpu) }; +// SAFETY: Each CPU's slot corresponding to `ptr` is currently uninitialized, and no +// one else has a reference to it. Therefore, we can freely write to it without +// worrying about the need to drop what was there or whether we're racing with someone +// else. `PerCpuPtr::get_remote_ptr` guarantees that the pointer is valid since we +// derived it from a valid allocation and `cpu`. +unsafe { + (*remote_ptr).write (val.clone ()); + } + } + +let arc =Arc ::new (alloc, flags).ok ()?; + +Some (Self { alloc: arc }) + } +} +impl <T >PerCpu <T >for DynamicPerCpu <T > {unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T > {// SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the
@@ -81,3 +113,11 @@ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {unsafe {PerCpuToken ::new (guard, &self.alloc.0 ) } } } + +impl <T :InteriorMutable >CheckedPerCpu <T >for DynamicPerCpu <T > { +fn get (&mut self, guard:CpuGuard ) ->CheckedPerCpuToken <'_,T > { +// SAFETY: By the invariant of `DynamicPerCpu`, the memory location in each CPU's +// per-CPU area corresponding to this variable has been initialized. +unsafe {CheckedPerCpuToken ::new (guard, &self.alloc.0 ) } + } +}
samples/rust/rust_percpu.rs +92 -7
122 lines not on this page · raw message
Signature · 1 line
2.34.1
Yury Norov
maintainerRe: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 48 lines · Mitchell Levy
Add functionality to
PerCpuPtrto compute pointers to per-CPU variable slots on other CPUs. Use this facility to initialize per-CPU variables on all possible CPUs when a dynamic per-CPU variable is created with a non-zeroable type. SinceRefCelland otherCell-like types fall into this category,impl CheckedPerCpuonDynamicPerCpufor theseInteriorMutabletypes since they can now be used. Add examples of these usages tosamples/rust/rust_percpu.rs.Signed-off-by Mitchell Levy <[email protected]>--- rust/helpers/percpu.c | 5 +++ rust/kernel/percpu.rs | 15 +++++++ rust/kernel/percpu/dynamic.rs | 40 +++++++++++++++++ samples/rust/rust_percpu.rs | 99 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 152 insertions(+), 7 deletions(-)rust/helpers/percpu.c +5
-0@@ -8,6 +8,11 @@ void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align)return __alloc_percpu (sz, align); } +void *rust_helper_per_cpu_ptr (void __percpu *ptr,unsigned int cpu) +{ +return per_cpu_ptr (ptr, cpu); +} +void rust_helper_on_each_cpu (smp_call_func_t func,void *info,int wait) {on_each_cpu (func, info, wait);rust/kernel/percpu.rs +1
-0@@ -14,6 +14,7 @@use bindings::{alloc_percpu, free_percpu};use crate ::alloc::Flags ; +use crate ::cpu::CpuId ;use crate ::percpu::cpu_guard::CpuGuard ;use crate ::prelude::*;use crate ::sync::Arc ;@@ -115,6 +116,20 @@ pub fn get_ptr(&self) -> *mut MaybeUninit<T> {// the invariant that self.0 is a valid offset into the per-CPU area. (this_cpu_area).wrapping_add (self.0 as usize ).cast () }
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 128
Quoted 10 lines
rust/kernel/percpu.rs +10
-0+ +/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the CPU represented by `cpu`. Note +/// that without some kind of synchronization, use of the returned pointer may cause a data +/// race. It is the caller's responsibility to use the returned pointer in a reasonable way. +/// +/// # Safety +/// - The returned pointer is valid only if `self` is (that is, it points to a live allocation +/// correctly sized and aligned to hold a `T`) +/// - The returned pointer is valid only if the bit corresponding to `cpu` is set in +/// `Cpumask::possible()`.
Instead of explaining those rules in comments, can you just enforce them in code? Not sure about the 1st rule, but the 2nd one looks like a trivial check.
Quoted 26 lines
rust/kernel/percpu.rs +4
-0+pub unsafe fn get_remote_ptr (&self, cpu:CpuId ) -> *mut MaybeUninit <T > { +// SAFETY: The requirements of this function ensure this call is safe. +unsafe { bindings::per_cpu_ptr (self.0 .cast (), cpu.as_u32 ()) }.cast () + } }// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the rust/kernel/percpu/dynamic.rs +4
-0@@ -3,6 +3,8 @@use super ::*; +use crate ::cpumask::Cpumask ; +/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when /// dropped. pub struct PerCpuAllocation <T >(PerCpuPtr <T >);@@ -74,6 +76,36 @@ pub fn new_zero(flags: Flags) -> Option<Self> { } } +impl <T :Clone >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable
rust/kernel/percpu/dynamic.rs pub fn new_zero(flags: Flags) -> Option<Self> { · line 90
Quoted 10 lines
rust/kernel/percpu/dynamic.rs +10
-0+/// +/// # Arguments +/// * `val` - The initial value of the per-CPU variable on all CPUs. +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_with (val:T , flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_uninit ()?; +let ptr = alloc.0 ; + +for cpuin Cpumask ::possible ().iter () {
In C we've got the 'for_each_possible_cpu()'. Is there any way to preserve that semantics in rust? I really believe that similar semantics on higher level on both sides will help _a_lot_ for those transitioning into the rust world (like me).
Thanks,
Yury
Quoted 166 lines
rust/kernel/percpu/dynamic.rs +26
-0+// SAFETY: `ptr` is a valid allocation, and `cpu` appears in `Cpumask::possible()` +let remote_ptr =unsafe { ptr.get_remote_ptr (cpu) }; +// SAFETY: Each CPU's slot corresponding to `ptr` is currently uninitialized, and no +// one else has a reference to it. Therefore, we can freely write to it without +// worrying about the need to drop what was there or whether we're racing with someone +// else. `PerCpuPtr::get_remote_ptr` guarantees that the pointer is valid since we +// derived it from a valid allocation and `cpu`. +unsafe { + (*remote_ptr).write (val.clone ()); + } + } + +let arc =Arc ::new (alloc, flags).ok ()?; + +Some (Self { alloc: arc }) + } +} +impl <T >PerCpu <T >for DynamicPerCpu <T > {unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T > {// SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the @@ -81,3 +113,11 @@ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {unsafe {PerCpuToken ::new (guard, &self.alloc.0 ) } } } + +impl <T :InteriorMutable >CheckedPerCpu <T >for DynamicPerCpu <T > { +fn get (&mut self, guard:CpuGuard ) ->CheckedPerCpuToken <'_,T > { +// SAFETY: By the invariant of `DynamicPerCpu`, the memory location in each CPU's +// per-CPU area corresponding to this variable has been initialized. +unsafe {CheckedPerCpuToken ::new (guard, &self.alloc.0 ) } + } +}samples/rust/rust_percpu.rs +92
-7@@ -130,13 +130,72 @@ fn init(_module: &'static ThisModule) -> Result<Self, Error> {// SAFETY: No prerequisites for on_each_cpu. unsafe { -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),1 ); -on_each_cpu (Some (check_percpu), (&raw mut test).cast (),1 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),1 ); +on_each_cpu (Some (check_percpu_u64), (&raw mut test).cast (),1 ); } +let mut checked:DynamicPerCpu <RefCell <u64 >> = +DynamicPerCpu ::new_with (RefCell ::new (100 ),GFP_KERNEL ).unwrap (); + +// SAFETY: No prerequisites for on_each_cpu. +unsafe { +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),1 ); +on_each_cpu (Some (check_percpu_refcell_u64), (&raw mut checked).cast (),1 ); + } + + checked.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +assert! (*val.borrow () ==104 ); + +let mut checked_native =0 ; + *val.borrow_mut () =0 ; + + checked_native +=1 ; + *val.borrow_mut () +=1 ; +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native ==1 ); + + checked_native = checked_native.wrapping_add ((-1i64 )as u64 ); + val.replace_with (|old: &mut u64 | old.wrapping_add ((-1i64 )as u64 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native ==0 ); + + checked_native = checked_native.wrapping_add ((-1i64 )as u64 ); + val.replace_with (|old: &mut u64 | old.wrapping_add ((-1i64 )as u64 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native == (-1i64 )as u64 ); + + checked_native =0 ; + *val.borrow_mut () =0 ; + + checked_native = checked_native.wrapping_sub (1 ); + val.replace_with (|old: &mut u64 | old.wrapping_sub (1 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native == (-1i64 )as u64 ); +assert! (checked_native == *val.borrow () && checked_native ==u64 ::MAX ); + }); +pr_info! ("rust dynamic percpu test done\n" );// Return Err to unload the module @@ -144,7 +203,7 @@ fn init(_module: &'static ThisModule) -> Result<Self, Error> { } } -extern "C" fn inc_percpu (info: *mut c_void ) { +extern "C" fn inc_percpu_u64 (info: *mut c_void ) {// SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send. let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <u64 >)).clone () };pr_info! ("Incrementing on {}\n" ,CpuId ::current ().as_u32 ());@@ -153,7 +212,7 @@ extern "C" fn inc_percpu(info: *mut c_void) {unsafe { pcpu.get_mut (CpuGuard ::new ()) }.with (|val: &mut u64 | *val +=1 ); } -extern "C" fn check_percpu (info: *mut c_void ) { +extern "C" fn check_percpu_u64 (info: *mut c_void ) {// SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send. let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <u64 >)).clone () };pr_info! ("Asserting on {}\n" ,CpuId ::current ().as_u32 ());@@ -161,3 +220,29 @@ extern "C" fn check_percpu(info: *mut c_void) {// SAFETY: We don't have multiple clones of pcpu in scope unsafe { pcpu.get_mut (CpuGuard ::new ()) }.with (|val: &mut u64 |assert! (*val ==4 )); } + +extern "C" fn inc_percpu_refcell_u64 (info: *mut c_void ) { +// SAFETY: We know that info is a void *const DynamicPerCpu<RefCell<u64>> and +// DynamicPerCpu<RefCell<u64>> is Send. +let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <RefCell <u64 >>)).clone () }; +// SAFETY: smp_processor_id has no preconditions +pr_info! ("Incrementing on {}\n" ,CpuId ::current ().as_u32 ()); + + pcpu.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +let mut val = val.borrow_mut (); + *val +=1 ; + }); +} + +extern "C" fn check_percpu_refcell_u64 (info: *mut c_void ) { +// SAFETY: We know that info is a void *const DynamicPerCpu<RefCell<u64>> and +// DynamicPerCpu<RefCell<u64>> is Send. +let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <RefCell <u64 >>)).clone () }; +// SAFETY: smp_processor_id has no preconditions +pr_info! ("Asserting on {}\n" ,CpuId ::current ().as_u32 ()); + + pcpu.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +let val = val.borrow (); +assert! (*val ==104 ); + }); +}Signature · 2 lines
-- 2.34.1
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 55 lines · Yury Norov
Quoted 54 lines · Mitchell Levy
Add functionality to
PerCpuPtrto compute pointers to per-CPU variable slots on other CPUs. Use this facility to initialize per-CPU variables on all possible CPUs when a dynamic per-CPU variable is created with a non-zeroable type. SinceRefCelland otherCell-like types fall into this category,impl CheckedPerCpuonDynamicPerCpufor theseInteriorMutabletypes since they can now be used. Add examples of these usages tosamples/rust/rust_percpu.rs.Signed-off-by Mitchell Levy <[email protected]>--- rust/helpers/percpu.c | 5 +++ rust/kernel/percpu.rs | 15 +++++++ rust/kernel/percpu/dynamic.rs | 40 +++++++++++++++++ samples/rust/rust_percpu.rs | 99 ++++++++++++++++++++++++++++++++++++++++--- 4 files changed, 152 insertions(+), 7 deletions(-)rust/helpers/percpu.c +5
-0@@ -8,6 +8,11 @@ void __percpu *rust_helper_alloc_percpu(size_t sz, size_t align)return __alloc_percpu (sz, align); } +void *rust_helper_per_cpu_ptr (void __percpu *ptr,unsigned int cpu) +{ +return per_cpu_ptr (ptr, cpu); +} +void rust_helper_on_each_cpu (smp_call_func_t func,void *info,int wait) {on_each_cpu (func, info, wait);rust/kernel/percpu.rs +7
-0@@ -14,6 +14,7 @@use bindings::{alloc_percpu, free_percpu};use crate ::alloc::Flags ; +use crate ::cpu::CpuId ;use crate ::percpu::cpu_guard::CpuGuard ;use crate ::prelude::*;use crate ::sync::Arc ;@@ -115,6 +116,20 @@ pub fn get_ptr(&self) -> *mut MaybeUninit<T> {// the invariant that self.0 is a valid offset into the per-CPU area. (this_cpu_area).wrapping_add (self.0 as usize ).cast () } + +/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the CPU represented by `cpu`. Note +/// that without some kind of synchronization, use of the returned pointer may cause a data +/// race. It is the caller's responsibility to use the returned pointer in a reasonable way. +/// +/// # Safety
Quoted 8 lines
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 128
Quoted 4 lines
rust/kernel/percpu.rs +4
-0+/// - The returned pointer is valid only if `self` is (that is, it points to a live allocation +/// correctly sized and aligned to hold a `T`) +/// - The returned pointer is valid only if the bit corresponding to `cpu` is set in +/// `Cpumask::possible()`. Instead of explaining those rules in comments, can you just enforce them in code? Not sure about the 1st rule, but the 2nd one looks like a trivial check.
I don't think the first one is possible to check (at least, certainly not without peeking deeply into the per-CPU allocation management system).
For the second, I will probably be converting this function to a safe function (see my reply to Miguel), with these being requirements for the returned pointer to be valid, so panicking when the requirements aren't met would probably not be expected behavior.
Quoted 33 lines
Quoted 33 lines
rust/kernel/percpu.rs +4
-0+pub unsafe fn get_remote_ptr (&self, cpu:CpuId ) -> *mut MaybeUninit <T > { +// SAFETY: The requirements of this function ensure this call is safe. +unsafe { bindings::per_cpu_ptr (self.0 .cast (), cpu.as_u32 ()) }.cast () + } }// SAFETY: Sending a `PerCpuPtr<T>` to another thread is safe because as soon as it's sent, the rust/kernel/percpu/dynamic.rs +11
-0@@ -3,6 +3,8 @@use super ::*; +use crate ::cpumask::Cpumask ; +/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when /// dropped. pub struct PerCpuAllocation <T >(PerCpuPtr <T >);@@ -74,6 +76,36 @@ pub fn new_zero(flags: Flags) -> Option<Self> { } } +impl <T :Clone >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable +/// +/// # Arguments +/// * `val` - The initial value of the per-CPU variable on all CPUs. +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_with (val:T , flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_uninit ()?;
Quoted 8 lines
rust/kernel/percpu/dynamic.rs pub fn new_zero(flags: Flags) -> Option<Self> { · line 90
Quoted 3 lines
rust/kernel/percpu/dynamic.rs +3
-0+let ptr = alloc.0 ; + +for cpuin Cpumask ::possible ().iter () {In C we've got the '
for_each_possible_cpu()'. Is there any way to preserve that semantics in rust? I really believe that similar semantics on higher level on both sides will help _a_lot_ for those transitioning into the rust world (like me).
I'm not sure I understand what you mean --- I believe the semantics should be the same here (cpu takes on each value in cpu_possible_mask). Could you please clarify?
Thanks,
Mitchell
Quoted 169 lines
Thanks,
YuryQuoted 166 lines
rust/kernel/percpu/dynamic.rs +26
-0+// SAFETY: `ptr` is a valid allocation, and `cpu` appears in `Cpumask::possible()` +let remote_ptr =unsafe { ptr.get_remote_ptr (cpu) }; +// SAFETY: Each CPU's slot corresponding to `ptr` is currently uninitialized, and no +// one else has a reference to it. Therefore, we can freely write to it without +// worrying about the need to drop what was there or whether we're racing with someone +// else. `PerCpuPtr::get_remote_ptr` guarantees that the pointer is valid since we +// derived it from a valid allocation and `cpu`. +unsafe { + (*remote_ptr).write (val.clone ()); + } + } + +let arc =Arc ::new (alloc, flags).ok ()?; + +Some (Self { alloc: arc }) + } +} +impl <T >PerCpu <T >for DynamicPerCpu <T > {unsafe fn get_mut (&mut self, guard:CpuGuard ) ->PerCpuToken <'_,T > {// SAFETY: The requirements of `PerCpu::get_mut` and this type's invariant ensure that the @@ -81,3 +113,11 @@ unsafe fn get_mut(&mut self, guard: CpuGuard) -> PerCpuToken<'_, T> {unsafe {PerCpuToken ::new (guard, &self.alloc.0 ) } } } + +impl <T :InteriorMutable >CheckedPerCpu <T >for DynamicPerCpu <T > { +fn get (&mut self, guard:CpuGuard ) ->CheckedPerCpuToken <'_,T > { +// SAFETY: By the invariant of `DynamicPerCpu`, the memory location in each CPU's +// per-CPU area corresponding to this variable has been initialized. +unsafe {CheckedPerCpuToken ::new (guard, &self.alloc.0 ) } + } +}samples/rust/rust_percpu.rs +92
-7@@ -130,13 +130,72 @@ fn init(_module: &'static ThisModule) -> Result<Self, Error> {// SAFETY: No prerequisites for on_each_cpu. unsafe { -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),0 ); -on_each_cpu (Some (inc_percpu), (&raw mut test).cast (),1 ); -on_each_cpu (Some (check_percpu), (&raw mut test).cast (),1 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),0 ); +on_each_cpu (Some (inc_percpu_u64), (&raw mut test).cast (),1 ); +on_each_cpu (Some (check_percpu_u64), (&raw mut test).cast (),1 ); } +let mut checked:DynamicPerCpu <RefCell <u64 >> = +DynamicPerCpu ::new_with (RefCell ::new (100 ),GFP_KERNEL ).unwrap (); + +// SAFETY: No prerequisites for on_each_cpu. +unsafe { +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),0 ); +on_each_cpu (Some (inc_percpu_refcell_u64), (&raw mut checked).cast (),1 ); +on_each_cpu (Some (check_percpu_refcell_u64), (&raw mut checked).cast (),1 ); + } + + checked.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +assert! (*val.borrow () ==104 ); + +let mut checked_native =0 ; + *val.borrow_mut () =0 ; + + checked_native +=1 ; + *val.borrow_mut () +=1 ; +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native ==1 ); + + checked_native = checked_native.wrapping_add ((-1i64 )as u64 ); + val.replace_with (|old: &mut u64 | old.wrapping_add ((-1i64 )as u64 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native ==0 ); + + checked_native = checked_native.wrapping_add ((-1i64 )as u64 ); + val.replace_with (|old: &mut u64 | old.wrapping_add ((-1i64 )as u64 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native == (-1i64 )as u64 ); + + checked_native =0 ; + *val.borrow_mut () =0 ; + + checked_native = checked_native.wrapping_sub (1 ); + val.replace_with (|old: &mut u64 | old.wrapping_sub (1 )); +pr_info! ( +"Checked native: {}, *checked: {}\n" , + checked_native, + val.borrow () + ); +assert! (checked_native == *val.borrow () && checked_native == (-1i64 )as u64 ); +assert! (checked_native == *val.borrow () && checked_native ==u64 ::MAX ); + }); +pr_info! ("rust dynamic percpu test done\n" );// Return Err to unload the module @@ -144,7 +203,7 @@ fn init(_module: &'static ThisModule) -> Result<Self, Error> { } } -extern "C" fn inc_percpu (info: *mut c_void ) { +extern "C" fn inc_percpu_u64 (info: *mut c_void ) {// SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send. let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <u64 >)).clone () };pr_info! ("Incrementing on {}\n" ,CpuId ::current ().as_u32 ());@@ -153,7 +212,7 @@ extern "C" fn inc_percpu(info: *mut c_void) {unsafe { pcpu.get_mut (CpuGuard ::new ()) }.with (|val: &mut u64 | *val +=1 ); } -extern "C" fn check_percpu (info: *mut c_void ) { +extern "C" fn check_percpu_u64 (info: *mut c_void ) {// SAFETY: We know that info is a void *const DynamicPerCpu<u64> and DynamicPerCpu<u64> is Send. let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <u64 >)).clone () };pr_info! ("Asserting on {}\n" ,CpuId ::current ().as_u32 ());@@ -161,3 +220,29 @@ extern "C" fn check_percpu(info: *mut c_void) {// SAFETY: We don't have multiple clones of pcpu in scope unsafe { pcpu.get_mut (CpuGuard ::new ()) }.with (|val: &mut u64 |assert! (*val ==4 )); } + +extern "C" fn inc_percpu_refcell_u64 (info: *mut c_void ) { +// SAFETY: We know that info is a void *const DynamicPerCpu<RefCell<u64>> and +// DynamicPerCpu<RefCell<u64>> is Send. +let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <RefCell <u64 >>)).clone () }; +// SAFETY: smp_processor_id has no preconditions +pr_info! ("Incrementing on {}\n" ,CpuId ::current ().as_u32 ()); + + pcpu.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +let mut val = val.borrow_mut (); + *val +=1 ; + }); +} + +extern "C" fn check_percpu_refcell_u64 (info: *mut c_void ) { +// SAFETY: We know that info is a void *const DynamicPerCpu<RefCell<u64>> and +// DynamicPerCpu<RefCell<u64>> is Send. +let mut pcpu =unsafe { (*(infoas *const DynamicPerCpu <RefCell <u64 >>)).clone () }; +// SAFETY: smp_processor_id has no preconditions +pr_info! ("Asserting on {}\n" ,CpuId ::current ().as_u32 ()); + + pcpu.get (CpuGuard ::new ()).with (|val: &RefCell <u64 >| { +let val = val.borrow (); +assert! (*val ==104 ); + }); +}Signature · 2 lines
-- 2.34.1
Yury Norov
maintainerRe: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 2 lines · Mitchell Levy
Quoted 1 line · Yury Norov
On Thu, Aug 28, 2025 at 12:00:12PM -0700, Mitchell Levy wrote:
...
Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
rust/kernel/percpu/dynamic.rs +9
-0+impl <T :Clone >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable +/// +/// # Arguments +/// * `val` - The initial value of the per-CPU variable on all CPUs. +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_with (val:T , flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_uninit ()?;Quoted 8 lines
rust/kernel/percpu/dynamic.rs pub fn new_zero(flags: Flags) -> Option<Self> { · line 90
Quoted 3 lines
rust/kernel/percpu/dynamic.rs +3
-0+let ptr = alloc.0 ; + +for cpuin Cpumask ::possible ().iter () {In C we've got the '
for_each_possible_cpu()'. Is there any way to preserve that semantics in rust? I really believe that similar semantics on higher level on both sides will help _a_lot_ for those transitioning into the rust world (like me).
Quoted 4 lines
I'm not sure I understand what you mean --- I believe the semantics should be the same here (
cputakes on each value incpu_possible_mask). Could you please clarify?
I mean:
for_each_possible_cpu(cpu) {
let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) };
unsafe { (*remote_ptr).write(val.clone()); }
let arc = Arc::new(alloc, flags).ok()?;
Some(Self { alloc: arc })
}Is it possible to do the above in rust?
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 31 lines · Yury Norov
Quoted 2 lines · Mitchell Levy
Quoted 1 line · Yury Norov
On Thu, Aug 28, 2025 at 12:00:12PM -0700, Mitchell Levy wrote:
...
Quoted 18 lines
Quoted 9 lines
Quoted 9 lines
rust/kernel/percpu/dynamic.rs +9
-0+impl <T :Clone >DynamicPerCpu <T > { +/// Allocates a new per-CPU variable +/// +/// # Arguments +/// * `val` - The initial value of the per-CPU variable on all CPUs. +/// * `flags` - Flags used to allocate an `Arc` that keeps track of the underlying +/// `PerCpuAllocation`. +pub fn new_with (val:T , flags:Flags ) ->Option <Self > { +let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_uninit ()?;Quoted 8 lines
rust/kernel/percpu/dynamic.rs pub fn new_zero(flags: Flags) -> Option<Self> { · line 90
Quoted 3 lines
rust/kernel/percpu/dynamic.rs +3
-0+let ptr = alloc.0 ; + +for cpuin Cpumask ::possible ().iter () {In C we've got the '
for_each_possible_cpu()'. Is there any way to preserve that semantics in rust? I really believe that similar semantics on higher level on both sides will help _a_lot_ for those transitioning into the rust world (like me).Quoted 4 lines
I'm not sure I understand what you mean --- I believe the semantics should be the same here (
cputakes on each value incpu_possible_mask). Could you please clarify?I mean:
Quoted 8 lines
for_each_possible_cpu(cpu) { let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) }; unsafe { (*remote_ptr).write(val.clone()); } let arc = Arc::new(alloc, flags).ok()?; Some(Self { alloc: arc }) }Is it possible to do the above in rust?
Ah, I see.
The syntax would be slightly different, probably something like
use cpu::for_each_possible_cpu;
for_each_possible_cpu(|&cpu| {
let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) };
// ...
})it *might* also be possible to use a macro and dispense with the need for a closure, though I'm not familiar enough with proc macros to say for sure. That would probably look like
for_each_possible_cpu!(cpu) {
let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) };
// ...
}though personally I think the first one is better (simpler implementation without too much syntactic overhead, especially since closures are already used some within R4L).
Thanks,
Mitchell
Yury Norov
maintainerRe: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 25 lines
Quoted 9 lines
for_each_possible_cpu(cpu) { let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) }; unsafe { (*remote_ptr).write(val.clone()); } let arc = Arc::new(alloc, flags).ok()?; Some(Self { alloc: arc }) }Is it possible to do the above in rust?
Ah, I see.
The syntax would be slightly different, probably something like
use cpu::for_each_possible_cpu; for_each_possible_cpu(|&cpu| { let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) }; // ... })it *might* also be possible to use a macro and dispense with the need for a closure, though I'm not familiar enough with proc macros to say for sure. That would probably look like
Quoted 8 lines
for_each_possible_cpu!(cpu) { let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) }; // ... }though personally I think the first one is better (simpler implementation without too much syntactic overhead, especially since closures are already used some within R4L).
Sure, #1 is OK if you prefer it.
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
In reply to Mitchell LevyQuoted 5 lines · Mitchell Levy
it *might* also be possible to use a macro and dispense with the need for a closure, though I'm not familiar enough with proc macros to say for sure. That would probably look like
Quoted 8 lines
for_each_possible_cpu!(cpu) { let remote_ptr = unsafe { ptr.get_remote_ptr(cpu) }; // ... }though personally I think the first one is better (simpler implementation without too much syntactic overhead, especially since closures are already used some within R4L).
Yeah, please avoid macros as much as possible. Sometimes macros do have advantages, but if it is just to avoid a closure, then no, please avoid it.
Similarly, unless there is a concrete advantage needed with the function, please avoid it too -- the original for with the iterator is the normal way of doing it and already used plenty in the kernel.
Thanks!
Cheers,
Miguel
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
In reply to Mitchell Levyrust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 122
Quoted 4 lines · Mitchell Levy
rust/kernel/percpu.rs +3
-0+/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the CPU represented by `cpu`. Note +/// that without some kind of synchronization, use of the returned pointer may cause a data +/// race. It is the caller's responsibility to use the returned pointer in a reasonable way.
Please try to make the first paragraph ("short description" / title) smaller.
Does "reasonable" mean anything different than any other raw pointer?
Quoted 1 line
+ /// # Safety
Newline after section headers (also elsewhere).
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 128
Quoted 4 lines
rust/kernel/percpu.rs +4
-0+/// - The returned pointer is valid only if `self` is (that is, it points to a live allocation +/// correctly sized and aligned to hold a `T`) +/// - The returned pointer is valid only if the bit corresponding to `cpu` is set in +/// `Cpumask::possible()`.
It sounds like the returned pointer can be invalid without triggering UB -- could you please clarify why the method is unsafe?
In addition, please use intra-doc links wherever possible (e.g. there a was also an Arc elsewhere).
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 131
Quoted 2 lines
rust/kernel/percpu.rs +2
-0+// SAFETY: The requirements of this function ensure this call is safe. +unsafe { bindings::per_cpu_ptr (self.0 .cast (), cpu.as_u32 ()) }.cast ()
Please try to explain why, not just that it is. It isn't clear how the safety preconditions, which only seem to talk about the returned pointer, make this OK.
Thanks!
Cheers,
Miguel
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 7 lines · Miguel Ojeda
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 122
Quoted 4 lines · Mitchell Levy
rust/kernel/percpu.rs +3
-0+/// Get a `*mut MaybeUninit<T>` to the per-CPU variable on the CPU represented by `cpu`. Note +/// that without some kind of synchronization, use of the returned pointer may cause a data +/// race. It is the caller's responsibility to use the returned pointer in a reasonable way. Please try to make the first paragraph ("short description" / title) smaller.
Will do.
Quoted 1 line
Does "reasonable" mean anything different than any other raw pointer?
This will depend very heavily on T, there's a detailed discussion
here: https://docs.kernel.org/core-api/this_cpu_ops.html#remote-access-to-per-cpu-data
In general, remote accesses are strongly discouraged, and my intention was mostly just wanting to reflect that in this documentation.
Quoted 3 lines
Quoted 1 line
+ /// # Safety
Newline after section headers (also elsewhere).
Will do.
Quoted 7 lines
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 128
Quoted 4 lines
rust/kernel/percpu.rs +4
-0+/// - The returned pointer is valid only if `self` is (that is, it points to a live allocation +/// correctly sized and aligned to hold a `T`) +/// - The returned pointer is valid only if the bit corresponding to `cpu` is set in +/// `Cpumask::possible()`. It sounds like the returned pointer can be invalid without triggering UB -- could you please clarify why the method is
unsafe?
Yes, this is true; strictly speaking, calling this function without dereferencing the returned pointer is always safe. I suppose I find it clearer that, when a function has preconditions that are necessary for it to return a valid pointer, the "safe-ness" has more to do with the functions preconditions than the act of dereferencing the pointer.
In this case, the pointer shouldn't be going very far, but I think this logic applies especially well in cases where pointers might be getting stored away for later (and so the validity of the dereference later on might rely on non-local conditions).
All that said, I'm alright with having this be a safe function, with the documentation noting these requirements for the returned pointer to be valid.
Quoted 2 lines
In addition, please use intra-doc links wherever possible (e.g. there a was also an
Arcelsewhere).
Will do.
Quoted 6 lines
rust/kernel/percpu.rs pub fn get_ptr(&self) -> *mut MaybeUninit<T> { · line 131
Quoted 2 lines
rust/kernel/percpu.rs +2
-0+// SAFETY: The requirements of this function ensure this call is safe. +unsafe { bindings::per_cpu_ptr (self.0 .cast (), cpu.as_u32 ()) }.cast ()Please try to explain why, not just that it is. It isn't clear how the safety preconditions, which only seem to talk about the returned pointer, make this OK.
This flows from the first requirement (that self is a live allocation, which is necessary for per_cpu_ptr to return a valid pointer). Though, as above, simply possessing this pointer isn't UB, so it's arguable that any call to per_cpu_ptr (on x86 at least, I'm not sure how it's implemented on other arches) is always safe. Regardless, I agree this comment should be more clear (even if the function is safe, it's probably worth noting why the pointer returned is valid when the function preconditions are met); will fix.
Thanks,
Mitchell
Quoted 4 lines
Thanks!
Cheers,
Miguel
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 2 lines · Mitchell Levy
Will do.
By the way, sorry -- just to clarify, I didn't mean to remove anything, but rather split it (likely at the first full stop).
Quoted 2 lines
Yes, this is true; strictly speaking, calling this function without dereferencing the returned pointer is always safe. I suppose I find i
t> clearer that, when a function has preconditions that are necessary for
Quoted 2 lines
it to return a valid pointer, the "safe-ness" has more to do with the functions preconditions than the act of dereferencing the pointer.
In that case, I would make it safe -- we don't use safety preconditions to mark "dangerous" operations, so to speak.
Quoted 4 lines
In this case, the pointer shouldn't be going very far, but I think this logic applies especially well in cases where pointers might be getting stored away for later (and so the validity of the dereference later on might rely on non-local conditions).
But that applies to any returned raw pointer, no?
Quoted 8 lines
This flows from the first requirement (that
selfis a live allocation, which is necessary forper_cpu_ptrto return a valid pointer). Though, as above, simply possessing this pointer isn't UB, so it's arguable that any call toper_cpu_ptr(on x86 at least, I'm not sure how it's implemented on other arches) is always safe. Regardless, I agree this comment should be more clear (even if the function is safe, it's probably worth noting why the pointer returned is valid when the function preconditions are met); will fix.
Sounds good, thanks!
If you have cases you think other architectures may have different requirements, and you think it is likely one could enable the support for other arches and break it, then I would suggest adding a comment about it.
Or, ideally, try to see if other architectures are fine already, even if you still don't enable the code in other arches.
Cheers,
Miguel
Re: [PATCH v3 5/7] rust: percpu: Support non-zeroable types for DynamicPerCpu
Quoted 6 lines · Miguel Ojeda
Quoted 2 lines · Mitchell Levy
Will do.
By the way, sorry -- just to clarify, I didn't mean to remove anything, but rather split it (likely at the first full stop).
Oh no worries, I gotcha
Quoted 8 lines
Quoted 2 lines
Yes, this is true; strictly speaking, calling this function without dereferencing the returned pointer is always safe. I suppose I find i
t> clearer that, when a function has preconditions that are necessary for
Quoted 2 lines
it to return a valid pointer, the "safe-ness" has more to do with the functions preconditions than the act of dereferencing the pointer.
In that case, I would make it safe -- we don't use safety preconditions to mark "dangerous" operations, so to speak.
Understood.
Quoted 6 lines
Quoted 4 lines
In this case, the pointer shouldn't be going very far, but I think this logic applies especially well in cases where pointers might be getting stored away for later (and so the validity of the dereference later on might rely on non-local conditions).
But that applies to any returned raw pointer, no?
This is a fair point :)
Quoted 11 lines
Quoted 8 lines
This flows from the first requirement (that
selfis a live allocation, which is necessary forper_cpu_ptrto return a valid pointer). Though, as above, simply possessing this pointer isn't UB, so it's arguable that any call toper_cpu_ptr(on x86 at least, I'm not sure how it's implemented on other arches) is always safe. Regardless, I agree this comment should be more clear (even if the function is safe, it's probably worth noting why the pointer returned is valid when the function preconditions are met); will fix.Sounds good, thanks!
Quoted 7 lines
If you have cases you think other architectures may have different requirements, and you think it is likely one could enable the support for other arches and break it, then I would suggest adding a comment about it.
Or, ideally, try to see if other architectures are fine already, even if you still don't enable the code in other arches.
Yeah agreed, I'd like to get ARM64 support going soon-ish at the very least. My hope is that I should only need to mess with percpu::numeric and PerCpuPtr::get_ptr... usage of per_cpu_ptr *shouldn't* have any prerequisites, but there's just this note:
https://elixir.bootlin.com/linux/v6.17-rc4/source/include/asm-generic/percpu.h#L29
so I don't want to make strong claims :)
(note, I think the comment about x86_64 might be out-of-date, see
https://elixir.bootlin.com/linux/v6.17-rc4/source/arch/x86/kernel/setup_percpu.c#L32
and
https://elixir.bootlin.com/linux/v6.17-rc4/source/arch/x86/kernel/setup_percpu.c#L165
)
Thanks,
Mitchell
Quoted 2 lines
Cheers,
Miguel
PATCH v3 6/7 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.
--- rust/kernel/percpu.rs | 1 + rust/kernel/percpu/dynamic.rs | 2 +- rust/kernel/percpu/numeric.rs | 128 ++++++++++++++++++++++++++++++++++++++++++ samples/rust/rust_percpu.rs | 36 ++++++++++++ 4 files changed, 166 insertions(+), 1 deletion(-)
rust/kernel/percpu.rs +1 -0
@@ -4,6 +4,7 @@pub mod cpu_guard;mod dynamic; +pub mod numeric;mod static_;#[doc(inline)]
rust/kernel/percpu/dynamic.rs +1 -1
@@ -7,7 +7,7 @@/// Represents a dynamic allocation of a per-CPU variable via alloc_percpu. Calls free_percpu when /// dropped. -pub struct PerCpuAllocation <T >(PerCpuPtr <T >); +pub struct PerCpuAllocation <T >(pub (super )PerCpuPtr <T >);impl <T :Zeroable >PerCpuAllocation <T > {/// Dynamically allocates a space in the per-CPU area suitably sized and aligned to hold a `T`,
rust/kernel/percpu/numeric.rs new file +128 -0
128 lines not on this page · raw message
samples/rust/rust_percpu.rs +36 -0
@@ -27,6 +27,26 @@define_per_cpu! (UPERCPU :u64 =0 );define_per_cpu! (CHECKED :RefCell <u64 > =RefCell ::new (0 )); +macro_rules! make_optimization_test { + ($ty:ty) => { +let mut test:DynamicPerCpu <$ty> =DynamicPerCpu ::new_zero (GFP_KERNEL ).unwrap (); + { +let _ =CpuGuard ::new (); +// SAFETY: No other usage of `test` +unsafe { test.get_mut (CpuGuard ::new ()) }.with (|val: &mut $ty| *val =10 ); + test.num ().add (1 ); +// SAFETY: No other usage of `test` +unsafe { test.get_mut (CpuGuard ::new ()) }.with (|val: &mut $ty|assert_eq! (*val,11 )); + test.num ().add (10 ); +// SAFETY: No other usage of `test` +unsafe { test.get_mut (CpuGuard ::new ()) }.with (|val: &mut $ty|assert_eq! (*val,21 )); + test.num ().sub (5 ); +// SAFETY: No other usage of `test` +unsafe { test.get_mut (CpuGuard ::new ()) }.with (|val: &mut $ty|assert_eq! (*val,16 )); + } + }; +} +impl kernel::Module for PerCpuMod {fn init (_module: &'staticThisModule ) ->Result <Self ,Error > {pr_info! ("rust percpu test start\n" );
@@ -198,6 +218,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 ) }
Signature · 1 line
2.34.1
PATCH v3 7/7 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/dynamic.rs | 8 ++++++-- rust/kernel/percpu/numeric.rs | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-)
rust/kernel/percpu/dynamic.rs +6 -2
@@ -58,7 +58,10 @@ fn drop(&mut self) {pub struct DynamicPerCpu <T > {// INVARIANT: The memory location in each CPU's per-CPU area pointed at by `alloc.0` has been // initialized. +// INVARIANT: `ptr` is the per-CPU pointer managed by `alloc`, which does not change for the +// lifetime of `self`. pub (super ) alloc:Arc <PerCpuAllocation <T >>, +pub (super ) ptr:PerCpuPtr <T >, }impl <T :Zeroable >DynamicPerCpu <T > {
@@ -70,9 +73,10 @@ impl<T: Zeroable> DynamicPerCpu<T> {pub fn new_zero (flags:Flags ) ->Option <Self > {let alloc:PerCpuAllocation <T > =PerCpuAllocation ::new_zero ()?; +let ptr = alloc.0 ;let arc =Arc ::new (alloc, flags).ok ()?; -Some (Self { alloc: arc }) +Some (Self { alloc: arc, ptr }) } }
@@ -102,7 +106,7 @@ pub fn new_with(val: T, flags: Flags) -> Option<Self> {let arc =Arc ::new (alloc, flags).ok ()?; -Some (Self { alloc: arc }) +Some (Self { alloc: arc, ptr }) } }
rust/kernel/percpu/numeric.rs +2 -2
@@ -20,7 +20,7 @@ impl DynamicPerCpu<$ty> {pub fn num (&mut self) ->PerCpuNumeric <'_, $ty> {// The invariant is satisfied because `DynamicPerCpu`'s invariant guarantees that // this pointer is valid and initialized on all CPUs. -PerCpuNumeric { ptr: &self.alloc.0 } +PerCpuNumeric { ptr: &self.ptr } } }impl StaticPerCpu <$ty> {
@@ -71,7 +71,7 @@ impl DynamicPerCpu<$ty> {pub fn num (&mut self) ->PerCpuNumeric <'_, $ty> {// The invariant is satisfied because `DynamicPerCpu`'s invariant guarantees that // this pointer is valid and initialized on all CPUs. -PerCpuNumeric { ptr: &self.alloc.0 } +PerCpuNumeric { ptr: &self.ptr } } }impl StaticPerCpu <$ty> {
Signature · 1 line
2.34.1