mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
* [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership
@ 2026-11-09 19:13 Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 01/14] bit_spinlock: Fix the __bitlock() definition Bart Van Assche
                   ` (13 more replies)
  0 siblings, 14 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche

Hi Peter and Marco,

Since commit 7c451541743c ("lockdep: Annotate lockdep assertions for
context analysis"), lockdep_assert_held*() and lockdep_assert_in_rcu_*()
not only verify at runtime that a lock is held but also instruct Clang
context analysis to assume that the lock is held. This has the following
disadvantages:
- These assertions silence context analysis warnings in functions that
  lack a __must_hold*() annotation. Hence, the compiler neither reports
  the missing function annotation nor checks at compile time whether
  callers hold the lock.
- lockdep_assert_held() makes context analysis assume exclusive lock
  ownership, even if a reader-writer lock is held in shared mode.
- lockdep_assert_held*() can only be used with lock types that are
  defined with context_lock_struct().

This patch series removes the context assumptions from the lockdep and
RCU assertion macros and introduces separate macros for code that needs
to inform context analysis that a lock is held:
- lockdep_assume_ctx_held() and lockdep_assume_shared_ctx_held().
- lockdep_assume_in_rcu_read_lock(), lockdep_assume_in_rcu_read_lock_bh(),
  lockdep_assume_in_rcu_read_lock_sched() and
  lockdep_assume_in_rcu_reader().
Unlike the __assume_*() macros, which only inform context analysis, the
new lockdep_assume_*() macros also verify the assumption at runtime.

This patch series is organized as follows:
- Patches 1-4 fix issues in the existing context analysis support:
  __bitlock() operator precedence, false positives for __free() helpers,
  enabling WARN_CONTEXT_ANALYSIS_ALL for allmodconfig and allyesconfig
  builds, and a too strict SRCU reader requirement in
  srcu_dereference_check().
- Patches 5-7 add __must_hold*() annotations to functions that currently
  rely on lockdep_assert_held() for context analysis.
- Patch 8 introduces lockdep_assume_ctx_held() and
  lockdep_assume_shared_ctx_held().
- Patches 9-11 convert code that accesses locks through pointer aliases
  or that uses __assume_ctx_lock() to lockdep_assume_ctx_held().
- Patch 12 removes the context assumptions from lockdep_assert_held*().
- Patches 13-14 do the same for the RCU assertions and introduce the
  lockdep_assume_in_rcu_*() macros.

Please consider this patch series for the next merge window.

Thanks,

Bart.

Changes compared to v1:
 - In the commit message of patch 8/14 it is now explained why the new
   macros are introduced.
 - lockdep_assume_shared_ctx_held() now calls lockdep_assert_held_read()
   instead of lockdep_assert_held().

Bart Van Assche (14):
  bit_spinlock: Fix the __bitlock() definition
  cleanup: Disable context analysis for __free() helpers
  tracing: Disable branch profiling for COMPILE_TEST builds
  srcu: Remove check from srcu_dereference_check()
  futex: Annotate __attach_to_pi_owner() and fixup_pi_*owner()
  locking/rwsem: Annotate rwsem internal functions with __must_hold()
  nvme-fabrics: Annotate nvmf_lookup_transport() with
    __must_hold_shared()
  lockdep: Introduce lockdep_assume_ctx_held()
  kfence: Use lockdep_assume_ctx_held() in kfence_report_error()
  Bluetooth: Add lock context annotations for connection aliases
  futex: Verify exit_mutex ownership in futex_exit_recursive()
  lockdep: Stop lockdep_assert_held*() from assuming lock ownership
  compiler-context-analysis: Split the RCU assertion tests
  rcu: Introduce lockdep_assume_in_rcu_*()

 Documentation/dev-tools/context-analysis.rst | 20 +++--
 drivers/nvme/host/fabrics.c                  |  1 +
 include/linux/bit_spinlock.h                 |  2 +-
 include/linux/cleanup.h                      |  6 +-
 include/linux/lockdep.h                      | 72 +++++++++++++++--
 include/linux/rcupdate.h                     | 82 +++++++++++++++++---
 include/linux/srcu.h                         | 20 +++--
 kernel/futex/core.c                          |  2 +-
 kernel/futex/futex.h                         |  3 +-
 kernel/futex/pi.c                            |  3 +
 kernel/locking/rtmutex.c                     |  7 +-
 kernel/locking/rwsem.c                       |  3 +
 kernel/trace/Kconfig                         |  3 +-
 lib/test_context-analysis.c                  | 38 +++++++--
 mm/kfence/report.c                           |  2 +-
 net/bluetooth/6lowpan.c                      |  2 +-
 net/bluetooth/iso.c                          |  8 +-
 net/bluetooth/l2cap_core.c                   | 48 ++++++------
 net/bluetooth/mgmt.c                         |  2 +-
 net/bluetooth/smp.c                          |  2 +-
 20 files changed, 251 insertions(+), 75 deletions(-)


^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 01/14] bit_spinlock: Fix the __bitlock() definition
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 02/14] cleanup: Disable context analysis for __free() helpers Bart Van Assche
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche

Surround the bitnum argument and the entire __bitlock() macro expansion
with parentheses because bitnum could be an expression that includes an
operator with a lower precedence than the addition operator (e.g. the
exclusive-or operator) and to prevent operator precedence issues in
callers.

Reviewed-by: Marco Elver <elver@google.com>
Fixes: eb7d96a13bf4 ("bit_spinlock: Support Clang's context analysis")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 include/linux/bit_spinlock.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/include/linux/bit_spinlock.h b/include/linux/bit_spinlock.h
index 7869a6e59b6a..c31d4f4d09ee 100644
--- a/include/linux/bit_spinlock.h
+++ b/include/linux/bit_spinlock.h
@@ -17,7 +17,7 @@
  * static analysis can use.
  */
 context_lock_struct(__context_bitlock) { };
-#define __bitlock(bitnum, addr) (struct __context_bitlock *)(bitnum + (addr))
+#define __bitlock(bitnum, addr) ((struct __context_bitlock *)((bitnum) + (addr)))
 
 /*
  *  bit-based spin_lock()

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 02/14] cleanup: Disable context analysis for __free() helpers
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 01/14] bit_spinlock: Fix the __bitlock() definition Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 03/14] tracing: Disable branch profiling for COMPILE_TEST builds Bart Van Assche
                   ` (11 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Nathan Chancellor, Dmitry Ilvokhin,
	Christian Brauner, H. Peter Anvin

The __free_##_name() helper functions defined by DEFINE_FREE() call the
free expression that has been passed to DEFINE_FREE(). If that
expression releases a lock, e.g. because it is a call to an unlock
function, the Clang thread-safety analyzer complains because it cannot
know that the variable that is passed to the helper function holds a
lock. Hence, disable context analysis for the __free() helper
functions.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 include/linux/cleanup.h | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/include/linux/cleanup.h b/include/linux/cleanup.h
index 1fb8058b897d..f6afe80781a4 100644
--- a/include/linux/cleanup.h
+++ b/include/linux/cleanup.h
@@ -207,8 +207,10 @@
  * Without the NULL test it turns into a mess and the compiler can't help us.
  */
 
-#define DEFINE_FREE(_name, _type, _free) \
-	static __always_inline void __free_##_name(void *p) { _type _T = *(_type *)p; _free; }
+#define DEFINE_FREE(_name, _type, _free)				\
+	static __always_inline void __free_##_name(void *p)		\
+		__context_unsafe(/* _free may release a lock */)		\
+	{ _type _T = *(_type *)p; _free; }
 
 #define __free(_name)	__cleanup(__free_##_name)
 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 03/14] tracing: Disable branch profiling for COMPILE_TEST builds
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 01/14] bit_spinlock: Fix the __bitlock() definition Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 02/14] cleanup: Disable context analysis for __free() helpers Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 04/14] srcu: Remove check from srcu_dereference_check() Bart Van Assche
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Steven Rostedt, Masami Hiramatsu,
	Nathan Chancellor

