rust: add SRCU abstraction
Subsystems (3)
Onur Özkan
maintainerPATCH v2 1/4 rust: add SRCU abstraction
Add a Rust abstraction for sleepable RCU (SRCU), backed by srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.
--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 18 +++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 109 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rs
rust/helpers/helpers.c +1 -0
@@ -60,6 +60,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c"
rust/helpers/srcu.c new file +18 -0
@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/srcu.h> + +__rust_helper int rust_helper_init_srcu_struct(struct srcu_struct *ssp) +{ +return init_srcu_struct (ssp); +} + +__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp) +{ +return srcu_read_lock (ssp); +} + +__rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx) +{ +srcu_read_unlock (ssp, idx); +}
rust/kernel/sync.rs +2 -0
@@ -21,6 +21,7 @@pub mod rcu;mod refcount;mod set_once; +pub mod srcu;pub use arc::{Arc ,ArcBorrow ,UniqueArc };pub use completion::Completion ;
@@ -31,6 +32,7 @@pub use locked_by::LockedBy ;pub use refcount::Refcount ;pub use set_once::SetOnce ; +pub use srcu::Srcu ;/// Represents a lockdep class. ///
rust/kernel/sync/srcu.rs new file +109 -0
109 lines not on this page · raw message
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v2 2/4 MAINTAINERS: add Rust SRCU files to SRCU entry
Add the Rust SRCU helper and abstraction files to the SRCU entry and add Onur Özkan as a maintainer for the Rust SRCU pieces.
--- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+)
MAINTAINERS +3 -0
@@ -24403,6 +24403,7 @@ F: drivers/net/can/slcan/ SLEEPABLE READ-COPY UPDATE (SRCU) M: Lai Jiangshan <[email protected]> +M: Onur Özkan <[email protected]> (RUST) M: "Paul E. McKenney" <[email protected]> M: Josh Triplett <[email protected]> R: Steven Rostedt <[email protected]>
@@ -24413,6 +24414,8 @@ W: http://www.rdrop.com/users/paulmck/RCU/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev F: include/linux/srcu*.h F: kernel/rcu/srcu*.c +F: rust/helpers/srcu.c +F: rust/kernel/sync/srcu.rs SMACK SECURITY MODULE M: Casey Schaufler <[email protected]>
Signature · 1 line
2.51.2
Boqun Feng
maintainerQuoted 32 lines · Onur Özkan
Add a Rust abstraction for sleepable RCU (SRCU), backed by
srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.Signed-off-by Onur Özkan <[email protected]>--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 18 +++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 109 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rsrust/helpers/helpers.c +1
-0@@ -60,6 +60,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c" rust/helpers/srcu.c new file +0
-0
rust/helpers/srcu.c line 7
Quoted 7 lines
rust/helpers/srcu.c new file +7
-0@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/srcu.h> + +__rust_helper int rust_helper_init_srcu_struct(struct srcu_struct *ssp) +{ +return init_srcu_struct (ssp);
init_srcu_struct() is defined as a macro when LOCKDEP=y so that each srcu_struct has a own lock_class_key. Using a binding helper like this will make all srcu_struct share the same key. You can refer to how rust_helper___spin_lock_init() does (including how new_spinlock!() works).
Quoted 109 lines
rust/helpers/srcu.c new file +11
-0+} + +__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp) +{ +return srcu_read_lock (ssp); +} + +__rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx) +{ +srcu_read_unlock (ssp, idx); +}rust/kernel/sync.rs +2
-0@@ -21,6 +21,7 @@pub mod rcu;mod refcount;mod set_once; +pub mod srcu;pub use arc::{Arc ,ArcBorrow ,UniqueArc };pub use completion::Completion ;@@ -31,6 +32,7 @@pub use locked_by::LockedBy ;pub use refcount::Refcount ;pub use set_once::SetOnce ; +pub use srcu::Srcu ;/// Represents a lockdep class. /// rust/kernel/sync/srcu.rs new file +71
-0@@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Sleepable read-copy update (SRCU) abstraction. +//! +//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h) + +use crate ::{ + bindings, + error::to_result, + prelude::*, + types::{ +NotThreadSafe , +Opaque ,// + }, +}; + +use pin_init::pin_data; + +/// Creates an [`Srcu`] initialiser. +#[macro_export] +macro_rules! new_srcu { + () => { + $crate::sync::Srcu ::new () + }; +} + +/// Sleepable read-copy update primitive. +/// +/// SRCU readers may sleep while holding the read-side guard. +#[repr(transparent)] +#[pin_data( PinnedDrop )] +pub struct Srcu { +#[pin] + inner:Opaque <bindings::srcu_struct >, +} + +impl Srcu { +/// Creates a new SRCU instance. +pub fn new () ->impl PinInit <Self ,Error > { +try_pin_init! (Self { + inner <-Opaque ::try_ffi_init (|ptr: *mut bindings::srcu_struct | { +// SAFETY: `ptr` points to valid uninitialised memory for a `srcu_struct`. +to_result (unsafe { bindings::init_srcu_struct (ptr) }) + }), + }) + } + +/// Enters an SRCU read-side critical section. +pub fn read_lock (&self) ->Guard <'_> { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +let idx =unsafe { bindings::srcu_read_lock (self.inner.get ()) }; + +Guard { + srcu: self, + idx, + _nts:NotThreadSafe , + } + } + +/// Waits until all pre-existing SRCU readers have completed. +pub fn synchronize (&self) { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +unsafe { bindings::synchronize_srcu (self.inner.get ()) }; + } + +/// Waits until all pre-existing SRCU readers have completed, expedited. +/// +/// This requests a lower-latency grace period than [`Srcu::synchronize`] typically +/// at the cost of higher system-wide overhead. Prefer [`Srcu::synchronize`] by default +/// and use this variant only when reducing reset or teardown latency is more important +/// than the extra cost.
rust/kernel/sync/srcu.rs line 81
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+pub fn synchronize_expedited (&self) { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +unsafe { bindings::synchronize_srcu_expedited (self.inner.get ()) }; + } +} + +#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +// SAFETY: `self` is pinned and `inner` contains a valid initialized `srcu_struct`.
I think we need srcu_barrier() here to ensure all SRCU callbacks have finished. Otherwise if there is a pending callback, one of the "leak it" branch will be taken, and we cannot free the srcu_struct since other thread may still access it. (It's impossible for the current API to allow this happen since call_srcu() is not supported yet, but that's a natural extension after the initial support).
Please do double-check whether other "leak it" conditions can happen or not in cleanup_srcu_struct().
Quoted 4 lines
rust/kernel/sync/srcu.rs new file +4
-0+unsafe { bindings::cleanup_srcu_struct (self.as_ref ().get_ref ().inner.get ()) }; + } +} +
rust/kernel/sync/srcu.rs line 95
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+// SAFETY: `srcu_struct` may be shared and used across threads. +unsafe impl Send for Srcu {} +// SAFETY: `srcu_struct` may be shared and used concurrently. +unsafe impl Sync for Srcu {} + +/// Guard for an active SRCU read-side critical section on a particular [`Srcu`]. +pub struct Guard <'a> { + srcu: &'aSrcu , + idx: core::ffi::c_int , + _nts:NotThreadSafe ,
Just want to bring this up for completeness, if we use srcu_{up,down}_read() then the Guard is Send. Not sure there is a need for this at the moment, just make a note in case that anyone might need it.
Regards,
Boqun
Quoted 18 lines
rust/kernel/sync/srcu.rs new file +14
-0+} + +impl Guard <'_> { +/// Explicitly exits the SRCU read-side critical section. +pub fn unlock (self) {} +} + +impl Drop for Guard <'_> { +fn drop (&mut self) { +// SAFETY: `Guard` is only constructible through `Srcu::read_lock()`, +// which returns a valid index for the SRCU instance. +unsafe { bindings::srcu_read_unlock (self.srcu.inner.get (), self.idx) }; + } +}Signature · 1 line
2.51.2
Onur Özkan
maintainerQuoted 38 lines · On Thu, 16 Apr 2026 14:50:56 -0700 Boqun Feng <[email protected]> wrote:
Quoted 37 lines · Onur Özkan
Add a Rust abstraction for sleepable RCU (SRCU), backed by
srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.Signed-off-by Onur Özkan <[email protected]>--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 18 +++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 109 +++++++++++++++++++++++++++++++++++++++ 4 files changed, 130 insertions(+) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rsrust/helpers/helpers.c +1
-0@@ -60,6 +60,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c" rust/helpers/srcu.c new file +5
-0@@ -0,0 +1,18 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/srcu.h> + +__rust_helper int rust_helper_init_srcu_struct(struct srcu_struct *ssp)
Quoted 8 lines
rust/helpers/srcu.c line 7
Quoted 2 lines
rust/helpers/srcu.c new file +2
-0+{ +return init_srcu_struct (ssp);
init_srcu_struct()is defined as a macro when LOCKDEP=y so that eachsrcu_structhas a ownlock_class_key. Using a binding helper like this will make allsrcu_structshare the same key. You can refer to howrust_helper___spin_lock_init()does (including how new_spinlock!() works).
I see, will do that. Thanks!
Quoted 128 lines
Quoted 110 lines
rust/helpers/srcu.c new file +11
-0+} + +__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp) +{ +return srcu_read_lock (ssp); +} + +__rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx) +{ +srcu_read_unlock (ssp, idx); +}rust/kernel/sync.rs +2
-0@@ -21,6 +21,7 @@pub mod rcu;mod refcount;mod set_once; +pub mod srcu;pub use arc::{Arc ,ArcBorrow ,UniqueArc };pub use completion::Completion ;@@ -31,6 +32,7 @@pub use locked_by::LockedBy ;pub use refcount::Refcount ;pub use set_once::SetOnce ; +pub use srcu::Srcu ;/// Represents a lockdep class. /// rust/kernel/sync/srcu.rs new file +71
-0@@ -0,0 +1,109 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Sleepable read-copy update (SRCU) abstraction. +//! +//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h) + +use crate ::{ + bindings, + error::to_result, + prelude::*, + types::{ +NotThreadSafe , +Opaque ,// + }, +}; + +use pin_init::pin_data; + +/// Creates an [`Srcu`] initialiser. +#[macro_export] +macro_rules! new_srcu { + () => { + $crate::sync::Srcu ::new () + }; +} + +/// Sleepable read-copy update primitive. +/// +/// SRCU readers may sleep while holding the read-side guard. +#[repr(transparent)] +#[pin_data( PinnedDrop )] +pub struct Srcu { +#[pin] + inner:Opaque <bindings::srcu_struct >, +} + +impl Srcu { +/// Creates a new SRCU instance. +pub fn new () ->impl PinInit <Self ,Error > { +try_pin_init! (Self { + inner <-Opaque ::try_ffi_init (|ptr: *mut bindings::srcu_struct | { +// SAFETY: `ptr` points to valid uninitialised memory for a `srcu_struct`. +to_result (unsafe { bindings::init_srcu_struct (ptr) }) + }), + }) + } + +/// Enters an SRCU read-side critical section. +pub fn read_lock (&self) ->Guard <'_> { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +let idx =unsafe { bindings::srcu_read_lock (self.inner.get ()) }; + +Guard { + srcu: self, + idx, + _nts:NotThreadSafe , + } + } + +/// Waits until all pre-existing SRCU readers have completed. +pub fn synchronize (&self) { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +unsafe { bindings::synchronize_srcu (self.inner.get ()) }; + } + +/// Waits until all pre-existing SRCU readers have completed, expedited. +/// +/// This requests a lower-latency grace period than [`Srcu::synchronize`] typically +/// at the cost of higher system-wide overhead. Prefer [`Srcu::synchronize`] by default +/// and use this variant only when reducing reset or teardown latency is more important +/// than the extra cost. rust/kernel/sync/srcu.rs line 81
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+pub fn synchronize_expedited (&self) { +// SAFETY: By the type invariants, `self.inner.get()` is a valid initialized `srcu_struct`. +unsafe { bindings::synchronize_srcu_expedited (self.inner.get ()) }; + } +} + +#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +// SAFETY: `self` is pinned and `inner` contains a valid initialized `srcu_struct`. I think we need
srcu_barrier()here to ensure all SRCU callbacks have finished. Otherwise if there is a pending callback, one of the "leak it" branch will be taken, and we cannot free thesrcu_structsince other thread may still access it. (It's impossible for the current API to allow this happen sincecall_srcu()is not supported yet, but that's a natural extension after the initial support).
Quoted 3 lines
Please do double-check whether other "leak it" conditions can happen or not in
cleanup_srcu_struct().
I checked it and it cannot happen with the current API. But adding the barrier still seems reasonable to make the API future-proof. Good point, thanks.
Quoted 11 lines
Quoted 11 lines
rust/kernel/sync/srcu.rs new file +11
-0+unsafe { bindings::cleanup_srcu_struct (self.as_ref ().get_ref ().inner.get ()) }; + } +} + +// SAFETY: `srcu_struct` may be shared and used across threads. +unsafe impl Send for Srcu {} +// SAFETY: `srcu_struct` may be shared and used concurrently. +unsafe impl Sync for Srcu {} + +/// Guard for an active SRCU read-side critical section on a particular [`Srcu`]. +pub struct Guard <'a> {
Quoted 8 lines
rust/kernel/sync/srcu.rs line 95
Quoted 3 lines
rust/kernel/sync/srcu.rs new file +3
-0+ srcu: &'a Srcu, + idx: core::ffi::c_int, + _nts: NotThreadSafe,Just want to bring this up for completeness, if we use srcu_{up,down}_read() then the Guard is Send. Not sure there is a need for this at the moment, just make a note in case that anyone might need it.
I think we should have different Guard for that extension and I don't want to add a note like "This guard should be not used with srcu_{up,down}_read()" as we don't provide that yet. Maybe there is a better way to express that, but I am not sure if that's worth it, what do you think?
Quoted 22 lines
Regards,
BoqunQuoted 18 lines
rust/kernel/sync/srcu.rs new file +14
-0+} + +impl Guard <'_> { +/// Explicitly exits the SRCU read-side critical section. +pub fn unlock (self) {} +} + +impl Drop for Guard <'_> { +fn drop (&mut self) { +// SAFETY: `Guard` is only constructible through `Srcu::read_lock()`, +// which returns a valid index for the SRCU instance. +unsafe { bindings::srcu_read_unlock (self.srcu.inner.get (), self.idx) }; + } +}Signature · 1 line
2.51.2