mirror of
https://github.com/torvalds/linux.git
synced 2026-03-08 03:04:51 +01:00
locking/rwsem: Support Clang's context analysis
Add support for Clang's context analysis for rw_semaphore. Signed-off-by: Marco Elver <elver@google.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Link: https://patch.msgid.link/20251219154418.3592607-18-elver@google.com
This commit is contained in:
parent
5e256db932
commit
e4fd3be884
3 changed files with 112 additions and 26 deletions
|
|
@ -80,7 +80,7 @@ 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`).
|
||||
`bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`.
|
||||
|
||||
For context locks with an initialization function (e.g., `spin_lock_init()`),
|
||||
calling this function before initializing any guarded members or globals
|
||||
|
|
|
|||
|
|
@ -45,7 +45,7 @@
|
|||
* reduce the chance that they will share the same cacheline causing
|
||||
* cacheline bouncing problem.
|
||||
*/
|
||||
struct rw_semaphore {
|
||||
context_lock_struct(rw_semaphore) {
|
||||
atomic_long_t count;
|
||||
/*
|
||||
* Write owner or one of the read owners as well flags regarding
|
||||
|
|
@ -76,11 +76,13 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)
|
|||
}
|
||||
|
||||
static inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
WARN_ON(atomic_long_read(&sem->count) == RWSEM_UNLOCKED_VALUE);
|
||||
}
|
||||
|
||||
static inline void rwsem_assert_held_write_nolockdep(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
WARN_ON(!(atomic_long_read(&sem->count) & RWSEM_WRITER_LOCKED));
|
||||
}
|
||||
|
|
@ -119,6 +121,7 @@ do { \
|
|||
static struct lock_class_key __key; \
|
||||
\
|
||||
__init_rwsem((sem), #sem, &__key); \
|
||||
__assume_ctx_lock(sem); \
|
||||
} while (0)
|
||||
|
||||
/*
|
||||
|
|
@ -148,7 +151,7 @@ extern bool is_rwsem_reader_owned(struct rw_semaphore *sem);
|
|||
|
||||
#include <linux/rwbase_rt.h>
|
||||
|
||||
struct rw_semaphore {
|
||||
context_lock_struct(rw_semaphore) {
|
||||
struct rwbase_rt rwbase;
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
struct lockdep_map dep_map;
|
||||
|
|
@ -172,6 +175,7 @@ do { \
|
|||
static struct lock_class_key __key; \
|
||||
\
|
||||
__init_rwsem((sem), #sem, &__key); \
|
||||
__assume_ctx_lock(sem); \
|
||||
} while (0)
|
||||
|
||||
static __always_inline int rwsem_is_locked(const struct rw_semaphore *sem)
|
||||
|
|
@ -180,11 +184,13 @@ static __always_inline int rwsem_is_locked(const struct rw_semaphore *sem)
|
|||
}
|
||||
|
||||
static __always_inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
WARN_ON(!rwsem_is_locked(sem));
|
||||
}
|
||||
|
||||
static __always_inline void rwsem_assert_held_write_nolockdep(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
WARN_ON(!rw_base_is_write_locked(&sem->rwbase));
|
||||
}
|
||||
|
|
@ -202,6 +208,7 @@ static __always_inline int rwsem_is_contended(struct rw_semaphore *sem)
|
|||
*/
|
||||
|
||||
static inline void rwsem_assert_held(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_LOCKDEP))
|
||||
lockdep_assert_held(sem);
|
||||
|
|
@ -210,6 +217,7 @@ static inline void rwsem_assert_held(const struct rw_semaphore *sem)
|
|||
}
|
||||
|
||||
static inline void rwsem_assert_held_write(const struct rw_semaphore *sem)
|
||||
__assumes_ctx_lock(sem)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_LOCKDEP))
|
||||
lockdep_assert_held_write(sem);
|
||||
|
|
@ -220,48 +228,62 @@ static inline void rwsem_assert_held_write(const struct rw_semaphore *sem)
|
|||
/*
|
||||
* lock for reading
|
||||
*/
|
||||
extern void down_read(struct rw_semaphore *sem);
|
||||
extern int __must_check down_read_interruptible(struct rw_semaphore *sem);
|
||||
extern int __must_check down_read_killable(struct rw_semaphore *sem);
|
||||
extern void down_read(struct rw_semaphore *sem) __acquires_shared(sem);
|
||||
extern int __must_check down_read_interruptible(struct rw_semaphore *sem) __cond_acquires_shared(0, sem);
|
||||
extern int __must_check down_read_killable(struct rw_semaphore *sem) __cond_acquires_shared(0, sem);
|
||||
|
||||
/*
|
||||
* trylock for reading -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
extern int down_read_trylock(struct rw_semaphore *sem);
|
||||
extern int down_read_trylock(struct rw_semaphore *sem) __cond_acquires_shared(true, sem);
|
||||
|
||||
/*
|
||||
* lock for writing
|
||||
*/
|
||||
extern void down_write(struct rw_semaphore *sem);
|
||||
extern int __must_check down_write_killable(struct rw_semaphore *sem);
|
||||
extern void down_write(struct rw_semaphore *sem) __acquires(sem);
|
||||
extern int __must_check down_write_killable(struct rw_semaphore *sem) __cond_acquires(0, sem);
|
||||
|
||||
/*
|
||||
* trylock for writing -- returns 1 if successful, 0 if contention
|
||||
*/
|
||||
extern int down_write_trylock(struct rw_semaphore *sem);
|
||||
extern int down_write_trylock(struct rw_semaphore *sem) __cond_acquires(true, sem);
|
||||
|
||||
/*
|
||||
* release a read lock
|
||||
*/
|
||||
extern void up_read(struct rw_semaphore *sem);
|
||||
extern void up_read(struct rw_semaphore *sem) __releases_shared(sem);
|
||||
|
||||
/*
|
||||
* release a write lock
|
||||
*/
|
||||
extern void up_write(struct rw_semaphore *sem);
|
||||
extern void up_write(struct rw_semaphore *sem) __releases(sem);
|
||||
|
||||
DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T))
|
||||
DEFINE_GUARD_COND(rwsem_read, _try, down_read_trylock(_T))
|
||||
DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0)
|
||||
DEFINE_LOCK_GUARD_1(rwsem_read, struct rw_semaphore, down_read(_T->lock), up_read(_T->lock))
|
||||
DEFINE_LOCK_GUARD_1_COND(rwsem_read, _try, down_read_trylock(_T->lock))
|
||||
DEFINE_LOCK_GUARD_1_COND(rwsem_read, _intr, down_read_interruptible(_T->lock), _RET == 0)
|
||||
|
||||
DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T))
|
||||
DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T))
|
||||
DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0)
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_read_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read, _T)
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read_try, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_read_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read_try, _T)
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read_intr, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_read_intr_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read_intr, _T)
|
||||
|
||||
DEFINE_LOCK_GUARD_1(rwsem_write, struct rw_semaphore, down_write(_T->lock), up_write(_T->lock))
|
||||
DEFINE_LOCK_GUARD_1_COND(rwsem_write, _try, down_write_trylock(_T->lock))
|
||||
DEFINE_LOCK_GUARD_1_COND(rwsem_write, _kill, down_write_killable(_T->lock), _RET == 0)
|
||||
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write, __acquires(_T), __releases(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_write_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write, _T)
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_try, __acquires(_T), __releases(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_write_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write_try, _T)
|
||||
DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_kill, __acquires(_T), __releases(*(struct rw_semaphore **)_T))
|
||||
#define class_rwsem_write_kill_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write_kill, _T)
|
||||
|
||||
/*
|
||||
* downgrade write lock to read lock
|
||||
*/
|
||||
extern void downgrade_write(struct rw_semaphore *sem);
|
||||
extern void downgrade_write(struct rw_semaphore *sem) __releases(sem) __acquires_shared(sem);
|
||||
|
||||
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
||||
/*
|
||||
|
|
@ -277,11 +299,11 @@ extern void downgrade_write(struct rw_semaphore *sem);
|
|||
* lockdep_set_class() at lock initialization time.
|
||||
* See Documentation/locking/lockdep-design.rst for more details.)
|
||||
*/
|
||||
extern void down_read_nested(struct rw_semaphore *sem, int subclass);
|
||||
extern int __must_check down_read_killable_nested(struct rw_semaphore *sem, int subclass);
|
||||
extern void down_write_nested(struct rw_semaphore *sem, int subclass);
|
||||
extern int down_write_killable_nested(struct rw_semaphore *sem, int subclass);
|
||||
extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock);
|
||||
extern void down_read_nested(struct rw_semaphore *sem, int subclass) __acquires_shared(sem);
|
||||
extern int __must_check down_read_killable_nested(struct rw_semaphore *sem, int subclass) __cond_acquires_shared(0, sem);
|
||||
extern void down_write_nested(struct rw_semaphore *sem, int subclass) __acquires(sem);
|
||||
extern int down_write_killable_nested(struct rw_semaphore *sem, int subclass) __cond_acquires(0, sem);
|
||||
extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock) __acquires(sem);
|
||||
|
||||
# define down_write_nest_lock(sem, nest_lock) \
|
||||
do { \
|
||||
|
|
@ -295,8 +317,8 @@ do { \
|
|||
* [ This API should be avoided as much as possible - the
|
||||
* proper abstraction for this case is completions. ]
|
||||
*/
|
||||
extern void down_read_non_owner(struct rw_semaphore *sem);
|
||||
extern void up_read_non_owner(struct rw_semaphore *sem);
|
||||
extern void down_read_non_owner(struct rw_semaphore *sem) __acquires_shared(sem);
|
||||
extern void up_read_non_owner(struct rw_semaphore *sem) __releases_shared(sem);
|
||||
#else
|
||||
# define down_read_nested(sem, subclass) down_read(sem)
|
||||
# define down_read_killable_nested(sem, subclass) down_read_killable(sem)
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@
|
|||
#include <linux/build_bug.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/rcupdate.h>
|
||||
#include <linux/rwsem.h>
|
||||
#include <linux/seqlock.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/srcu.h>
|
||||
|
|
@ -262,6 +263,69 @@ static void __used test_seqlock_scoped(struct test_seqlock_data *d)
|
|||
}
|
||||
}
|
||||
|
||||
struct test_rwsem_data {
|
||||
struct rw_semaphore sem;
|
||||
int counter __guarded_by(&sem);
|
||||
};
|
||||
|
||||
static void __used test_rwsem_init(struct test_rwsem_data *d)
|
||||
{
|
||||
init_rwsem(&d->sem);
|
||||
d->counter = 0;
|
||||
}
|
||||
|
||||
static void __used test_rwsem_reader(struct test_rwsem_data *d)
|
||||
{
|
||||
down_read(&d->sem);
|
||||
(void)d->counter;
|
||||
up_read(&d->sem);
|
||||
|
||||
if (down_read_trylock(&d->sem)) {
|
||||
(void)d->counter;
|
||||
up_read(&d->sem);
|
||||
}
|
||||
}
|
||||
|
||||
static void __used test_rwsem_writer(struct test_rwsem_data *d)
|
||||
{
|
||||
down_write(&d->sem);
|
||||
d->counter++;
|
||||
up_write(&d->sem);
|
||||
|
||||
down_write(&d->sem);
|
||||
d->counter++;
|
||||
downgrade_write(&d->sem);
|
||||
(void)d->counter;
|
||||
up_read(&d->sem);
|
||||
|
||||
if (down_write_trylock(&d->sem)) {
|
||||
d->counter++;
|
||||
up_write(&d->sem);
|
||||
}
|
||||
}
|
||||
|
||||
static void __used test_rwsem_assert(struct test_rwsem_data *d)
|
||||
{
|
||||
rwsem_assert_held_nolockdep(&d->sem);
|
||||
d->counter++;
|
||||
}
|
||||
|
||||
static void __used test_rwsem_guard(struct test_rwsem_data *d)
|
||||
{
|
||||
{ guard(rwsem_read)(&d->sem); (void)d->counter; }
|
||||
{ guard(rwsem_write)(&d->sem); d->counter++; }
|
||||
}
|
||||
|
||||
static void __used test_rwsem_cond_guard(struct test_rwsem_data *d)
|
||||
{
|
||||
scoped_cond_guard(rwsem_read_try, return, &d->sem) {
|
||||
(void)d->counter;
|
||||
}
|
||||
scoped_cond_guard(rwsem_write_try, return, &d->sem) {
|
||||
d->counter++;
|
||||
}
|
||||
}
|
||||
|
||||
struct test_bit_spinlock_data {
|
||||
unsigned long bits;
|
||||
int counter __guarded_by(__bitlock(3, &bits));
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue