mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
locking/ww_mutex: Support Clang's context analysis
Add support for Clang's context analysis for ww_mutex. The programming model for ww_mutex is subtly more complex than other locking primitives when using ww_acquire_ctx. Encoding the respective pre-conditions for ww_mutex lock/unlock based on ww_acquire_ctx state using Clang's context analysis makes incorrect use of the API harder. Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20251219154418.3592607-21-elver@google.com
This commit is contained in:
parent
d3febf16de
commit
47907461e4
3 changed files with 87 additions and 7 deletions
|
|
@ -80,7 +80,8 @@ Supported Kernel Primitives
|
|||
|
||||
Currently the following synchronization primitives are supported:
|
||||
`raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`, `seqlock_t`,
|
||||
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`.
|
||||
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`,
|
||||
`ww_mutex`.
|
||||
|
||||
For context locks with an initialization function (e.g., `spin_lock_init()`),
|
||||
calling this function before initializing any guarded members or globals
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ struct ww_class {
|
|||
unsigned int is_wait_die;
|
||||
};
|
||||
|
||||
struct ww_mutex {
|
||||
context_lock_struct(ww_mutex) {
|
||||
struct WW_MUTEX_BASE base;
|
||||
struct ww_acquire_ctx *ctx;
|
||||
#ifdef DEBUG_WW_MUTEXES
|
||||
|
|
@ -52,7 +52,7 @@ struct ww_mutex {
|
|||
#endif
|
||||
};
|
||||
|
||||
struct ww_acquire_ctx {
|
||||
context_lock_struct(ww_acquire_ctx) {
|
||||
struct task_struct *task;
|
||||
unsigned long stamp;
|
||||
unsigned int acquired;
|
||||
|
|
@ -107,6 +107,7 @@ struct ww_acquire_ctx {
|
|||
*/
|
||||
static inline void ww_mutex_init(struct ww_mutex *lock,
|
||||
struct ww_class *ww_class)
|
||||
__assumes_ctx_lock(lock)
|
||||
{
|
||||
ww_mutex_base_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key);
|
||||
lock->ctx = NULL;
|
||||
|
|
@ -141,6 +142,7 @@ static inline void ww_mutex_init(struct ww_mutex *lock,
|
|||
*/
|
||||
static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
|
||||
struct ww_class *ww_class)
|
||||
__acquires(ctx) __no_context_analysis
|
||||
{
|
||||
ctx->task = current;
|
||||
ctx->stamp = atomic_long_inc_return_relaxed(&ww_class->stamp);
|
||||
|
|
@ -179,6 +181,7 @@ static inline void ww_acquire_init(struct ww_acquire_ctx *ctx,
|
|||
* data structures.
|
||||
*/
|
||||
static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
|
||||
__releases(ctx) __acquires_shared(ctx) __no_context_analysis
|
||||
{
|
||||
#ifdef DEBUG_WW_MUTEXES
|
||||
lockdep_assert_held(ctx);
|
||||
|
|
@ -196,6 +199,7 @@ static inline void ww_acquire_done(struct ww_acquire_ctx *ctx)
|
|||
* mutexes have been released with ww_mutex_unlock.
|
||||
*/
|
||||
static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
|
||||
__releases_shared(ctx) __no_context_analysis
|
||||
{
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
mutex_release(&ctx->first_lock_dep_map, _THIS_IP_);
|
||||
|
|
@ -245,7 +249,8 @@ static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx)
|
|||
*
|
||||
* A mutex acquired with this function must be released with ww_mutex_unlock.
|
||||
*/
|
||||
extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx);
|
||||
extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
|
||||
__cond_acquires(0, lock) __must_hold(ctx);
|
||||
|
||||
/**
|
||||
* ww_mutex_lock_interruptible - acquire the w/w mutex, interruptible
|
||||
|
|
@ -278,7 +283,8 @@ extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acq
|
|||
* A mutex acquired with this function must be released with ww_mutex_unlock.
|
||||
*/
|
||||
extern int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
|
||||
struct ww_acquire_ctx *ctx);
|
||||
struct ww_acquire_ctx *ctx)
|
||||
__cond_acquires(0, lock) __must_hold(ctx);
|
||||
|
||||
/**
|
||||
* ww_mutex_lock_slow - slowpath acquiring of the w/w mutex
|
||||
|
|
@ -305,6 +311,7 @@ extern int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock,
|
|||
*/
|
||||
static inline void
|
||||
ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
|
||||
__acquires(lock) __must_hold(ctx) __no_context_analysis
|
||||
{
|
||||
int ret;
|
||||
#ifdef DEBUG_WW_MUTEXES
|
||||
|
|
@ -342,6 +349,7 @@ ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx)
|
|||
static inline int __must_check
|
||||
ww_mutex_lock_slow_interruptible(struct ww_mutex *lock,
|
||||
struct ww_acquire_ctx *ctx)
|
||||
__cond_acquires(0, lock) __must_hold(ctx)
|
||||
{
|
||||
#ifdef DEBUG_WW_MUTEXES
|
||||
DEBUG_LOCKS_WARN_ON(!ctx->contending_lock);
|
||||
|
|
@ -349,10 +357,11 @@ ww_mutex_lock_slow_interruptible(struct ww_mutex *lock,
|
|||
return ww_mutex_lock_interruptible(lock, ctx);
|
||||
}
|
||||
|
||||
extern void ww_mutex_unlock(struct ww_mutex *lock);
|
||||
extern void ww_mutex_unlock(struct ww_mutex *lock) __releases(lock);
|
||||
|
||||
extern int __must_check ww_mutex_trylock(struct ww_mutex *lock,
|
||||
struct ww_acquire_ctx *ctx);
|
||||
struct ww_acquire_ctx *ctx)
|
||||
__cond_acquires(true, lock) __must_hold(ctx);
|
||||
|
||||
/***
|
||||
* ww_mutex_destroy - mark a w/w mutex unusable
|
||||
|
|
@ -363,6 +372,7 @@ extern int __must_check ww_mutex_trylock(struct ww_mutex *lock,
|
|||
* this function is called.
|
||||
*/
|
||||
static inline void ww_mutex_destroy(struct ww_mutex *lock)
|
||||
__must_not_hold(lock)
|
||||
{
|
||||
#ifndef CONFIG_PREEMPT_RT
|
||||
mutex_destroy(&lock->base);
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/seqlock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/srcu.h>
|
||||
#include <linux/ww_mutex.h>
|
||||
|
||||
/*
|
||||
* Test that helper macros work as expected.
|
||||
|
|
@ -531,3 +532,71 @@ static void __used test_local_trylock(void)
|
|||
local_unlock(&test_local_trylock_data.lock);
|
||||
}
|
||||
}
|
||||
|
||||
static DEFINE_WD_CLASS(ww_class);
|
||||
|
||||
struct test_ww_mutex_data {
|
||||
struct ww_mutex mtx;
|
||||
int counter __guarded_by(&mtx);
|
||||
};
|
||||
|
||||
static void __used test_ww_mutex_init(struct test_ww_mutex_data *d)
|
||||
{
|
||||
ww_mutex_init(&d->mtx, &ww_class);
|
||||
d->counter = 0;
|
||||
}
|
||||
|
||||
static void __used test_ww_mutex_lock_noctx(struct test_ww_mutex_data *d)
|
||||
{
|
||||
if (!ww_mutex_lock(&d->mtx, NULL)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
if (!ww_mutex_lock_interruptible(&d->mtx, NULL)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
if (ww_mutex_trylock(&d->mtx, NULL)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
ww_mutex_lock_slow(&d->mtx, NULL);
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
|
||||
ww_mutex_destroy(&d->mtx);
|
||||
}
|
||||
|
||||
static void __used test_ww_mutex_lock_ctx(struct test_ww_mutex_data *d)
|
||||
{
|
||||
struct ww_acquire_ctx ctx;
|
||||
|
||||
ww_acquire_init(&ctx, &ww_class);
|
||||
|
||||
if (!ww_mutex_lock(&d->mtx, &ctx)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
if (!ww_mutex_lock_interruptible(&d->mtx, &ctx)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
if (ww_mutex_trylock(&d->mtx, &ctx)) {
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
}
|
||||
|
||||
ww_mutex_lock_slow(&d->mtx, &ctx);
|
||||
d->counter++;
|
||||
ww_mutex_unlock(&d->mtx);
|
||||
|
||||
ww_acquire_done(&ctx);
|
||||
ww_acquire_fini(&ctx);
|
||||
|
||||
ww_mutex_destroy(&d->mtx);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue