Commit 370f0a34 authored by Marco Elver's avatar Marco Elver Committed by Peter Zijlstra
Browse files

locking/mutex: Support Clang's context analysis



Add support for Clang's context analysis for mutex.

Signed-off-by: default avatarMarco Elver <elver@google.com>
Signed-off-by: default avatarPeter Zijlstra (Intel) <peterz@infradead.org>
Link: https://patch.msgid.link/20251219154418.3592607-11-elver@google.com
parent 38f1311a
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -79,7 +79,7 @@ Supported Kernel Primitives
~~~~~~~~~~~~~~~~~~~~~~~~~~~

Currently the following synchronization primitives are supported:
`raw_spinlock_t`, `spinlock_t`, `rwlock_t`.
`raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`.

For context locks with an initialization function (e.g., `spin_lock_init()`),
calling this function before initializing any guarded members or globals
+23 −15
Original line number Diff line number Diff line
@@ -62,6 +62,7 @@ do { \
	static struct lock_class_key __key;				\
									\
	__mutex_init((mutex), #mutex, &__key);				\
	__assume_ctx_lock(mutex);					\
} while (0)

/**
@@ -182,13 +183,13 @@ static inline int __must_check __devm_mutex_init(struct device *dev, struct mute
 * Also see Documentation/locking/mutex-design.rst.
 */
#ifdef CONFIG_DEBUG_LOCK_ALLOC
extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass);
extern void mutex_lock_nested(struct mutex *lock, unsigned int subclass) __acquires(lock);
extern void _mutex_lock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock);
extern int __must_check mutex_lock_interruptible_nested(struct mutex *lock,
					unsigned int subclass);
					unsigned int subclass) __cond_acquires(0, lock);
extern int __must_check _mutex_lock_killable(struct mutex *lock,
		unsigned int subclass, struct lockdep_map *nest_lock);
extern void mutex_lock_io_nested(struct mutex *lock, unsigned int subclass);
		unsigned int subclass, struct lockdep_map *nest_lock) __cond_acquires(0, lock);
extern void mutex_lock_io_nested(struct mutex *lock, unsigned int subclass) __acquires(lock);

#define mutex_lock(lock) mutex_lock_nested(lock, 0)
#define mutex_lock_interruptible(lock) mutex_lock_interruptible_nested(lock, 0)
@@ -211,10 +212,10 @@ do { \
	_mutex_lock_killable(lock, subclass, NULL)

#else
extern void mutex_lock(struct mutex *lock);
extern int __must_check mutex_lock_interruptible(struct mutex *lock);
extern int __must_check mutex_lock_killable(struct mutex *lock);
extern void mutex_lock_io(struct mutex *lock);
extern void mutex_lock(struct mutex *lock) __acquires(lock);
extern int __must_check mutex_lock_interruptible(struct mutex *lock) __cond_acquires(0, lock);
extern int __must_check mutex_lock_killable(struct mutex *lock) __cond_acquires(0, lock);
extern void mutex_lock_io(struct mutex *lock) __acquires(lock);

# define mutex_lock_nested(lock, subclass) mutex_lock(lock)
# define mutex_lock_interruptible_nested(lock, subclass) mutex_lock_interruptible(lock)
@@ -232,7 +233,7 @@ extern void mutex_lock_io(struct mutex *lock);
 */

#ifdef CONFIG_DEBUG_LOCK_ALLOC
extern int _mutex_trylock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock);
extern int _mutex_trylock_nest_lock(struct mutex *lock, struct lockdep_map *nest_lock) __cond_acquires(true, lock);

