[PATCH 08/14] lockdep: Introduce lockdep_assume_ctx_held()

From: Bart Van Assche

Date: Mon Nov 02 2026 - 17:18:02 EST


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. These macros call respectively
__assume_ctx_lock() and __assume_shared_ctx_lock() and also verify at
runtime with lockdep 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().

This patch prepares for removing the context assumptions from
lockdep_assert_held*().

Cc: Marco Elver <elver@xxxxxxxxxx>
Cc: Peter Zijlstra <peterz@xxxxxxxxxxxxx>
Signed-off-by: Bart Van Assche <bvanassche@xxxxxxx>
---
include/linux/lockdep.h | 59 +++++++++++++++++++++++++++++++++++++
lib/test_context-analysis.c | 16 ++++++++--
2 files changed, 73 insertions(+), 2 deletions(-)

diff --git a/include/linux/lockdep.h b/include/linux/lockdep.h
index a6451ecbbe9a..f774b22a35c5 100644
--- a/include/linux/lockdep.h
+++ b/include/linux/lockdep.h
@@ -412,6 +412,65 @@ 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(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() that @var is held. 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.
+ *
+ * Since lockdep_assert_held() is used, no lockdep warning is reported if
+ * @var is held for writing instead of for reading. Holding a lock
+ * exclusively implies that shared access is allowed. Use
+ * lockdep_assume_ctx_held() if exclusive access is required.
+ *
+ * @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; }