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

locking/rwlock, spinlock: Support Clang's context analysis



Add support for Clang's context analysis for raw_spinlock_t,
spinlock_t, and rwlock. This wholesale conversion is required because
all three of them are interdependent.

To avoid warnings in constructors, the initialization functions mark a
lock as acquired when initialized before guarded variables.

The test verifies that common patterns do not generate false positives.

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-9-elver@google.com
parent 7c451541
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -78,7 +78,8 @@ More details are also documented `here
Supported Kernel Primitives
~~~~~~~~~~~~~~~~~~~~~~~~~~~

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

For context locks with an initialization function (e.g., `spin_lock_init()`),
calling this function before initializing any guarded members or globals
+13 −12
Original line number Diff line number Diff line
@@ -22,23 +22,24 @@ do { \
	static struct lock_class_key __key;			\
								\
	__rwlock_init((lock), #lock, &__key);			\
	__assume_ctx_lock(lock);				\
} while (0)
#else
# define rwlock_init(lock)					\
	do { *(lock) = __RW_LOCK_UNLOCKED(lock); } while (0)
	do { *(lock) = __RW_LOCK_UNLOCKED(lock); __assume_ctx_lock(lock); } while (0)
#endif

#ifdef CONFIG_DEBUG_SPINLOCK
 extern void do_raw_read_lock(rwlock_t *lock) __acquires(lock);
 extern void do_raw_read_lock(rwlock_t *lock) __acquires_shared(lock);
 extern int do_raw_read_trylock(rwlock_t *lock);
 extern void do_raw_read_unlock(rwlock_t *lock) __releases(lock);
 extern void do_raw_read_unlock(rwlock_t *lock) __releases_shared(lock);
 extern void do_raw_write_lock(rwlock_t *lock) __acquires(lock);
 extern int do_raw_write_trylock(rwlock_t *lock);
 extern void do_raw_write_unlock(rwlock_t *lock) __releases(lock);
#else
# define do_raw_read_lock(rwlock)	do {__acquire(lock); arch_read_lock(&(rwlock)->raw_lock); } while (0)
# define do_raw_read_lock(rwlock)	do {__acquire_shared(lock); arch_read_lock(&(rwlock)->raw_lock); } while (0)
# define do_raw_read_trylock(rwlock)	arch_read_trylock(&(rwlock)->raw_lock)
# define do_raw_read_unlock(rwlock)	do {arch_read_unlock(&(rwlock)->raw_lock); __release(lock); } while (0)
# define do_raw_read_unlock(rwlock)	do {arch_read_unlock(&(rwlock)->raw_lock); __release_shared(lock); } while (0)
# define do_raw_write_lock(rwlock)	do {__acquire(lock); arch_write_lock(&(rwlock)->raw_lock); } while (0)
# define do_raw_write_trylock(rwlock)	arch_write_trylock(&(rwlock)->raw_lock)
# define do_raw_write_unlock(rwlock)	do {arch_write_unlock(&(rwlock)->raw_lock); __release(lock); } while (0)
@@ -49,7 +50,7 @@ do { \
 * regardless of whether CONFIG_SMP or CONFIG_PREEMPT are set. The various
 * methods are defined as nops in the case they are not required.
 */
#define read_trylock(lock)	__cond_lock(lock, _raw_read_trylock(lock))
#define read_trylock(lock)	__cond_lock_shared(lock, _raw_read_trylock(lock))
#define write_trylock(lock)	__cond_lock(lock, _raw_write_trylock(lock))

#define write_lock(lock)	_raw_write_lock(lock)
@@ -113,11 +114,11 @@ do { \
#define write_unlock_bh(lock)		_raw_write_unlock_bh(lock)

#define write_trylock_irqsave(lock, flags)		\
({ \
	__cond_lock(lock, ({				\
		local_irq_save(flags);			\
	write_trylock(lock) ? \
		_raw_write_trylock(lock) ?		\
		1 : ({ local_irq_restore(flags); 0; });	\
})
	}))

#ifdef arch_rwlock_is_contended
#define rwlock_is_contended(lock) \
+23 −6
Original line number Diff line number Diff line
@@ -15,12 +15,12 @@
 * Released under the General Public License (GPL).
 */

void __lockfunc _raw_read_lock(rwlock_t *lock)		__acquires(lock);
void __lockfunc _raw_read_lock(rwlock_t *lock)		__acquires_shared(lock);
void __lockfunc _raw_write_lock(rwlock_t *lock)		__acquires(lock);
void __lockfunc _raw_write_lock_nested(rwlock_t *lock, int subclass)	__acquires(lock);
void __lockfunc _raw_read_lock_bh(rwlock_t *lock)	__acquires(lock);
void __lockfunc _raw_read_lock_bh(rwlock_t *lock)	__acquires_shared(lock);
void __lockfunc _raw_write_lock_bh(rwlock_t *lock)	__acquires(lock);
void __lockfunc _raw_read_lock_irq(rwlock_t *lock)	__acquires(lock);
void __lockfunc _raw_read_lock_irq(rwlock_t *lock)	__acquires_shared(lock);
void __lockfunc _raw_write_lock_irq(rwlock_t *lock)	__acquires(lock);
unsigned long __lockfunc _raw_read_lock_irqsave(rwlock_t *lock)
							__acquires(lock);
@@ -28,11 +28,11 @@ unsigned long __lockfunc _raw_write_lock_irqsave(rwlock_t *lock)
							__acquires(lock);
int __lockfunc _raw_read_trylock(rwlock_t *lock);
int __lockfunc _raw_write_trylock(rwlock_t *lock);
void __lockfunc _raw_read_unlock(rwlock_t *lock)	__releases(lock);
void __lockfunc _raw_read_unlock(rwlock_t *lock)	__releases_shared(lock);
void __lockfunc _raw_write_unlock(rwlock_t *lock)	__releases(lock);
void __lockfunc _raw_read_unlock_bh(rwlock_t *lock)	__releases(lock);
void __lockfunc _raw_read_unlock_bh(rwlock_t *lock)	__releases_shared(lock);
void __lockfunc _raw_write_unlock_bh(rwlock_t *lock)	__releases(lock);
void __lockfunc _raw_read_unlock_irq(rwlock_t *lock)	__releases(lock);
void __lockfunc _raw_read_unlock_irq(rwlock_t *lock)	__releases_shared(lock);
void __lockfunc _raw_write_unlock_irq(rwlock_t *lock)	__releases(lock);
void __lockfunc
_raw_read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
@@ -145,6 +145,7 @@ static inline int __raw_write_trylock(rwlock_t *lock)
#if !defined(CONFIG_GENERIC_LOCKBREAK) || defined(CONFIG_DEBUG_LOCK_ALLOC)

static inline void __raw_read_lock(rwlock_t *lock)
	__acquires_shared(lock) __no_context_analysis
{
	preempt_disable();
	rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
@@ -152,6 +153,7 @@ static inline void __raw_read_lock(rwlock_t *lock)
}

static inline unsigned long __raw_read_lock_irqsave(rwlock_t *lock)
	__acquires_shared(lock) __no_context_analysis
{
	unsigned long flags;

@@ -163,6 +165,7 @@ static inline unsigned long __raw_read_lock_irqsave(rwlock_t *lock)
}

static inline void __raw_read_lock_irq(rwlock_t *lock)
	__acquires_shared(lock) __no_context_analysis
{
	local_irq_disable();
	preempt_disable();
@@ -171,6 +174,7 @@ static inline void __raw_read_lock_irq(rwlock_t *lock)
}

static inline void __raw_read_lock_bh(rwlock_t *lock)
	__acquires_shared(lock) __no_context_analysis
{
	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
	rwlock_acquire_read(&lock->dep_map, 0, 0, _RET_IP_);
@@ -178,6 +182,7 @@ static inline void __raw_read_lock_bh(rwlock_t *lock)
}

static inline unsigned long __raw_write_lock_irqsave(rwlock_t *lock)
	__acquires(lock) __no_context_analysis
{
	unsigned long flags;

@@ -189,6 +194,7 @@ static inline unsigned long __raw_write_lock_irqsave(rwlock_t *lock)
}

static inline void __raw_write_lock_irq(rwlock_t *lock)
	__acquires(lock) __no_context_analysis
{
	local_irq_disable();
	preempt_disable();
@@ -197,6 +203,7 @@ static inline void __raw_write_lock_irq(rwlock_t *lock)
}

static inline void __raw_write_lock_bh(rwlock_t *lock)
	__acquires(lock) __no_context_analysis
{
	__local_bh_disable_ip(_RET_IP_, SOFTIRQ_LOCK_OFFSET);
	rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
@@ -204,6 +211,7 @@ static inline void __raw_write_lock_bh(rwlock_t *lock)
}

static inline void __raw_write_lock(rwlock_t *lock)
	__acquires(lock) __no_context_analysis
{
	preempt_disable();
	rwlock_acquire(&lock->dep_map, 0, 0, _RET_IP_);
@@ -211,6 +219,7 @@ static inline void __raw_write_lock(rwlock_t *lock)
}

static inline void __raw_write_lock_nested(rwlock_t *lock, int subclass)
	__acquires(lock) __no_context_analysis
{
	preempt_disable();
	rwlock_acquire(&lock->dep_map, subclass, 0, _RET_IP_);
@@ -220,6 +229,7 @@ static inline void __raw_write_lock_nested(rwlock_t *lock, int subclass)
#endif /* !CONFIG_GENERIC_LOCKBREAK || CONFIG_DEBUG_LOCK_ALLOC */

static inline void __raw_write_unlock(rwlock_t *lock)
	__releases(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_write_unlock(lock);
@@ -227,6 +237,7 @@ static inline void __raw_write_unlock(rwlock_t *lock)
}

static inline void __raw_read_unlock(rwlock_t *lock)
	__releases_shared(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_read_unlock(lock);
@@ -235,6 +246,7 @@ static inline void __raw_read_unlock(rwlock_t *lock)

static inline void
__raw_read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
	__releases_shared(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_read_unlock(lock);
@@ -243,6 +255,7 @@ __raw_read_unlock_irqrestore(rwlock_t *lock, unsigned long flags)
}

static inline void __raw_read_unlock_irq(rwlock_t *lock)
	__releases_shared(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_read_unlock(lock);
@@ -251,6 +264,7 @@ static inline void __raw_read_unlock_irq(rwlock_t *lock)
}

static inline void __raw_read_unlock_bh(rwlock_t *lock)
	__releases_shared(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_read_unlock(lock);
@@ -259,6 +273,7 @@ static inline void __raw_read_unlock_bh(rwlock_t *lock)

static inline void __raw_write_unlock_irqrestore(rwlock_t *lock,
					     unsigned long flags)
	__releases(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_write_unlock(lock);
@@ -267,6 +282,7 @@ static inline void __raw_write_unlock_irqrestore(rwlock_t *lock,
}

static inline void __raw_write_unlock_irq(rwlock_t *lock)
	__releases(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_write_unlock(lock);
@@ -275,6 +291,7 @@ static inline void __raw_write_unlock_irq(rwlock_t *lock)
}

static inline void __raw_write_unlock_bh(rwlock_t *lock)
	__releases(lock)
{
	rwlock_release(&lock->dep_map, _RET_IP_);
	do_raw_write_unlock(lock);
+24 −11
Original line number Diff line number Diff line
@@ -22,28 +22,32 @@ do { \
							\
	init_rwbase_rt(&(rwl)->rwbase);			\
	__rt_rwlock_init(rwl, #rwl, &__key);		\
	__assume_ctx_lock(rwl);				\
} while (0)

extern void rt_read_lock(rwlock_t *rwlock)	__acquires(rwlock);
extern void rt_read_lock(rwlock_t *rwlock)	__acquires_shared(rwlock);
extern int rt_read_trylock(rwlock_t *rwlock);
extern void rt_read_unlock(rwlock_t *rwlock)	__releases(rwlock);
extern void rt_read_unlock(rwlock_t *rwlock)	__releases_shared(rwlock);
extern void rt_write_lock(rwlock_t *rwlock)	__acquires(rwlock);
extern void rt_write_lock_nested(rwlock_t *rwlock, int subclass)	__acquires(rwlock);
extern int rt_write_trylock(rwlock_t *rwlock);
extern void rt_write_unlock(rwlock_t *rwlock)	__releases(rwlock);

static __always_inline void read_lock(rwlock_t *rwlock)
	__acquires_shared(rwlock)
{
	rt_read_lock(rwlock);
}

static __always_inline void read_lock_bh(rwlock_t *rwlock)
	__acquires_shared(rwlock)
{
	local_bh_disable();
	rt_read_lock(rwlock);
}

static __always_inline void read_lock_irq(rwlock_t *rwlock)
	__acquires_shared(rwlock)
{
	rt_read_lock(rwlock);
}
@@ -55,37 +59,43 @@ static __always_inline void read_lock_irq(rwlock_t *rwlock)
		flags = 0;				\
	} while (0)

#define read_trylock(lock)	__cond_lock(lock, rt_read_trylock(lock))
#define read_trylock(lock)	__cond_lock_shared(lock, rt_read_trylock(lock))

static __always_inline void read_unlock(rwlock_t *rwlock)
	__releases_shared(rwlock)
{
	rt_read_unlock(rwlock);
}

static __always_inline void read_unlock_bh(rwlock_t *rwlock)
	__releases_shared(rwlock)
{
	rt_read_unlock(rwlock);
	local_bh_enable();
}

static __always_inline void read_unlock_irq(rwlock_t *rwlock)
	__releases_shared(rwlock)
{
	rt_read_unlock(rwlock);
}

static __always_inline void read_unlock_irqrestore(rwlock_t *rwlock,
						   unsigned long flags)
	__releases_shared(rwlock)
{
	rt_read_unlock(rwlock);
}

static __always_inline void write_lock(rwlock_t *rwlock)
	__acquires(rwlock)
{
	rt_write_lock(rwlock);
}

#ifdef CONFIG_DEBUG_LOCK_ALLOC
static __always_inline void write_lock_nested(rwlock_t *rwlock, int subclass)
	__acquires(rwlock)
{
	rt_write_lock_nested(rwlock, subclass);
}
@@ -94,12 +104,14 @@ static __always_inline void write_lock_nested(rwlock_t *rwlock, int subclass)
#endif

static __always_inline void write_lock_bh(rwlock_t *rwlock)
	__acquires(rwlock)
{
	local_bh_disable();
	rt_write_lock(rwlock);
}

static __always_inline void write_lock_irq(rwlock_t *rwlock)
	__acquires(rwlock)
{
	rt_write_lock(rwlock);
}
@@ -114,33 +126,34 @@ static __always_inline void write_lock_irq(rwlock_t *rwlock)
#define write_trylock(lock)	__cond_lock(lock, rt_write_trylock(lock))

#define write_trylock_irqsave(lock, flags)		\
({							\
	int __locked;					\
							\
	__cond_lock(lock, ({				\
		typecheck(unsigned long, flags);	\
		flags = 0;				\
	__locked = write_trylock(lock);			\
	__locked;					\
})
		rt_write_trylock(lock);			\
	}))

static __always_inline void write_unlock(rwlock_t *rwlock)
	__releases(rwlock)
{
	rt_write_unlock(rwlock);
}

static __always_inline void write_unlock_bh(rwlock_t *rwlock)
	__releases(rwlock)
{
	rt_write_unlock(rwlock);
	local_bh_enable();
}

static __always_inline void write_unlock_irq(rwlock_t *rwlock)
	__releases(rwlock)
{
	rt_write_unlock(rwlock);
}

static __always_inline void write_unlock_irqrestore(rwlock_t *rwlock,
						    unsigned long flags)
	__releases(rwlock)
{
	rt_write_unlock(rwlock);
}
+6 −4
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@
 * portions Copyright 2005, Red Hat, Inc., Ingo Molnar
 * Released under the General Public License (GPL).
 */
typedef struct {
context_lock_struct(rwlock) {
	arch_rwlock_t raw_lock;
#ifdef CONFIG_DEBUG_SPINLOCK
	unsigned int magic, owner_cpu;
@@ -31,7 +31,8 @@ typedef struct {
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map dep_map;
#endif
} rwlock_t;
};
typedef struct rwlock rwlock_t;

#define RWLOCK_MAGIC		0xdeaf1eed

@@ -54,13 +55,14 @@ typedef struct {

#include <linux/rwbase_rt.h>

typedef struct {
context_lock_struct(rwlock) {
	struct rwbase_rt	rwbase;
	atomic_t		readers;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
	struct lockdep_map	dep_map;
#endif
} rwlock_t;
};
typedef struct rwlock rwlock_t;

#define __RWLOCK_RT_INITIALIZER(name)					\
{									\
Loading