#define mutex_trylock_nest_lock(lock, nest_lock)		\
(								\
@@ -242,17 +243,24 @@ extern int _mutex_trylock_nest_lock(struct mutex *lock, struct lockdep_map *nest

#define mutex_trylock(lock) _mutex_trylock_nest_lock(lock, NULL)
#else
extern int mutex_trylock(struct mutex *lock);
extern int mutex_trylock(struct mutex *lock) __cond_acquires(true, lock);
#define mutex_trylock_nest_lock(lock, nest_lock) mutex_trylock(lock)
#endif

extern void mutex_unlock(struct mutex *lock);
extern void mutex_unlock(struct mutex *lock) __releases(lock);

extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock);
extern int atomic_dec_and_mutex_lock(atomic_t *cnt, struct mutex *lock) __cond_acquires(true, lock);

DEFINE_GUARD(mutex, struct mutex *, mutex_lock(_T), mutex_unlock(_T))
DEFINE_GUARD_COND(mutex, _try, mutex_trylock(_T))
DEFINE_GUARD_COND(mutex, _intr, mutex_lock_interruptible(_T), _RET == 0)
DEFINE_LOCK_GUARD_1(mutex, struct mutex, mutex_lock(_T->lock), mutex_unlock(_T->lock))
DEFINE_LOCK_GUARD_1_COND(mutex, _try, mutex_trylock(_T->lock))
DEFINE_LOCK_GUARD_1_COND(mutex, _intr, mutex_lock_interruptible(_T->lock), _RET == 0)

DECLARE_LOCK_GUARD_1_ATTRS(mutex,	__acquires(_T), __releases(*(struct mutex **)_T))
#define class_mutex_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex, _T)
DECLARE_LOCK_GUARD_1_ATTRS(mutex_try,	__acquires(_T), __releases(*(struct mutex **)_T))
#define class_mutex_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex_try, _T)
DECLARE_LOCK_GUARD_1_ATTRS(mutex_intr,	__acquires(_T), __releases(*(struct mutex **)_T))
#define class_mutex_intr_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(mutex_intr, _T)

extern unsigned long mutex_get_owner(struct mutex *lock);

+2 −2
Original line number Diff line number Diff line
@@ -38,7 +38,7 @@
 * - detects multi-task circular deadlocks and prints out all affected
 *   locks and tasks (and only those tasks)
 */
struct mutex {
context_lock_struct(mutex) {
	atomic_long_t		owner;
	raw_spinlock_t		wait_lock;
#ifdef CONFIG_MUTEX_SPIN_ON_OWNER
@@ -59,7 +59,7 @@ struct mutex {
 */
#include <linux/rtmutex.h>

struct mutex {
context_lock_struct(mutex) {
	struct rt_mutex_base	rtmutex;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
+64 −0
Original line number Diff line number Diff line
@@ -5,6 +5,7 @@
 */

#include <linux/build_bug.h>
#include <linux/mutex.h>
#include <linux/spinlock.h>

/*
@@ -144,3 +145,66 @@ TEST_SPINLOCK_COMMON(read_lock,
		     read_unlock,
		     read_trylock,
		     TEST_OP_RO);

struct test_mutex_data {
	struct mutex mtx;
	int counter __guarded_by(&mtx);
};

static void __used test_mutex_init(struct test_mutex_data *d)
{
	mutex_init(&d->mtx);
	d->counter = 0;
}

static void __used test_mutex_lock(struct test_mutex_data *d)
{
	mutex_lock(&d->mtx);
	d->counter++;
	mutex_unlock(&d->mtx);
	mutex_lock_io(&d->mtx);
	d->counter++;
	mutex_unlock(&d->mtx);
}

static void __used test_mutex_trylock(struct test_mutex_data *d, atomic_t *a)
{
	if (!mutex_lock_interruptible(&d->mtx)) {
		d->counter++;
		mutex_unlock(&d->mtx);
	}
	if (!mutex_lock_killable(&d->mtx)) {
		d->counter++;
		mutex_unlock(&d->mtx);
	}
	if (mutex_trylock(&d->mtx)) {
		d->counter++;
		mutex_unlock(&d->mtx);
	}
	if (atomic_dec_and_mutex_lock(a, &d->mtx)) {
		d->counter++;
		mutex_unlock(&d->mtx);
	}
}

static void __used test_mutex_assert(struct test_mutex_data *d)
{
	lockdep_assert_held(&d->mtx);
	d->counter++;
}

static void __used test_mutex_guard(struct test_mutex_data *d)
{
	guard(mutex)(&d->mtx);
	d->counter++;
}

static void __used test_mutex_cond_guard(struct test_mutex_data *d)
{
	scoped_cond_guard(mutex_try, return, &d->mtx) {
		d->counter++;
	}
	scoped_cond_guard(mutex_intr, return, &d->mtx) {
		d->counter++;
	}
}