rust: Add Per-CPU Variable API
Subsystems (4)
v2 cover rust: Add Per-CPU Variable API
This series adds an API for declaring an using per-CPU variables from Rust, and it also adds support for Rust access to C per-CPU variables (subject to some soundness requirements). It also adds a small test module, lib/percpu_test_rust.rs, in the vein of lib/percpu_test.c.
---
This series builds on my earlier RFC by adding support for dynamically allocated per-CPU variables. In the process of this work, I became convinced that the API in the original RFC was untenable. In particular, copying a PerCpuRef would need to be an unsafe operation whose safety requirements would be very onerous to verify. Essentially, a PerCpuRef would need to be entirely unique on each CPU core (except for the moment the copy is created, immediately before it is sent off to another thread --- technically the requirement is that the copy could not be Deref'd before being sent). This is essentially impossible to satisfy if you wish to use on_each_cpu, and it is certainly not ergonomic.
The key challenge was preventing the creation of aliasing &mut T's while still allowing PerCpu<T>'s to be freely copied. Ultimately, Boqun suggested a closure-based API in which the user asserts that the contents of the closure don't "nest" with clones. That is, the user asserts that they're *not* doing something like:
```
let mut my_pcpu: PerCpu<u32> = /* ... */;
let mut my_pcpu2: PerCpu<u32> = my_pcpu.clone();
unsafe { my_pcpu.get(CpuGuard::new()) }.with(|my_pcpu_val: &mut u32| {
// UNSAFETY: This is bad
unsafe { my_pcpu2.get(CpuGuard::new()) }
.with(|aliasing_ref: &mut u32| { /* ... */ });
}
```This safety condition is (in my opinion) much easier to verify because we must only ensure the contents of the closure are well-behaved, rather than try and trace all possible paths that a PerCpuRef may take.
This API is certainly different from the original RFC, which is why I've sent another RFC. It's now much closer to the user-space thread-local API, though still avoids requiring the use of Cell-like types.
A small number of simplifying choices are made here, namely that allocations are always GFP_KERNEL. This was done with the intention of keeping the patch small while the overall shape of the API is evaluated.
---
Changes from RFC:
- 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 (3):
rust: percpu: introduce a rust API for per-CPU variables
rust: rust-analyzer: add lib to dirs searched for crates
rust: percpu: add a rust per-CPU variable test
lib/Kconfig.debug | 9 ++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 119 +++++++++++++++++++ rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 20 ++++ rust/helpers/preempt.c | 14 +++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 239 ++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 29 +++++ scripts/generate_rust_analyzer.py | 2 +- 10 files changed, 437 insertions(+), 1 deletion(-)
---
base-commit: a2cc6ff5ec8f91bc463fd3b0c26b61166a07eb11
change-id: 20240813-rust-percpu-ea2f54b5da33
Best regards,
Signature · 1 line
Mitchell Levy <[email protected]>
RFC PATCH v2 1/3 rust: percpu: introduce a rust API for per-CPU variables
Add a CpuGuard type that disables preemption for its lifetime. Add a PerCpuAllocation type used to track and, in the case of dynamic allocations, create and free per-CPU allocations. Add a define_per_cpu! macro to create static per-CPU allocations. Add a PerCpu type that manages PerCpuAllocation's being used from multiple cores, and add a PerCpuToken type used to assert exclusive access to a particular per-CPU variable.
--- rust/helpers/helpers.c | 2 + rust/helpers/percpu.c | 9 ++ rust/helpers/preempt.c | 14 +++ rust/kernel/lib.rs | 3 + rust/kernel/percpu.rs | 239 ++++++++++++++++++++++++++++++++++++++++ rust/kernel/percpu/cpu_guard.rs | 29 +++++ 6 files changed, 296 insertions(+)
rust/helpers/helpers.c +2 -0
@@ -23,7 +23,9 @@#include "page.c" #include "platform.c" #include "pci.c" +#include "percpu.c" #include "pid_namespace.c" +#include "preempt.c" #include "rbtree.c" #include "rcu.c" #include "refcount.c"
rust/helpers/percpu.c new file +9 -0
@@ -0,0 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/percpu.h> + +void __percpu *rust_helper_alloc_percpu (size_t sz,size_t align) +{ +return __alloc_percpu (sz, align); +} +
rust/helpers/preempt.c new file +14 -0
@@ -0,0 +1,14 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/preempt.h> + +void rust_helper_preempt_disable (void ) +{ +preempt_disable (); +} + +void rust_helper_preempt_enable (void ) +{ +preempt_enable (); +} +
rust/kernel/lib.rs +3 -0
@@ -67,6 +67,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 +239 -0
239 lines not on this page · raw message
rust/kernel/percpu/cpu_guard.rs new file +29 -0
@@ -0,0 +1,29 @@ +// 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 Drop for CpuGuard { +fn drop (&mut self) { +// SAFETY: There are no preconditions required to call preempt_enable +unsafe { + bindings::preempt_enable (); + } + } +}
Signature · 1 line
2.34.1
RFC PATCH v2 2/3 rust: rust-analyzer: add lib to dirs searched for crates
In reply to Mitchell LevyWhen generating rust-project.json, also include crates in lib/
--- scripts/generate_rust_analyzer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-)
scripts/generate_rust_analyzer.py +1 -1
@@ -144,7 +144,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs): # Then, the rest outside of `rust/`. # # We explicitly mention the top-level folders we want to cover. - extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers")) + extra_dirs = map(lambda dir: srctree / dir, ("samples", "drivers", "lib")) if external_src is not None: extra_dirs = [external_src] for folder in extra_dirs:
Signature · 1 line
2.34.1
RFC PATCH v2 3/3 rust: percpu: add a rust per-CPU variable test
In reply to Mitchell LevyAdd a short exercise for Rust's per-CPU variable API, modelled after lib/percpu_test.c
--- lib/Kconfig.debug | 9 ++++ lib/Makefile | 1 + lib/percpu_test_rust.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++++ rust/helpers/percpu.c | 11 +++++ 4 files changed, 140 insertions(+)
lib/Kconfig.debug +9 -0
@@ -2425,6 +2425,15 @@ config PERCPU_TEST If unsure, say N. +config PERCPU_TEST_RUST + tristate "Rust per cpu operations test" + depends on m && DEBUG_KERNEL && RUST + help + Enable this option to build a test module which validates Rust per-cpu + operations. + + If unsure, say N. + config ATOMIC64_SELFTEST tristate "Perform an atomic64_t self-test" help
lib/Makefile +1 -0
@@ -289,6 +289,7 @@ obj-$(CONFIG_RBTREE_TEST) += rbtree_test.o obj-$(CONFIG_INTERVAL_TREE_TEST) += interval_tree_test.o obj-$(CONFIG_PERCPU_TEST) += percpu_test.o +obj-$(CONFIG_PERCPU_TEST_RUST) += percpu_test_rust.o obj-$(CONFIG_ASN1) += asn1_decoder.o obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
lib/percpu_test_rust.rs new file +119 -0
119 lines not on this page · raw message
rust/helpers/percpu.c +11 -0
@@ -1,9 +1,20 @@// SPDX-License-Identifier: GPL-2.0 #include <linux/percpu.h> +#include <linux/smp.h> void __percpu *rust_helper_alloc_percpu (size_t sz,size_t align) {return __alloc_percpu (sz, align); } +void rust_helper_on_each_cpu (smp_call_func_t func,void *info,int wait) +{ +on_each_cpu (func, info, wait); +} + +int rust_helper_smp_processor_id (void ) +{ +return smp_processor_id (); +} +
Signature · 1 line
2.34.1