[PATCH RFC 0/3] rust: Add Per-CPU Variable API

Mitchell Levy posted 3 patches 1 year, 1 month ago
There is a newer version of this series
lib/Kconfig.debug                 |   9 +++
lib/Makefile                      |   1 +
lib/percpu_test_rust.rs           |  65 +++++++++++++++
rust/helpers/helpers.c            |   1 +
rust/helpers/preempt.c            |  14 ++++
rust/kernel/lib.rs                |   3 +
rust/kernel/percpu.rs             | 161 ++++++++++++++++++++++++++++++++++++++
rust/kernel/percpu/cpu_guard.rs   |  29 +++++++
scripts/generate_rust_analyzer.py |   2 +-
9 files changed, 284 insertions(+), 1 deletion(-)
[PATCH RFC 0/3] rust: Add Per-CPU Variable API
Posted by Mitchell Levy 1 year, 1 month ago
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.

---
I am sending this patch as an RFC to gather feedback on the API as well
as get some input on correctness. In particular, the unsafe getter macro
is somewhat cumbersome, though its safety requirements for pure-Rust
code aren't too difficult to verify (interoperation with C code is
certainly much more tricky).

It was suggested that I base my implementation on Rust's thread-local
storage API. However, this API requires that for a thread-local T, any
particular usage must be via a &T. Thus, many times it is required that
thread-local variables be in the form RefCell<T> or Cell<T>. This has
some pretty severe disadvantages in the context of per-CPU variables.
Namely, RefCell<T> may panic if .borrow_mut is called multiple times at
different points in the call stack. This is similar to the problem
inherent in my current implementation (that is, unsafe_get_per_cpu_ref
must be used carefully so as to not create aliasing mut references), but
this implementation flags this potential problem via an unsafe block and
the resulting PerCpuRef can be easily passed along and used fairly
easily. Cell<T> on the other hand requires a copy any time the
underlying value is used, which is entirely avoided here.

Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>

---
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           |  65 +++++++++++++++
 rust/helpers/helpers.c            |   1 +
 rust/helpers/preempt.c            |  14 ++++
 rust/kernel/lib.rs                |   3 +
 rust/kernel/percpu.rs             | 161 ++++++++++++++++++++++++++++++++++++++
 rust/kernel/percpu/cpu_guard.rs   |  29 +++++++
 scripts/generate_rust_analyzer.py |   2 +-
 9 files changed, 284 insertions(+), 1 deletion(-)
---
base-commit: 0c5928deada15a8d075516e6e0d9ee19011bb000
change-id: 20240813-rust-percpu-ea2f54b5da33

Best regards,
-- 
Mitchell Levy <levymitchell0@gmail.com>
Re: [PATCH RFC 0/3] rust: Add Per-CPU Variable API
Posted by Dennis Zhou 1 year, 1 month ago
Hello,

On Thu, Dec 19, 2024 at 01:08:25PM -0800, Mitchell Levy wrote:
> 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.
> 
> ---
> I am sending this patch as an RFC to gather feedback on the API as well
> as get some input on correctness. In particular, the unsafe getter macro
> is somewhat cumbersome, though its safety requirements for pure-Rust
> code aren't too difficult to verify (interoperation with C code is
> certainly much more tricky).
> 

I've been thinking a little bit about this over the holidays and fwiw am
not really the right person to evaluate the rust side of this. I did
briefly discuss this with Hudson (cced) who is more familiar with rust.

In addition to Christoph's comments:
 1. This implementation seems targeted at static allocations and the
    safety of this api relies on the end user doing the right thing.

    There are many percpu variables created dynamically via
    alloc_percpu() and the lifetime of those variables are tied to a
    larger object. So access to those variables is safe via the
    understanding the larger object exists. I feel like there is room
    here for rust to do this checking somehow? Maybe in rust-only percpu
    variables, but something better nonetheless.

 2. As Christoph mentioned, the percpu api relies heavily on macros for
    architecture specific optimizations. Your implementation seems
    asm-generic correct, but we should find a way to export the
    architecture specific percpu macros.

    `this_cpu_off` is a percpu variable that helps x86 `arch_raw_cpu_ptr()`.

 3. Maybe a good use case to start off with would be percpu_refcount and
    how rust can use it or how it can make something like that safer?

Some of Hudson's direct comments about the Rust implementation:
 1. DerefMut for PerCpuRef is ultimately constructing a reference from a
    raw integer without preserving the provenance of the original
    pointer in any way (since this integer is being constructed from raw
    register reads). I believe this violates Rust's pointer provenance
    rules for unsafe code.

 2. The safety requirements of unsafe_get_per_cpu_ref seem like they
    would often not be possible to verify at the callsite of the macro
    without global knowledge -- especially for percpu variables defined
    in C code. Is this a misunderstanding? Can you show an example of
    where it would be straightforward to build a higher-level safe API
    on top of this unsafe one?

> It was suggested that I base my implementation on Rust's thread-local
> storage API. However, this API requires that for a thread-local T, any
> particular usage must be via a &T. Thus, many times it is required that
> thread-local variables be in the form RefCell<T> or Cell<T>. This has
> some pretty severe disadvantages in the context of per-CPU variables.
> Namely, RefCell<T> may panic if .borrow_mut is called multiple times at
> different points in the call stack. This is similar to the problem
> inherent in my current implementation (that is, unsafe_get_per_cpu_ref
> must be used carefully so as to not create aliasing mut references), but
> this implementation flags this potential problem via an unsafe block and
> the resulting PerCpuRef can be easily passed along and used fairly
> easily. Cell<T> on the other hand requires a copy any time the
> underlying value is used, which is entirely avoided here.
> 
> Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
> 