WARN_CONTEXT_ANALYSIS depends on !TRACE_BRANCH_PROFILING because branch
profiling instruments every if-condition and obscures control flow from
Clang's thread-safety analyzer. Make PROFILE_ANNOTATED_BRANCHES and
PROFILE_ALL_BRANCHES depend on !COMPILE_TEST so that allmodconfig and
allyesconfig builds do not enable TRACE_BRANCH_PROFILING and instead
enable WARN_CONTEXT_ANALYSIS_ALL.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/trace/Kconfig | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/kernel/trace/Kconfig b/kernel/trace/Kconfig
index 35d043b19332..afbf17099a68 100644
--- a/kernel/trace/Kconfig
+++ b/kernel/trace/Kconfig
@@ -687,6 +687,7 @@ config BRANCH_PROFILE_NONE
 
 config PROFILE_ANNOTATED_BRANCHES
 	bool "Trace likely/unlikely profiler"
+	depends on !COMPILE_TEST
 	select TRACE_BRANCH_PROFILING
 	help
 	  This tracer profiles all likely and unlikely macros
@@ -698,7 +699,7 @@ config PROFILE_ANNOTATED_BRANCHES
 	  on if you need to profile the system's use of these macros.
 
 config PROFILE_ALL_BRANCHES
-	bool "Profile all if conditionals" if !FORTIFY_SOURCE
+	bool "Profile all if conditionals" if !FORTIFY_SOURCE && !COMPILE_TEST
 	select TRACE_BRANCH_PROFILING
 	help
 	  This tracer profiles all branch conditions. Every if ()

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 04/14] srcu: Remove check from srcu_dereference_check()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (2 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 03/14] tracing: Disable branch profiling for COMPILE_TEST builds Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 05/14] futex: Annotate __attach_to_pi_owner() and fixup_pi_*owner() Bart Van Assche
                   ` (9 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Lai Jiangshan, Josh Triplett, Onur Özkan,
	Nathan Chancellor

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@google.com>
Fixes: f0b7ce22d718 ("srcu: Support Clang's context analysis")
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 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 d4dcaeb83e2b..5ef07357507b 100644
--- a/include/linux/srcu.h
+++ b/include/linux/srcu.h
@@ -278,8 +278,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) { }
 
@@ -297,7 +300,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);	\
@@ -315,7 +317,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
@@ -323,7 +329,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.

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 05/14] futex: Annotate __attach_to_pi_owner() and fixup_pi_*owner()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (3 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 04/14] srcu: Remove check from srcu_dereference_check() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 06/14] locking/rwsem: Annotate rwsem internal functions with __must_hold() Bart Van Assche
                   ` (8 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Thomas Gleixner, Nathan Chancellor

Annotate __attach_to_pi_owner() with __must_hold(&p->pi_lock) and
annotate fixup_pi_state_owner() and fixup_pi_owner() with
__must_hold(q->lock_ptr).

__attach_to_pi_owner() modifies p->futex.pi_state_list and attaches a
new pi_state to owner task p, which requires holding p->pi_lock.
Enforce this requirement at compile time with __must_hold(&p->pi_lock).

__fixup_pi_state_owner() requires holding q->lock_ptr. Its caller
fixup_pi_state_owner() and outer caller fixup_pi_owner() must both be
called with the hash bucket lock held. Currently,
lockdep_assert_held(q->lock_ptr) in fixup_pi_state_owner() implicitly
assumes lock ownership for Clang context analysis. Prepare for removing
the context assumption from lockdep_assert_held() by annotating both
fixup_pi_state_owner() and fixup_pi_owner() with
__must_hold(q->lock_ptr).

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/futex/futex.h | 3 ++-
 kernel/futex/pi.c    | 3 +++
 2 files changed, 5 insertions(+), 1 deletion(-)

diff --git a/kernel/futex/futex.h b/kernel/futex/futex.h
index f00f0863ed44..3e162837e9d2 100644
--- a/kernel/futex/futex.h
+++ b/kernel/futex/futex.h
@@ -394,7 +394,8 @@ extern int futex_lock_pi_atomic(u32 __user *uaddr, struct futex_hash_bucket *hb,
 extern int refill_pi_state_cache(void);
 extern void get_pi_state(struct futex_pi_state *pi_state);
 extern void put_pi_state(struct futex_pi_state *pi_state);
-extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked);
+extern int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked)
+	__must_hold(q->lock_ptr);
 
 /*
  * Express the locking dependencies for lockdep:
diff --git a/kernel/futex/pi.c b/kernel/futex/pi.c
index 98f1b962e59a..e0310776b484 100644
--- a/kernel/futex/pi.c
+++ b/kernel/futex/pi.c
@@ -420,6 +420,7 @@ static int handle_exit_race(u32 __user *uaddr, u32 uval)
 
 static void __attach_to_pi_owner(struct task_struct *p, union futex_key *key,
 				 struct futex_pi_state **ps)
+	__must_hold(&p->pi_lock)
 {
 	/*
 	 * No existing pi state. First waiter. [2]
@@ -912,6 +913,7 @@ static int __fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
 
 static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
 				struct task_struct *argowner)
+	__must_hold(q->lock_ptr)
 {
 	struct futex_pi_state *pi_state = q->pi_state;
 	int ret;
@@ -940,6 +942,7 @@ static int fixup_pi_state_owner(u32 __user *uaddr, struct futex_q *q,
  *  - <0 - on error (-EFAULT)
  */
 int fixup_pi_owner(u32 __user *uaddr, struct futex_q *q, int locked)
