* [PATCH v9 1/4] rust: helpers: add SRCU helpers
2026-05-29 13:39 [PATCH v9 0/4] rust: add SRCU abstraction Onur Özkan
@ 2026-05-29 13:39 ` Onur Özkan
2026-06-04 18:40 ` Paul E. McKenney
2026-05-29 13:39 ` [PATCH v9 2/4] srcu: expose srcu_readers_active() Onur Özkan
` (2 subsequent siblings)
3 siblings, 1 reply; 10+ messages in thread
From: Onur Özkan @ 2026-05-29 13:39 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 | 29 ++++++++++++++++++++---------
kernel/rcu/srcutiny.c | 10 +++++-----
kernel/rcu/srcutree.c | 9 +++++----
rust/helpers/helpers.c | 1 +
rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++
5 files changed, 61 insertions(+), 18 deletions(-)
create mode 100644 rust/helpers/srcu.c
diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index 81b1938512d5..a028a5b5ebef 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
#ifdef CONFIG_DEBUG_LOCK_ALLOC
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key);
+static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key)
+{
+ return init_srcu_struct_lockdep(ssp, name, key);
+}
#ifndef CONFIG_TINY_SRCU
int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
struct lock_class_key *key);
#endif // #ifndef CONFIG_TINY_SRCU
-#define init_srcu_struct(ssp) \
-({ \
- static struct lock_class_key __srcu_key; \
- \
- __init_srcu_struct((ssp), #ssp, &__srcu_key); \
-})
-
#define init_srcu_struct_fast(ssp) \
({ \
static struct lock_class_key __srcu_key; \
@@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
#define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name },
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
-int init_srcu_struct(struct srcu_struct *ssp);
+int init_srcu_struct_generic(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_generic(ssp);
+}
#ifndef CONFIG_TINY_SRCU
int init_srcu_struct_fast(struct srcu_struct *ssp);
int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
@@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
#define __SRCU_DEP_MAP_INIT(srcu_name)
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
+#define init_srcu_struct(ssp) \
+({ \
+ static struct lock_class_key __srcu_key; \
+ \
+ __init_srcu_struct((ssp), #ssp, &__srcu_key); \
+})
+
/* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */
#define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
#define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
index a2e2d516e51b..780a95e8cad7 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -48,15 +48,15 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
#ifdef CONFIG_DEBUG_LOCK_ALLOC
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
- struct lock_class_key *key)
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key)
{
/* Don't re-initialize a lock while it is held. */
debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
lockdep_init_map(&ssp->dep_map, name, key, 0);
return init_srcu_struct_fields(ssp);
}
-EXPORT_SYMBOL_GPL(__init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
#else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
@@ -68,11 +68,11 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct);
* to any other function. Each srcu_struct represents a separate domain
* of SRCU protection.
*/
-int init_srcu_struct(struct srcu_struct *ssp)
+int init_srcu_struct_generic(struct srcu_struct *ssp)
{
return init_srcu_struct_fields(ssp);
}
-EXPORT_SYMBOL_GPL(init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
index 0d01cd8c4b4a..45154630c54e 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock
return init_srcu_struct_fields(ssp, false);
}
-int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
+int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
+ struct lock_class_key *key)
{
ssp->srcu_reader_flavor = 0;
return __init_srcu_struct_common(ssp, name, key);
}
-EXPORT_SYMBOL_GPL(__init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
{
@@ -301,12 +302,12 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
* to any other function. Each srcu_struct represents a separate domain
* of SRCU protection.
*/
-int init_srcu_struct(struct srcu_struct *ssp)
+int init_srcu_struct_generic(struct srcu_struct *ssp)
{
ssp->srcu_reader_flavor = 0;
return init_srcu_struct_fields(ssp, false);
}
-EXPORT_SYMBOL_GPL(init_srcu_struct);
+EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
/**
* init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
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] 10+ messages in thread* Re: [PATCH v9 1/4] rust: helpers: add SRCU helpers
2026-05-29 13:39 ` [PATCH v9 1/4] rust: helpers: add SRCU helpers Onur Özkan
@ 2026-06-04 18:40 ` Paul E. McKenney
2026-06-04 18:42 ` Paul E. McKenney
2026-06-05 6:32 ` Onur Özkan
0 siblings, 2 replies; 10+ messages in thread
From: Paul E. McKenney @ 2026-06-04 18:40 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
[-- Attachment #1: Type: text/plain, Size: 7999 bytes --]
On Fri, May 29, 2026 at 04:39:51PM +0300, Onur Özkan wrote:
> 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 | 29 ++++++++++++++++++++---------
> kernel/rcu/srcutiny.c | 10 +++++-----
> kernel/rcu/srcutree.c | 9 +++++----
> rust/helpers/helpers.c | 1 +
> rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++
> 5 files changed, 61 insertions(+), 18 deletions(-)
> create mode 100644 rust/helpers/srcu.c
On the next version, please split the rust/helpers changes into a
separate patch. It looks like the simplification to SRCU adds code
rather than deleting it?
But let me see if I understand the structure.
> diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> index 81b1938512d5..a028a5b5ebef 100644
> --- a/include/linux/srcu.h
> +++ b/include/linux/srcu.h
> @@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
>
> #ifdef CONFIG_DEBUG_LOCK_ALLOC
>
> -int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
> +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> + struct lock_class_key *key);
> +static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
> + struct lock_class_key *key)
> +{
> + return init_srcu_struct_lockdep(ssp, name, key);
> +}
__init_srcu_struct() invokes init_srcu_struct_lockdep()...
> #ifndef CONFIG_TINY_SRCU
> int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
> int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
> struct lock_class_key *key);
> #endif // #ifndef CONFIG_TINY_SRCU
>
> -#define init_srcu_struct(ssp) \
> -({ \
> - static struct lock_class_key __srcu_key; \
> - \
> - __init_srcu_struct((ssp), #ssp, &__srcu_key); \
> -})
init_srcu_struct() invokes __init_srcu_struct()... (moved)
> #define init_srcu_struct_fast(ssp) \
> ({ \
> static struct lock_class_key __srcu_key; \
> @@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
> #define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name },
> #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
>
> -int init_srcu_struct(struct srcu_struct *ssp);
> +int init_srcu_struct_generic(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_generic(ssp);
> +}
__init_srcu_struct() invokes init_srcu_struct_generic()...
> #ifndef CONFIG_TINY_SRCU
> int init_srcu_struct_fast(struct srcu_struct *ssp);
> int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
> @@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
> #define __SRCU_DEP_MAP_INIT(srcu_name)
> #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
>
> +#define init_srcu_struct(ssp) \
> +({ \
> + static struct lock_class_key __srcu_key; \
> + \
> + __init_srcu_struct((ssp), #ssp, &__srcu_key); \
> +})
...and here is where it moved to.
> +
> /* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */
> #define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
> #define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
> diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
> index a2e2d516e51b..780a95e8cad7 100644
> --- a/kernel/rcu/srcutiny.c
> +++ b/kernel/rcu/srcutiny.c
> @@ -48,15 +48,15 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
>
> #ifdef CONFIG_DEBUG_LOCK_ALLOC
>
> -int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
> - struct lock_class_key *key)
> +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> + struct lock_class_key *key)
> {
> /* Don't re-initialize a lock while it is held. */
> debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
> lockdep_init_map(&ssp->dep_map, name, key, 0);
> return init_srcu_struct_fields(ssp);
> }
> -EXPORT_SYMBOL_GPL(__init_srcu_struct);
> +EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
init_srcu_struct_lockdep() invokes init_srcu_struct_fields()...
> #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
>
> @@ -68,11 +68,11 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct);
> * to any other function. Each srcu_struct represents a separate domain
> * of SRCU protection.
> */
> -int init_srcu_struct(struct srcu_struct *ssp)
> +int init_srcu_struct_generic(struct srcu_struct *ssp)
> {
> return init_srcu_struct_fields(ssp);
> }
> -EXPORT_SYMBOL_GPL(init_srcu_struct);
> +EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
init_srcu_struct_generic() invokes init_srcu_struct_fields()...
> #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
>
> diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
> index 0d01cd8c4b4a..45154630c54e 100644
> --- a/kernel/rcu/srcutree.c
> +++ b/kernel/rcu/srcutree.c
> @@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock
> return init_srcu_struct_fields(ssp, false);
> }
>
> -int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
> +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> + struct lock_class_key *key)
> {
> ssp->srcu_reader_flavor = 0;
> return __init_srcu_struct_common(ssp, name, key);
> }
> -EXPORT_SYMBOL_GPL(__init_srcu_struct);
> +EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
init_srcu_struct_lockdep() invokes __init_srcu_struct_common()...
> int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
> {
> @@ -301,12 +302,12 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
> * to any other function. Each srcu_struct represents a separate domain
> * of SRCU protection.
> */
> -int init_srcu_struct(struct srcu_struct *ssp)
> +int init_srcu_struct_generic(struct srcu_struct *ssp)
> {
> ssp->srcu_reader_flavor = 0;
> return init_srcu_struct_fields(ssp, false);
> }
> -EXPORT_SYMBOL_GPL(init_srcu_struct);
> +EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
init_srcu_struct_generic() invokes init_srcu_struct_fields()...
This gets me the structure shown in the attached PDF. Did I get it
right?
I am surprised not to see any changes involving cleanup_srcu_struct().
What did I miss?
And again, please put the following in a later patch to allow separate
evaluation of the restructuring.
Thanx, Paul
> /**
> * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
> 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
>
[-- Attachment #2: init_srcu_struct.pdf --]
[-- Type: application/pdf, Size: 12190 bytes --]
^ permalink raw reply [flat|nested] 10+ messages in thread* Re: [PATCH v9 1/4] rust: helpers: add SRCU helpers
2026-06-04 18:40 ` Paul E. McKenney
@ 2026-06-04 18:42 ` Paul E. McKenney
2026-06-05 6:32 ` Onur Özkan
1 sibling, 0 replies; 10+ messages in thread
From: Paul E. McKenney @ 2026-06-04 18:42 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, Jun 04, 2026 at 11:40:59AM -0700, Paul E. McKenney wrote:
> On Fri, May 29, 2026 at 04:39:51PM +0300, Onur Özkan wrote:
> > 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 | 29 ++++++++++++++++++++---------
> > kernel/rcu/srcutiny.c | 10 +++++-----
> > kernel/rcu/srcutree.c | 9 +++++----
> > rust/helpers/helpers.c | 1 +
> > rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++
> > 5 files changed, 61 insertions(+), 18 deletions(-)
> > create mode 100644 rust/helpers/srcu.c
>
> On the next version, please split the rust/helpers changes into a
> separate patch. It looks like the simplification to SRCU adds code
> rather than deleting it?
>
> But let me see if I understand the structure.
[ . . . ]
> I am surprised not to see any changes involving cleanup_srcu_struct().
> What did I miss?
Never mind, next patch!
Thanx, Paul
^ permalink raw reply [flat|nested] 10+ messages in thread
* Re: [PATCH v9 1/4] rust: helpers: add SRCU helpers
2026-06-04 18:40 ` Paul E. McKenney
2026-06-04 18:42 ` Paul E. McKenney
@ 2026-06-05 6:32 ` Onur Özkan
1 sibling, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2026-06-05 6:32 UTC (permalink / raw)
To: Paul E. McKenney
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, 04 Jun 2026 11:40:59 -0700
"Paul E. McKenney" <paulmck@kernel.org> wrote:
> On Fri, May 29, 2026 at 04:39:51PM +0300, Onur Özkan wrote:
> > 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 | 29 ++++++++++++++++++++---------
> > kernel/rcu/srcutiny.c | 10 +++++-----
> > kernel/rcu/srcutree.c | 9 +++++----
> > rust/helpers/helpers.c | 1 +
> > rust/helpers/srcu.c | 30 ++++++++++++++++++++++++++++++
> > 5 files changed, 61 insertions(+), 18 deletions(-)
> > create mode 100644 rust/helpers/srcu.c
>
> On the next version, please split the rust/helpers changes into a
> separate patch. It looks like the simplification to SRCU adds code
> rather than deleting it?
>
Well, it's mostly renaming (e.g. init_srcu_struct -> init_srcu_struct_generic)
and moving code around (moving init_srcu_struct outside CONFIG_DEBUG_LOCK_ALLOC
guard). The actual "added" code lines are around 10 lines or something.
> But let me see if I understand the structure.
>
> > diff --git a/include/linux/srcu.h b/include/linux/srcu.h
> > index 81b1938512d5..a028a5b5ebef 100644
> > --- a/include/linux/srcu.h
> > +++ b/include/linux/srcu.h
> > @@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
> >
> > #ifdef CONFIG_DEBUG_LOCK_ALLOC
> >
> > -int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
> > +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> > + struct lock_class_key *key);
> > +static inline int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
> > + struct lock_class_key *key)
> > +{
> > + return init_srcu_struct_lockdep(ssp, name, key);
> > +}
>
> __init_srcu_struct() invokes init_srcu_struct_lockdep()...
>
> > #ifndef CONFIG_TINY_SRCU
> > int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key);
> > int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
> > struct lock_class_key *key);
> > #endif // #ifndef CONFIG_TINY_SRCU
> >
> > -#define init_srcu_struct(ssp) \
> > -({ \
> > - static struct lock_class_key __srcu_key; \
> > - \
> > - __init_srcu_struct((ssp), #ssp, &__srcu_key); \
> > -})
>
> init_srcu_struct() invokes __init_srcu_struct()... (moved)
>
> > #define init_srcu_struct_fast(ssp) \
> > ({ \
> > static struct lock_class_key __srcu_key; \
> > @@ -56,7 +55,12 @@ int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name,
> > #define __SRCU_DEP_MAP_INIT(srcu_name) .dep_map = { .name = #srcu_name },
> > #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> >
> > -int init_srcu_struct(struct srcu_struct *ssp);
> > +int init_srcu_struct_generic(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_generic(ssp);
> > +}
>
> __init_srcu_struct() invokes init_srcu_struct_generic()...
>
> > #ifndef CONFIG_TINY_SRCU
> > int init_srcu_struct_fast(struct srcu_struct *ssp);
> > int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
> > @@ -65,6 +69,13 @@ int init_srcu_struct_fast_updown(struct srcu_struct *ssp);
> > #define __SRCU_DEP_MAP_INIT(srcu_name)
> > #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> >
> > +#define init_srcu_struct(ssp) \
> > +({ \
> > + static struct lock_class_key __srcu_key; \
> > + \
> > + __init_srcu_struct((ssp), #ssp, &__srcu_key); \
> > +})
>
> ...and here is where it moved to.
>
> > +
> > /* Values for SRCU Tree srcu_data ->srcu_reader_flavor, but also used by rcutorture. */
> > #define SRCU_READ_FLAVOR_NORMAL 0x1 // srcu_read_lock().
> > #define SRCU_READ_FLAVOR_NMI 0x2 // srcu_read_lock_nmisafe().
> > diff --git a/kernel/rcu/srcutiny.c b/kernel/rcu/srcutiny.c
> > index a2e2d516e51b..780a95e8cad7 100644
> > --- a/kernel/rcu/srcutiny.c
> > +++ b/kernel/rcu/srcutiny.c
> > @@ -48,15 +48,15 @@ static int init_srcu_struct_fields(struct srcu_struct *ssp)
> >
> > #ifdef CONFIG_DEBUG_LOCK_ALLOC
> >
> > -int __init_srcu_struct(struct srcu_struct *ssp, const char *name,
> > - struct lock_class_key *key)
> > +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> > + struct lock_class_key *key)
> > {
> > /* Don't re-initialize a lock while it is held. */
> > debug_check_no_locks_freed((void *)ssp, sizeof(*ssp));
> > lockdep_init_map(&ssp->dep_map, name, key, 0);
> > return init_srcu_struct_fields(ssp);
> > }
> > -EXPORT_SYMBOL_GPL(__init_srcu_struct);
> > +EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
>
> init_srcu_struct_lockdep() invokes init_srcu_struct_fields()...
>
> > #else /* #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> >
> > @@ -68,11 +68,11 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct);
> > * to any other function. Each srcu_struct represents a separate domain
> > * of SRCU protection.
> > */
> > -int init_srcu_struct(struct srcu_struct *ssp)
> > +int init_srcu_struct_generic(struct srcu_struct *ssp)
> > {
> > return init_srcu_struct_fields(ssp);
> > }
> > -EXPORT_SYMBOL_GPL(init_srcu_struct);
> > +EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
>
> init_srcu_struct_generic() invokes init_srcu_struct_fields()...
>
> > #endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */
> >
> > diff --git a/kernel/rcu/srcutree.c b/kernel/rcu/srcutree.c
> > index 0d01cd8c4b4a..45154630c54e 100644
> > --- a/kernel/rcu/srcutree.c
> > +++ b/kernel/rcu/srcutree.c
> > @@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock
> > return init_srcu_struct_fields(ssp, false);
> > }
> >
> > -int __init_srcu_struct(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
> > +int init_srcu_struct_lockdep(struct srcu_struct *ssp, const char *name,
> > + struct lock_class_key *key)
> > {
> > ssp->srcu_reader_flavor = 0;
> > return __init_srcu_struct_common(ssp, name, key);
> > }
> > -EXPORT_SYMBOL_GPL(__init_srcu_struct);
> > +EXPORT_SYMBOL_GPL(init_srcu_struct_lockdep);
>
> init_srcu_struct_lockdep() invokes __init_srcu_struct_common()...
>
> > int __init_srcu_struct_fast(struct srcu_struct *ssp, const char *name, struct lock_class_key *key)
> > {
> > @@ -301,12 +302,12 @@ EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown);
> > * to any other function. Each srcu_struct represents a separate domain
> > * of SRCU protection.
> > */
> > -int init_srcu_struct(struct srcu_struct *ssp)
> > +int init_srcu_struct_generic(struct srcu_struct *ssp)
> > {
> > ssp->srcu_reader_flavor = 0;
> > return init_srcu_struct_fields(ssp, false);
> > }
> > -EXPORT_SYMBOL_GPL(init_srcu_struct);
> > +EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
>
> init_srcu_struct_generic() invokes init_srcu_struct_fields()...
>
> This gets me the structure shown in the attached PDF. Did I get it
> right?
>
Yes, it looks right.
> I am surprised not to see any changes involving cleanup_srcu_struct().
> What did I miss?
>
Do you mean changing cleanup_srcu_struct() on the C side? As I mentioned
previously [1], I would prefer to keep the current approach for now and
see what others think. If we receive complaints from other people, we can
then consider proposing an alternative approach which would likely involve
changing (or simply adding) C code.
Personally, I prefer the current approach because it doesn't add any
complexity. The problem is already documented and it's highly unlikely
that we will ever hit it in practice. It's fairly easy to catch those
kind of mistakes (leaking the guard explicitly with mem::forget) on
review.
Even if we add a dedicated cleanup function on the C side, leaking an
SRCU guard should still not be allowed and should be treated as a
"bad code" and should require a follow-up patch. Adding a special C
function that changes counters just switches "don't leak an SRCU guard"
problem into "don't call $the_special_cleanup function anywhere else".
[1]: https://lore.kernel.org/all/20260531191107.35357-1-work@onurozkan.dev
> And again, please put the following in a later patch to allow separate
> evaluation of the restructuring.
>
Sure thing. I will split them in the next version.
Thanks,
Onur
> Thanx, Paul
>
> > /**
> > * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure
> > 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 [flat|nested] 10+ messages in thread
* [PATCH v9 2/4] srcu: expose srcu_readers_active()
2026-05-29 13:39 [PATCH v9 0/4] rust: add SRCU abstraction Onur Özkan
2026-05-29 13:39 ` [PATCH v9 1/4] rust: helpers: add SRCU helpers Onur Özkan
@ 2026-05-29 13:39 ` Onur Özkan
2026-06-04 22:29 ` Paul E. McKenney
2026-05-29 13:39 ` [PATCH v9 3/4] rust: sync: add SRCU abstraction Onur Özkan
2026-05-29 13:39 ` [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry Onur Özkan
3 siblings, 1 reply; 10+ messages in thread
From: Onur Özkan @ 2026-05-29 13:39 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().
Reviewed-by: Gary Guo <gary@garyguo.net>
Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
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 780a95e8cad7..dde99876d473 100644
--- a/kernel/rcu/srcutiny.c
+++ b/kernel/rcu/srcutiny.c
@@ -85,7 +85,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
*/
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 45154630c54e..d1a69320b5c0 100644
--- a/kernel/rcu/srcutree.c
+++ b/kernel/rcu/srcutree.c
@@ -599,31 +599,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] 10+ messages in thread* Re: [PATCH v9 2/4] srcu: expose srcu_readers_active()
2026-05-29 13:39 ` [PATCH v9 2/4] srcu: expose srcu_readers_active() Onur Özkan
@ 2026-06-04 22:29 ` Paul E. McKenney
0 siblings, 0 replies; 10+ messages in thread
From: Paul E. McKenney @ 2026-06-04 22:29 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 Fri, May 29, 2026 at 04:39:52PM +0300, Onur Özkan wrote:
> 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().
>
> Reviewed-by: Gary Guo <gary@garyguo.net>
> Reviewed-by: Paul E. McKenney <paulmck@kernel.org>
> Signed-off-by: Onur Özkan <work@onurozkan.dev>
It would be good to merge the rust/helpers piece of this change into
the separate commit that I suggested for patch 1/4. Either way, I
still stand behind my Reviewed-by.
Thanx, Paul
> ---
> 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 780a95e8cad7..dde99876d473 100644
> --- a/kernel/rcu/srcutiny.c
> +++ b/kernel/rcu/srcutiny.c
> @@ -85,7 +85,7 @@ EXPORT_SYMBOL_GPL(init_srcu_struct_generic);
> */
> 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 45154630c54e..d1a69320b5c0 100644
> --- a/kernel/rcu/srcutree.c
> +++ b/kernel/rcu/srcutree.c
> @@ -599,31 +599,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 [flat|nested] 10+ messages in thread
* [PATCH v9 3/4] rust: sync: add SRCU abstraction
2026-05-29 13:39 [PATCH v9 0/4] rust: add SRCU abstraction Onur Özkan
2026-05-29 13:39 ` [PATCH v9 1/4] rust: helpers: add SRCU helpers Onur Özkan
2026-05-29 13:39 ` [PATCH v9 2/4] srcu: expose srcu_readers_active() Onur Özkan
@ 2026-05-29 13:39 ` Onur Özkan
2026-05-29 13:39 ` [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry Onur Özkan
3 siblings, 0 replies; 10+ messages in thread
From: Onur Özkan @ 2026-05-29 13:39 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`. It first checks for active read-side
sections and emits a warning if any guards were leaked. In that case, it
waits in `synchronize_srcu()` rather than risking a UAF by freeing the
`srcu_struct` that is still reachable from the C side. It then uses
`srcu_barrier()` to drain pending callbacks before finally calling
`cleanup_srcu_struct()`.
Signed-off-by: Onur Özkan <work@onurozkan.dev>
---
rust/kernel/sync.rs | 2 +
rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++
2 files changed, 173 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..723e5e277fd6
--- /dev/null
+++ b/rust/kernel/sync/srcu.rs
@@ -0,0 +1,171 @@
+// 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();
+
+ if crate::warn_on!(
+ // SAFETY: By the type invariants, `self` contains a valid and pinned `struct srcu_struct`
+ // and `srcu_readers_active()` only checks the active reader count.
+ unsafe { bindings::srcu_readers_active(ptr) }
+ ) {
+ // `cleanup_srcu_struct()` may return early if there are still active readers.
+ // This should only happen if a guard was leaked with `mem::forget`, which is
+ // "WRONG" code and may cause a UAF because Rust will free the `srcu_struct`
+ // while it is still referenced from the C side (e.g. by `call_srcu()` callbacks).
+ //
+ // Another consequence of leaking guards is that `call_srcu()` callbacks will
+ // never run because the grace period can never complete due to permanently
+ // active readers (i.e. leaked guards).
+ //
+ // If this ever happens, that means the guard was leaked by mistake and the
+ // caller must fix the bug. Sleeping here is intentional and less harmful
+ // than risking a UAF.
+ //
+ // 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] 10+ messages in thread* [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry
2026-05-29 13:39 [PATCH v9 0/4] rust: add SRCU abstraction Onur Özkan
` (2 preceding siblings ...)
2026-05-29 13:39 ` [PATCH v9 3/4] rust: sync: add SRCU abstraction Onur Özkan
@ 2026-05-29 13:39 ` Onur Özkan
2026-06-10 19:50 ` kernel test robot
3 siblings, 1 reply; 10+ messages in thread
From: Onur Özkan @ 2026-05-29 13:39 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] 10+ messages in thread* Re: [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry
2026-05-29 13:39 ` [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry Onur Özkan
@ 2026-06-10 19:50 ` kernel test robot
0 siblings, 0 replies; 10+ messages in thread
From: kernel test robot @ 2026-06-10 19:50 UTC (permalink / raw)
To: Onur Özkan, rcu, rust-for-linux, linux-kernel
Cc: llvm, oe-kbuild-all, ojeda, boqun, gary, bjorn3_gh, lossin,
a.hindborg, aliceryhl, tmgross, dakr, peterz, fujita.tomonori,
tamird, jiangshanlai, paulmck, josh, rostedt, mathieu.desnoyers,
Onur Özkan
Hi Onur,
kernel test robot noticed the following build warnings:
[auto build test WARNING on rcu/rcu/dev]
[also build test WARNING on rust/rust-next linus/master v7.1-rc7 next-20260610]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Onur-zkan/rust-helpers-add-SRCU-helpers/20260529-214529
base: https://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev
patch link: https://lore.kernel.org/r/20260529134004.396743-5-work%40onurozkan.dev
patch subject: [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20260610/202606102148.n3zUlB8z-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project f43d6834093b19baf79beda8c0337ab020ac5f17)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260610/202606102148.n3zUlB8z-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202606102148.n3zUlB8z-lkp@intel.com/
All warnings (new ones prefixed by >>):
>> Warning: kernel/rcu/srcutree.c:305 expecting prototype for init_srcu_struct(). Prototype was for init_srcu_struct_generic() instead
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 10+ messages in thread