rust: add SRCU abstraction
Subsystems (4)
Onur Özkan
maintainerv9 cover rust: add SRCU abstraction
The immediate motivation is the Tyr reset infrastructure [1] which needs to serialize reset sensitive hardware access against reset and teardown paths. That reset series started to require many independent dependencies so this SRCU support is split out as a standalone Rust API to keep the reset series focused on the reset logic and easier to review, rebase and land.
Changes since v8:
- Refactor srcu init handling to provide better init API (motivated by the "mutex.h" initializers.
- Use warn_on! instead of pr_warn! on leaked guard detection.
v8: https://lore.kernel.org/all/[email protected]
Changes since v7:
- Moved synchronize_srcu() call inside "if srcu_readers_active()" condition in "impl PinnedDrop for Srcu".
- Improved comments on the synchronize_srcu() call in "impl PinnedDrop for Srcu".
v7: https://lore.kernel.org/all/[email protected]
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/[email protected]
Changes since v5:
- Created separate srcu_readers_active() variants for "srcutiny.h" and "srcutree.h".
v5: https://lore.kernel.org/all/[email protected]
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/[email protected]
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/[email protected]
Changes since v2:
- Removed closure-based API.
- Added #[doc(hidden)] on new_srcu macro.
- Added #[must_use..] on srcu::Guard.
- Improved the clean-up path (PinnedDrop implementation) which eventually made read_lock safe with leaked guards.
v2: https://lore.kernel.org/all/[email protected]
Changes since v1:
- Made the owned SRCU read-side guard API unsafe and added a safe closure based helper for callers that do not need to keep the guard. This is to avoid UB on the C side cleanup_srcu_struct where the SRCU struct is freed while there are still active guards, which can happen if the caller leaks the guard e.g., with mem::forget().
- Improved doc comments.
v1: https://lore.kernel.org/all/[email protected]
[1]: https://lore.kernel.org/all/[email protected]
[2]: https://sashiko.dev/#/patchset/[email protected]?part=2
Onur Özkan (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 | 29 ++++--- include/linux/srcutiny.h | 13 +++ include/linux/srcutree.h | 24 ++++++ kernel/rcu/srcutiny.c | 12 +-- kernel/rcu/srcutree.c | 34 ++------ rust/helpers/helpers.c | 1 + rust/helpers/srcu.c | 35 ++++++++ rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 10 files changed, 280 insertions(+), 44 deletions(-) create mode 100644 rust/helpers/srcu.c create mode 100644 rust/kernel/sync/srcu.rs
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v9 1/4 rust: helpers: add SRCU helpers
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
--- 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
include/linux/srcu.h +20 -9
@@ -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().
kernel/rcu/srcutiny.c +5 -5
@@ -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 */
kernel/rcu/srcutree.c +5 -4
@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lockreturn 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
rust/helpers/helpers.c +1 -0
@@ -88,6 +88,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c"
rust/helpers/srcu.c new file +30 -0
@@ -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); +}
Signature · 1 line
2.51.2
Paul E. McKenney
maintainerRe: [PATCH v9 1/4] rust: helpers: add SRCU helpers
Quoted 5 lines · Onur Özkan
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
Signed-off-by Onur Özkan <[email protected]>
Quoted 8 lines
--- 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.
Quoted 6 lines
include/linux/srcu.h +0
-0@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);
include/linux/srcu.h context_lock_struct(srcu_struct, __reentrant_ctx_lock); · line 34
Quoted 10 lines
include/linux/srcu.h +7
-1#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()...
include/linux/srcu.h context_lock_struct(srcu_struct, __reentrant_ctx_lock); · line 40
Quoted 12 lines
include/linux/srcu.h +0
-6#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)
Quoted 4 lines
include/linux/srcu.h +0
-0#define init_srcu_struct_fast (ssp) \ ({ \ static struct lock_class_key __srcu_key; \
include/linux/srcu.h int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name, · line 63
Quoted 10 lines
include/linux/srcu.h +6
-1@@ -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()...
include/linux/srcu.h int init_srcu_struct_fast_updown(struct srcu_struct *ssp); · line 77
Quoted 13 lines
include/linux/srcu.h +6
-0#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.
Quoted 14 lines
include/linux/srcu.h +1
-0+/* 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().kernel/rcu/srcutiny.c +0
-2@@ -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)
kernel/rcu/srcutiny.c static int init_srcu_struct_fields(struct srcu_struct *ssp) · line 59
Quoted 10 lines
kernel/rcu/srcutiny.c +3
-1+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()...
kernel/rcu/srcutiny.c EXPORT_SYMBOL_GPL(__init_srcu_struct); · line 75
Quoted 13 lines
kernel/rcu/srcutiny.c +2
-2#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()...
Quoted 9 lines
kernel/rcu/srcutiny.c +0
-0#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ kernel/rcu/srcutree.c +0
-0@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lockreturn init_srcu_struct_fields (ssp, false); }
kernel/rcu/srcutree.c __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock · line 275
Quoted 10 lines
kernel/rcu/srcutree.c +3
-2-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()...
Quoted 4 lines
kernel/rcu/srcutree.c +0
-0int __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
kernel/rcu/srcutree.c EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown); · line 310
Quoted 10 lines
kernel/rcu/srcutree.c +2
-2* 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
Quoted 53 lines
kernel/rcu/srcutree.c +0
-0/** * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure rust/helpers/helpers.c +1
-0@@ -88,6 +88,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c" rust/helpers/srcu.c new file +30
-0@@ -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); +}Signature · 1 line
2.51.2
Paul E. McKenney
maintainerRe: [PATCH v9 1/4] rust: helpers: add SRCU helpers
Quoted 15 lines · Paul E. McKenney
Quoted 5 lines · Onur Özkan
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
Signed-off-by Onur Özkan <[email protected]>Quoted 8 lines
--- 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
Quoted 5 lines
On the next version, please split the
rust/helperschanges 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.
[ . . . ]
Quoted 2 lines
I am surprised not to see any changes involving
cleanup_srcu_struct(). What did I miss?
Never mind, next patch!
Thanx, Paul
Onur Özkan
maintainerRe: [PATCH v9 1/4] rust: helpers: add SRCU helpers
In reply to Paul E. McKenneyQuoted 15 lines · On Thu, 04 Jun 2026 11:40:59 -0700 "Paul E. McKenney" <[email protected]>…
Quoted 5 lines · Onur Özkan
Add helper wrappers for SRCU functions that are exposed to Rust through generated bindings.
Signed-off-by Onur Özkan <[email protected]>Quoted 8 lines
--- 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
Quoted 4 lines
On the next version, please split the
rust/helperschanges 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.
Quoted 151 lines
But let me see if I understand the structure.
Quoted 6 lines
include/linux/srcu.h +0
-0@@ -25,20 +25,19 @@ context_lock_struct(srcu_struct, __reentrant_ctx_lock);include/linux/srcu.h context_lock_struct(srcu_struct, __reentrant_ctx_lock); · line 34
Quoted 10 lines
include/linux/srcu.h +7
-1#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()invokesinit_srcu_struct_lockdep()...include/linux/srcu.h context_lock_struct(srcu_struct, __reentrant_ctx_lock); · line 40
Quoted 12 lines
include/linux/srcu.h +0
-6#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)Quoted 4 lines
include/linux/srcu.h +0
-0#define init_srcu_struct_fast (ssp) \ ({ \ static struct lock_class_key __srcu_key; \include/linux/srcu.h int __init_srcu_struct_fast_updown(struct srcu_struct *ssp, const char *name, · line 63
Quoted 10 lines
include/linux/srcu.h +6
-1@@ -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()invokesinit_srcu_struct_generic()...include/linux/srcu.h int init_srcu_struct_fast_updown(struct srcu_struct *ssp); · line 77
Quoted 13 lines
include/linux/srcu.h +6
-0#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.
Quoted 14 lines
include/linux/srcu.h +1
-0+/* 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().kernel/rcu/srcutiny.c +0
-2@@ -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)kernel/rcu/srcutiny.c static int init_srcu_struct_fields(struct srcu_struct *ssp) · line 59
Quoted 10 lines
kernel/rcu/srcutiny.c +3
-1+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()invokesinit_srcu_struct_fields()...kernel/rcu/srcutiny.c EXPORT_SYMBOL_GPL(__init_srcu_struct); · line 75
Quoted 13 lines
kernel/rcu/srcutiny.c +2
-2#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()invokesinit_srcu_struct_fields()...Quoted 9 lines
kernel/rcu/srcutiny.c +0
-0#endif /* #else #ifdef CONFIG_DEBUG_LOCK_ALLOC */ kernel/rcu/srcutree.c +0
-0@@ -266,12 +266,13 @@ __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lockreturn init_srcu_struct_fields (ssp, false); }kernel/rcu/srcutree.c __init_srcu_struct_common(struct srcu_struct *ssp, const char *name, struct lock · line 275
Quoted 10 lines
kernel/rcu/srcutree.c +3
-2-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()...Quoted 4 lines
kernel/rcu/srcutree.c +0
-0int __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 kernel/rcu/srcutree.c EXPORT_SYMBOL_GPL(__init_srcu_struct_fast_updown); · line 310
Quoted 10 lines
kernel/rcu/srcutree.c +2
-2* 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()invokesinit_srcu_struct_fields()...
Quoted 3 lines
This gets me the structure shown in the attached PDF. Did I get it right?
Yes, it looks right.
Quoted 3 lines
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/[email protected]
Quoted 3 lines
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
Quoted 55 lines
Thanx, Paul
Quoted 53 lines
kernel/rcu/srcutree.c +0
-0/** * init_srcu_struct_fast - initialize a fast-reader sleep-RCU structure rust/helpers/helpers.c +1
-0@@ -88,6 +88,7 @@#include "signal.c" #include "slab.c" #include "spinlock.c" +#include "srcu.c" #include "sync.c" #include "task.c" #include "time.c" rust/helpers/srcu.c new file +30
-0@@ -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); +}Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v9 2/4 srcu: expose srcu_readers_active()
In reply to Onur ÖzkanThis 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().
--- 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(-)
include/linux/srcutiny.h +13 -0
@@ -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
include/linux/srcutree.h +24 -0
@@ -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
kernel/rcu/srcutiny.c +1 -1
@@ -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);
kernel/rcu/srcutree.c +0 -25
@@ -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
rust/helpers/srcu.c +5 -0
@@ -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);
Signature · 1 line
2.51.2
Paul E. McKenney
maintainerRe: [PATCH v9 2/4] srcu: expose srcu_readers_active()
Quoted 9 lines · Onur Özkan
This is needed by
rust/helpers/srcu.cwhich now addsrust_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".
Quoted 7 lines
2- Implement a matching
srcu_readers_active()in
"include/linux/srcutiny.h" and use it on the existing open-codedWARN_ON()check incleanup_srcu_struct().Reviewed-by Gary Guo <[email protected]>Reviewed-by Paul E. McKenney <[email protected]>Signed-off-by Onur Özkan <[email protected]>
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
Quoted 131 lines
--- 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(-)include/linux/srcutiny.h +13
-0@@ -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 include/linux/srcutree.h +24
-0@@ -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 kernel/rcu/srcutiny.c +1
-1@@ -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);kernel/rcu/srcutree.c +0
-25@@ -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 rust/helpers/srcu.c +5
-0@@ -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);Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v9 3/4 rust: sync: add SRCU abstraction
In reply to Onur ÖzkanAdd a Rust abstraction for sleepable RCU (SRCU), backed by C srcu_struct. Provide FFI helpers and a safe wrapper with a guard-based API for read-side critical sections.
Cleanup is handled via PinnedDrop. 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().
--- rust/kernel/sync.rs | 2 + rust/kernel/sync/srcu.rs | 171 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 173 insertions(+) create mode 100644 rust/kernel/sync/srcu.rs
rust/kernel/sync.rs +2 -0
@@ -21,6 +21,7 @@pub mod rcu;mod refcount;mod set_once; +pub mod srcu;pub use arc::{Arc ,ArcBorrow ,UniqueArc };pub use completion::Completion ;
@@ -31,6 +32,7 @@pub use locked_by::LockedBy ;pub use refcount::Refcount ;pub use set_once::SetOnce ; +pub use srcu::Srcu ;/// Represents a lockdep class. ///
rust/kernel/sync/srcu.rs new file +171 -0
171 lines not on this page · raw message
Signature · 1 line
2.51.2
Onur Özkan
maintainerPATCH v9 4/4 MAINTAINERS: add Rust SRCU files to SRCU entry
In reply to Onur ÖzkanInclude Rust side implementation files to the SRCU maintainer entry.
--- MAINTAINERS | 3 +++ 1 file changed, 3 insertions(+)
MAINTAINERS +3 -0
@@ -24624,6 +24624,7 @@ SLEEPABLE READ-COPY UPDATE (SRCU) M: Lai Jiangshan <[email protected]> M: "Paul E. McKenney" <[email protected]> M: Josh Triplett <[email protected]> +M: Onur Özkan <[email protected]> (RUST) R: Steven Rostedt <[email protected]> R: Mathieu Desnoyers <[email protected]> L: [email protected]
@@ -24632,6 +24633,8 @@ W: http://www.rdrop.com/users/paulmck/RCU/ T: git git://git.kernel.org/pub/scm/linux/kernel/git/rcu/linux.git rcu/dev F: include/linux/srcu*.h F: kernel/rcu/srcu*.c +F: rust/helpers/srcu.c +F: rust/kernel/sync/srcu.rs SMACK SECURITY MODULE M: Casey Schaufler <[email protected]>
Signature · 1 line
2.51.2
kernel test robot
build botRe: [PATCH v9 4/4] MAINTAINERS: add Rust SRCU files to SRCU entry
kernel test robot noticed the following build warnings:
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/[email protected]/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/[email protected]/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 <[email protected]>
| Closes: https://lore.kernel.org/oe-kbuild-all/[email protected]/
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