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/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

authored by

Marco Elver and committed by
Peter Zijlstra
47907461 d3febf16

+87 -7
+2 -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`), `rw_semaphore`, `local_lock_t`. 83 + `bit_spinlock`, RCU, SRCU (`srcu_struct`), `rw_semaphore`, `local_lock_t`, 84 + `ww_mutex`. 84 85 85 86 For context locks with an initialization function (e.g., `spin_lock_init()`), 86 87 calling this function before initializing any guarded members or globals
+16 -6
include/linux/ww_mutex.h
··· 44 44 unsigned int is_wait_die; 45 45 }; 46 46 47 - struct ww_mutex { 47 + context_lock_struct(ww_mutex) { 48 48 struct WW_MUTEX_BASE base; 49 49 struct ww_acquire_ctx *ctx; 50 50 #ifdef DEBUG_WW_MUTEXES ··· 52 52 #endif 53 53 }; 54 54 55 - struct ww_acquire_ctx { 55 + context_lock_struct(ww_acquire_ctx) { 56 56 struct task_struct *task; 57 57 unsigned long stamp; 58 58 unsigned int acquired; ··· 107 107 */ 108 108 static inline void ww_mutex_init(struct ww_mutex *lock, 109 109 struct ww_class *ww_class) 110 + __assumes_ctx_lock(lock) 110 111 { 111 112 ww_mutex_base_init(&lock->base, ww_class->mutex_name, &ww_class->mutex_key); 112 113 lock->ctx = NULL; ··· 142 141 */ 143 142 static inline void ww_acquire_init(struct ww_acquire_ctx *ctx, 144 143 struct ww_class *ww_class) 144 + __acquires(ctx) __no_context_analysis 145 145 { 146 146 ctx->task = current; 147 147 ctx->stamp = atomic_long_inc_return_relaxed(&ww_class->stamp); ··· 181 179 * data structures. 182 180 */ 183 181 static inline void ww_acquire_done(struct ww_acquire_ctx *ctx) 182 + __releases(ctx) __acquires_shared(ctx) __no_context_analysis 184 183 { 185 184 #ifdef DEBUG_WW_MUTEXES 186 185 lockdep_assert_held(ctx); ··· 199 196 * mutexes have been released with ww_mutex_unlock. 200 197 */ 201 198 static inline void ww_acquire_fini(struct ww_acquire_ctx *ctx) 199 + __releases_shared(ctx) __no_context_analysis 202 200 { 203 201 #ifdef CONFIG_DEBUG_LOCK_ALLOC 204 202 mutex_release(&ctx->first_lock_dep_map, _THIS_IP_); ··· 249 245 * 250 246 * A mutex acquired with this function must be released with ww_mutex_unlock. 251 247 */ 252 - extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx); 248 + extern int /* __must_check */ ww_mutex_lock(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) 249 + __cond_acquires(0, lock) __must_hold(ctx); 253 250 254 251 /** 255 252 * ww_mutex_lock_interruptible - acquire the w/w mutex, interruptible ··· 283 278 * A mutex acquired with this function must be released with ww_mutex_unlock. 284 279 */ 285 280 extern int __must_check ww_mutex_lock_interruptible(struct ww_mutex *lock, 286 - struct ww_acquire_ctx *ctx); 281 + struct ww_acquire_ctx *ctx) 282 + __cond_acquires(0, lock) __must_hold(ctx); 287 283 288 284 /** 289 285 * ww_mutex_lock_slow - slowpath acquiring of the w/w mutex ··· 311 305 */ 312 306 static inline void 313 307 ww_mutex_lock_slow(struct ww_mutex *lock, struct ww_acquire_ctx *ctx) 308 + __acquires(lock) __must_hold(ctx) __no_context_analysis 314 309 { 315 310 int ret; 316 311 #ifdef DEBUG_WW_MUTEXES ··· 349 342 static inline int __must_check 350 343 ww_mutex_lock_slow_interruptible(struct ww_mutex *lock, 351 344 struct ww_acquire_ctx *ctx) 345 + __cond_acquires(0, lock) __must_hold(ctx) 352 346 { 353 347 #ifdef DEBUG_WW_MUTEXES 354 348 DEBUG_LOCKS_WARN_ON(!ctx->contending_lock); ··· 357 349 return ww_mutex_lock_interruptible(lock, ctx); 358 350 } 359 351 360 - extern void ww_mutex_unlock(struct ww_mutex *lock); 352 + extern void ww_mutex_unlock(struct ww_mutex *lock) __releases(lock); 361 353 362 354 extern int __must_check ww_mutex_trylock(struct ww_mutex *lock, 363 - struct ww_acquire_ctx *ctx); 355 + struct ww_acquire_ctx *ctx) 356 + __cond_acquires(true, lock) __must_hold(ctx); 364 357 365 358 /*** 366 359 * ww_mutex_destroy - mark a w/w mutex unusable ··· 372 363 * this function is called. 373 364 */ 374 365 static inline void ww_mutex_destroy(struct ww_mutex *lock) 366 + __must_not_hold(lock) 375 367 { 376 368 #ifndef CONFIG_PREEMPT_RT 377 369 mutex_destroy(&lock->base);
+69
lib/test_context-analysis.c
··· 14 14 #include <linux/seqlock.h> 15 15 #include <linux/spinlock.h> 16 16 #include <linux/srcu.h> 17 + #include <linux/ww_mutex.h> 17 18 18 19 /* 19 20 * Test that helper macros work as expected. ··· 531 530 this_cpu_add(test_local_trylock_data.counter, 1); 532 531 local_unlock(&test_local_trylock_data.lock); 533 532 } 533 + } 534 + 535 + static DEFINE_WD_CLASS(ww_class); 536 + 537 + struct test_ww_mutex_data { 538 + struct ww_mutex mtx; 539 + int counter __guarded_by(&mtx); 540 + }; 541 + 542 + static void __used test_ww_mutex_init(struct test_ww_mutex_data *d) 543 + { 544 + ww_mutex_init(&d->mtx, &ww_class); 545 + d->counter = 0; 546 + } 547 + 548 + static void __used test_ww_mutex_lock_noctx(struct test_ww_mutex_data *d) 549 + { 550 + if (!ww_mutex_lock(&d->mtx, NULL)) { 551 + d->counter++; 552 + ww_mutex_unlock(&d->mtx); 553 + } 554 + 555 + if (!ww_mutex_lock_interruptible(&d->mtx, NULL)) { 556 + d->counter++; 557 + ww_mutex_unlock(&d->mtx); 558 + } 559 + 560 + if (ww_mutex_trylock(&d->mtx, NULL)) { 561 + d->counter++; 562 + ww_mutex_unlock(&d->mtx); 563 + } 564 + 565 + ww_mutex_lock_slow(&d->mtx, NULL); 566 + d->counter++; 567 + ww_mutex_unlock(&d->mtx); 568 + 569 + ww_mutex_destroy(&d->mtx); 570 + } 571 + 572 + static void __used test_ww_mutex_lock_ctx(struct test_ww_mutex_data *d) 573 + { 574 + struct ww_acquire_ctx ctx; 575 + 576 + ww_acquire_init(&ctx, &ww_class); 577 + 578 + if (!ww_mutex_lock(&d->mtx, &ctx)) { 579 + d->counter++; 580 + ww_mutex_unlock(&d->mtx); 581 + } 582 + 583 + if (!ww_mutex_lock_interruptible(&d->mtx, &ctx)) { 584 + d->counter++; 585 + ww_mutex_unlock(&d->mtx); 586 + } 587 + 588 + if (ww_mutex_trylock(&d->mtx, &ctx)) { 589 + d->counter++; 590 + ww_mutex_unlock(&d->mtx); 591 + } 592 + 593 + ww_mutex_lock_slow(&d->mtx, &ctx); 594 + d->counter++; 595 + ww_mutex_unlock(&d->mtx); 596 + 597 + ww_acquire_done(&ctx); 598 + ww_acquire_fini(&ctx); 599 + 600 + ww_mutex_destroy(&d->mtx); 534 601 }