I think this is a cool topic and has helped inspire me to learn a bit
more rust. Hopefully I can be more helpful in the future.
 
Thanks,
Dennis
Re: [PATCH RFC 0/3] rust: Add Per-CPU Variable API
Posted by Mitchell Levy 1 year, 1 month ago
On Sat, Jan 04, 2025 at 02:45:43PM -0800, Dennis Zhou wrote:
> Hello,
> 
> On Thu, Dec 19, 2024 at 01:08:25PM -0800, Mitchell Levy wrote:
> > 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.
> > 
> > ---
> > I am sending this patch as an RFC to gather feedback on the API as well
> > as get some input on correctness. In particular, the unsafe getter macro
> > is somewhat cumbersome, though its safety requirements for pure-Rust
> > code aren't too difficult to verify (interoperation with C code is
> > certainly much more tricky).
> > 
> 
> I've been thinking a little bit about this over the holidays and fwiw am
> not really the right person to evaluate the rust side of this. I did
> briefly discuss this with Hudson (cced) who is more familiar with rust.
> 
> In addition to Christoph's comments:
>  1. This implementation seems targeted at static allocations and the
>     safety of this api relies on the end user doing the right thing.
> 
>     There are many percpu variables created dynamically via
>     alloc_percpu() and the lifetime of those variables are tied to a
>     larger object. So access to those variables is safe via the
>     understanding the larger object exists. I feel like there is room
>     here for rust to do this checking somehow? Maybe in rust-only percpu
>     variables, but something better nonetheless.

You're correct; my hope is that adding dynamic allocations on top of
this API should be straightforward. Because we don't have to worry
about aliasing concerns (since we're in control of the memory that gets
set aside), we can just directly spit out a PerCpuRef. The one wrinkle
would be adding a lifetime parameter to PerCpuRef and making everything
line up.

>  2. As Christoph mentioned, the percpu api relies heavily on macros for
>     architecture specific optimizations. Your implementation seems
>     asm-generic correct, but we should find a way to export the
>     architecture specific percpu macros.
> 
>     `this_cpu_off` is a percpu variable that helps x86 `arch_raw_cpu_ptr()`.

Definitely! I'm hoping to add some operator overloading to make using
the optimized operations really smooth. I didn't want to bite off too
much for this RFC, though.

>  3. Maybe a good use case to start off with would be percpu_refcount and
>     how rust can use it or how it can make something like that safer?

This looks like a great idea, thank you!

> Some of Hudson's direct comments about the Rust implementation:
>  1. DerefMut for PerCpuRef is ultimately constructing a reference from a
>     raw integer without preserving the provenance of the original
>     pointer in any way (since this integer is being constructed from raw
>     register reads). I believe this violates Rust's pointer provenance
>     rules for unsafe code.

Caveat: I'm not super familiar with Rust's provenance model.

My understanding is that there are no "provenance issues" in a loose
sense. What I mean by this is that, if everything is treated as the
pointer it "should be" the process looks like:

per-cpu subsystem owns per-cpu area -> pointer is stored in
this_cpu_area -> we create a new pointer (via an in-bounds offset +
shrinking to sizeof(T)) to the contents of the per-cpu variable in
question

It looks like asm! actually supports reading into pointer types (I had
misread the table in the Rust Reference, and thought it only supported
reading into integer types) --- this might help avoid discarding
provenance during a cast to usize? I'm definitely interested in hearing more
on this point.

>  2. The safety requirements of unsafe_get_per_cpu_ref seem like they
>     would often not be possible to verify at the callsite of the macro
>     without global knowledge -- especially for percpu variables defined
>     in C code. Is this a misunderstanding? Can you show an example of
>     where it would be straightforward to build a higher-level safe API
>     on top of this unsafe one?

Yeah interoperation with C per-cpu variables is certainly very
difficult. Unfortunately, I think this problem crops up any time you're
sharing a piece of memory between Rust and C since the existence of &T
that points at some piece of memory is a guarantee to the compiler that
the pointed-to memory will not change. This facility is intended more as
a trap-door for when there's no better option than as an API building
block.

> > It was suggested that I base my implementation on Rust's thread-local
> > storage API. However, this API requires that for a thread-local T, any
> > particular usage must be via a &T. Thus, many times it is required that
> > thread-local variables be in the form RefCell<T> or Cell<T>. This has
> > some pretty severe disadvantages in the context of per-CPU variables.
> > Namely, RefCell<T> may panic if .borrow_mut is called multiple times at
> > different points in the call stack. This is similar to the problem
> > inherent in my current implementation (that is, unsafe_get_per_cpu_ref
> > must be used carefully so as to not create aliasing mut references), but
> > this implementation flags this potential problem via an unsafe block and
> > the resulting PerCpuRef can be easily passed along and used fairly
> > easily. Cell<T> on the other hand requires a copy any time the
> > underlying value is used, which is entirely avoided here.
> > 
> > Signed-off-by: Mitchell Levy <levymitchell0@gmail.com>
> > 
> 
> I think this is a cool topic and has helped inspire me to learn a bit
> more rust. Hopefully I can be more helpful in the future.

I appreciate it; thanks to you and Hudson for your help!

> Thanks,
> Dennis