+	__must_hold(q->lock_ptr)
 {
 	if (locked) {
 		/*

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 06/14] locking/rwsem: Annotate rwsem internal functions with __must_hold()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (4 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 05/14] futex: Annotate __attach_to_pi_owner() and fixup_pi_*owner() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 07/14] nvme-fabrics: Annotate nvmf_lookup_transport() with __must_hold_shared() Bart Van Assche
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Nathan Chancellor

Annotate rwsem_del_waiter(), rwsem_mark_wake(), and
rwsem_cond_wake_waiter() with __must_hold(&sem->wait_lock).

rwsem_del_waiter(), rwsem_mark_wake(), and rwsem_cond_wake_waiter()
access members guarded by sem->wait_lock and call functions requiring
sem->wait_lock. Currently, lockdep_assert_held(&sem->wait_lock)
implicitly assumes lock ownership for Clang context analysis. Prepare
for removing the context assumption from lockdep_assert_held() by
explicitly annotating these functions with __must_hold(&sem->wait_lock).

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/locking/rwsem.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/kernel/locking/rwsem.c b/kernel/locking/rwsem.c
index b9c180ac1eee..c76a3a712c31 100644
--- a/kernel/locking/rwsem.c
+++ b/kernel/locking/rwsem.c
@@ -392,6 +392,7 @@ bool __rwsem_del_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
  */
 static inline bool
 rwsem_del_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
+	__must_hold(&sem->wait_lock)
 {
 	lockdep_assert_held(&sem->wait_lock);
 	if (__rwsem_del_waiter(sem, waiter))
@@ -429,6 +430,7 @@ struct rwsem_waiter *next_waiter(const struct rw_semaphore *sem,
 static void rwsem_mark_wake(struct rw_semaphore *sem,
 			    enum rwsem_wake_type wake_type,
 			    struct wake_q_head *wake_q)
+	__must_hold(&sem->wait_lock)
 {
 	struct rwsem_waiter *waiter, *next;
 	long oldcount, woken = 0, adjustment = 0;
@@ -995,6 +997,7 @@ rwsem_spin_on_owner(struct rw_semaphore *sem)
  */
 static inline void rwsem_cond_wake_waiter(struct rw_semaphore *sem, long count,
 					  struct wake_q_head *wake_q)
+	__must_hold(&sem->wait_lock)
 {
 	enum rwsem_wake_type wake_type;
 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 07/14] nvme-fabrics: Annotate nvmf_lookup_transport() with __must_hold_shared()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (5 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 06/14] locking/rwsem: Annotate rwsem internal functions with __must_hold() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 08/14] lockdep: Introduce lockdep_assume_ctx_held() Bart Van Assche
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Keith Busch, Jens Axboe, Sagi Grimberg,
	Nathan Chancellor

Annotate nvmf_lookup_transport() with
__must_hold_shared(&nvmf_transports_rwsem).

nvmf_lookup_transport() reads nvmf_transports while holding
nvmf_transports_rwsem for reading. Currently,
lockdep_assert_held(&nvmf_transports_rwsem) implicitly assumes lock
ownership for Clang context analysis. Prepare for removing the context
assumption from lockdep_assert_held() by explicitly annotating
nvmf_lookup_transport() with __must_hold_shared() to declare that
callers must hold nvmf_transports_rwsem in shared mode.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 drivers/nvme/host/fabrics.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/nvme/host/fabrics.c b/drivers/nvme/host/fabrics.c
index 3d7ad86c1955..d3d00adedd14 100644
--- a/drivers/nvme/host/fabrics.c
+++ b/drivers/nvme/host/fabrics.c
@@ -644,6 +644,7 @@ EXPORT_SYMBOL_GPL(nvmf_unregister_transport);
 
 static struct nvmf_transport_ops *nvmf_lookup_transport(
 		struct nvmf_ctrl_options *opts)
+	__must_hold_shared(&nvmf_transports_rwsem)
 {
 	struct nvmf_transport_ops *ops;
 

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 08/14] lockdep: Introduce lockdep_assume_ctx_held()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (6 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 07/14] nvme-fabrics: Annotate nvmf_lookup_transport() with __must_hold_shared() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 09/14] kfence: Use lockdep_assume_ctx_held() in kfence_report_error() Bart Van Assche
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Andrew Morton, Nathan Chancellor

Introduce lockdep_assume_ctx_held() and lockdep_assume_shared_ctx_held()
for code that needs to inform Clang's context analysis that a lock is
held in code paths where lock ownership cannot be proven at compile
time, e.g. because the lock is accessed through a pointer alias that
the analysis cannot resolve. Make these macros call respectively
__assume_ctx_lock() and __assume_shared_ctx_lock() and also verify at
runtime with lockdep_assert_held() or lockdep_assert_held_read() that
the lock is held. Store the macro argument in a local variable with a
unique name such that it is evaluated only once and such that nested
use of these macros does not cause shadowing. Rely on Clang's context
analysis resolving that local variable to the expression it has been
initialized with. Document both macros with kernel-doc comments,
including that __assume_ctx_lock() and __assume_shared_ctx_lock() must
be used for context locks without lockdep support, e.g. bit spinlocks
and the RCU context tokens.

Convert the lockdep_assert_held() tests in lib/test_context-analysis.c
into lockdep_assume_ctx_held() tests and add tests for
lockdep_assume_shared_ctx_held().

Prepare for removing the context assumptions from lockdep_assert_held*()
so that pre-existing lockdep_assert_held*() calls across the kernel tree
stop silencing Clang context analysis warnings in functions that lack a
__must_hold() or __must_hold_shared() annotation when
WARN_CONTEXT_ANALYSIS_ALL is enabled. Use lockdep_assume_ctx_held() and
lockdep_assume_shared_ctx_held() only in the few places in the kernel
where static analysis cannot prove lock ownership at compile time
(futex, kfence, rtmutex and Bluetooth).

Cc: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 include/linux/lockdep.h     | 54 +++++++++++++++++++++++++++++++++++++
 lib/test_context-analysis.c | 16 +++++++++--
 2 files changed, 68 insertions(+), 2 deletions(-)

diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index a6451ecbbe9a..23ffc5f0557c 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -412,6 +412,60 @@ extern int lockdep_is_held(const void *);
 
 #endif /* !LOCKDEP */
 
+#define _lockdep_assume_ctx_held(var, uniq)				\
+	do {								\
+		__auto_type uniq = (var);				\
+									\
+		__assume_ctx_lock(uniq);				\
+		lockdep_assert_held(uniq);				\
+	} while (0)
+
+/**
+ * lockdep_assume_ctx_held() - assume and verify that a context lock is held
+ * @var: context lock instance pointer
+ *
+ * Instruct the compiler's context analysis to assume that @var is held
+ * exclusively from this point on and verify at runtime with
+ * lockdep_assert_held() that @var is held. Use this macro in code paths
+ * where lock ownership cannot be proven at compile time, e.g. because the
+ * lock is accessed through an alias the analysis cannot resolve or because
+ * a function does not always require the lock, making __must_hold()
+ * inappropriate. @var is evaluated only once.
+ *
+ * @var must point at a lock object that is tracked by lockdep, i.e. that
+ * has a dep_map member. For context locks that are not tracked by lockdep,
+ * e.g. bit spinlocks and the RCU context tokens, use
+ * __assume_ctx_lock() instead.
+ */
+#define lockdep_assume_ctx_held(var)					\
+	_lockdep_assume_ctx_held(var, __UNIQUE_ID(ctx_lock))
+
+#define _lockdep_assume_shared_ctx_held(var, uniq)			\
+	do {								\
+		__auto_type uniq = (var);				\
+									\
+		__assume_shared_ctx_lock(uniq);				\
+		lockdep_assert_held_read(uniq);				\
+	} while (0)
+
+/**
+ * lockdep_assume_shared_ctx_held() - assume and verify shared lock ownership
+ * @var: context lock instance pointer
+ *
+ * Instruct the compiler's context analysis to assume that @var is held with
+ * shared access from this point on and verify at runtime with
+ * lockdep_assert_held_read() that @var is held for reading. Use this macro
+ * for reader-writer locks, e.g. rwlock_t and struct rw_semaphore, in code
+ * paths where shared lock ownership cannot be proven at compile time. @var
+ * is evaluated only once.
+ *
+ * @var must point at a lock object that is tracked by lockdep, i.e. that
+ * has a dep_map member. For context locks that are not tracked by lockdep,
+ * e.g. the RCU context tokens, use __assume_shared_ctx_lock() instead.
+ */
+#define lockdep_assume_shared_ctx_held(var)				\
+	_lockdep_assume_shared_ctx_held(var, __UNIQUE_ID(ctx_lock))
+
 #ifdef CONFIG_PROVE_LOCKING
 void lockdep_set_lock_cmp_fn(struct lockdep_map *, lock_cmp_fn, lock_print_fn);
 
diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
index 316f4dfcda65..022891999c9c 100644
--- a/lib/test_context-analysis.c
+++ b/lib/test_context-analysis.c
@@ -68,7 +68,7 @@ static void __used test_common_helpers(void)
 	}											\
 	static void __used test_##class##_assert(struct test_##class##_data *d)			\
 	{											\
-		lockdep_assert_held(&d->lock);							\
+		lockdep_assume_ctx_held(&d->lock);						\
 		op(d->counter);									\
 	}											\
 	static void __used test_##class##_guard(struct test_##class##_data *d)			\
@@ -156,6 +156,12 @@ TEST_SPINLOCK_COMMON(read_lock,
 		     read_trylock,
 		     TEST_OP_RO);
 
+static void __used test_read_lock_assert_shared(struct test_read_lock_data *d)
+{
+	lockdep_assume_shared_ctx_held(&d->lock);
+	TEST_OP_RO(d->counter);
+}
+
 struct test_mutex_data {
 	struct mutex mtx;
 	int counter __guarded_by(&mtx);
@@ -203,7 +209,7 @@ static void __used test_mutex_trylock(struct test_mutex_data *d, atomic_t *a)
 
 static void __used test_mutex_assert(struct test_mutex_data *d)
 {
-	lockdep_assert_held(&d->mtx);
+	lockdep_assume_ctx_held(&d->mtx);
 	d->counter++;
 }
 
@@ -339,6 +345,12 @@ static void __used test_rwsem_assert(struct test_rwsem_data *d)
 	d->counter++;
 }
 
+static void __used test_rwsem_assert_shared(struct test_rwsem_data *d)
+{
+	lockdep_assume_shared_ctx_held(&d->sem);
+	(void)d->counter;
+}
+
 static void __used test_rwsem_guard(struct test_rwsem_data *d)
 {
 	{ guard(rwsem_read)(&d->sem); (void)d->counter; }

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 09/14] kfence: Use lockdep_assume_ctx_held() in kfence_report_error()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (7 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 08/14] lockdep: Introduce lockdep_assume_ctx_held() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 10/14] Bluetooth: Add lock context annotations for connection aliases Bart Van Assche
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Alexander Potapenko, Andrew Morton,
	Nathan Chancellor

Replace lockdep_assert_held(&meta->lock) with
lockdep_assume_ctx_held(&meta->lock) in kfence_report_error().

kfence_print_object() requires holding &meta->lock. In
kfence_report_error(), meta is optional (nullable), so the function
cannot be annotated with __must_hold(&meta->lock). Currently,
lockdep_assert_held(&meta->lock) implicitly assumes lock ownership for
Clang context analysis. Prepare for removing the context assumption from
lockdep_assert_held() by using lockdep_assume_ctx_held(&meta->lock)
inside the "if (meta)" block. This informs Clang context analysis that
meta->lock is held and preserves the runtime lockdep check.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 mm/kfence/report.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/mm/kfence/report.c b/mm/kfence/report.c
index d548536864b1..5b8588d35b77 100644
--- a/mm/kfence/report.c
+++ b/mm/kfence/report.c
@@ -286,7 +286,7 @@ kfence_report_error(unsigned long address, bool is_write, struct pt_regs *regs,
 	stack_trace_print(stack_entries + skipnr, num_stack_entries - skipnr, 0);
 
 	if (meta) {
-		lockdep_assert_held(&meta->lock);
+		lockdep_assume_ctx_held(&meta->lock);
 		pr_err("\n");
 		kfence_print_object(NULL, meta);
 	}

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 10/14] Bluetooth: Add lock context annotations for connection aliases
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (8 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 09/14] kfence: Use lockdep_assume_ctx_held() in kfence_report_error() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 11/14] futex: Verify exit_mutex ownership in futex_exit_recursive() Bart Van Assche
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Marcel Holtmann, Luiz Augusto von Dentz,
	Nathan Chancellor

Replace lockdep_assert_held() with lockdep_assume_ctx_held() where
hdev->lock, an L2CAP connection lock or an L2CAP channel lock is
accessed through a pointer alias.

Currently, lockdep_assert_held() implicitly assumes lock ownership for
Clang context analysis. Prepare for removing the context assumption from
lockdep_assert_held(). In Bluetooth code, functions access members
guarded by hdev->lock (such as l2cap_data) or call functions requiring
hdev->lock through connection pointer aliases (hcon->hdev->lock,
conn->hdev->lock, link->conn->hdev->lock). Similarly, L2CAP functions
call functions that require chan->conn->lock while holding conn->lock,
and l2cap_ecred_rsp_defer() relies on rsp->locked_chan->lock,
rsp->locked_chan->conn->lock, chan->lock and chan->conn->lock being
held.

Replace lockdep_assert_held() with lockdep_assume_ctx_held() at these
alias access points. This informs Clang context analysis that the lock
is held and preserves the runtime lockdep checks.

Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 net/bluetooth/6lowpan.c    |  2 +-
 net/bluetooth/iso.c        |  8 +++----
 net/bluetooth/l2cap_core.c | 48 +++++++++++++++++++-------------------
 net/bluetooth/mgmt.c       |  2 +-
 net/bluetooth/smp.c        |  2 +-
 5 files changed, 31 insertions(+), 31 deletions(-)

