rust: add SRCU abstraction
Subsystems (4)
Onur Özkan
maintainerv4 cover rust: add SRCU abstraction
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Changes since v3 (which are for Sashiko notes [2]):
- Added rust helpers for srcu_barrier() and synchronize_srcu_expedited() so the abstraction builds with CONFIG_TINY_SRCU, where these are static inline functions.
- Added missing INVARIANT comment in Srcu::new() about why the type invariants hold after successful initialization.
Changes since v2:
- Removed closure-based API.
- Added #[doc(hidden)] on new_srcu macro.
- Added #[must_use..] on srcu::Guard.
- Improved the clean-up path (PinnedDrop implementation) which eventually made read_lock safe with leaked guards.
v2: https://lore.kernel.org/all/[email protected]
Changes since v1:
- Made the owned SRCU read-side guard API unsafe and added a safe closure based helper for callers that do not need to keep the guard. This is to avoid UB on the C side cleanup_srcu_struct where the SRCU struct is freed while there are still active guards, which can happen if the caller leaks the guard e.g., with mem::forget().
- Improved doc comments.
v1: https://lore.kernel.org/all/[email protected]
[1]: https://lore.kernel.org/all/[email protected]
[2]: https://sashiko.dev/#/patchset/[email protected]?part=2
Onur Özkan (3):
rust: helpers: add SRCU helpers
rust: sync: add SRCU abstraction
MAINTAINERS: add Rust SRCU files to SRCU entry
MAINTAINERS | 3 + rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 34 +++++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 158 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 198 insertions(+) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rs
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v4 1/3 rust: helpers: add SRCU helpers
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
--- rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 24 ++++++++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 rust/helpers/srcu.c
rust/helpers/helpers.c +1 -0
@@ -88,6 +88,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 +24 -0
@@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0 + +#include <linux/srcu.h> + +__rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp, +const char *name, +struct lock_class_key *key) +{ +#ifdef CONFIG_DEBUG_LOCK_ALLOC +return __init_srcu_struct (ssp, name, key); +#else /* !CONFIG_DEBUG_LOCK_ALLOC */ +return init_srcu_struct (ssp); +#endif /* CONFIG_DEBUG_LOCK_ALLOC */ +} + +__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); +}
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v4 2/3 rust: sync: add SRCU abstraction
In reply to Onur ÖzkanAdd a Rust abstraction for sleepable RCU (SRCU), backed by C srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.
Cleanup is handled via PinnedDrop, which explicitly drains pending grace periods and callbacks via synchronize_srcu and srcu_barrier before executing cleanup_srcu_struct to guarantee memory safety e.g. when there are leaked guards (via mem::forget($guard)).
--- rust/helpers/srcu.c | 10 +++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 158 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 rust/kernel/sync/srcu.rs
rust/helpers/srcu.c +10 -0
@@ -22,3 +22,13 @@ __rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx {srcu_read_unlock (ssp, idx); } + +__rust_helper void rust_helper_srcu_barrier(struct srcu_struct *ssp) +{ +srcu_barrier (ssp); +} + +__rust_helper void rust_helper_synchronize_srcu_expedited(struct srcu_struct *ssp) +{ +synchronize_srcu_expedited (ssp); +}
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 +158 -0
158 lines not on this page · raw message
Signature · 1 line
2.51.2
Boqun Feng
maintainerRe: [PATCH v4 2/3] rust: sync: add SRCU abstraction
Quoted 165 lines · Onur Özkan
Add a Rust abstraction for sleepable RCU (SRCU), backed by C
srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.Cleanup is handled via
PinnedDrop, which explicitly drains pending grace periods and callbacks viasynchronize_srcuandsrcu_barrierbefore executingcleanup_srcu_structto guarantee memory safety e.g. when there are leaked guards (viamem::forget($guard)).Signed-off-by Onur Özkan <[email protected]>--- rust/helpers/srcu.c | 10 +++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 158 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 rust/kernel/sync/srcu.rsrust/helpers/srcu.c +10
-0@@ -22,3 +22,13 @@ __rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx {srcu_read_unlock (ssp, idx); } + +__rust_helper void rust_helper_srcu_barrier(struct srcu_struct *ssp) +{ +srcu_barrier (ssp); +} + +__rust_helper void rust_helper_synchronize_srcu_expedited(struct srcu_struct *ssp) +{ +synchronize_srcu_expedited (ssp); +}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 +104
-0@@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Sleepable read-copy update (SRCU) support. +//! +//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h) + +use crate ::{ + bindings, + error::to_result, + prelude::*, + sync::LockClassKey , + types::{ +NotThreadSafe , +Opaque ,// + }, +}; + +use pin_init::pin_data; + +/// Creates an [`Srcu`] initialiser with the given name and a newly-created lock class. +#[doc(hidden)] +#[macro_export] +macro_rules! new_srcu { + ($($name:literal)?) => { + $crate::sync::Srcu ::new ($crate::optional_name!($($name)?), $crate::static_lock_class!()) + }; +} +pub use new_srcu; + +/// Sleepable read-copy update primitive. +/// +/// SRCU readers may sleep while holding the read-side guard. +/// +/// The destructor waits for active readers and callbacks, so it may sleep. +/// If a read-side guard has been leaked, dropping an [`Srcu`] may never return. +/// +/// # Invariants +/// +/// This represents a valid `struct srcu_struct` initialized by the C SRCU API +/// and it remains pinned and valid until the pinned destructor runs. +#[repr(transparent)] +#[pin_data( PinnedDrop )] +pub struct Srcu { +#[pin] + inner:Opaque <bindings::srcu_struct >, +} + +impl Srcu { +/// Creates a new SRCU instance. +#[inline] +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self ,Error > { +try_pin_init! (Self { +// INVARIANT: On success, the C initializer creates a valid `srcu_struct` and +// it remains pinned until `PinnedDrop` runs. + 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_with_key (ptr, name.as_char_ptr (), key.as_ptr ()) + }) + }), + }) + } + +/// Enters an SRCU read-side critical section. +/// +/// Leaking the returned [`Guard`] leaves the SRCU read-side critical +/// section active and makes `drop` sleep forever. +#[inline] +pub fn read_lock (&self) ->Guard <'_> { +// SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`. +let idx =unsafe { bindings::srcu_read_lock (self.inner.get ()) }; + +// INVARIANT: `idx` was returned by `srcu_read_lock()` for this `Srcu`. +Guard { + srcu: self, + idx, + _not_send:NotThreadSafe , + } + } + +/// Waits until all pre-existing SRCU readers have completed. +#[inline] +pub fn synchronize (&self) { +// SAFETY: By the type invariants, `self` contains a valid `struct 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. +#[inline] +pub fn synchronize_expedited (&self) { +// SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`. +unsafe { bindings::synchronize_srcu_expedited (self.inner.get ()) }; + } +} + +#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +let ptr = self.inner.get ();
rust/kernel/sync/srcu.rs line 114
Quoted 10 lines
rust/kernel/sync/srcu.rs new file +10
-0+ +// `cleanup_srcu_struct()` may return early if readers are still active. Because `Srcu` +// owns the embedded `srcu_struct`, returning from `drop` in that state could free memory +// that is still referenced by the C side. +// +// Wait for all readers to complete first. If any `Guard` was leaked, `synchronize_srcu()` +// will sleep forever. +// +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::synchronize_srcu (ptr) };
Sorry for being slow on this. But I think your approach is the right one here. However, even though this makes Srcu safe, it's still undesired if an Srcu::drop() blocks forever *silently*. I think we should call srcu_active_readers() here and throw a warning if a leaked Guard is detected.
The rest of the patch set looks good to me.
Regards,
Boqun
Quoted 47 lines
rust/kernel/sync/srcu.rs new file +44
-0+ +// Ensure all SRCU callbacks have been finished before freeing. +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::srcu_barrier (ptr) }; + +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::cleanup_srcu_struct (ptr) }; + } +} + +// 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`]. +/// +/// Leaking this guard with [`core::mem::forget`] leaves the SRCU read-side +/// critical section active and makes dropping the associated [`Srcu`] sleep forever. +/// +/// # Invariants +/// +/// `idx` is the index returned by `srcu_read_lock()` for `srcu`. +#[must_use = "if unused, the lock will be immediately unlocked" ] +pub struct Guard <'a> { + srcu: &'aSrcu , + idx:i32 , + _not_send:NotThreadSafe , +} + +impl Guard <'_> { +/// Explicitly releases the SRCU read-side critical section. +#[inline] +pub fn unlock (self) {} +} + +impl Drop for Guard <'_> { +#[inline] +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
maintainerRe: [PATCH v4 2/3] rust: sync: add SRCU abstraction
Quoted 174 lines · On Tue, 26 May 2026 08:33:58 -0700 Boqun Feng <[email protected]> wrote:
Quoted 173 lines · Onur Özkan
Add a Rust abstraction for sleepable RCU (SRCU), backed by C
srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.Cleanup is handled via
PinnedDrop, which explicitly drains pending grace periods and callbacks viasynchronize_srcuandsrcu_barrierbefore executingcleanup_srcu_structto guarantee memory safety e.g. when there are leaked guards (viamem::forget($guard)).Signed-off-by Onur Özkan <[email protected]>--- rust/helpers/srcu.c | 10 +++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 158 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 170 insertions(+) create mode 100644 rust/kernel/sync/srcu.rsrust/helpers/srcu.c +10
-0@@ -22,3 +22,13 @@ __rust_helper void rust_helper_srcu_read_unlock(struct srcu_struct *ssp, int idx {srcu_read_unlock (ssp, idx); } + +__rust_helper void rust_helper_srcu_barrier(struct srcu_struct *ssp) +{ +srcu_barrier (ssp); +} + +__rust_helper void rust_helper_synchronize_srcu_expedited(struct srcu_struct *ssp) +{ +synchronize_srcu_expedited (ssp); +}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 +112
-0@@ -0,0 +1,158 @@ +// SPDX-License-Identifier: GPL-2.0 + +//! Sleepable read-copy update (SRCU) support. +//! +//! C header: [`include/linux/srcu.h`](srctree/include/linux/srcu.h) + +use crate ::{ + bindings, + error::to_result, + prelude::*, + sync::LockClassKey , + types::{ +NotThreadSafe , +Opaque ,// + }, +}; + +use pin_init::pin_data; + +/// Creates an [`Srcu`] initialiser with the given name and a newly-created lock class. +#[doc(hidden)] +#[macro_export] +macro_rules! new_srcu { + ($($name:literal)?) => { + $crate::sync::Srcu ::new ($crate::optional_name!($($name)?), $crate::static_lock_class!()) + }; +} +pub use new_srcu; + +/// Sleepable read-copy update primitive. +/// +/// SRCU readers may sleep while holding the read-side guard. +/// +/// The destructor waits for active readers and callbacks, so it may sleep. +/// If a read-side guard has been leaked, dropping an [`Srcu`] may never return. +/// +/// # Invariants +/// +/// This represents a valid `struct srcu_struct` initialized by the C SRCU API +/// and it remains pinned and valid until the pinned destructor runs. +#[repr(transparent)] +#[pin_data( PinnedDrop )] +pub struct Srcu { +#[pin] + inner:Opaque <bindings::srcu_struct >, +} + +impl Srcu { +/// Creates a new SRCU instance. +#[inline] +pub fn new (name: &'staticCStr , key:Pin <&'staticLockClassKey >) ->impl PinInit <Self ,Error > { +try_pin_init! (Self { +// INVARIANT: On success, the C initializer creates a valid `srcu_struct` and +// it remains pinned until `PinnedDrop` runs. + 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_with_key (ptr, name.as_char_ptr (), key.as_ptr ()) + }) + }), + }) + } + +/// Enters an SRCU read-side critical section. +/// +/// Leaking the returned [`Guard`] leaves the SRCU read-side critical +/// section active and makes `drop` sleep forever. +#[inline] +pub fn read_lock (&self) ->Guard <'_> { +// SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`. +let idx =unsafe { bindings::srcu_read_lock (self.inner.get ()) }; + +// INVARIANT: `idx` was returned by `srcu_read_lock()` for this `Srcu`. +Guard { + srcu: self, + idx, + _not_send:NotThreadSafe , + } + } + +/// Waits until all pre-existing SRCU readers have completed. +#[inline] +pub fn synchronize (&self) { +// SAFETY: By the type invariants, `self` contains a valid `struct 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. +#[inline] +pub fn synchronize_expedited (&self) { +// SAFETY: By the type invariants, `self` contains a valid `struct srcu_struct`. +unsafe { bindings::synchronize_srcu_expedited (self.inner.get ()) }; + } +} + +#[pinned_drop] +impl PinnedDrop for Srcu { +fn drop (self:Pin <&mut Self >) { +let ptr = self.inner.get (); + +// `cleanup_srcu_struct()` may return early if readers are still active. Because `Srcu` +// owns the embedded `srcu_struct`, returning from `drop` in that state could free memory +// that is still referenced by the C side. +// +// Wait for all readers to complete first. If any `Guard` was leaked, `synchronize_srcu()` +// will sleep forever. +//
Quoted 8 lines
rust/kernel/sync/srcu.rs line 114
Quoted 2 lines
rust/kernel/sync/srcu.rs new file +2
-0+// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::synchronize_srcu (ptr) };Sorry for being slow on this. But I think your approach is the right one here. However, even though this makes Srcu safe, it's still undesired if an
Srcu::drop()blocks forever *silently*. I think we should callsrcu_active_readers()here and throw a warning if a leakedGuardis detected.
Sure, makes sense. I will send another version with this change.
Thanks,
Onur
Quoted 53 lines
The rest of the patch set looks good to me.
Regards,
BoqunQuoted 47 lines
rust/kernel/sync/srcu.rs new file +44
-0+ +// Ensure all SRCU callbacks have been finished before freeing. +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::srcu_barrier (ptr) }; + +// SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`. +unsafe { bindings::cleanup_srcu_struct (ptr) }; + } +} + +// 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`]. +/// +/// Leaking this guard with [`core::mem::forget`] leaves the SRCU read-side +/// critical section active and makes dropping the associated [`Srcu`] sleep forever. +/// +/// # Invariants +/// +/// `idx` is the index returned by `srcu_read_lock()` for `srcu`. +#[must_use = "if unused, the lock will be immediately unlocked" ] +pub struct Guard <'a> { + srcu: &'aSrcu , + idx:i32 , + _not_send:NotThreadSafe , +} + +impl Guard <'_> { +/// Explicitly releases the SRCU read-side critical section. +#[inline] +pub fn unlock (self) {} +} + +impl Drop for Guard <'_> { +#[inline] +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
maintainerPATCH v4 3/3 MAINTAINERS: add Rust SRCU files to SRCU entry
In reply to Onur ÖzkanInclude Rust side implementation files to the SRCU maintainer entry.
--- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+)
MAINTAINERS +3 -0
@@ -24624,6 +24624,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU) M: Lai Jiangshan <[email protected]> M: "Paul E. McKenney" <[email protected]> M: Josh Triplett <[email protected]> +M: Onur Özkan <[email protected]> (RUST) R: Steven Rostedt <[email protected]> R: Mathieu Desnoyers <[email protected]> L: [email protected]
@@ -24632,6 +24633,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