[PATCH 04/14] srcu: Remove check from srcu_dereference_check()

From: Bart Van Assche

Date: Mon Nov 02 2026 - 17:15:43 EST


Users of srcu_dereference_check() must either hold an SRCU reader lock
or satisfy the lockdep condition 'c', e.g. by holding the update-side
lock. However, srcu_dereference_check() calls
__srcu_read_lock_must_hold(), which tells the Clang context analyzer
that the SRCU reader lock must be held. That requirement is too strict
for update-side callers. Hence, remove the __srcu_read_lock_must_hold()
call from srcu_dereference_check() while keeping the fake RCU acquire
and release around __rcu_dereference_check(). Retain the runtime lockdep
check in srcu_dereference_check().

srcu_dereference() and srcu_dereference_notrace() do not accept a
lockdep condition and hence may only be used by SRCU readers. Call
__srcu_read_lock_must_hold() from these macros such that Clang context
analysis keeps verifying at compile time that their callers hold the
SRCU reader lock.

Cc: Marco Elver <elver@xxxxxxxxxx>
Fixes: f0b7ce22d718 ("srcu: Support Clang's context analysis")
Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
---
include/linux/srcu.h | 20 +++++++++++++++-----
1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/include/linux/srcu.h b/include/linux/srcu.h
index 1a8a465a5650..e72d2e1dfd82 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -242,8 +242,11 @@ static inline int srcu_read_lock_held(const struct srcu_struct *ssp)
* No-op helper to denote that ssp must be held. Because SRCU-protected pointers
* should still be marked with __rcu_guarded, and we do not want to mark them
* with __guarded_by(ssp) as it would complicate annotations for writers, we
- * choose the following strategy: srcu_dereference_check() calls this helper
- * that checks that the passed ssp is held, and then fake-acquires 'RCU'.
+ * choose the following strategy: srcu_dereference() and
+ * srcu_dereference_notrace() call this helper that checks that the passed ssp
+ * is held, and srcu_dereference_check() fake-acquires 'RCU'.
+ * srcu_dereference_check() does not call this helper because its callers may
+ * satisfy the condition @c instead of holding ssp, e.g. on the update side.
*/
static inline void __srcu_read_lock_must_hold(const struct srcu_struct *ssp) __must_hold_shared(ssp) { }

@@ -261,7 +264,6 @@ static inline void __srcu_read_lock_must_hold(const struct srcu_struct *ssp) __m
*/
#define srcu_dereference_check(p, ssp, c) \
({ \
- __srcu_read_lock_must_hold(ssp); \
__acquire_shared_ctx_lock(RCU); \
__auto_type __v = __rcu_dereference_check((p), __UNIQUE_ID(rcu), \
(c) || srcu_read_lock_held(ssp), __rcu); \
@@ -279,7 +281,11 @@ static inline void __srcu_read_lock_must_hold(const struct srcu_struct *ssp) __m
* is enabled, invoking this outside of an RCU read-side critical
* section will result in an RCU-lockdep splat.
*/
-#define srcu_dereference(p, ssp) srcu_dereference_check((p), (ssp), 0)
+#define srcu_dereference(p, ssp) \
+({ \
+ __srcu_read_lock_must_hold(ssp); \
+ srcu_dereference_check((p), (ssp), 0); \
+})

/**
* srcu_dereference_notrace - no tracing and no lockdep calls from here
@@ -287,7 +293,11 @@ static inline void __srcu_read_lock_must_hold(const struct srcu_struct *ssp) __m
* @ssp: pointer to the srcu_struct, which is used to check that we
* really are in an SRCU read-side critical section.
*/
-#define srcu_dereference_notrace(p, ssp) srcu_dereference_check((p), (ssp), 1)
+#define srcu_dereference_notrace(p, ssp) \
+({ \
+ __srcu_read_lock_must_hold(ssp); \
+ srcu_dereference_check((p), (ssp), 1); \
+})

/**
* srcu_read_lock - register a new reader for an SRCU-protected structure.