diff --git a/net/bluetooth/6lowpan.c b/net/bluetooth/6lowpan.c
index 8c51c20f6c4b..0a1a067d4870 100644
--- a/net/bluetooth/6lowpan.c
+++ b/net/bluetooth/6lowpan.c
@@ -1019,7 +1019,7 @@ static int get_l2cap_conn(char *buf, bdaddr_t *addr, u8 *addr_type,
 		return -ENOENT;
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	*conn = l2cap_conn_hold_unless_zero(hcon->l2cap_data);
 
diff --git a/net/bluetooth/iso.c b/net/bluetooth/iso.c
index 06f1e7ba2cb1..c60af10a2e95 100644
--- a/net/bluetooth/iso.c
+++ b/net/bluetooth/iso.c
@@ -466,7 +466,7 @@ static int iso_connect_bis(struct sock *sk)
 			iso_pi(sk)->bc_sid = hcon->sid;
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	conn = iso_conn_add(hcon);
 	if (!conn) {
@@ -572,7 +572,7 @@ static int iso_connect_cis(struct sock *sk)
 		}
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	/* The socket lock keeps the current attachment and its hcon stable. */
 	if (iso_pi(sk)->conn && iso_pi(sk)->conn->hcon != hcon) {
@@ -1390,7 +1390,7 @@ static int iso_listen_bis(struct sock *sk)
 		goto unlock;
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	conn = iso_conn_add(hcon);
 	if (!conn) {
@@ -2657,7 +2657,7 @@ static void iso_connect_cfm(struct hci_conn *hcon, __u8 status)
 
 			list_for_each_entry_safe(link, t, &hcon->link_list,
 						 list) {
-				lockdep_assert_held(&link->conn->hdev->lock);
+				lockdep_assume_ctx_held(&link->conn->hdev->lock);
 				iso_conn_del(link->conn, bt_to_errno(status));
 			}
 
diff --git a/net/bluetooth/l2cap_core.c b/net/bluetooth/l2cap_core.c
index 70ad30b091e4..e45b860f99a9 100644
--- a/net/bluetooth/l2cap_core.c
+++ b/net/bluetooth/l2cap_core.c
@@ -1657,7 +1657,7 @@ static void l2cap_conn_start(struct l2cap_conn *conn)
 	list_for_each_entry_safe(chan, tmp, &conn->chan_l, list) {
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		if (chan->chan_type != L2CAP_CHAN_CONN_ORIENTED) {
 			l2cap_chan_ready(chan);
@@ -1775,7 +1775,7 @@ static void l2cap_conn_ready(struct l2cap_conn *conn)
 
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		if (hcon->type == LE_LINK) {
 			l2cap_le_start(chan);
@@ -1932,7 +1932,7 @@ static void l2cap_conn_del(struct hci_conn *hcon, int err)
 		l2cap_chan_hold(chan);
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		l2cap_chan_del(chan, err);
 
@@ -4063,8 +4063,8 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
 	    !test_and_clear_bit(FLAG_DEFER_SETUP, &chan->flags))
 		return;
 
-	lockdep_assert_held(&rsp->locked_chan->lock);
-	lockdep_assert_held(&rsp->locked_chan->conn->lock);
+	lockdep_assume_ctx_held(&rsp->locked_chan->lock);
+	lockdep_assume_ctx_held(&rsp->locked_chan->conn->lock);
 
 	l2cap_chan_hold(chan);
 
@@ -4076,8 +4076,8 @@ static void l2cap_ecred_rsp_defer(struct l2cap_chan *chan, void *data)
 	if (context_unsafe(!locked && chan != rsp->locked_chan))
 		goto done;
 
-	lockdep_assert_held(&chan->lock);
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	/* Reset ident so only one response is sent */
 	chan->ident = 0;
@@ -4275,7 +4275,7 @@ static struct l2cap_chan *l2cap_new_connection(struct l2cap_conn *conn,
 
 	__l2cap_chan_add(conn, chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	if (pchan->ops->new_connection &&
 	    pchan->ops->new_connection(pchan, chan) < 0) {
@@ -4478,7 +4478,7 @@ static int l2cap_connect_create_rsp(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	/* Only accept a response matching a connection request we sent and
 	 * that is still outstanding. A channel waiting in BT_CONNECT for
@@ -4589,7 +4589,7 @@ static inline int l2cap_config_req(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	if (chan->state != BT_CONFIG && chan->state != BT_CONNECT2 &&
 	    chan->state != BT_CONNECTED) {
@@ -4711,7 +4711,7 @@ static inline int l2cap_config_rsp(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	/* Connected channels may also receive reconfiguration responses. */
 	if (chan->state != BT_CONFIG && chan->state != BT_CONNECTED)
@@ -4829,7 +4829,7 @@ static inline int l2cap_disconnect_req(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	rsp.dcid = cpu_to_le16(chan->scid);
 	rsp.scid = cpu_to_le16(chan->dcid);
@@ -4871,7 +4871,7 @@ static inline int l2cap_disconnect_rsp(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	if (chan->state != BT_DISCONN) {
 		l2cap_chan_unlock(chan);
@@ -5085,7 +5085,7 @@ static int l2cap_le_connect_rsp(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	switch (result) {
 	case L2CAP_CR_LE_SUCCESS:
@@ -5305,7 +5305,7 @@ static int l2cap_le_connect_req(struct l2cap_conn *conn,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	bacpy(&chan->src, &conn->hcon->src);
 	bacpy(&chan->dst, &conn->hcon->dst);
@@ -5538,7 +5538,7 @@ static inline int l2cap_ecred_conn_req(struct l2cap_conn *conn,
 
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		bacpy(&chan->src, &conn->hcon->src);
 		bacpy(&chan->dst, &conn->hcon->dst);
@@ -5629,7 +5629,7 @@ static inline int l2cap_ecred_conn_rsp(struct l2cap_conn *conn,
 		l2cap_chan_hold(chan);
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		/* Check that there is a dcid for each pending channel */
 		if (cmd_len < sizeof(dcid)) {
@@ -5858,7 +5858,7 @@ static inline int l2cap_ecred_reconf_rsp(struct l2cap_conn *conn,
 			continue;
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		l2cap_chan_del(chan, ECONNRESET);
 
@@ -5889,7 +5889,7 @@ static inline int l2cap_le_command_rej(struct l2cap_conn *conn,
 		goto done;
 
 	l2cap_chan_lock(chan);
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 	l2cap_chan_del(chan, ECONNREFUSED);
 	l2cap_chan_unlock(chan);
 	l2cap_chan_put(chan);
@@ -7285,7 +7285,7 @@ static void l2cap_data_channel(struct l2cap_conn *conn, u16 cid,
 
 	l2cap_chan_lock(chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	BT_DBG("chan %p, len %d", chan, skb->len);
 
@@ -7673,7 +7673,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 		goto done;
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	conn = l2cap_conn_add(hcon);
 	if (!conn) {
@@ -7719,7 +7719,7 @@ int l2cap_chan_connect(struct l2cap_chan *chan, __le16 psm, u16 cid,
 
 	__l2cap_chan_add(conn, chan);
 
-	lockdep_assert_held(&chan->conn->lock);
+	lockdep_assume_ctx_held(&chan->conn->lock);
 
 	/* l2cap_chan_add takes its own ref so we can drop this one */
 	hci_conn_drop(hcon);
@@ -7977,7 +7977,7 @@ static void l2cap_security_cfm(struct hci_conn *hcon, u8 status, u8 encrypt)
 	list_for_each_entry(chan, &conn->chan_l, list) {
 		l2cap_chan_lock(chan);
 
-		lockdep_assert_held(&chan->conn->lock);
+		lockdep_assume_ctx_held(&chan->conn->lock);
 
 		BT_DBG("chan %p scid 0x%4.4x state %s", chan, chan->scid,
 		       state_to_string(chan->state));
@@ -8153,7 +8153,7 @@ int l2cap_recv_acldata(struct hci_dev *hdev, u16 handle,
 		return -ENOENT;
 	}
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	hci_conn_enter_active_mode(hcon, BT_POWER_FORCE_ACTIVE_OFF);
 
diff --git a/net/bluetooth/mgmt.c b/net/bluetooth/mgmt.c
index b9262ec134a2..3bb93d33d119 100644
--- a/net/bluetooth/mgmt.c
+++ b/net/bluetooth/mgmt.c
@@ -3904,7 +3904,7 @@ static int user_pairing_resp(struct sock *sk, struct hci_dev *hdev,
 	}
 
 	if (addr->type == BDADDR_LE_PUBLIC || addr->type == BDADDR_LE_RANDOM) {
-		lockdep_assert_held(&conn->hdev->lock);
+		lockdep_assume_ctx_held(&conn->hdev->lock);
 
 		err = smp_user_confirm_reply(conn, mgmt_op, passkey);
 		if (!err)
diff --git a/net/bluetooth/smp.c b/net/bluetooth/smp.c
index b7015e37e1f1..5bce13fd40ed 100644
--- a/net/bluetooth/smp.c
+++ b/net/bluetooth/smp.c
@@ -2459,7 +2459,7 @@ int smp_cancel_and_remove_pairing(struct hci_dev *hdev, bdaddr_t *bdaddr,
 	if (!hcon)
 		goto done;
 
-	lockdep_assert_held(&hcon->hdev->lock);
+	lockdep_assume_ctx_held(&hcon->hdev->lock);
 
 	conn = hcon->l2cap_data;
 	if (!conn)

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 11/14] futex: Verify exit_mutex ownership in futex_exit_recursive()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (9 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 10/14] Bluetooth: Add lock context annotations for connection aliases Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 12/14] lockdep: Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Thomas Gleixner, Nathan Chancellor

futex_exit_recursive() unlocks tsk->futex.exit_mutex if the futex state
is FUTEX_STATE_EXITING. It uses __assume_ctx_lock() to inform Clang's
context analysis that this mutex is held. However, __assume_ctx_lock()
does not verify that assumption. Convert the __assume_ctx_lock() call
into a lockdep_assume_ctx_held() call such that the lock ownership
assumption is also verified at runtime by lockdep.

Cc: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 kernel/futex/core.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/kernel/futex/core.c b/kernel/futex/core.c
index a240638f69e5..4e5aa0160dd2 100644
--- a/kernel/futex/core.c
+++ b/kernel/futex/core.c
@@ -1503,7 +1503,7 @@ void futex_exit_recursive(struct task_struct *tsk)
 {
 	/* If the state is FUTEX_STATE_EXITING then futex_exit_mutex is held */
 	if (tsk->futex.state == FUTEX_STATE_EXITING) {
-		__assume_ctx_lock(&tsk->futex.exit_mutex);
+		lockdep_assume_ctx_held(&tsk->futex.exit_mutex);
 		mutex_unlock(&tsk->futex.exit_mutex);
 	}
 	tsk->futex.state = FUTEX_STATE_DEAD;

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 12/14] lockdep: Stop lockdep_assert_held*() from assuming lock ownership
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (10 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 11/14] futex: Verify exit_mutex ownership in futex_exit_recursive() Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 13/14] compiler-context-analysis: Split the RCU assertion tests Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 14/14] rcu: Introduce lockdep_assume_in_rcu_*() Bart Van Assche
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Jonathan Corbet, Nathan Chancellor

Since commit 7c451541743c ("lockdep: Annotate lockdep assertions for
context analysis"), lockdep_assert_held(), lockdep_assert_held_write()
and lockdep_assert_held_read() instruct Clang's context analysis to
assume that a lock is held. This has the following disadvantages:
- lockdep_assert_held*() silences context analysis warnings in functions
  that lack a __must_hold() or __must_hold_shared() annotation. Hence,
  the compiler neither reports the missing function annotation nor
  checks at compile time whether callers hold the lock.
- lockdep_assert_held() makes context analysis assume exclusive lock
  ownership, even if a reader-writer lock is held in shared mode.
- lockdep_assert_held*() can only be used with lock types that are
  defined with context_lock_struct().

Hence, remove the context assumptions from lockdep_assert_held*() for
both CONFIG_LOCKDEP and !CONFIG_LOCKDEP builds. Code that needs to
inform context analysis that a lock is held should use
lockdep_assume_ctx_held() or lockdep_assume_shared_ctx_held() instead.
Mention this in the kernel-doc comments of these macros.

Convert the __assume_ctx_lock() calls in kernel/locking/rtmutex.c into
lockdep_assume_ctx_held() calls such that the lock ownership assumptions
are verified at runtime. Remove the lockdep_assert_held(&lock->wait_lock)
call from __rt_mutex_slowlock() because lockdep_assume_ctx_held() now
performs the same runtime check.

Update Documentation/dev-tools/context-analysis.rst accordingly.

Cc: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 Documentation/dev-tools/context-analysis.rst | 18 ++++++++++++------
 include/linux/lockdep.h                      | 18 ++++++++++++------
 kernel/locking/rtmutex.c                     |  7 +++----
 3 files changed, 27 insertions(+), 16 deletions(-)

diff --git a/Documentation/dev-tools/context-analysis.rst b/Documentation/dev-tools/context-analysis.rst
index 8e71e1e75b5b..88b4ed6a182f 100644
--- a/Documentation/dev-tools/context-analysis.rst
+++ b/Documentation/dev-tools/context-analysis.rst
@@ -111,12 +111,18 @@ checking): either with a ``context_unsafe(var = init)`` expression, or by
 marking small initialization functions with the ``__context_unsafe(init)``
 attribute.
 
-Lockdep assertions, such as `lockdep_assert_held()`, inform the compiler's
-context analysis that the associated synchronization primitive is held after
-the assertion. This avoids false positives in complex control-flow scenarios
-and encourages the use of Lockdep where static analysis is limited. For
-example, this is useful when a function doesn't *always* require a lock, making
-`__must_hold()` inappropriate.
+`lockdep_assume_ctx_held()` and `lockdep_assume_shared_ctx_held()` inform the
+compiler's context analysis that the associated synchronization primitive is
+held and verify this at runtime via Lockdep. This avoids false positives in
+complex control-flow scenarios while still checking lock ownership with Lockdep
+where static analysis is limited. For example, this is useful when a function
+doesn't *always* require a lock, making `__must_hold()` inappropriate.
+
+Lockdep assertions, such as `lockdep_assert_held()`, only perform a runtime
+check and do not inform the compiler's context analysis that a lock is held.
+For context locks that are not tracked by Lockdep, e.g. bit spinlocks, use
+`__assume_ctx_lock()` or `__assume_shared_ctx_lock()` instead. These only
+inform the compiler's context analysis and do not perform a runtime check.
 
 Keywords
 ~~~~~~~~
diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index 23ffc5f0557c..e2618271eb0e 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -285,16 +285,16 @@ extern u32 lock_sequence(struct lockdep_map *lock);
 	do { WARN_ON_ONCE(debug_locks && !(cond)); } while (0)
 
 #define lockdep_assert_held(l)		\
-	do { lockdep_assert(lockdep_is_held(l) != LOCK_STATE_NOT_HELD); __assume_ctx_lock(l); } while (0)
+	lockdep_assert(lockdep_is_held(l) != LOCK_STATE_NOT_HELD)
 
 #define lockdep_assert_not_held(l)	\
 	lockdep_assert(lockdep_is_held(l) != LOCK_STATE_HELD)
 
 #define lockdep_assert_held_write(l)	\
-	do { lockdep_assert(lockdep_is_held_type(l, 0)); __assume_ctx_lock(l); } while (0)
+	lockdep_assert(lockdep_is_held_type(l, 0))
 
 #define lockdep_assert_held_read(l)	\
-	do { lockdep_assert(lockdep_is_held_type(l, 1)); __assume_shared_ctx_lock(l); } while (0)
+	lockdep_assert(lockdep_is_held_type(l, 1))
 
 #define lockdep_assert_held_once(l)		\
 	lockdep_assert_once(lockdep_is_held(l) != LOCK_STATE_NOT_HELD)
@@ -392,10 +392,10 @@ extern int lockdep_is_held(const void *);
 #define lockdep_assert(c)			do { } while (0)
 #define lockdep_assert_once(c)			do { } while (0)
 
-#define lockdep_assert_held(l)			__assume_ctx_lock(l)
+#define lockdep_assert_held(l)			do { (void)(l); } while (0)
 #define lockdep_assert_not_held(l)		do { (void)(l); } while (0)
-#define lockdep_assert_held_write(l)		__assume_ctx_lock(l)
-#define lockdep_assert_held_read(l)		__assume_shared_ctx_lock(l)
+#define lockdep_assert_held_write(l)		do { (void)(l); } while (0)
+#define lockdep_assert_held_read(l)		do { (void)(l); } while (0)
 #define lockdep_assert_held_once(l)		do { (void)(l); } while (0)
 #define lockdep_assert_none_held_once()	do { } while (0)
 
@@ -436,6 +436,9 @@ extern int lockdep_is_held(const void *);
  * has a dep_map member. For context locks that are not tracked by lockdep,
  * e.g. bit spinlocks and the RCU context tokens, use
  * __assume_ctx_lock() instead.
+ *
+ * Note: lockdep_assert_held() does not inform the compiler's context
+ * analysis that a lock is held. Use this macro if that is needed.
  */
 #define lockdep_assume_ctx_held(var)					\
 	_lockdep_assume_ctx_held(var, __UNIQUE_ID(ctx_lock))
@@ -462,6 +465,9 @@ extern int lockdep_is_held(const void *);
  * @var must point at a lock object that is tracked by lockdep, i.e. that
  * has a dep_map member. For context locks that are not tracked by lockdep,
  * e.g. the RCU context tokens, use __assume_shared_ctx_lock() instead.
+ *
+ * Note: lockdep_assert_held_read() does not inform the compiler's context
+ * analysis that a lock is held. Use this macro if that is needed.
  */
 #define lockdep_assume_shared_ctx_held(var)				\
 	_lockdep_assume_shared_ctx_held(var, __UNIQUE_ID(ctx_lock))
diff --git a/kernel/locking/rtmutex.c b/kernel/locking/rtmutex.c
index 5a9534c715b8..424cd38a72d2 100644
--- a/kernel/locking/rtmutex.c
+++ b/kernel/locking/rtmutex.c
@@ -1259,7 +1259,7 @@ static int __sched task_blocks_on_rt_mutex(struct rt_mutex_base *lock,
 
 		/* Check whether the waiter should back out immediately */
 		rtm = container_of(lock, struct rt_mutex, rtmutex);
-		__assume_ctx_lock(&rtm->rtmutex.wait_lock);
+		lockdep_assume_ctx_held(&rtm->rtmutex.wait_lock);
 		res = __ww_mutex_add_waiter(waiter, rtm, ww_ctx, wake_q);
 		if (res) {
 			raw_spin_lock(&task->pi_lock);
@@ -1634,7 +1634,7 @@ static int __sched rt_mutex_slowlock_block(struct rt_mutex_base *lock,
 	struct task_struct *owner;
 	int ret = 0;
 
-	__assume_ctx_lock(&rtm->rtmutex.wait_lock);
+	lockdep_assume_ctx_held(&rtm->rtmutex.wait_lock);
 
 	lockevent_inc(rtmutex_slow_block);
 	for (;;) {
@@ -1724,8 +1724,7 @@ static int __sched __rt_mutex_slowlock(struct rt_mutex_base *lock,
 	struct ww_mutex *ww = ww_container_of(rtm);
 	int ret;
 
-	__assume_ctx_lock(&rtm->rtmutex.wait_lock);
-	lockdep_assert_held(&lock->wait_lock);
+	lockdep_assume_ctx_held(&rtm->rtmutex.wait_lock);
 	lockevent_inc(rtmutex_slowlock);
 
 	/* Try to acquire the lock again: */

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 13/14] compiler-context-analysis: Split the RCU assertion tests
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (11 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 12/14] lockdep: Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  2026-11-09 19:13 ` [PATCH v2 14/14] rcu: Introduce lockdep_assume_in_rcu_*() Bart Van Assche
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Andrew Morton

Split test_rcu_assert_variants() in lib/test_context-analysis.c into
test_rcu_assert(), test_rcu_assert_bh() and test_rcu_assert_sched() such
that each RCU assertion variant is tested independently. Add
test_rcu_assert_reader() to test lockdep_assert_in_rcu_reader().

Cc: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 lib/test_context-analysis.c | 14 +++++++++++++-
 1 file changed, 13 insertions(+), 1 deletion(-)

diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
index 022891999c9c..048b839bae92 100644
--- a/lib/test_context-analysis.c
+++ b/lib/test_context-analysis.c
@@ -464,18 +464,30 @@ static void __used test_rcu_lock_reentrant(void)
 	rcu_read_unlock();
 }
 
-static void __used test_rcu_assert_variants(void)
+static void __used test_rcu_assert(void)
 {
 	lockdep_assert_in_rcu_read_lock();
 	wants_rcu_held();
+}
 
+static void __used test_rcu_assert_bh(void)
+{
 	lockdep_assert_in_rcu_read_lock_bh();
 	wants_rcu_held_bh();
+}
 
+static void __used test_rcu_assert_sched(void)
+{
 	lockdep_assert_in_rcu_read_lock_sched();
 	wants_rcu_held_sched();
 }
 
+static void __used test_rcu_assert_reader(void)
+{
+	lockdep_assert_in_rcu_reader();
+	wants_rcu_held();
+}
+
 struct test_srcu_data {
 	struct srcu_struct srcu;
 	long __rcu_guarded *data;

^ permalink raw reply	[flat|nested] 15+ messages in thread

* [PATCH v2 14/14] rcu: Introduce lockdep_assume_in_rcu_*()
  2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
                   ` (12 preceding siblings ...)
  2026-11-09 19:13 ` [PATCH v2 13/14] compiler-context-analysis: Split the RCU assertion tests Bart Van Assche
@ 2026-11-09 19:13 ` Bart Van Assche
  13 siblings, 0 replies; 15+ messages in thread
From: Bart Van Assche @ 2026-11-09 19:13 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, Will Deacon, Boqun Feng, Waiman Long, linux-kernel,
	Marco Elver, Paul E . McKenney, Christoph Hellwig,
	Bart Van Assche, Jonathan Corbet, Frederic Weisbecker,
	Neeraj Upadhyay, Joel Fernandes, Josh Triplett, Uladzislau Rezki,
	Andrew Morton, Nathan Chancellor

Remove the context assumptions from lockdep_assert_rcu_helper() and from
the !CONFIG_PROVE_RCU definitions of lockdep_assert_in_rcu_*() in
include/linux/rcupdate.h. Introduce the following macros and document
them with kernel-doc comments:
- lockdep_assume_in_rcu_read_lock(): assume RCU and call
  lockdep_assert_in_rcu_read_lock().
- lockdep_assume_in_rcu_read_lock_bh(): assume RCU and RCU_BH and call
  lockdep_assert_in_rcu_read_lock_bh().
- lockdep_assume_in_rcu_read_lock_sched(): assume RCU and RCU_SCHED and
  call lockdep_assert_in_rcu_read_lock_sched().
- lockdep_assume_in_rcu_reader(): assume RCU and call
  lockdep_assert_in_rcu_reader().
Assume RCU in the _bh() and _sched() variants because rcu_read_lock_bh()
and rcu_read_lock_sched() acquire RCU in addition to RCU_BH and
RCU_SCHED, and because __rcu_guarded pointers are guarded by RCU. Use
the lockdep_assume_ prefix for consistency with lockdep_assume_ctx_held()
and lockdep_assume_shared_ctx_held(): __assume_*() macros only inform
context analysis while lockdep_assume_*() macros also verify the
assumption at runtime.

Mention lockdep_assume_in_rcu_read_lock() in
Documentation/dev-tools/context-analysis.rst. Convert the RCU assertion
tests in lib/test_context-analysis.c to the lockdep_assume_in_rcu_*()
macros and verify in the _bh() and _sched() tests that RCU is assumed
too.

Prevent lockdep_assert_in_rcu_*() from silencing Clang context analysis
warnings in functions that lack a __must_hold_shared(RCU) annotation,
such that the compiler reports missing function annotations and checks
callers for RCU read lock ownership at compile time. This makes the RCU
assertions consistent with lockdep_assert_held*().

Verify at runtime with lockdep that the RCU read lock is held whenever
one of the lockdep_assume_in_rcu_*() macros instructs Clang context
analysis to assume RCU read lock ownership without compile-time proof.

Change lockdep_assert_rcu_helper() from __always_inline back into inline.
Commit 451c3a035609 ("rcu: Mark lockdep_assert_rcu_helper()
__always_inline") made this function __always_inline because if it was
not inlined, its callers passed the address of the RCU, RCU_BH or
RCU_SCHED context token to it. Since these tokens are not defined
anywhere, that resulted in link failures. Now that the ctx argument has
been removed from lockdep_assert_rcu_helper(), the context tokens are no
longer referenced and hence __always_inline is no longer necessary.

Cc: Marco Elver <elver@google.com>
Cc: Peter Zijlstra <peterz@infradead.org>
Cc: Paul E. McKenney <paulmck@kernel.org>
Signed-off-by: Bart Van Assche <bvanassche@acm.org>
---
 Documentation/dev-tools/context-analysis.rst | 26 ++++---
 include/linux/rcupdate.h                     | 82 +++++++++++++++++---
 lib/test_context-analysis.c                  | 10 ++-
 3 files changed, 92 insertions(+), 26 deletions(-)

diff --git a/Documentation/dev-tools/context-analysis.rst b/Documentation/dev-tools/context-analysis.rst
index 88b4ed6a182f..bc94e5bb56a5 100644
--- a/Documentation/dev-tools/context-analysis.rst
+++ b/Documentation/dev-tools/context-analysis.rst
@@ -111,18 +111,20 @@ checking): either with a ``context_unsafe(var = init)`` expression, or by
 marking small initialization functions with the ``__context_unsafe(init)``
 attribute.
 
-`lockdep_assume_ctx_held()` and `lockdep_assume_shared_ctx_held()` inform the
-compiler's context analysis that the associated synchronization primitive is
-held and verify this at runtime via Lockdep. This avoids false positives in
-complex control-flow scenarios while still checking lock ownership with Lockdep
-where static analysis is limited. For example, this is useful when a function
-doesn't *always* require a lock, making `__must_hold()` inappropriate.
-
-Lockdep assertions, such as `lockdep_assert_held()`, only perform a runtime
-check and do not inform the compiler's context analysis that a lock is held.
-For context locks that are not tracked by Lockdep, e.g. bit spinlocks, use
-`__assume_ctx_lock()` or `__assume_shared_ctx_lock()` instead. These only
-inform the compiler's context analysis and do not perform a runtime check.
+`lockdep_assume_ctx_held()`, `lockdep_assume_shared_ctx_held()` and
+`lockdep_assume_in_rcu_read_lock()` inform the compiler's context analysis that
+the associated synchronization primitive is held and verify this at runtime via
+Lockdep. This avoids false positives in complex control-flow scenarios while
+still checking lock ownership with Lockdep where static analysis is limited.
+For example, this is useful when a function doesn't *always* require a lock,
+making `__must_hold()` inappropriate.
+
+Lockdep assertions, such as `lockdep_assert_held()` and
+`lockdep_assert_in_rcu_read_lock()`, only perform a runtime check and do not
+inform the compiler's context analysis that a lock is held. For context locks
+that are not tracked by Lockdep, e.g. bit spinlocks, use `__assume_ctx_lock()`
+or `__assume_shared_ctx_lock()` instead. These only inform the compiler's
+context analysis and do not perform a runtime check.
 
 Keywords
 ~~~~~~~~
diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h
index 3f74ae6d6e1f..3effadda45a3 100644
--- a/include/linux/rcupdate.h
+++ b/include/linux/rcupdate.h
@@ -403,8 +403,7 @@ static inline void rcu_preempt_sleep_check(void) { }
 
 // See RCU_LOCKDEP_WARN() for an explanation of the double call to
 // debug_lockdep_rcu_enabled().
-static __always_inline bool lockdep_assert_rcu_helper(bool c, const struct __ctx_lock_RCU *ctx)
-	__assumes_shared_ctx_lock(RCU) __assumes_shared_ctx_lock(ctx)
+static inline bool lockdep_assert_rcu_helper(bool c)
 {
 	return debug_lockdep_rcu_enabled() &&
 	       (c || !rcu_is_watching() || !rcu_lockdep_current_cpu_online()) &&
@@ -417,7 +416,7 @@ static __always_inline bool lockdep_assert_rcu_helper(bool c, const struct __ctx
  * Splats if lockdep is enabled and there is no rcu_read_lock() in effect.
  */
 #define lockdep_assert_in_rcu_read_lock() \
-	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_lock_map), RCU))
+	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_lock_map)))
 
 /**
  * lockdep_assert_in_rcu_read_lock_bh - WARN if not protected by rcu_read_lock_bh()
@@ -427,7 +426,7 @@ static __always_inline bool lockdep_assert_rcu_helper(bool c, const struct __ctx
  * actual rcu_read_lock_bh() is required.
  */
 #define lockdep_assert_in_rcu_read_lock_bh() \
-	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_bh_lock_map), RCU_BH))
+	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_bh_lock_map)))
 
 /**
  * lockdep_assert_in_rcu_read_lock_sched - WARN if not protected by rcu_read_lock_sched()
@@ -437,7 +436,7 @@ static __always_inline bool lockdep_assert_rcu_helper(bool c, const struct __ctx
  * instead an actual rcu_read_lock_sched() is required.
  */
 #define lockdep_assert_in_rcu_read_lock_sched() \
-	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_sched_lock_map), RCU_SCHED))
+	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_sched_lock_map)))
 
 /**
  * lockdep_assert_in_rcu_reader - WARN if not within some type of RCU reader
@@ -455,20 +454,83 @@ static __always_inline bool lockdep_assert_rcu_helper(bool c, const struct __ctx
 	WARN_ON_ONCE(lockdep_assert_rcu_helper(!lock_is_held(&rcu_lock_map) &&			\
 					       !lock_is_held(&rcu_bh_lock_map) &&		\
 					       !lock_is_held(&rcu_sched_lock_map) &&		\
-					       preemptible(), RCU))
+					       preemptible()))
 
 #else /* #ifdef CONFIG_PROVE_RCU */
 
 #define RCU_LOCKDEP_WARN(c, s) do { } while (0 && (c))
 #define rcu_sleep_check() do { } while (0)
 
-#define lockdep_assert_in_rcu_read_lock() __assume_shared_ctx_lock(RCU)
-#define lockdep_assert_in_rcu_read_lock_bh() __assume_shared_ctx_lock(RCU_BH)
-#define lockdep_assert_in_rcu_read_lock_sched() __assume_shared_ctx_lock(RCU_SCHED)
-#define lockdep_assert_in_rcu_reader() __assume_shared_ctx_lock(RCU)
+#define lockdep_assert_in_rcu_read_lock() do { } while (0)
+#define lockdep_assert_in_rcu_read_lock_bh() do { } while (0)
+#define lockdep_assert_in_rcu_read_lock_sched() do { } while (0)
+#define lockdep_assert_in_rcu_reader() do { } while (0)
 
 #endif /* #else #ifdef CONFIG_PROVE_RCU */
 
+/**
+ * lockdep_assume_in_rcu_read_lock() - assume and verify RCU protection
+ *
+ * Instruct the compiler's context analysis to assume that the RCU read lock
+ * is held from this point on and verify at runtime with
+ * lockdep_assert_in_rcu_read_lock() that rcu_read_lock() is in effect. Use
+ * this macro in functions that are called with rcu_read_lock() held but for
+ * which a __must_hold_shared(RCU) annotation is not appropriate.
+ */
+#define lockdep_assume_in_rcu_read_lock()		\
+	do {						\
+		__assume_shared_ctx_lock(RCU);		\
+		lockdep_assert_in_rcu_read_lock();	\
+	} while (0)
+
+/**
+ * lockdep_assume_in_rcu_read_lock_bh() - assume and verify RCU-bh protection
+ *
+ * Instruct the compiler's context analysis to assume that both RCU and
+ * RCU_BH are held from this point on and verify at runtime with
+ * lockdep_assert_in_rcu_read_lock_bh() that rcu_read_lock_bh() is in
+ * effect. RCU is assumed too because rcu_read_lock_bh() acquires both RCU
+ * and RCU_BH, and because __rcu_guarded pointers are guarded by RCU.
+ */
+#define lockdep_assume_in_rcu_read_lock_bh()		\
+	do {						\
+		__assume_shared_ctx_lock(RCU);		\
+		__assume_shared_ctx_lock(RCU_BH);	\
+		lockdep_assert_in_rcu_read_lock_bh();	\
+	} while (0)
+
+/**
+ * lockdep_assume_in_rcu_read_lock_sched() - assume and verify RCU-sched
+ *
+ * Instruct the compiler's context analysis to assume that both RCU and
+ * RCU_SCHED are held from this point on and verify at runtime with
+ * lockdep_assert_in_rcu_read_lock_sched() that rcu_read_lock_sched() is in
+ * effect. RCU is assumed too because rcu_read_lock_sched() acquires both
+ * RCU and RCU_SCHED, and because __rcu_guarded pointers are guarded by RCU.
+ */
+#define lockdep_assume_in_rcu_read_lock_sched()			\
+	do {							\
+		__assume_shared_ctx_lock(RCU);			\
+		__assume_shared_ctx_lock(RCU_SCHED);		\
+		lockdep_assert_in_rcu_read_lock_sched();	\
+	} while (0)
+
+/**
+ * lockdep_assume_in_rcu_reader() - assume and verify any RCU reader
+ *
+ * Instruct the compiler's context analysis to assume that the RCU read lock
+ * is held from this point on and verify at runtime with
+ * lockdep_assert_in_rcu_reader() that some type of RCU reader is in effect.
+ * Unlike lockdep_assume_in_rcu_read_lock(), this macro also accepts regions of
+ * code in which preemption, softirqs or interrupts are disabled, since such
+ * regions also qualify as RCU readers.
+ */
+#define lockdep_assume_in_rcu_reader()			\
+	do {						\
+		__assume_shared_ctx_lock(RCU);		\
+		lockdep_assert_in_rcu_reader();		\
+	} while (0)
+
 /*
  * Helper functions for rcu_dereference_check(), rcu_dereference_protected()
  * and rcu_assign_pointer().  Some of these could be folded into their
diff --git a/lib/test_context-analysis.c b/lib/test_context-analysis.c
index 048b839bae92..2b5e8612c14e 100644
--- a/lib/test_context-analysis.c
+++ b/lib/test_context-analysis.c
@@ -466,25 +466,27 @@ static void __used test_rcu_lock_reentrant(void)
 
 static void __used test_rcu_assert(void)
 {
-	lockdep_assert_in_rcu_read_lock();
+	lockdep_assume_in_rcu_read_lock();
 	wants_rcu_held();
 }
 
 static void __used test_rcu_assert_bh(void)
 {
-	lockdep_assert_in_rcu_read_lock_bh();
+	lockdep_assume_in_rcu_read_lock_bh();
+	wants_rcu_held();
 	wants_rcu_held_bh();
 }
 
 static void __used test_rcu_assert_sched(void)
 {
-	lockdep_assert_in_rcu_read_lock_sched();
+	lockdep_assume_in_rcu_read_lock_sched();
+	wants_rcu_held();
 	wants_rcu_held_sched();
 }
 
 static void __used test_rcu_assert_reader(void)
 {
-	lockdep_assert_in_rcu_reader();
+	lockdep_assume_in_rcu_reader();
 	wants_rcu_held();
 }
 

^ permalink raw reply	[flat|nested] 15+ messages in thread

end of thread, other threads:[~2026-11-09 19:14 UTC | newest]

Thread overview: 15+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-11-09 19:13 [PATCH v2 00/14] Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 01/14] bit_spinlock: Fix the __bitlock() definition Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 02/14] cleanup: Disable context analysis for __free() helpers Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 03/14] tracing: Disable branch profiling for COMPILE_TEST builds Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 04/14] srcu: Remove check from srcu_dereference_check() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 05/14] futex: Annotate __attach_to_pi_owner() and fixup_pi_*owner() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 06/14] locking/rwsem: Annotate rwsem internal functions with __must_hold() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 07/14] nvme-fabrics: Annotate nvmf_lookup_transport() with __must_hold_shared() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 08/14] lockdep: Introduce lockdep_assume_ctx_held() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 09/14] kfence: Use lockdep_assume_ctx_held() in kfence_report_error() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 10/14] Bluetooth: Add lock context annotations for connection aliases Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 11/14] futex: Verify exit_mutex ownership in futex_exit_recursive() Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 12/14] lockdep: Stop lockdep_assert_held*() from assuming lock ownership Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 13/14] compiler-context-analysis: Split the RCU assertion tests Bart Van Assche
2026-11-09 19:13 ` [PATCH v2 14/14] rcu: Introduce lockdep_assume_in_rcu_*() Bart Van Assche

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®