Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

authored by

Marco Elver and committed by
Peter Zijlstra
e4fd3be8 5e256db9

+112 -26
+1 -1
Documentation/dev-tools/context-analysis.rst
··· 80 80 81 81 Currently the following synchronization primitives are supported: 82 82 `raw_spinlock_t`, `spinlock_t`, `rwlock_t`, `mutex`, `seqlock_t`, 83 - `bit_spinlock`, RCU, SRCU (`srcu_struct`). 83 + `bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`. 84 84 85 85 For context locks with an initialization function (e.g., `spin_lock_init()`), 86 86 calling this function before initializing any guarded members or globals
+47 -25
include/linux/rwsem.h
··· 45 45 * reduce the chance that they will share the same cacheline causing 46 46 * cacheline bouncing problem. 47 47 */ 48 - struct rw_semaphore { 48 + context_lock_struct(rw_semaphore) { 49 49 atomic_long_t count; 50 50 /* 51 51 * Write owner or one of the read owners as well flags regarding ··· 76 76 } 77 77 78 78 static inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem) 79 + __assumes_ctx_lock(sem) 79 80 { 80 81 WARN_ON(atomic_long_read(&sem->count) == RWSEM_UNLOCKED_VALUE); 81 82 } 82 83 83 84 static inline void rwsem_assert_held_write_nolockdep(const struct rw_semaphore *sem) 85 + __assumes_ctx_lock(sem) 84 86 { 85 87 WARN_ON(!(atomic_long_read(&sem->count) & RWSEM_WRITER_LOCKED)); 86 88 } ··· 121 119 static struct lock_class_key __key; \ 122 120 \ 123 121 __init_rwsem((sem), #sem, &__key); \ 122 + __assume_ctx_lock(sem); \ 124 123 } while (0) 125 124 126 125 /* ··· 151 148 152 149 #include <linux/rwbase_rt.h> 153 150 154 - struct rw_semaphore { 151 + context_lock_struct(rw_semaphore) { 155 152 struct rwbase_rt rwbase; 156 153 #ifdef CONFIG_DEBUG_LOCK_ALLOC 157 154 struct lockdep_map dep_map; ··· 175 172 static struct lock_class_key __key; \ 176 173 \ 177 174 __init_rwsem((sem), #sem, &__key); \ 175 + __assume_ctx_lock(sem); \ 178 176 } while (0) 179 177 180 178 static __always_inline int rwsem_is_locked(const struct rw_semaphore *sem) ··· 184 180 } 185 181 186 182 static __always_inline void rwsem_assert_held_nolockdep(const struct rw_semaphore *sem) 183 + __assumes_ctx_lock(sem) 187 184 { 188 185 WARN_ON(!rwsem_is_locked(sem)); 189 186 } 190 187 191 188 static __always_inline void rwsem_assert_held_write_nolockdep(const struct rw_semaphore *sem) 189 + __assumes_ctx_lock(sem) 192 190 { 193 191 WARN_ON(!rw_base_is_write_locked(&sem->rwbase)); 194 192 } ··· 208 202 */ 209 203 210 204 static inline void rwsem_assert_held(const struct rw_semaphore *sem) 205 + __assumes_ctx_lock(sem) 211 206 { 212 207 if (IS_ENABLED(CONFIG_LOCKDEP)) 213 208 lockdep_assert_held(sem); ··· 217 210 } 218 211 219 212 static inline void rwsem_assert_held_write(const struct rw_semaphore *sem) 213 + __assumes_ctx_lock(sem) 220 214 { 221 215 if (IS_ENABLED(CONFIG_LOCKDEP)) 222 216 lockdep_assert_held_write(sem); ··· 228 220 /* 229 221 * lock for reading 230 222 */ 231 - extern void down_read(struct rw_semaphore *sem); 232 - extern int __must_check down_read_interruptible(struct rw_semaphore *sem); 233 - extern int __must_check down_read_killable(struct rw_semaphore *sem); 223 + extern void down_read(struct rw_semaphore *sem) __acquires_shared(sem); 224 + extern int __must_check down_read_interruptible(struct rw_semaphore *sem) __cond_acquires_shared(0, sem); 225 + extern int __must_check down_read_killable(struct rw_semaphore *sem) __cond_acquires_shared(0, sem); 234 226 235 227 /* 236 228 * trylock for reading -- returns 1 if successful, 0 if contention 237 229 */ 238 - extern int down_read_trylock(struct rw_semaphore *sem); 230 + extern int down_read_trylock(struct rw_semaphore *sem) __cond_acquires_shared(true, sem); 239 231 240 232 /* 241 233 * lock for writing 242 234 */ 243 - extern void down_write(struct rw_semaphore *sem); 244 - extern int __must_check down_write_killable(struct rw_semaphore *sem); 235 + extern void down_write(struct rw_semaphore *sem) __acquires(sem); 236 + extern int __must_check down_write_killable(struct rw_semaphore *sem) __cond_acquires(0, sem); 245 237 246 238 /* 247 239 * trylock for writing -- returns 1 if successful, 0 if contention 248 240 */ 249 - extern int down_write_trylock(struct rw_semaphore *sem); 241 + extern int down_write_trylock(struct rw_semaphore *sem) __cond_acquires(true, sem); 250 242 251 243 /* 252 244 * release a read lock 253 245 */ 254 - extern void up_read(struct rw_semaphore *sem); 246 + extern void up_read(struct rw_semaphore *sem) __releases_shared(sem); 255 247 256 248 /* 257 249 * release a write lock 258 250 */ 259 - extern void up_write(struct rw_semaphore *sem); 251 + extern void up_write(struct rw_semaphore *sem) __releases(sem); 260 252 261 - DEFINE_GUARD(rwsem_read, struct rw_semaphore *, down_read(_T), up_read(_T)) 262 - DEFINE_GUARD_COND(rwsem_read, _try, down_read_trylock(_T)) 263 - DEFINE_GUARD_COND(rwsem_read, _intr, down_read_interruptible(_T), _RET == 0) 253 + DEFINE_LOCK_GUARD_1(rwsem_read, struct rw_semaphore, down_read(_T->lock), up_read(_T->lock)) 254 + DEFINE_LOCK_GUARD_1_COND(rwsem_read, _try, down_read_trylock(_T->lock)) 255 + DEFINE_LOCK_GUARD_1_COND(rwsem_read, _intr, down_read_interruptible(_T->lock), _RET == 0) 264 256 265 - DEFINE_GUARD(rwsem_write, struct rw_semaphore *, down_write(_T), up_write(_T)) 266 - DEFINE_GUARD_COND(rwsem_write, _try, down_write_trylock(_T)) 267 - DEFINE_GUARD_COND(rwsem_write, _kill, down_write_killable(_T), _RET == 0) 257 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T)) 258 + #define class_rwsem_read_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read, _T) 259 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read_try, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T)) 260 + #define class_rwsem_read_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read_try, _T) 261 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_read_intr, __acquires_shared(_T), __releases_shared(*(struct rw_semaphore **)_T)) 262 + #define class_rwsem_read_intr_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_read_intr, _T) 263 + 264 + DEFINE_LOCK_GUARD_1(rwsem_write, struct rw_semaphore, down_write(_T->lock), up_write(_T->lock)) 265 + DEFINE_LOCK_GUARD_1_COND(rwsem_write, _try, down_write_trylock(_T->lock)) 266 + DEFINE_LOCK_GUARD_1_COND(rwsem_write, _kill, down_write_killable(_T->lock), _RET == 0) 267 + 268 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write, __acquires(_T), __releases(*(struct rw_semaphore **)_T)) 269 + #define class_rwsem_write_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write, _T) 270 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_try, __acquires(_T), __releases(*(struct rw_semaphore **)_T)) 271 + #define class_rwsem_write_try_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write_try, _T) 272 + DECLARE_LOCK_GUARD_1_ATTRS(rwsem_write_kill, __acquires(_T), __releases(*(struct rw_semaphore **)_T)) 273 + #define class_rwsem_write_kill_constructor(_T) WITH_LOCK_GUARD_1_ATTRS(rwsem_write_kill, _T) 268 274 269 275 /* 270 276 * downgrade write lock to read lock 271 277 */ 272 - extern void downgrade_write(struct rw_semaphore *sem); 278 + extern void downgrade_write(struct rw_semaphore *sem) __releases(sem) __acquires_shared(sem); 273 279 274 280 #ifdef CONFIG_DEBUG_LOCK_ALLOC 275 281 /* ··· 299 277 * lockdep_set_class() at lock initialization time. 300 278 * See Documentation/locking/lockdep-design.rst for more details.) 301 279 */ 302 - extern void down_read_nested(struct rw_semaphore *sem, int subclass); 303 - extern int __must_check down_read_killable_nested(struct rw_semaphore *sem, int subclass); 304 - extern void down_write_nested(struct rw_semaphore *sem, int subclass); 305 - extern int down_write_killable_nested(struct rw_semaphore *sem, int subclass); 306 - extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock); 280 + extern void down_read_nested(struct rw_semaphore *sem, int subclass) __acquires_shared(sem); 281 + extern int __must_check down_read_killable_nested(struct rw_semaphore *sem, int subclass) __cond_acquires_shared(0, sem); 282 + extern void down_write_nested(struct rw_semaphore *sem, int subclass) __acquires(sem); 283 + extern int down_write_killable_nested(struct rw_semaphore *sem, int subclass) __cond_acquires(0, sem); 284 + extern void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest_lock) __acquires(sem); 307 285 308 286 # define down_write_nest_lock(sem, nest_lock) \ 309 287 do { \ ··· 317 295 * [ This API should be avoided as much as possible - the 318 296 * proper abstraction for this case is completions. ] 319 297 */ 320 - extern void down_read_non_owner(struct rw_semaphore *sem); 321 - extern void up_read_non_owner(struct rw_semaphore *sem); 298 + extern void down_read_non_owner(struct rw_semaphore *sem) __acquires_shared(sem); 299 + extern void up_read_non_owner(struct rw_semaphore *sem) __releases_shared(sem); 322 300 #else 323 301 # define down_read_nested(sem, subclass) down_read(sem) 324 302 # define down_read_killable_nested(sem, subclass) down_read_killable(sem)
+64
lib/test_context-analysis.c
··· 8 8 #include <linux/build_bug.h> 9 9 #include <linux/mutex.h> 10 10 #include <linux/rcupdate.h> 11 + #include <linux/rwsem.h> 11 12 #include <linux/seqlock.h> 12 13 #include <linux/spinlock.h> 13 14 #include <linux/srcu.h> ··· 260 259 { 261 260 scoped_seqlock_read (&d->sl, ss_lockless) { 262 261 (void)d->counter; 262 + } 263 + } 264 + 265 + struct test_rwsem_data { 266 + struct rw_semaphore sem; 267 + int counter __guarded_by(&sem); 268 + }; 269 + 270 + static void __used test_rwsem_init(struct test_rwsem_data *d) 271 + { 272 + init_rwsem(&d->sem); 273 + d->counter = 0; 274 + } 275 + 276 + static void __used test_rwsem_reader(struct test_rwsem_data *d) 277 + { 278 + down_read(&d->sem); 279 + (void)d->counter; 280 + up_read(&d->sem); 281 + 282 + if (down_read_trylock(&d->sem)) { 283 + (void)d->counter; 284 + up_read(&d->sem); 285 + } 286 + } 287 + 288 + static void __used test_rwsem_writer(struct test_rwsem_data *d) 289 + { 290 + down_write(&d->sem); 291 + d->counter++; 292 + up_write(&d->sem); 293 + 294 + down_write(&d->sem); 295 + d->counter++; 296 + downgrade_write(&d->sem); 297 + (void)d->counter; 298 + up_read(&d->sem); 299 + 300 + if (down_write_trylock(&d->sem)) { 301 + d->counter++; 302 + up_write(&d->sem); 303 + } 304 + } 305 + 306 + static void __used test_rwsem_assert(struct test_rwsem_data *d) 307 + { 308 + rwsem_assert_held_nolockdep(&d->sem); 309 + d->counter++; 310 + } 311 + 312 + static void __used test_rwsem_guard(struct test_rwsem_data *d) 313 + { 314 + { guard(rwsem_read)(&d->sem); (void)d->counter; } 315 + { guard(rwsem_write)(&d->sem); d->counter++; } 316 + } 317 + 318 + static void __used test_rwsem_cond_guard(struct test_rwsem_data *d) 319 + { 320 + scoped_cond_guard(rwsem_read_try, return, &d->sem) { 321 + (void)d->counter; 322 + } 323 + scoped_cond_guard(rwsem_write_try, return, &d->sem) { 324 + d->counter++; 263 325 } 264 326 } 265 327