* [PATCH v7 1/4] rust: helpers: add SRCU helpers
2026-05-28 6:27 [PATCH v7 0/4] rust: helpers: add SRCU helpers Onur Özkan
@ 2026-05-28 6:27 ` Onur Özkan
2026-05-28 6:27 ` [PATCH v7 2/4] srcu: expose srcu_readers_active() Onur Özkan
` (3 subsequent siblings)
4 siblings, 0 replies; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 6:27 UTC (permalink / raw)
To: rcu, rust-for-linux, linux-kernel
Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
Add helper wrappers for SRCU functions that are exposed to Rust
through generated bindings.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
include/linux/srcu.h | 5 +++++
rust/helpers/helpers.c | 1 +
rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++
3 files changed, 36 insertions(+)
create mode 100644 rust/helpers/srcu.c
diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index 81b1938512d5..790a4ef713c0 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -57,6 +57,11 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
int init_srcu_struct(struct srcu_struct *ssp);
+static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key)
+{
+ return init_srcu_struct(ssp);
+}
#ifndef CONFIG_TINY_SRCU
int init_srcu_struct_fast(struct srcu_struct *ssp);
int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
diff --git a/rust/helpers/helpers.c b/rust/helpers/helpers.c
index 625921e27dfb..f3562d3b3888 100644
--- a/rust/helpers/helpers.c
+++ b/rust/helpers/helpers.c
@@ -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"
diff --git a/rust/helpers/srcu.c b/rust/helpers/srcu.c
new file mode 100644
index 000000000000..225b3bf9334a
--- /dev/null
+++ b/rust/helpers/srcu.c
@@ -0,0 +1,30 @@
+// 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)
+{
+ return __init_srcu_struct(ssp, name, key);
+}
+
+__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_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);
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v7 2/4] srcu: expose srcu_readers_active()
2026-05-28 6:27 [PATCH v7 0/4] rust: helpers: add SRCU helpers Onur Özkan
2026-05-28 6:27 ` [PATCH v7 1/4] " Onur Özkan
@ 2026-05-28 6:27 ` Onur Özkan
2026-05-28 6:27 ` [PATCH v7 3/4] rust: sync: add SRCU abstraction Onur Özkan
` (2 subsequent siblings)
4 siblings, 0 replies; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 6:27 UTC (permalink / raw)
To: rcu, rust-for-linux, linux-kernel
Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
This is needed by rust/helpers/srcu.c which now adds
rust_helper_srcu_readers_active() as a wrapper around the SRCU helper
for Rust callers.
To achive this:
1- Move the srcu_readers_active() implementation from
"kernel/rcu/srcutree.c" to "include/linux/srcutree.h".
2- Implement a matching srcu_readers_active() in
"include/linux/srcutiny.h" and use it on the existing open-coded
WARN_ON() check in cleanup_srcu_struct().
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
include/linux/srcutiny.h | 13 +++++++++++++
include/linux/srcutree.h | 24 ++++++++++++++++++++++++
kernel/rcu/srcutiny.c | 2 +-
kernel/rcu/srcutree.c | 25 -------------------------
rust/helpers/srcu.c | 5 +++++
5 files changed, 43 insertions(+), 26 deletions(-)
diff --git a/include/linux/srcutiny.h b/include/linux/srcutiny.h
index 905b629e8fa3..fbcf13bc12d1 100644
--- a/include/linux/srcutiny.h
+++ b/include/linux/srcutiny.h
@@ -154,4 +154,17 @@ static inline void srcu_torture_stats_print(struct srcu_struct *ssp,
data_race(READ_ONCE(ssp->srcu_idx_max)));
}
+/**
+ * srcu_readers_active - returns true if there are readers. and false otherwise.
+ * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
+ *
+ * Note that this is not an atomic primitive, and can therefore suffer
+ * severe errors when invoked on an active srcu_struct. That said, it
+ * can be useful as an error check at cleanup time.
+ */
+static inline bool srcu_readers_active(struct srcu_struct *ssp)
+{
+ return READ_ONCE(ssp->srcu_lock_nesting[0]) || READ_ONCE(ssp->srcu_lock_nesting[1]);
+}
+
#endif
diff --git a/include/linux/srcutree.h b/include/linux/srcutree.h
index fd1a9270cb9a..75e54e4f963f 100644
--- a/include/linux/srcutree.h
+++ b/include/linux/srcutree.h
@@ -374,4 +374,28 @@ static inline void srcu_check_read_flavor(struct srcu_struct *ssp, int read_flav
__srcu_check_read_flavor(ssp, read_flavor);
}
+/**
+ * srcu_readers_active - returns true if there are readers. and false otherwise.
+ * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
+ *
+ * Note that this is not an atomic primitive, and can therefore suffer
+ * severe errors when invoked on an active srcu_struct. That said, it
+ * can be useful as an error check at cleanup time.
+ */
+static inline bool srcu_readers_active(struct srcu_struct *ssp)
+{
+ int cpu;
+ unsigned long sum = 0;
+
+ for_each_possible_cpu(cpu) {
+ struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
+
+ sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
+ sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
+ sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
+ sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
+ }
+ return sum;
+}
+
#endif
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index a2e2d516e51b..5dc26af604bf 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -85,7 +85,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct);
*/
void cleanup_srcu_struct(struct srcu_struct *ssp)
{
- WARN_ON(ssp->srcu_lock_nesting[0] || ssp->srcu_lock_nesting[1]);
+ WARN_ON(srcu_readers_active(ssp));
irq_work_sync(&ssp->srcu_irq_work);
flush_work(&ssp->srcu_work);
WARN_ON(ssp->srcu_gp_running);
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index 0d01cd8c4b4a..b1e97ba2e53f 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -598,31 +598,6 @@ static bool srcu_readers_active_idx_check(struct srcu_struct *ssp, int idx)
return srcu_readers_lock_idx(ssp, idx, did_gp, unlocks);
}
-/**
- * srcu_readers_active - returns true if there are readers. and false
- * otherwise
- * @ssp: which srcu_struct to count active readers (holding srcu_read_lock).
- *
- * Note that this is not an atomic primitive, and can therefore suffer
- * severe errors when invoked on an active srcu_struct. That said, it
- * can be useful as an error check at cleanup time.
- */
-static bool srcu_readers_active(struct srcu_struct *ssp)
-{
- int cpu;
- unsigned long sum = 0;
-
- for_each_possible_cpu(cpu) {
- struct srcu_data *sdp = per_cpu_ptr(ssp->sda, cpu);
-
- sum += atomic_long_read(&sdp->srcu_ctrs[0].srcu_locks);
- sum += atomic_long_read(&sdp->srcu_ctrs[1].srcu_locks);
- sum -= atomic_long_read(&sdp->srcu_ctrs[0].srcu_unlocks);
- sum -= atomic_long_read(&sdp->srcu_ctrs[1].srcu_unlocks);
- }
- return sum;
-}
-
/*
* We use an adaptive strategy for synchronize_srcu() and especially for
* synchronize_srcu_expedited(). We spin for a fixed time period
diff --git a/rust/helpers/srcu.c b/rust/helpers/srcu.c
index 225b3bf9334a..1a2f563640e0 100644
--- a/rust/helpers/srcu.c
+++ b/rust/helpers/srcu.c
@@ -9,6 +9,11 @@ __rust_helper int rust_helper_init_srcu_struct_with_key(struct srcu_struct *ssp,
return __init_srcu_struct(ssp, name, key);
}
+__rust_helper bool rust_helper_srcu_readers_active(struct srcu_struct *ssp)
+{
+ return srcu_readers_active(ssp);
+}
+
__rust_helper int rust_helper_srcu_read_lock(struct srcu_struct *ssp)
{
return srcu_read_lock(ssp);
--
2.51.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 6:27 [PATCH v7 0/4] rust: helpers: add SRCU helpers Onur Özkan
2026-05-28 6:27 ` [PATCH v7 1/4] " Onur Özkan
2026-05-28 6:27 ` [PATCH v7 2/4] srcu: expose srcu_readers_active() Onur Özkan
@ 2026-05-28 6:27 ` Onur Özkan
2026-05-28 8:20 ` Onur Özkan
2026-05-28 6:27 ` [PATCH v7 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry Onur Özkan
2026-05-28 18:50 ` [PATCH v7 0/4] rust: helpers: add SRCU helpers Paul E. McKenney
4 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 6:27 UTC (permalink / raw)
To: rcu, rust-for-linux, linux-kernel
Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
paulmck, josh, rostedt, mathieu.desnoyers, 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 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)`).
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync.rs | 2 +
rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
2 files changed, 168 insertions(+)
create mode 100644 rust/kernel/sync/srcu.rs
diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
index 993dbf2caa0e..0d6a5f1300c3 100644
--- a/rust/kernel/sync.rs
+++ b/rust/kernel/sync.rs
@@ -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.
///
diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
new file mode 100644
index 000000000000..343f00d070c7
--- /dev/null
+++ b/rust/kernel/sync/srcu.rs
@@ -0,0 +1,166 @@
+// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
+
+ // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
+ // and `srcu_readers_active()` only checks the active reader count.
+ if unsafe { bindings::srcu_readers_active(ptr) } {
+ crate::pr_warn!(
+ "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
+ );
+ }
+
+ // `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) };
+
+ // 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: &'a Srcu,
+ 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) };
+ }
+}
--
2.51.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 6:27 ` [PATCH v7 3/4] rust: sync: add SRCU abstraction Onur Özkan
@ 2026-05-28 8:20 ` Onur Özkan
2026-05-28 8:35 ` Onur Özkan
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 8:20 UTC (permalink / raw)
To: Onur Özkan
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Thu, 28 May 2026 09:27:35 +0300
Onur Özkan <work@onurozkan.dev> wrote:
> 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 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)`).
>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
> ---
> rust/kernel/sync.rs | 2 +
> rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 168 insertions(+)
> create mode 100644 rust/kernel/sync/srcu.rs
>
> diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> index 993dbf2caa0e..0d6a5f1300c3 100644
> --- a/rust/kernel/sync.rs
> +++ b/rust/kernel/sync.rs
> @@ -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.
> ///
> diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> new file mode 100644
> index 000000000000..343f00d070c7
> --- /dev/null
> +++ b/rust/kernel/sync/srcu.rs
> @@ -0,0 +1,166 @@
> +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> +
> + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> + // and `srcu_readers_active()` only checks the active reader count.
> + if unsafe { bindings::srcu_readers_active(ptr) } {
> + crate::pr_warn!(
> + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> + );
> + }
> +
> + // `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) };
Sashiko got a good point here which is calling synchronize_srcu() only if there
are active readers. That's a nice low-effort improvement we can have in the next
version.
Onur
> +
> + // 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: &'a Srcu,
> + 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) };
> + }
> +}
> --
> 2.51.2
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 8:20 ` Onur Özkan
@ 2026-05-28 8:35 ` Onur Özkan
2026-05-28 12:23 ` Gary Guo
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 8:35 UTC (permalink / raw)
To: Onur Özkan
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Thu, 28 May 2026 11:20:10 +0300
Onur Özkan <work@onurozkan.dev> wrote:
> On Thu, 28 May 2026 09:27:35 +0300
> Onur Özkan <work@onurozkan.dev> wrote:
>
> > 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 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)`).
> >
> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
> > ---
> > rust/kernel/sync.rs | 2 +
> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> > 2 files changed, 168 insertions(+)
> > create mode 100644 rust/kernel/sync/srcu.rs
> >
> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> > index 993dbf2caa0e..0d6a5f1300c3 100644
> > --- a/rust/kernel/sync.rs
> > +++ b/rust/kernel/sync.rs
> > @@ -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.
> > ///
> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> > new file mode 100644
> > index 000000000000..343f00d070c7
> > --- /dev/null
> > +++ b/rust/kernel/sync/srcu.rs
> > @@ -0,0 +1,166 @@
> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> > +
> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> > + // and `srcu_readers_active()` only checks the active reader count.
> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> > + crate::pr_warn!(
> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> > + );
> > + }
> > +
> > + // `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) };
>
> Sashiko got a good point here which is calling synchronize_srcu() only if there
> are active readers. That's a nice low-effort improvement we can have in the next
> version.
>
> Onur
Actually, now I am now thinking about whether we can come up with a better
approach when we detect leaked guards. Initially I came up with the
synchronize_srcu() solution because it would handle leaked guards automatically
without requiring any additional checks. But now that we can actually detect
whether guards are leaked the question becomes:
"Is there a better option than effectively sleeping forever when leaked
guards are detected?"
I have no plans for tomorrow other than finalizing this series including the
question above.
Onur
>
> > +
> > + // 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: &'a Srcu,
> > + 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) };
> > + }
> > +}
> > --
> > 2.51.2
> >
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 8:35 ` Onur Özkan
@ 2026-05-28 12:23 ` Gary Guo
2026-05-29 6:57 ` Onur Özkan
2026-05-29 15:36 ` Paul E. McKenney
0 siblings, 2 replies; 22+ messages in thread
From: Gary Guo @ 2026-05-28 12:23 UTC (permalink / raw)
To: Onur Özkan
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Thu May 28, 2026 at 9:35 AM BST, Onur Özkan wrote:
> On Thu, 28 May 2026 11:20:10 +0300
> Onur Özkan <work@onurozkan.dev> wrote:
>
>> On Thu, 28 May 2026 09:27:35 +0300
>> Onur Özkan <work@onurozkan.dev> wrote:
>>
>> > 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 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)`).
>> >
>> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
>> > ---
>> > rust/kernel/sync.rs | 2 +
>> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
>> > 2 files changed, 168 insertions(+)
>> > create mode 100644 rust/kernel/sync/srcu.rs
>> >
>> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
>> > index 993dbf2caa0e..0d6a5f1300c3 100644
>> > --- a/rust/kernel/sync.rs
>> > +++ b/rust/kernel/sync.rs
>> > @@ -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.
>> > ///
>> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
>> > new file mode 100644
>> > index 000000000000..343f00d070c7
>> > --- /dev/null
>> > +++ b/rust/kernel/sync/srcu.rs
>> > @@ -0,0 +1,166 @@
>> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
>> > +
>> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
>> > + // and `srcu_readers_active()` only checks the active reader count.
>> > + if unsafe { bindings::srcu_readers_active(ptr) } {
>> > + crate::pr_warn!(
>> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
>> > + );
I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
condition.
>> > + }
>> > +
>> > + // `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) };
>>
>> Sashiko got a good point here which is calling synchronize_srcu() only if there
>> are active readers. That's a nice low-effort improvement we can have in the next
>> version.
>>
>> Onur
>
> Actually, now I am now thinking about whether we can come up with a better
> approach when we detect leaked guards. Initially I came up with the
> synchronize_srcu() solution because it would handle leaked guards automatically
> without requiring any additional checks. But now that we can actually detect
> whether guards are leaked the question becomes:
>
> "Is there a better option than effectively sleeping forever when leaked
> guards are detected?"
>
> I have no plans for tomorrow other than finalizing this series including the
> question above.
The best solution is to proceed cleanups anyway, given Rust rules ensure that
these are actual leaks and not just srcu read-side critical section that failed
to synchronize with the destruction of SRCU.
This obviously require changes to the SRCU code though.
Best,
Gary
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 12:23 ` Gary Guo
@ 2026-05-29 6:57 ` Onur Özkan
2026-05-29 12:07 ` Gary Guo
2026-05-29 15:36 ` Paul E. McKenney
1 sibling, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-29 6:57 UTC (permalink / raw)
To: Gary Guo
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Thu, 28 May 2026 13:23:39 +0100
Gary Guo <gary@garyguo.net> wrote:
> On Thu May 28, 2026 at 9:35 AM BST, Onur Özkan wrote:
> > On Thu, 28 May 2026 11:20:10 +0300
> > Onur Özkan <work@onurozkan.dev> wrote:
> >
> >> On Thu, 28 May 2026 09:27:35 +0300
> >> Onur Özkan <work@onurozkan.dev> wrote:
> >>
> >> > 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 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)`).
> >> >
> >> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
> >> > ---
> >> > rust/kernel/sync.rs | 2 +
> >> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> >> > 2 files changed, 168 insertions(+)
> >> > create mode 100644 rust/kernel/sync/srcu.rs
> >> >
> >> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> >> > index 993dbf2caa0e..0d6a5f1300c3 100644
> >> > --- a/rust/kernel/sync.rs
> >> > +++ b/rust/kernel/sync.rs
> >> > @@ -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.
> >> > ///
> >> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> >> > new file mode 100644
> >> > index 000000000000..343f00d070c7
> >> > --- /dev/null
> >> > +++ b/rust/kernel/sync/srcu.rs
> >> > @@ -0,0 +1,166 @@
> >> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> >> > +
> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> >> > + // and `srcu_readers_active()` only checks the active reader count.
> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> >> > + crate::pr_warn!(
> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> >> > + );
>
> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> condition.
We also call cleanup_srcu_struct below. The idea was to provide additional
information, we don't need to call warn_on twice.
>
> >> > + }
> >> > +
> >> > + // `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) };
> >>
> >> Sashiko got a good point here which is calling synchronize_srcu() only if there
> >> are active readers. That's a nice low-effort improvement we can have in the next
> >> version.
> >>
> >> Onur
> >
> > Actually, now I am now thinking about whether we can come up with a better
> > approach when we detect leaked guards. Initially I came up with the
> > synchronize_srcu() solution because it would handle leaked guards automatically
> > without requiring any additional checks. But now that we can actually detect
> > whether guards are leaked the question becomes:
> >
> > "Is there a better option than effectively sleeping forever when leaked
> > guards are detected?"
> >
> > I have no plans for tomorrow other than finalizing this series including the
> > question above.
>
> The best solution is to proceed cleanups anyway, given Rust rules ensure that
> these are actual leaks and not just srcu read-side critical section that failed
> to synchronize with the destruction of SRCU.
>
> This obviously require changes to the SRCU code though.
The issue is difficult to fix purely from the C side. Once drop returns Rust
is free to destroy srcu_struct. If srcu still has pending callback associated
with that srcu_struct, for example from a future call_srcu() wrapper then
returning from drop while readers are active can turn into a UAF. There is also
no way to handle callbacks in a reasonable way in cleanup logic while there are
active readers.
I mean in theory this could be fixed in the C code, but that would require to
re-write srcu cores/semantics for this special case. The $clean_something helper
would need know that the active readers are abandoned and will never unlock and
it would also need to decide what to do with the pending callbacks, which is
also a big problem (as gp will never complete, callbacks will never run).
It's also worth to note that calling mem::forget on the srcu guard is WRONG
CODE and very easy to catch on review (by us and also Sashiko/any LLM). So
finding a solution that doesn't add too much complexity should be a key
consideration here. With that in mind, keeping the synchronize_srcu() not really
a bad solution. Sleeping forever is a bad failure mode, but it is better than a
potential UAF and either case requires sending a fix patch for the leaked guard
anyway.
- Onur
>
> Best,
> Gary
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 6:57 ` Onur Özkan
@ 2026-05-29 12:07 ` Gary Guo
2026-05-29 12:29 ` Onur Özkan
0 siblings, 1 reply; 22+ messages in thread
From: Gary Guo @ 2026-05-29 12:07 UTC (permalink / raw)
To: Onur Özkan, Gary Guo
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Fri May 29, 2026 at 7:57 AM BST, Onur Özkan wrote:
>> >> > +#[pinned_drop]
>> >> > +impl PinnedDrop for Srcu {
>> >> > + fn drop(self: Pin<&mut Self>) {
>> >> > + let ptr = self.inner.get();
>> >> > +
>> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
>> >> > + // and `srcu_readers_active()` only checks the active reader count.
>> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
>> >> > + crate::pr_warn!(
>> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
>> >> > + );
>>
>> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
>> condition.
>
> We also call cleanup_srcu_struct below. The idea was to provide additional
> information, we don't need to call warn_on twice.
If the code blocks on `synchronize_srcu` then there's no call to
`cleanup_srcu_struct`.
>
>>
>> >> > + }
>> >> > +
>> >> > + // `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) };
>> >>
>> >> Sashiko got a good point here which is calling synchronize_srcu() only if there
>> >> are active readers. That's a nice low-effort improvement we can have in the next
>> >> version.
>> >>
>> >> Onur
>> >
>> > Actually, now I am now thinking about whether we can come up with a better
>> > approach when we detect leaked guards. Initially I came up with the
>> > synchronize_srcu() solution because it would handle leaked guards automatically
>> > without requiring any additional checks. But now that we can actually detect
>> > whether guards are leaked the question becomes:
>> >
>> > "Is there a better option than effectively sleeping forever when leaked
>> > guards are detected?"
>> >
>> > I have no plans for tomorrow other than finalizing this series including the
>> > question above.
>>
>> The best solution is to proceed cleanups anyway, given Rust rules ensure that
>> these are actual leaks and not just srcu read-side critical section that failed
>> to synchronize with the destruction of SRCU.
>>
>> This obviously require changes to the SRCU code though.
>
>
> The issue is difficult to fix purely from the C side. Once drop returns Rust
> is free to destroy srcu_struct. If srcu still has pending callback associated
> with that srcu_struct, for example from a future call_srcu() wrapper then
> returning from drop while readers are active can turn into a UAF. There is also
> no way to handle callbacks in a reasonable way in cleanup logic while there are
> active readers.
Callbacks should be flushed during the drop due to srcu_barrier. Am I missing
something?
I'm pretty sure that, if we disregard potential misuses from C side, removing
all "leak it" paths would be fine and won't leak to UAF if all users are from
Rust side.
To be very clear, I am not advocating to actually implement this way. I agree
with your conclusion below that this is broken code and a warning + blocking is
good enough. This is really just my thoughts on your "is there a better option"
question, and I think it's better in ideal world, but I think blocking is a
good pragmatic choice.
Best,
Gary
>
> I mean in theory this could be fixed in the C code, but that would require to
> re-write srcu cores/semantics for this special case. The $clean_something helper
> would need know that the active readers are abandoned and will never unlock and
> it would also need to decide what to do with the pending callbacks, which is
> also a big problem (as gp will never complete, callbacks will never run).
>
> It's also worth to note that calling mem::forget on the srcu guard is WRONG
> CODE and very easy to catch on review (by us and also Sashiko/any LLM). So
> finding a solution that doesn't add too much complexity should be a key
> consideration here. With that in mind, keeping the synchronize_srcu() not really
> a bad solution. Sleeping forever is a bad failure mode, but it is better than a
> potential UAF and either case requires sending a fix patch for the leaked guard
> anyway.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 12:07 ` Gary Guo
@ 2026-05-29 12:29 ` Onur Özkan
2026-05-29 12:41 ` Gary Guo
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-29 12:29 UTC (permalink / raw)
To: Gary Guo
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Fri, 29 May 2026 13:07:18 +0100
Gary Guo <gary@garyguo.net> wrote:
> On Fri May 29, 2026 at 7:57 AM BST, Onur Özkan wrote:
> >> >> > +#[pinned_drop]
> >> >> > +impl PinnedDrop for Srcu {
> >> >> > + fn drop(self: Pin<&mut Self>) {
> >> >> > + let ptr = self.inner.get();
> >> >> > +
> >> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> >> >> > + // and `srcu_readers_active()` only checks the active reader count.
> >> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> >> >> > + crate::pr_warn!(
> >> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> >> >> > + );
> >>
> >> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> >> condition.
> >
> > We also call cleanup_srcu_struct below. The idea was to provide additional
> > information, we don't need to call warn_on twice.
>
> If the code blocks on `synchronize_srcu` then there's no call to
> `cleanup_srcu_struct`.
Ah right. I can do that in this case but honestly it's still more informative
with the current way. It explicitly tells you what the problem is.
>
> >
> >>
> >> >> > + }
> >> >> > +
> >> >> > + // `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) };
> >> >>
> >> >> Sashiko got a good point here which is calling synchronize_srcu() only if there
> >> >> are active readers. That's a nice low-effort improvement we can have in the next
> >> >> version.
> >> >>
> >> >> Onur
> >> >
> >> > Actually, now I am now thinking about whether we can come up with a better
> >> > approach when we detect leaked guards. Initially I came up with the
> >> > synchronize_srcu() solution because it would handle leaked guards automatically
> >> > without requiring any additional checks. But now that we can actually detect
> >> > whether guards are leaked the question becomes:
> >> >
> >> > "Is there a better option than effectively sleeping forever when leaked
> >> > guards are detected?"
> >> >
> >> > I have no plans for tomorrow other than finalizing this series including the
> >> > question above.
> >>
> >> The best solution is to proceed cleanups anyway, given Rust rules ensure that
> >> these are actual leaks and not just srcu read-side critical section that failed
> >> to synchronize with the destruction of SRCU.
> >>
> >> This obviously require changes to the SRCU code though.
> >
> >
> > The issue is difficult to fix purely from the C side. Once drop returns Rust
> > is free to destroy srcu_struct. If srcu still has pending callback associated
> > with that srcu_struct, for example from a future call_srcu() wrapper then
> > returning from drop while readers are active can turn into a UAF. There is also
> > no way to handle callbacks in a reasonable way in cleanup logic while there are
> > active readers.
>
> Callbacks should be flushed during the drop due to srcu_barrier. Am I missing
> something?
No. Callbacks can only be invoked once the grace period has completed [1], which
can never happen while there is an active reader.
[1]: https://elixir.bootlin.com/linux/v7.1-rc5/source/kernel/rcu/srcutree.c#L1452-L1454
>
> I'm pretty sure that, if we disregard potential misuses from C side, removing
> all "leak it" paths would be fine and won't leak to UAF if all users are from
> Rust side.
>
> To be very clear, I am not advocating to actually implement this way. I agree
> with your conclusion below that this is broken code and a warning + blocking is
> good enough. This is really just my thoughts on your "is there a better option"
> question, and I think it's better in ideal world, but I think blocking is a
> good pragmatic choice.
I see. Maybe I should have phrased the question like "Is there a better option
with similar complexity" to be more clear.
Onur
>
> Best,
> Gary
>
> >
> > I mean in theory this could be fixed in the C code, but that would require to
> > re-write srcu cores/semantics for this special case. The $clean_something helper
> > would need know that the active readers are abandoned and will never unlock and
> > it would also need to decide what to do with the pending callbacks, which is
> > also a big problem (as gp will never complete, callbacks will never run).
> >
> > It's also worth to note that calling mem::forget on the srcu guard is WRONG
> > CODE and very easy to catch on review (by us and also Sashiko/any LLM). So
> > finding a solution that doesn't add too much complexity should be a key
> > consideration here. With that in mind, keeping the synchronize_srcu() not really
> > a bad solution. Sleeping forever is a bad failure mode, but it is better than a
> > potential UAF and either case requires sending a fix patch for the leaked guard
> > anyway.
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 12:29 ` Onur Özkan
@ 2026-05-29 12:41 ` Gary Guo
2026-05-29 12:49 ` Onur Özkan
0 siblings, 1 reply; 22+ messages in thread
From: Gary Guo @ 2026-05-29 12:41 UTC (permalink / raw)
To: Onur Özkan, Gary Guo
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Fri May 29, 2026 at 1:29 PM BST, Onur Özkan wrote:
> On Fri, 29 May 2026 13:07:18 +0100
> Gary Guo <gary@garyguo.net> wrote:
>
>> On Fri May 29, 2026 at 7:57 AM BST, Onur Özkan wrote:
>> >> >> > +#[pinned_drop]
>> >> >> > +impl PinnedDrop for Srcu {
>> >> >> > + fn drop(self: Pin<&mut Self>) {
>> >> >> > + let ptr = self.inner.get();
>> >> >> > +
>> >> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
>> >> >> > + // and `srcu_readers_active()` only checks the active reader count.
>> >> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
>> >> >> > + crate::pr_warn!(
>> >> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
>> >> >> > + );
>> >>
>> >> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
>> >> condition.
>> >
>> > We also call cleanup_srcu_struct below. The idea was to provide additional
>> > information, we don't need to call warn_on twice.
>>
>> If the code blocks on `synchronize_srcu` then there's no call to
>> `cleanup_srcu_struct`.
>
>
> Ah right. I can do that in this case but honestly it's still more informative
> with the current way. It explicitly tells you what the problem is.
While the error message itself is not that informative (there's some potential
to improve this, see
https://lore.kernel.org/all/DI1IQE7MDV4O.5B2DVIXMX2OT@garyguo.net/), the stack
trace produced by a `warn_on` would have all the information and is more useful
to troubleshoot than a `pr_warn` which doesn't tell you which `Srcu` being
dropped is causing the issue.
>> >> >
>> >> > Actually, now I am now thinking about whether we can come up with a better
>> >> > approach when we detect leaked guards. Initially I came up with the
>> >> > synchronize_srcu() solution because it would handle leaked guards automatically
>> >> > without requiring any additional checks. But now that we can actually detect
>> >> > whether guards are leaked the question becomes:
>> >> >
>> >> > "Is there a better option than effectively sleeping forever when leaked
>> >> > guards are detected?"
>> >> >
>> >> > I have no plans for tomorrow other than finalizing this series including the
>> >> > question above.
>> >>
>> >> The best solution is to proceed cleanups anyway, given Rust rules ensure that
>> >> these are actual leaks and not just srcu read-side critical section that failed
>> >> to synchronize with the destruction of SRCU.
>> >>
>> >> This obviously require changes to the SRCU code though.
>> >
>> >
>> > The issue is difficult to fix purely from the C side. Once drop returns Rust
>> > is free to destroy srcu_struct. If srcu still has pending callback associated
>> > with that srcu_struct, for example from a future call_srcu() wrapper then
>> > returning from drop while readers are active can turn into a UAF. There is also
>> > no way to handle callbacks in a reasonable way in cleanup logic while there are
>> > active readers.
>>
>> Callbacks should be flushed during the drop due to srcu_barrier. Am I missing
>> something?
>
> No. Callbacks can only be invoked once the grace period has completed [1], which
> can never happen while there is an active reader.
>
> [1]: https://elixir.bootlin.com/linux/v7.1-rc5/source/kernel/rcu/srcutree.c#L1452-L1454
Well, then srcu_barrier will not return. When `srcu_barrier` returns all
in-flight SRCU callbacks must have been executed.
Best,
Gary
>
>>
>> I'm pretty sure that, if we disregard potential misuses from C side, removing
>> all "leak it" paths would be fine and won't leak to UAF if all users are from
>> Rust side.
>>
>> To be very clear, I am not advocating to actually implement this way. I agree
>> with your conclusion below that this is broken code and a warning + blocking is
>> good enough. This is really just my thoughts on your "is there a better option"
>> question, and I think it's better in ideal world, but I think blocking is a
>> good pragmatic choice.
>
> I see. Maybe I should have phrased the question like "Is there a better option
> with similar complexity" to be more clear.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 12:41 ` Gary Guo
@ 2026-05-29 12:49 ` Onur Özkan
2026-05-31 18:04 ` Paul E. McKenney
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-29 12:49 UTC (permalink / raw)
To: Gary Guo
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, paulmck, josh, rostedt,
mathieu.desnoyers
On Fri, 29 May 2026 13:41:10 +0100
Gary Guo <gary@garyguo.net> wrote:
> On Fri May 29, 2026 at 1:29 PM BST, Onur Özkan wrote:
> > On Fri, 29 May 2026 13:07:18 +0100
> > Gary Guo <gary@garyguo.net> wrote:
> >
> >> On Fri May 29, 2026 at 7:57 AM BST, Onur Özkan wrote:
> >> >> >> > +#[pinned_drop]
> >> >> >> > +impl PinnedDrop for Srcu {
> >> >> >> > + fn drop(self: Pin<&mut Self>) {
> >> >> >> > + let ptr = self.inner.get();
> >> >> >> > +
> >> >> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> >> >> >> > + // and `srcu_readers_active()` only checks the active reader count.
> >> >> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> >> >> >> > + crate::pr_warn!(
> >> >> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> >> >> >> > + );
> >> >>
> >> >> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> >> >> condition.
> >> >
> >> > We also call cleanup_srcu_struct below. The idea was to provide additional
> >> > information, we don't need to call warn_on twice.
> >>
> >> If the code blocks on `synchronize_srcu` then there's no call to
> >> `cleanup_srcu_struct`.
> >
> >
> > Ah right. I can do that in this case but honestly it's still more informative
> > with the current way. It explicitly tells you what the problem is.
>
> While the error message itself is not that informative (there's some potential
> to improve this, see
> https://lore.kernel.org/all/DI1IQE7MDV4O.5B2DVIXMX2OT@garyguo.net/), the stack
> trace produced by a `warn_on` would have all the information and is more useful
> to troubleshoot than a `pr_warn` which doesn't tell you which `Srcu` being
> dropped is causing the issue.
Cool, I will use it in the next version. Thanks!
>
> >> >> >
> >> >> > Actually, now I am now thinking about whether we can come up with a better
> >> >> > approach when we detect leaked guards. Initially I came up with the
> >> >> > synchronize_srcu() solution because it would handle leaked guards automatically
> >> >> > without requiring any additional checks. But now that we can actually detect
> >> >> > whether guards are leaked the question becomes:
> >> >> >
> >> >> > "Is there a better option than effectively sleeping forever when leaked
> >> >> > guards are detected?"
> >> >> >
> >> >> > I have no plans for tomorrow other than finalizing this series including the
> >> >> > question above.
> >> >>
> >> >> The best solution is to proceed cleanups anyway, given Rust rules ensure that
> >> >> these are actual leaks and not just srcu read-side critical section that failed
> >> >> to synchronize with the destruction of SRCU.
> >> >>
> >> >> This obviously require changes to the SRCU code though.
> >> >
> >> >
> >> > The issue is difficult to fix purely from the C side. Once drop returns Rust
> >> > is free to destroy srcu_struct. If srcu still has pending callback associated
> >> > with that srcu_struct, for example from a future call_srcu() wrapper then
> >> > returning from drop while readers are active can turn into a UAF. There is also
> >> > no way to handle callbacks in a reasonable way in cleanup logic while there are
> >> > active readers.
> >>
> >> Callbacks should be flushed during the drop due to srcu_barrier. Am I missing
> >> something?
> >
> > No. Callbacks can only be invoked once the grace period has completed [1], which
> > can never happen while there is an active reader.
> >
> > [1]: https://elixir.bootlin.com/linux/v7.1-rc5/source/kernel/rcu/srcutree.c#L1452-L1454
>
> Well, then srcu_barrier will not return. When `srcu_barrier` returns all
> in-flight SRCU callbacks must have been executed.
Yes, or there weren't any callbacks posted.
>
> Best,
> Gary
>
> >
> >>
> >> I'm pretty sure that, if we disregard potential misuses from C side, removing
> >> all "leak it" paths would be fine and won't leak to UAF if all users are from
> >> Rust side.
> >>
> >> To be very clear, I am not advocating to actually implement this way. I agree
> >> with your conclusion below that this is broken code and a warning + blocking is
> >> good enough. This is really just my thoughts on your "is there a better option"
> >> question, and I think it's better in ideal world, but I think blocking is a
> >> good pragmatic choice.
> >
> > I see. Maybe I should have phrased the question like "Is there a better option
> > with similar complexity" to be more clear.
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 12:49 ` Onur Özkan
@ 2026-05-31 18:04 ` Paul E. McKenney
0 siblings, 0 replies; 22+ messages in thread
From: Paul E. McKenney @ 2026-05-31 18:04 UTC (permalink / raw)
To: Onur Özkan
Cc: Gary Guo, rcu, rust-for-linux, linux-kernel, ojeda, boqun,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Fri, May 29, 2026 at 03:49:31PM +0300, Onur Özkan wrote:
> On Fri, 29 May 2026 13:41:10 +0100
> Gary Guo <gary@garyguo.net> wrote:
> > On Fri May 29, 2026 at 1:29 PM BST, Onur Özkan wrote:
> > > On Fri, 29 May 2026 13:07:18 +0100
> > > Gary Guo <gary@garyguo.net> wrote:
> > >> On Fri May 29, 2026 at 7:57 AM BST, Onur Özkan wrote:
[ . . . ]
> > >> >> > Actually, now I am now thinking about whether we can come up with a better
> > >> >> > approach when we detect leaked guards. Initially I came up with the
> > >> >> > synchronize_srcu() solution because it would handle leaked guards automatically
> > >> >> > without requiring any additional checks. But now that we can actually detect
> > >> >> > whether guards are leaked the question becomes:
> > >> >> >
> > >> >> > "Is there a better option than effectively sleeping forever when leaked
> > >> >> > guards are detected?"
> > >> >> >
> > >> >> > I have no plans for tomorrow other than finalizing this series including the
> > >> >> > question above.
> > >> >>
> > >> >> The best solution is to proceed cleanups anyway, given Rust rules ensure that
> > >> >> these are actual leaks and not just srcu read-side critical section that failed
> > >> >> to synchronize with the destruction of SRCU.
> > >> >>
> > >> >> This obviously require changes to the SRCU code though.
> > >> >
> > >> >
> > >> > The issue is difficult to fix purely from the C side. Once drop returns Rust
> > >> > is free to destroy srcu_struct. If srcu still has pending callback associated
> > >> > with that srcu_struct, for example from a future call_srcu() wrapper then
> > >> > returning from drop while readers are active can turn into a UAF. There is also
> > >> > no way to handle callbacks in a reasonable way in cleanup logic while there are
> > >> > active readers.
> > >>
> > >> Callbacks should be flushed during the drop due to srcu_barrier. Am I missing
> > >> something?
> > >
> > > No. Callbacks can only be invoked once the grace period has completed [1], which
> > > can never happen while there is an active reader.
> > >
> > > [1]: https://elixir.bootlin.com/linux/v7.1-rc5/source/kernel/rcu/srcutree.c#L1452-L1454
> >
> > Well, then srcu_barrier will not return. When `srcu_barrier` returns all
> > in-flight SRCU callbacks must have been executed.
>
> Yes, or there weren't any callbacks posted.
Just to be clear, this all requires that Rust is preventing any later
calls to call_srcu(). If there are later calls to call_srcu(), then
the earlier srcu_barrier() is not guaranteed to wait on them.
Thanx, Paul
> > Best,
> > Gary
> >
> > >
> > >>
> > >> I'm pretty sure that, if we disregard potential misuses from C side, removing
> > >> all "leak it" paths would be fine and won't leak to UAF if all users are from
> > >> Rust side.
> > >>
> > >> To be very clear, I am not advocating to actually implement this way. I agree
> > >> with your conclusion below that this is broken code and a warning + blocking is
> > >> good enough. This is really just my thoughts on your "is there a better option"
> > >> question, and I think it's better in ideal world, but I think blocking is a
> > >> good pragmatic choice.
> > >
> > > I see. Maybe I should have phrased the question like "Is there a better option
> > > with similar complexity" to be more clear.
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-28 12:23 ` Gary Guo
2026-05-29 6:57 ` Onur Özkan
@ 2026-05-29 15:36 ` Paul E. McKenney
2026-05-30 6:27 ` Onur Özkan
1 sibling, 1 reply; 22+ messages in thread
From: Paul E. McKenney @ 2026-05-29 15:36 UTC (permalink / raw)
To: Gary Guo
Cc: Onur Özkan, rcu, rust-for-linux, linux-kernel, ojeda, boqun,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Thu, May 28, 2026 at 01:23:39PM +0100, Gary Guo wrote:
> On Thu May 28, 2026 at 9:35 AM BST, Onur Özkan wrote:
> > On Thu, 28 May 2026 11:20:10 +0300
> > Onur Özkan <work@onurozkan.dev> wrote:
> >
> >> On Thu, 28 May 2026 09:27:35 +0300
> >> Onur Özkan <work@onurozkan.dev> wrote:
> >>
> >> > 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 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)`).
> >> >
> >> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
> >> > ---
> >> > rust/kernel/sync.rs | 2 +
> >> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> >> > 2 files changed, 168 insertions(+)
> >> > create mode 100644 rust/kernel/sync/srcu.rs
> >> >
> >> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> >> > index 993dbf2caa0e..0d6a5f1300c3 100644
> >> > --- a/rust/kernel/sync.rs
> >> > +++ b/rust/kernel/sync.rs
> >> > @@ -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.
> >> > ///
> >> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> >> > new file mode 100644
> >> > index 000000000000..343f00d070c7
> >> > --- /dev/null
> >> > +++ b/rust/kernel/sync/srcu.rs
> >> > @@ -0,0 +1,166 @@
> >> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> >> > +
> >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> >> > + // and `srcu_readers_active()` only checks the active reader count.
> >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> >> > + crate::pr_warn!(
> >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> >> > + );
>
> I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> condition.
>
> >> > + }
> >> > +
> >> > + // `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) };
> >>
> >> Sashiko got a good point here which is calling synchronize_srcu() only if there
> >> are active readers. That's a nice low-effort improvement we can have in the next
> >> version.
> >>
> >> Onur
> >
> > Actually, now I am now thinking about whether we can come up with a better
> > approach when we detect leaked guards. Initially I came up with the
> > synchronize_srcu() solution because it would handle leaked guards automatically
> > without requiring any additional checks. But now that we can actually detect
> > whether guards are leaked the question becomes:
> >
> > "Is there a better option than effectively sleeping forever when leaked
> > guards are detected?"
> >
> > I have no plans for tomorrow other than finalizing this series including the
> > question above.
>
> The best solution is to proceed cleanups anyway, given Rust rules ensure that
> these are actual leaks and not just srcu read-side critical section that failed
> to synchronize with the destruction of SRCU.
>
> This obviously require changes to the SRCU code though.
Right now, the C-language SRCU code would splat and leak the srcu_struct
structure. Of course, we *could* provide something that zeroed the
reader counts to avoid the splat and leak, but yikes! Or is there a
way to make such a function available *only* to the Rust code associated
with cleanup_srcu_struct(), and not to other C/Rust code?
Thanx, Paul
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-29 15:36 ` Paul E. McKenney
@ 2026-05-30 6:27 ` Onur Özkan
2026-05-31 18:06 ` Paul E. McKenney
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-30 6:27 UTC (permalink / raw)
To: Paul E. McKenney
Cc: Gary Guo, rcu, rust-for-linux, linux-kernel, ojeda, boqun,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Fri, 29 May 2026 08:36:16 -0700
"Paul E. McKenney" <paulmck@kernel.org> wrote:
> On Thu, May 28, 2026 at 01:23:39PM +0100, Gary Guo wrote:
> > On Thu May 28, 2026 at 9:35 AM BST, Onur Özkan wrote:
> > > On Thu, 28 May 2026 11:20:10 +0300
> > > Onur Özkan <work@onurozkan.dev> wrote:
> > >
> > >> On Thu, 28 May 2026 09:27:35 +0300
> > >> Onur Özkan <work@onurozkan.dev> wrote:
> > >>
> > >> > 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 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)`).
> > >> >
> > >> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
> > >> > ---
> > >> > rust/kernel/sync.rs | 2 +
> > >> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> > >> > 2 files changed, 168 insertions(+)
> > >> > create mode 100644 rust/kernel/sync/srcu.rs
> > >> >
> > >> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> > >> > index 993dbf2caa0e..0d6a5f1300c3 100644
> > >> > --- a/rust/kernel/sync.rs
> > >> > +++ b/rust/kernel/sync.rs
> > >> > @@ -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.
> > >> > ///
> > >> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> > >> > new file mode 100644
> > >> > index 000000000000..343f00d070c7
> > >> > --- /dev/null
> > >> > +++ b/rust/kernel/sync/srcu.rs
> > >> > @@ -0,0 +1,166 @@
> > >> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> > >> > +
> > >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> > >> > + // and `srcu_readers_active()` only checks the active reader count.
> > >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> > >> > + crate::pr_warn!(
> > >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> > >> > + );
> >
> > I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> > condition.
> >
> > >> > + }
> > >> > +
> > >> > + // `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) };
> > >>
> > >> Sashiko got a good point here which is calling synchronize_srcu() only if there
> > >> are active readers. That's a nice low-effort improvement we can have in the next
> > >> version.
> > >>
> > >> Onur
> > >
> > > Actually, now I am now thinking about whether we can come up with a better
> > > approach when we detect leaked guards. Initially I came up with the
> > > synchronize_srcu() solution because it would handle leaked guards automatically
> > > without requiring any additional checks. But now that we can actually detect
> > > whether guards are leaked the question becomes:
> > >
> > > "Is there a better option than effectively sleeping forever when leaked
> > > guards are detected?"
> > >
> > > I have no plans for tomorrow other than finalizing this series including the
> > > question above.
> >
> > The best solution is to proceed cleanups anyway, given Rust rules ensure that
> > these are actual leaks and not just srcu read-side critical section that failed
> > to synchronize with the destruction of SRCU.
> >
> > This obviously require changes to the SRCU code though.
>
> Right now, the C-language SRCU code would splat and leak the srcu_struct
> structure. Of course, we *could* provide something that zeroed the
> reader counts to avoid the splat and leak, but yikes! Or is there a
> way to make such a function available *only* to the Rust code associated
> with cleanup_srcu_struct(), and not to other C/Rust code?
>
Anyone can use them if they want from `bindings::` once it's exposed to Rust.
Regards,
Onur
> Thanx, Paul
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-30 6:27 ` Onur Özkan
@ 2026-05-31 18:06 ` Paul E. McKenney
2026-05-31 18:53 ` Miguel Ojeda
0 siblings, 1 reply; 22+ messages in thread
From: Paul E. McKenney @ 2026-05-31 18:06 UTC (permalink / raw)
To: Onur Özkan
Cc: Gary Guo, rcu, rust-for-linux, linux-kernel, ojeda, boqun,
bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Sat, May 30, 2026 at 09:27:29AM +0300, Onur Özkan wrote:
> On Fri, 29 May 2026 08:36:16 -0700
> "Paul E. McKenney" <paulmck@kernel.org> wrote:
>
> > On Thu, May 28, 2026 at 01:23:39PM +0100, Gary Guo wrote:
> > > On Thu May 28, 2026 at 9:35 AM BST, Onur Özkan wrote:
> > > > On Thu, 28 May 2026 11:20:10 +0300
> > > > Onur Özkan <work@onurozkan.dev> wrote:
> > > >
> > > >> On Thu, 28 May 2026 09:27:35 +0300
> > > >> Onur Özkan <work@onurozkan.dev> wrote:
> > > >>
> > > >> > 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 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)`).
> > > >> >
> > > >> > Signed-off-by: Onur Özkan <work@onurozkan.dev>
> > > >> > ---
> > > >> > rust/kernel/sync.rs | 2 +
> > > >> > rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> > > >> > 2 files changed, 168 insertions(+)
> > > >> > create mode 100644 rust/kernel/sync/srcu.rs
> > > >> >
> > > >> > diff --git a/rust/kernel/sync.rs b/rust/kernel/sync.rs
> > > >> > index 993dbf2caa0e..0d6a5f1300c3 100644
> > > >> > --- a/rust/kernel/sync.rs
> > > >> > +++ b/rust/kernel/sync.rs
> > > >> > @@ -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.
> > > >> > ///
> > > >> > diff --git a/rust/kernel/sync/srcu.rs b/rust/kernel/sync/srcu.rs
> > > >> > new file mode 100644
> > > >> > index 000000000000..343f00d070c7
> > > >> > --- /dev/null
> > > >> > +++ b/rust/kernel/sync/srcu.rs
> > > >> > @@ -0,0 +1,166 @@
> > > >> > +// 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: &'static CStr, key: Pin<&'static LockClassKey>) -> 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();
> > > >> > +
> > > >> > + // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
> > > >> > + // and `srcu_readers_active()` only checks the active reader count.
> > > >> > + if unsafe { bindings::srcu_readers_active(ptr) } {
> > > >> > + crate::pr_warn!(
> > > >> > + "Leaked `Guard` detected while dropping SRCU; drop will block forever.\n"
> > > >> > + );
> > >
> > > I think this could be a `warn_on` similar to how cleanup_srcu_struct handle the
> > > condition.
> > >
> > > >> > + }
> > > >> > +
> > > >> > + // `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) };
> > > >>
> > > >> Sashiko got a good point here which is calling synchronize_srcu() only if there
> > > >> are active readers. That's a nice low-effort improvement we can have in the next
> > > >> version.
> > > >>
> > > >> Onur
> > > >
> > > > Actually, now I am now thinking about whether we can come up with a better
> > > > approach when we detect leaked guards. Initially I came up with the
> > > > synchronize_srcu() solution because it would handle leaked guards automatically
> > > > without requiring any additional checks. But now that we can actually detect
> > > > whether guards are leaked the question becomes:
> > > >
> > > > "Is there a better option than effectively sleeping forever when leaked
> > > > guards are detected?"
> > > >
> > > > I have no plans for tomorrow other than finalizing this series including the
> > > > question above.
> > >
> > > The best solution is to proceed cleanups anyway, given Rust rules ensure that
> > > these are actual leaks and not just srcu read-side critical section that failed
> > > to synchronize with the destruction of SRCU.
> > >
> > > This obviously require changes to the SRCU code though.
> >
> > Right now, the C-language SRCU code would splat and leak the srcu_struct
> > structure. Of course, we *could* provide something that zeroed the
> > reader counts to avoid the splat and leak, but yikes! Or is there a
> > way to make such a function available *only* to the Rust code associated
> > with cleanup_srcu_struct(), and not to other C/Rust code?
>
> Anyone can use them if they want from `bindings::` once it's exposed to Rust.
Is there a Rust-language counterpart to checkpatch.pl that could warn
when it sees patches adding calls to this function?
Thanx, Paul
> Regards,
> Onur
>
> > Thanx, Paul
>
^ permalink raw reply [flat|nested] 22+ messages in thread* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-31 18:06 ` Paul E. McKenney
@ 2026-05-31 18:53 ` Miguel Ojeda
2026-05-31 19:11 ` Onur Özkan
0 siblings, 1 reply; 22+ messages in thread
From: Miguel Ojeda @ 2026-05-31 18:53 UTC (permalink / raw)
To: paulmck
Cc: Onur Özkan, Gary Guo, rcu, rust-for-linux, linux-kernel,
ojeda, boqun, bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross,
dakr, peterz, fujita.tomonori, tamird, jiangshanlai, josh,
rostedt, mathieu.desnoyers
On Sun, May 31, 2026 at 8:06 PM Paul E. McKenney <paulmck@kernel.org> wrote:
>
> Is there a Rust-language counterpart to checkpatch.pl that could warn
> when it sees patches adding calls to this function?
We had some patches for proposed Rust checks for `checkpatch.pl`, so
it could be there (there was also a proposed `rust_checkpatch.pl`).
Having said that, Clippy's `disallowed_methods` is likely better
(modulo bugs), and we already have a couple entries on it (please see
`.clippy.toml` in the root). So I would suggest that first.
There is also Klint if something more custom/complex is needed.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-31 18:53 ` Miguel Ojeda
@ 2026-05-31 19:11 ` Onur Özkan
2026-06-01 9:29 ` Miguel Ojeda
0 siblings, 1 reply; 22+ messages in thread
From: Onur Özkan @ 2026-05-31 19:11 UTC (permalink / raw)
To: Miguel Ojeda
Cc: paulmck, Gary Guo, rcu, rust-for-linux, linux-kernel, ojeda,
boqun, bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr,
peterz, fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Sun, 31 May 2026 20:53:07 +0200
Miguel Ojeda <miguel.ojeda.sandonis@gmail.com> wrote:
> On Sun, May 31, 2026 at 8:06 PM Paul E. McKenney <paulmck@kernel.org> wrote:
> >
> > Is there a Rust-language counterpart to checkpatch.pl that could warn
> > when it sees patches adding calls to this function?
>
> We had some patches for proposed Rust checks for `checkpatch.pl`, so
> it could be there (there was also a proposed `rust_checkpatch.pl`).
>
> Having said that, Clippy's `disallowed_methods` is likely better
> (modulo bugs), and we already have a couple entries on it (please see
> `.clippy.toml` in the root). So I would suggest that first.
>
> There is also Klint if something more custom/complex is needed.
>
I am waiting for the v9 [1] reviews. If we get a complain on the current
approach we can consider the Rust-specific clean-up C function +
disallowed_methods from Clippy.
Thanks,
Onur
[1]: https://lore.kernel.org/all/20260529134004.396743-1-work@onurozkan.dev
> Cheers,
> Miguel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-05-31 19:11 ` Onur Özkan
@ 2026-06-01 9:29 ` Miguel Ojeda
2026-06-04 17:50 ` Paul E. McKenney
0 siblings, 1 reply; 22+ messages in thread
From: Miguel Ojeda @ 2026-06-01 9:29 UTC (permalink / raw)
To: Onur Özkan
Cc: paulmck, Gary Guo, rcu, rust-for-linux, linux-kernel, ojeda,
boqun, bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross, dakr,
peterz, fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Mon, Jun 1, 2026 at 11:17 AM Onur Özkan <work@onurozkan.dev> wrote:
>
> I am waiting for the v9 [1] reviews. If we get a complain on the current
> approach we can consider the Rust-specific clean-up C function +
> disallowed_methods from Clippy.
Just to clarify, I only meant to reply to Paul about linting in Rust
in general, i.e. I didn't mean to ask for a particular approach here.
Cheers,
Miguel
^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [PATCH v7 3/4] rust: sync: add SRCU abstraction
2026-06-01 9:29 ` Miguel Ojeda
@ 2026-06-04 17:50 ` Paul E. McKenney
0 siblings, 0 replies; 22+ messages in thread
From: Paul E. McKenney @ 2026-06-04 17:50 UTC (permalink / raw)
To: Miguel Ojeda
Cc: Onur Özkan, Gary Guo, rcu, rust-for-linux, linux-kernel,
ojeda, boqun, bjorn3_gh, lossin, a.hindborg, aliceryhl, tmgross,
dakr, peterz, fujita.tomonori, tamird, jiangshanlai, josh,
rostedt, mathieu.desnoyers
On Mon, Jun 01, 2026 at 11:29:40AM +0200, Miguel Ojeda wrote:
> On Mon, Jun 1, 2026 at 11:17 AM Onur Özkan <work@onurozkan.dev> wrote:
> >
> > I am waiting for the v9 [1] reviews. If we get a complain on the current
> > approach we can consider the Rust-specific clean-up C function +
> > disallowed_methods from Clippy.
>
> Just to clarify, I only meant to reply to Paul about linting in Rust
> in general, i.e. I didn't mean to ask for a particular approach here.
Apologies for being slow! Onur, could you please use whichever method
works best so that inappropriate use of the new C-language SRCU functions
for Rust get complained about?
Thanx, Paul
^ permalink raw reply [flat|nested] 22+ messages in thread
* [PATCH v7 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry
2026-05-28 6:27 [PATCH v7 0/4] rust: helpers: add SRCU helpers Onur Özkan
` (2 preceding siblings ...)
2026-05-28 6:27 ` [PATCH v7 3/4] rust: sync: add SRCU abstraction Onur Özkan
@ 2026-05-28 6:27 ` Onur Özkan
2026-05-28 18:50 ` [PATCH v7 0/4] rust: helpers: add SRCU helpers Paul E. McKenney
4 siblings, 0 replies; 22+ messages in thread
From: Onur Özkan @ 2026-05-28 6:27 UTC (permalink / raw)
To: rcu, rust-for-linux, linux-kernel
Cc: ojeda, boqun, gary, bjorn3_gh, lossin, a.hindborg, aliceryhl,
tmgross, dakr, peterz, fujita.tomonori, tamird, jiangshanlai,
paulmck, josh, rostedt, mathieu.desnoyers, Onur Özkan
Include Rust side implementation files to the SRCU maintainer
entry.
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
MAINTAINERS | 3 +++
1 file changed, 3 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index e0b307b2108c..7739a435f258 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -24624,6 +24624,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU)
M: Lai Jiangshan <jiangshanlai@gmail.com>
M: "Paul E. McKenney" <paulmck@kernel.org>
M: Josh Triplett <josh@joshtriplett.org>
+M: Onur Özkan <work@onurozkan.dev> (RUST)
R: Steven Rostedt <rostedt@goodmis.org>
R: Mathieu Desnoyers <mathieu.desnoyers@efficios.com>
L: rcu@vger.kernel.org
@@ -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 <casey@schaufler-ca.com>
--
2.51.2
^ permalink raw reply related [flat|nested] 22+ messages in thread* Re: [PATCH v7 0/4] rust: helpers: add SRCU helpers
2026-05-28 6:27 [PATCH v7 0/4] rust: helpers: add SRCU helpers Onur Özkan
` (3 preceding siblings ...)
2026-05-28 6:27 ` [PATCH v7 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry Onur Özkan
@ 2026-05-28 18:50 ` Paul E. McKenney
4 siblings, 0 replies; 22+ messages in thread
From: Paul E. McKenney @ 2026-05-28 18:50 UTC (permalink / raw)
To: Onur Özkan
Cc: rcu, rust-for-linux, linux-kernel, ojeda, boqun, gary, bjorn3_gh,
lossin, a.hindborg, aliceryhl, tmgross, dakr, peterz,
fujita.tomonori, tamird, jiangshanlai, josh, rostedt,
mathieu.desnoyers
On Thu, May 28, 2026 at 09:27:32AM +0300, Onur Özkan wrote:
> 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.
For the first two:
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
They also pass short rcutorture testing, which is reassuring:
tools/testing/selftests/rcutorture/bin/kvm.sh --allcpus --duration 40 --configs "SRCU-N SRCU-P SRCU-T SRCU-U" --trust-make
> Changes since v6:
>
> - Removed "CONFIG_DEBUG_LOCK_ALLOC" condition from "rust/helpers/srcu.c"
> and created a simple wrapper inside "include/linux/srcu.h" for it.
>
> v6: https://lore.kernel.org/all/20260527203615.163688-1-work@onurozkan.dev
>
> Changes since v5:
>
> - Created separate srcu_readers_active() variants for "srcutiny.h" and
> "srcutree.h".
>
> v5: https://lore.kernel.org/all/20260527174120.510447-1-work@onurozkan.dev
>
> Changes since v4:
>
> - Exposed srcu_readers_active from C side and wired it to the Rust
> helpers.
> - Used srcu_readers_active() in SRCU drop and logged with pr_warn if
> there are leaked guards during the drop.
>
> v4: https://lore.kernel.org/all/20260525175706.124910-1-work@onurozkan.dev
>
> 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.
>
> v3: https://lore.kernel.org/all/20260522054228.114814-1-work@onurozkan.dev
>
> 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/20260502162833.34334-1-work@onurozkan.dev
>
> 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/20260428103437.156236-1-work@onurozkan.dev
>
>
> [1]: https://lore.kernel.org/all/20260416171728.205141-1-work@onurozkan.dev
> [2]: https://sashiko.dev/#/patchset/20260522054228.114814-1-work@onurozkan.dev?part=2
>
> Onur Özkan (4):
> rust: helpers: add SRCU helpers
> srcu: expose srcu_readers_active()
> rust: sync: add SRCU abstraction
> MAINTAINERS: add Rust SRCU files to SRCU entry
>
> MAINTAINERS | 3 +
> include/linux/srcu.h | 5 ++
> include/linux/srcutiny.h | 13 +++
> include/linux/srcutree.h | 24 ++++++
> kernel/rcu/srcutiny.c | 2 +-
> kernel/rcu/srcutree.c | 25 ------
> rust/helpers/helpers.c | 1 +
> rust/helpers/srcu.c | 35 +++++++++
> rust/kernel/sync.rs | 2 +
> rust/kernel/sync/srcu.rs | 166 +++++++++++++++++++++++++++++++++++++++
> 10 files changed, 250 insertions(+), 26 deletions(-)
> create mode 100644 rust/helpers/srcu.c
> create mode 100644 rust/kernel/sync/srcu.rs
>
> --
> 2.51.2
>
>
^ permalink raw reply [flat|nested] 22+ messages in thread