mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:44:45 +01:00
locking/mutex: Support Clang's context analysis
Add support for Clang's context analysis for mutex. Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20251219154418.3592607-11-elver@google.com
This commit is contained in:
parent
38f1311a22
commit
370f0a345a
4 changed files with 90 additions and 18 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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++;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue