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

Merge tag 'locking-core-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip

Pull locking updates from Ingo Molnar:
"Locking primitives:
- Micro-optimize percpu_{,try_}cmpxchg{64,128}_op() and
{,try_}cmpxchg{64,128} on x86 (Uros Bizjak)
- mutexes: extend debug checks in mutex_lock() (Yunhui Cui)
- Misc cleanups (Uros Bizjak)

Lockdep:
- Fix might_fault() lockdep check of current->mm->mmap_lock (Peter
Zijlstra)
- Don't disable interrupts on RT in disable_irq_nosync_lockdep.*()
(Sebastian Andrzej Siewior)
- Disable KASAN instrumentation of lockdep.c (Waiman Long)
- Add kasan_check_byte() check in lock_acquire() (Waiman Long)
- Misc cleanups (Sebastian Andrzej Siewior)

Rust runtime integration:
- Use Pin for all LockClassKey usages (Mitchell Levy)
- sync: Add accessor for the lock behind a given guard (Alice Ryhl)
- sync: condvar: Add wait_interruptible_freezable() (Alice Ryhl)
- sync: lock: Add an example for Guard:: Lock_ref() (Boqun Feng)

Split-lock detection feature (x86):
- Fix warning mode with disabled mitigation mode (Maksim Davydov)

Locking events:
- Add locking events for rtmutex slow paths (Waiman Long)
- Add locking events for lockdep (Waiman Long)"

* tag 'locking-core-2025-03-22' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip:
lockdep: Remove disable_irq_lockdep()
lockdep: Don't disable interrupts on RT in disable_irq_nosync_lockdep.*()
rust: lockdep: Use Pin for all LockClassKey usages
rust: sync: condvar: Add wait_interruptible_freezable()
rust: sync: lock: Add an example for Guard:: Lock_ref()
rust: sync: Add accessor for the lock behind a given guard
locking/lockdep: Add kasan_check_byte() check in lock_acquire()
locking/lockdep: Disable KASAN instrumentation of lockdep.c
locking/lock_events: Add locking events for lockdep
locking/lock_events: Add locking events for rtmutex slow paths
x86/split_lock: Fix the delayed detection logic
lockdep/mm: Fix might_fault() lockdep check of current->mm->mmap_lock
x86/locking: Remove semicolon from "lock" prefix
locking/mutex: Add MUTEX_WARN_ON() into fast path
x86/locking: Use asm_inline for {,try_}cmpxchg{64,128} emulations
x86/locking: Use ALT_OUTPUT_SP() for percpu_{,try_}cmpxchg{64,128}_op()

+294 -109
+1 -1
arch/x86/include/asm/alternative.h
··· 48 48 ".popsection\n" \ 49 49 "671:" 50 50 51 - #define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock; " 51 + #define LOCK_PREFIX LOCK_PREFIX_HERE "\n\tlock " 52 52 53 53 #else /* ! CONFIG_SMP */ 54 54 #define LOCK_PREFIX_HERE ""
+4 -4
arch/x86/include/asm/barrier.h
··· 12 12 */ 13 13 14 14 #ifdef CONFIG_X86_32 15 - #define mb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "mfence", \ 15 + #define mb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "mfence", \ 16 16 X86_FEATURE_XMM2) ::: "memory", "cc") 17 - #define rmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "lfence", \ 17 + #define rmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "lfence", \ 18 18 X86_FEATURE_XMM2) ::: "memory", "cc") 19 - #define wmb() asm volatile(ALTERNATIVE("lock; addl $0,-4(%%esp)", "sfence", \ 19 + #define wmb() asm volatile(ALTERNATIVE("lock addl $0,-4(%%esp)", "sfence", \ 20 20 X86_FEATURE_XMM2) ::: "memory", "cc") 21 21 #else 22 22 #define __mb() asm volatile("mfence":::"memory") ··· 50 50 #define __dma_rmb() barrier() 51 51 #define __dma_wmb() barrier() 52 52 53 - #define __smp_mb() asm volatile("lock; addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc") 53 + #define __smp_mb() asm volatile("lock addl $0,-4(%%" _ASM_SP ")" ::: "memory", "cc") 54 54 55 55 #define __smp_rmb() dma_rmb() 56 56 #define __smp_wmb() barrier()
+2 -2
arch/x86/include/asm/cmpxchg.h
··· 134 134 __raw_cmpxchg((ptr), (old), (new), (size), LOCK_PREFIX) 135 135 136 136 #define __sync_cmpxchg(ptr, old, new, size) \ 137 - __raw_cmpxchg((ptr), (old), (new), (size), "lock; ") 137 + __raw_cmpxchg((ptr), (old), (new), (size), "lock ") 138 138 139 139 #define __cmpxchg_local(ptr, old, new, size) \ 140 140 __raw_cmpxchg((ptr), (old), (new), (size), "") ··· 222 222 __raw_try_cmpxchg((ptr), (pold), (new), (size), LOCK_PREFIX) 223 223 224 224 #define __sync_try_cmpxchg(ptr, pold, new, size) \ 225 - __raw_try_cmpxchg((ptr), (pold), (new), (size), "lock; ") 225 + __raw_try_cmpxchg((ptr), (pold), (new), (size), "lock ") 226 226 227 227 #define __try_cmpxchg_local(ptr, pold, new, size) \ 228 228 __raw_try_cmpxchg((ptr), (pold), (new), (size), "")
+20 -16
arch/x86/include/asm/cmpxchg_32.h
··· 91 91 union __u64_halves o = { .full = (_old), }, \ 92 92 n = { .full = (_new), }; \ 93 93 \ 94 - asm volatile(ALTERNATIVE(_lock_loc \ 95 - "call cmpxchg8b_emu", \ 96 - _lock "cmpxchg8b %a[ptr]", X86_FEATURE_CX8) \ 97 - : ALT_OUTPUT_SP("+a" (o.low), "+d" (o.high)) \ 98 - : "b" (n.low), "c" (n.high), [ptr] "S" (_ptr) \ 99 - : "memory"); \ 94 + asm_inline volatile( \ 95 + ALTERNATIVE(_lock_loc \ 96 + "call cmpxchg8b_emu", \ 97 + _lock "cmpxchg8b %a[ptr]", X86_FEATURE_CX8) \ 98 + : ALT_OUTPUT_SP("+a" (o.low), "+d" (o.high)) \ 99 + : "b" (n.low), "c" (n.high), \ 100 + [ptr] "S" (_ptr) \ 101 + : "memory"); \ 100 102 \ 101 103 o.full; \ 102 104 }) 103 105 104 106 static __always_inline u64 arch_cmpxchg64(volatile u64 *ptr, u64 old, u64 new) 105 107 { 106 - return __arch_cmpxchg64_emu(ptr, old, new, LOCK_PREFIX_HERE, "lock; "); 108 + return __arch_cmpxchg64_emu(ptr, old, new, LOCK_PREFIX_HERE, "lock "); 107 109 } 108 110 #define arch_cmpxchg64 arch_cmpxchg64 109 111 ··· 121 119 n = { .full = (_new), }; \ 122 120 bool ret; \ 123 121 \ 124 - asm volatile(ALTERNATIVE(_lock_loc \ 125 - "call cmpxchg8b_emu", \ 126 - _lock "cmpxchg8b %a[ptr]", X86_FEATURE_CX8) \ 127 - CC_SET(e) \ 128 - : ALT_OUTPUT_SP(CC_OUT(e) (ret), \ 129 - "+a" (o.low), "+d" (o.high)) \ 130 - : "b" (n.low), "c" (n.high), [ptr] "S" (_ptr) \ 131 - : "memory"); \ 122 + asm_inline volatile( \ 123 + ALTERNATIVE(_lock_loc \ 124 + "call cmpxchg8b_emu", \ 125 + _lock "cmpxchg8b %a[ptr]", X86_FEATURE_CX8) \ 126 + CC_SET(e) \ 127 + : ALT_OUTPUT_SP(CC_OUT(e) (ret), \ 128 + "+a" (o.low), "+d" (o.high)) \ 129 + : "b" (n.low), "c" (n.high), \ 130 + [ptr] "S" (_ptr) \ 131 + : "memory"); \ 132 132 \ 133 133 if (unlikely(!ret)) \ 134 134 *(_oldp) = o.full; \ ··· 140 136 141 137 static __always_inline bool arch_try_cmpxchg64(volatile u64 *ptr, u64 *oldp, u64 new) 142 138 { 143 - return __arch_try_cmpxchg64_emu(ptr, oldp, new, LOCK_PREFIX_HERE, "lock; "); 139 + return __arch_try_cmpxchg64_emu(ptr, oldp, new, LOCK_PREFIX_HERE, "lock "); 144 140 } 145 141 #define arch_try_cmpxchg64 arch_try_cmpxchg64 146 142
+1 -1
arch/x86/include/asm/edac.h
··· 13 13 * are interrupt, DMA and SMP safe. 14 14 */ 15 15 for (i = 0; i < size / 4; i++, virt_addr++) 16 - asm volatile("lock; addl $0, %0"::"m" (*virt_addr)); 16 + asm volatile("lock addl $0, %0"::"m" (*virt_addr)); 17 17 } 18 18 19 19 #endif /* _ASM_X86_EDAC_H */
+37 -40
arch/x86/include/asm/percpu.h
··· 348 348 old__.var = _oval; \ 349 349 new__.var = _nval; \ 350 350 \ 351 - asm qual (ALTERNATIVE("call this_cpu_cmpxchg8b_emu", \ 352 - "cmpxchg8b " __percpu_arg([var]), X86_FEATURE_CX8) \ 353 - : [var] "+m" (__my_cpu_var(_var)), \ 354 - "+a" (old__.low), \ 355 - "+d" (old__.high) \ 356 - : "b" (new__.low), \ 357 - "c" (new__.high), \ 358 - "S" (&(_var)) \ 359 - : "memory"); \ 351 + asm_inline qual ( \ 352 + ALTERNATIVE("call this_cpu_cmpxchg8b_emu", \ 353 + "cmpxchg8b " __percpu_arg([var]), X86_FEATURE_CX8) \ 354 + : ALT_OUTPUT_SP([var] "+m" (__my_cpu_var(_var)), \ 355 + "+a" (old__.low), "+d" (old__.high)) \ 356 + : "b" (new__.low), "c" (new__.high), \ 357 + "S" (&(_var)) \ 358 + : "memory"); \ 360 359 \ 361 360 old__.var; \ 362 361 }) ··· 377 378 old__.var = *_oval; \ 378 379 new__.var = _nval; \ 379 380 \ 380 - asm qual (ALTERNATIVE("call this_cpu_cmpxchg8b_emu", \ 381 - "cmpxchg8b " __percpu_arg([var]), X86_FEATURE_CX8) \ 382 - CC_SET(z) \ 383 - : CC_OUT(z) (success), \ 384 - [var] "+m" (__my_cpu_var(_var)), \ 385 - "+a" (old__.low), \ 386 - "+d" (old__.high) \ 387 - : "b" (new__.low), \ 388 - "c" (new__.high), \ 389 - "S" (&(_var)) \ 390 - : "memory"); \ 381 + asm_inline qual ( \ 382 + ALTERNATIVE("call this_cpu_cmpxchg8b_emu", \ 383 + "cmpxchg8b " __percpu_arg([var]), X86_FEATURE_CX8) \ 384 + CC_SET(z) \ 385 + : ALT_OUTPUT_SP(CC_OUT(z) (success), \ 386 + [var] "+m" (__my_cpu_var(_var)), \ 387 + "+a" (old__.low), "+d" (old__.high)) \ 388 + : "b" (new__.low), "c" (new__.high), \ 389 + "S" (&(_var)) \ 390 + : "memory"); \ 391 391 if (unlikely(!success)) \ 392 392 *_oval = old__.var; \ 393 393 \ ··· 417 419 old__.var = _oval; \ 418 420 new__.var = _nval; \ 419 421 \ 420 - asm qual (ALTERNATIVE("call this_cpu_cmpxchg16b_emu", \ 421 - "cmpxchg16b " __percpu_arg([var]), X86_FEATURE_CX16) \ 422 - : [var] "+m" (__my_cpu_var(_var)), \ 423 - "+a" (old__.low), \ 424 - "+d" (old__.high) \ 425 - : "b" (new__.low), \ 426 - "c" (new__.high), \ 427 - "S" (&(_var)) \ 428 - : "memory"); \ 422 + asm_inline qual ( \ 423 + ALTERNATIVE("call this_cpu_cmpxchg16b_emu", \ 424 + "cmpxchg16b " __percpu_arg([var]), X86_FEATURE_CX16) \ 425 + : ALT_OUTPUT_SP([var] "+m" (__my_cpu_var(_var)), \ 426 + "+a" (old__.low), "+d" (old__.high)) \ 427 + : "b" (new__.low), "c" (new__.high), \ 428 + "S" (&(_var)) \ 429 + : "memory"); \ 429 430 \ 430 431 old__.var; \ 431 432 }) ··· 446 449 old__.var = *_oval; \ 447 450 new__.var = _nval; \ 448 451 \ 449 - asm qual (ALTERNATIVE("call this_cpu_cmpxchg16b_emu", \ 450 - "cmpxchg16b " __percpu_arg([var]), X86_FEATURE_CX16) \ 451 - CC_SET(z) \ 452 - : CC_OUT(z) (success), \ 453 - [var] "+m" (__my_cpu_var(_var)), \ 454 - "+a" (old__.low), \ 455 - "+d" (old__.high) \ 456 - : "b" (new__.low), \ 457 - "c" (new__.high), \ 458 - "S" (&(_var)) \ 459 - : "memory"); \ 452 + asm_inline qual ( \ 453 + ALTERNATIVE("call this_cpu_cmpxchg16b_emu", \ 454 + "cmpxchg16b " __percpu_arg([var]), X86_FEATURE_CX16) \ 455 + CC_SET(z) \ 456 + : ALT_OUTPUT_SP(CC_OUT(z) (success), \ 457 + [var] "+m" (__my_cpu_var(_var)), \ 458 + "+a" (old__.low), "+d" (old__.high)) \ 459 + : "b" (new__.low), "c" (new__.high), \ 460 + "S" (&(_var)) \ 461 + : "memory"); \ 460 462 if (unlikely(!success)) \ 461 463 *_oval = old__.var; \ 464 + \ 462 465 likely(success); \ 463 466 }) 464 467
+6 -6
arch/x86/include/asm/sync_bitops.h
··· 31 31 */ 32 32 static inline void sync_set_bit(long nr, volatile unsigned long *addr) 33 33 { 34 - asm volatile("lock; " __ASM_SIZE(bts) " %1,%0" 34 + asm volatile("lock " __ASM_SIZE(bts) " %1,%0" 35 35 : "+m" (ADDR) 36 36 : "Ir" (nr) 37 37 : "memory"); ··· 49 49 */ 50 50 static inline void sync_clear_bit(long nr, volatile unsigned long *addr) 51 51 { 52 - asm volatile("lock; " __ASM_SIZE(btr) " %1,%0" 52 + asm volatile("lock " __ASM_SIZE(btr) " %1,%0" 53 53 : "+m" (ADDR) 54 54 : "Ir" (nr) 55 55 : "memory"); ··· 66 66 */ 67 67 static inline void sync_change_bit(long nr, volatile unsigned long *addr) 68 68 { 69 - asm volatile("lock; " __ASM_SIZE(btc) " %1,%0" 69 + asm volatile("lock " __ASM_SIZE(btc) " %1,%0" 70 70 : "+m" (ADDR) 71 71 : "Ir" (nr) 72 72 : "memory"); ··· 82 82 */ 83 83 static inline bool sync_test_and_set_bit(long nr, volatile unsigned long *addr) 84 84 { 85 - return GEN_BINARY_RMWcc("lock; " __ASM_SIZE(bts), *addr, c, "Ir", nr); 85 + return GEN_BINARY_RMWcc("lock " __ASM_SIZE(bts), *addr, c, "Ir", nr); 86 86 } 87 87 88 88 /** ··· 95 95 */ 96 96 static inline int sync_test_and_clear_bit(long nr, volatile unsigned long *addr) 97 97 { 98 - return GEN_BINARY_RMWcc("lock; " __ASM_SIZE(btr), *addr, c, "Ir", nr); 98 + return GEN_BINARY_RMWcc("lock " __ASM_SIZE(btr), *addr, c, "Ir", nr); 99 99 } 100 100 101 101 /** ··· 108 108 */ 109 109 static inline int sync_test_and_change_bit(long nr, volatile unsigned long *addr) 110 110 { 111 - return GEN_BINARY_RMWcc("lock; " __ASM_SIZE(btc), *addr, c, "Ir", nr); 111 + return GEN_BINARY_RMWcc("lock " __ASM_SIZE(btc), *addr, c, "Ir", nr); 112 112 } 113 113 114 114 #define sync_test_bit(nr, addr) test_bit(nr, addr)
+16 -4
arch/x86/kernel/cpu/bus_lock.c
··· 192 192 { 193 193 sld_update_msr(true); 194 194 } 195 - static DECLARE_DELAYED_WORK(sl_reenable, __split_lock_reenable); 195 + /* 196 + * In order for each CPU to schedule its delayed work independently of the 197 + * others, delayed work struct must be per-CPU. This is not required when 198 + * sysctl_sld_mitigate is enabled because of the semaphore that limits 199 + * the number of simultaneously scheduled delayed works to 1. 200 + */ 201 + static DEFINE_PER_CPU(struct delayed_work, sl_reenable); 196 202 197 203 /* 198 204 * If a CPU goes offline with pending delayed work to re-enable split lock ··· 219 213 220 214 static void split_lock_warn(unsigned long ip) 221 215 { 222 - struct delayed_work *work; 216 + struct delayed_work *work = NULL; 223 217 int cpu; 224 218 225 219 if (!current->reported_split_lock) ··· 241 235 if (down_interruptible(&buslock_sem) == -EINTR) 242 236 return; 243 237 work = &sl_reenable_unlock; 244 - } else { 245 - work = &sl_reenable; 246 238 } 247 239 248 240 cpu = get_cpu(); 241 + 242 + if (!work) { 243 + work = this_cpu_ptr(&sl_reenable); 244 + /* Deferred initialization of per-CPU struct */ 245 + if (!work->work.func) 246 + INIT_DELAYED_WORK(work, __split_lock_reenable); 247 + } 248 + 249 249 schedule_delayed_work_on(cpu, work, 2); 250 250 251 251 /* Disable split lock detection on this CPU to make progress */
+4 -12
include/linux/interrupt.h
··· 448 448 static inline void disable_irq_nosync_lockdep(unsigned int irq) 449 449 { 450 450 disable_irq_nosync(irq); 451 - #ifdef CONFIG_LOCKDEP 451 + #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_PREEMPT_RT) 452 452 local_irq_disable(); 453 453 #endif 454 454 } ··· 456 456 static inline void disable_irq_nosync_lockdep_irqsave(unsigned int irq, unsigned long *flags) 457 457 { 458 458 disable_irq_nosync(irq); 459 - #ifdef CONFIG_LOCKDEP 459 + #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_PREEMPT_RT) 460 460 local_irq_save(*flags); 461 - #endif 462 - } 463 - 464 - static inline void disable_irq_lockdep(unsigned int irq) 465 - { 466 - disable_irq(irq); 467 - #ifdef CONFIG_LOCKDEP 468 - local_irq_disable(); 469 461 #endif 470 462 } 471 463 472 464 static inline void enable_irq_lockdep(unsigned int irq) 473 465 { 474 - #ifdef CONFIG_LOCKDEP 466 + #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_PREEMPT_RT) 475 467 local_irq_enable(); 476 468 #endif 477 469 enable_irq(irq); ··· 471 479 472 480 static inline void enable_irq_lockdep_irqrestore(unsigned int irq, unsigned long *flags) 473 481 { 474 - #ifdef CONFIG_LOCKDEP 482 + #if defined(CONFIG_LOCKDEP) && !defined(CONFIG_PREEMPT_RT) 475 483 local_irq_restore(*flags); 476 484 #endif 477 485 enable_irq(irq);
+2 -1
kernel/locking/Makefile
··· 5 5 6 6 obj-y += mutex.o semaphore.o rwsem.o percpu-rwsem.o 7 7 8 - # Avoid recursion lockdep -> sanitizer -> ... -> lockdep. 8 + # Avoid recursion lockdep -> sanitizer -> ... -> lockdep & improve performance. 9 + KASAN_SANITIZE_lockdep.o := n 9 10 KCSAN_SANITIZE_lockdep.o := n 10 11 11 12 ifdef CONFIG_FUNCTION_TRACER
+28
kernel/locking/lock_events_list.h
··· 67 67 LOCK_EVENT(rwsem_wlock) /* # of write locks acquired */ 68 68 LOCK_EVENT(rwsem_wlock_fail) /* # of failed write lock acquisitions */ 69 69 LOCK_EVENT(rwsem_wlock_handoff) /* # of write lock handoffs */ 70 + 71 + /* 72 + * Locking events for rtlock_slowlock() 73 + */ 74 + LOCK_EVENT(rtlock_slowlock) /* # of rtlock_slowlock() calls */ 75 + LOCK_EVENT(rtlock_slow_acq1) /* # of locks acquired after wait_lock */ 76 + LOCK_EVENT(rtlock_slow_acq2) /* # of locks acquired in for loop */ 77 + LOCK_EVENT(rtlock_slow_sleep) /* # of sleeps */ 78 + LOCK_EVENT(rtlock_slow_wake) /* # of wakeup's */ 79 + 80 + /* 81 + * Locking events for rt_mutex_slowlock() 82 + */ 83 + LOCK_EVENT(rtmutex_slowlock) /* # of rt_mutex_slowlock() calls */ 84 + LOCK_EVENT(rtmutex_slow_block) /* # of rt_mutex_slowlock_block() calls */ 85 + LOCK_EVENT(rtmutex_slow_acq1) /* # of locks acquired after wait_lock */ 86 + LOCK_EVENT(rtmutex_slow_acq2) /* # of locks acquired at the end */ 87 + LOCK_EVENT(rtmutex_slow_acq3) /* # of locks acquired in *block() */ 88 + LOCK_EVENT(rtmutex_slow_sleep) /* # of sleeps */ 89 + LOCK_EVENT(rtmutex_slow_wake) /* # of wakeup's */ 90 + LOCK_EVENT(rtmutex_deadlock) /* # of rt_mutex_handle_deadlock()'s */ 91 + 92 + /* 93 + * Locking events for lockdep 94 + */ 95 + LOCK_EVENT(lockdep_acquire) 96 + LOCK_EVENT(lockdep_lock) 97 + LOCK_EVENT(lockdep_nocheck)
+16 -1
kernel/locking/lockdep.c
··· 57 57 #include <linux/lockdep.h> 58 58 #include <linux/context_tracking.h> 59 59 #include <linux/console.h> 60 + #include <linux/kasan.h> 60 61 61 62 #include <asm/sections.h> 62 63 63 64 #include "lockdep_internals.h" 65 + #include "lock_events.h" 64 66 65 67 #include <trace/events/lock.h> 66 68 ··· 172 170 static int graph_lock(void) 173 171 { 174 172 lockdep_lock(); 173 + lockevent_inc(lockdep_lock); 175 174 /* 176 175 * Make sure that if another CPU detected a bug while 177 176 * walking the graph we dont change it (while the other ··· 5094 5091 if (unlikely(lock->key == &__lockdep_no_track__)) 5095 5092 return 0; 5096 5093 5097 - if (!prove_locking || lock->key == &__lockdep_no_validate__) 5094 + lockevent_inc(lockdep_acquire); 5095 + 5096 + if (!prove_locking || lock->key == &__lockdep_no_validate__) { 5098 5097 check = 0; 5098 + lockevent_inc(lockdep_nocheck); 5099 + } 5099 5100 5100 5101 if (subclass < NR_LOCKDEP_CACHING_CLASSES) 5101 5102 class = lock->class_cache[subclass]; ··· 5830 5823 5831 5824 if (!debug_locks) 5832 5825 return; 5826 + 5827 + /* 5828 + * As KASAN instrumentation is disabled and lock_acquire() is usually 5829 + * the first lockdep call when a task tries to acquire a lock, add 5830 + * kasan_check_byte() here to check for use-after-free and other 5831 + * memory errors. 5832 + */ 5833 + kasan_check_byte(lock); 5833 5834 5834 5835 if (unlikely(!lockdep_enabled())) { 5835 5836 /* XXX allow trylock from NMI ?!? */
+2
kernel/locking/mutex.c
··· 143 143 unsigned long curr = (unsigned long)current; 144 144 unsigned long zero = 0UL; 145 145 146 + MUTEX_WARN_ON(lock->magic != lock); 147 + 146 148 if (atomic_long_try_cmpxchg_acquire(&lock->owner, &zero, curr)) 147 149 return true; 148 150
+24 -5
kernel/locking/rtmutex.c
··· 27 27 #include <trace/events/lock.h> 28 28 29 29 #include "rtmutex_common.h" 30 + #include "lock_events.h" 30 31 31 32 #ifndef WW_RT 32 33 # define build_ww_mutex() (false) ··· 1613 1612 struct task_struct *owner; 1614 1613 int ret = 0; 1615 1614 1615 + lockevent_inc(rtmutex_slow_block); 1616 1616 for (;;) { 1617 1617 /* Try to acquire the lock: */ 1618 - if (try_to_take_rt_mutex(lock, current, waiter)) 1618 + if (try_to_take_rt_mutex(lock, current, waiter)) { 1619 + lockevent_inc(rtmutex_slow_acq3); 1619 1620 break; 1621 + } 1620 1622 1621 1623 if (timeout && !timeout->task) { 1622 1624 ret = -ETIMEDOUT; ··· 1642 1638 owner = NULL; 1643 1639 raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); 1644 1640 1645 - if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) 1641 + if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) { 1642 + lockevent_inc(rtmutex_slow_sleep); 1646 1643 rt_mutex_schedule(); 1644 + } 1647 1645 1648 1646 raw_spin_lock_irq(&lock->wait_lock); 1649 1647 set_current_state(state); ··· 1700 1694 int ret; 1701 1695 1702 1696 lockdep_assert_held(&lock->wait_lock); 1697 + lockevent_inc(rtmutex_slowlock); 1703 1698 1704 1699 /* Try to acquire the lock again: */ 1705 1700 if (try_to_take_rt_mutex(lock, current, NULL)) { ··· 1708 1701 __ww_mutex_check_waiters(rtm, ww_ctx, wake_q); 1709 1702 ww_mutex_lock_acquired(ww, ww_ctx); 1710 1703 } 1704 + lockevent_inc(rtmutex_slow_acq1); 1711 1705 return 0; 1712 1706 } 1713 1707 ··· 1727 1719 __ww_mutex_check_waiters(rtm, ww_ctx, wake_q); 1728 1720 ww_mutex_lock_acquired(ww, ww_ctx); 1729 1721 } 1722 + lockevent_inc(rtmutex_slow_acq2); 1730 1723 } else { 1731 1724 __set_current_state(TASK_RUNNING); 1732 1725 remove_waiter(lock, waiter); 1733 1726 rt_mutex_handle_deadlock(ret, chwalk, lock, waiter); 1727 + lockevent_inc(rtmutex_deadlock); 1734 1728 } 1735 1729 1736 1730 /* ··· 1761 1751 &waiter, wake_q); 1762 1752 1763 1753 debug_rt_mutex_free_waiter(&waiter); 1754 + lockevent_cond_inc(rtmutex_slow_wake, !wake_q_empty(wake_q)); 1764 1755 return ret; 1765 1756 } 1766 1757 ··· 1834 1823 struct task_struct *owner; 1835 1824 1836 1825 lockdep_assert_held(&lock->wait_lock); 1826 + lockevent_inc(rtlock_slowlock); 1837 1827 1838 - if (try_to_take_rt_mutex(lock, current, NULL)) 1828 + if (try_to_take_rt_mutex(lock, current, NULL)) { 1829 + lockevent_inc(rtlock_slow_acq1); 1839 1830 return; 1831 + } 1840 1832 1841 1833 rt_mutex_init_rtlock_waiter(&waiter); 1842 1834 ··· 1852 1838 1853 1839 for (;;) { 1854 1840 /* Try to acquire the lock again */ 1855 - if (try_to_take_rt_mutex(lock, current, &waiter)) 1841 + if (try_to_take_rt_mutex(lock, current, &waiter)) { 1842 + lockevent_inc(rtlock_slow_acq2); 1856 1843 break; 1844 + } 1857 1845 1858 1846 if (&waiter == rt_mutex_top_waiter(lock)) 1859 1847 owner = rt_mutex_owner(lock); ··· 1863 1847 owner = NULL; 1864 1848 raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); 1865 1849 1866 - if (!owner || !rtmutex_spin_on_owner(lock, &waiter, owner)) 1850 + if (!owner || !rtmutex_spin_on_owner(lock, &waiter, owner)) { 1851 + lockevent_inc(rtlock_slow_sleep); 1867 1852 schedule_rtlock(); 1853 + } 1868 1854 1869 1855 raw_spin_lock_irq(&lock->wait_lock); 1870 1856 set_current_state(TASK_RTLOCK_WAIT); ··· 1883 1865 debug_rt_mutex_free_waiter(&waiter); 1884 1866 1885 1867 trace_contention_end(lock, 0); 1868 + lockevent_cond_inc(rtlock_slow_wake, !wake_q_empty(wake_q)); 1886 1869 } 1887 1870 1888 1871 static __always_inline void __sched rtlock_slowlock(struct rt_mutex_base *lock)
-2
mm/memory.c
··· 6834 6834 if (pagefault_disabled()) 6835 6835 return; 6836 6836 __might_sleep(file, line); 6837 - #if defined(CONFIG_DEBUG_ATOMIC_SLEEP) 6838 6837 if (current->mm) 6839 6838 might_lock_read(&current->mm->mmap_lock); 6840 - #endif 6841 6839 } 6842 6840 EXPORT_SYMBOL(__might_fault); 6843 6841 #endif
+1
rust/helpers/helpers.c
··· 31 31 #include "signal.c" 32 32 #include "slab.c" 33 33 #include "spinlock.c" 34 + #include "sync.c" 34 35 #include "task.c" 35 36 #include "uaccess.c" 36 37 #include "vmalloc.c"
+13
rust/helpers/sync.c
··· 1 + // SPDX-License-Identifier: GPL-2.0 2 + 3 + #include <linux/lockdep.h> 4 + 5 + void rust_helper_lockdep_register_key(struct lock_class_key *k) 6 + { 7 + lockdep_register_key(k); 8 + } 9 + 10 + void rust_helper_lockdep_unregister_key(struct lock_class_key *k) 11 + { 12 + lockdep_unregister_key(k); 13 + }
+54 -3
rust/kernel/sync.rs
··· 5 5 //! This module contains the kernel APIs related to synchronisation that have been ported or 6 6 //! wrapped for usage by Rust code in the kernel. 7 7 8 + use crate::pin_init; 9 + use crate::prelude::*; 8 10 use crate::types::Opaque; 9 11 10 12 mod arc; ··· 25 23 26 24 /// Represents a lockdep class. It's a wrapper around C's `lock_class_key`. 27 25 #[repr(transparent)] 28 - pub struct LockClassKey(Opaque<bindings::lock_class_key>); 26 + #[pin_data(PinnedDrop)] 27 + pub struct LockClassKey { 28 + #[pin] 29 + inner: Opaque<bindings::lock_class_key>, 30 + } 29 31 30 32 // SAFETY: `bindings::lock_class_key` is designed to be used concurrently from multiple threads and 31 33 // provides its own synchronization. 32 34 unsafe impl Sync for LockClassKey {} 33 35 34 36 impl LockClassKey { 37 + /// Initializes a dynamically allocated lock class key. In the common case of using a 38 + /// statically allocated lock class key, the static_lock_class! macro should be used instead. 39 + /// 40 + /// # Example 41 + /// ``` 42 + /// # use kernel::{c_str, stack_pin_init}; 43 + /// # use kernel::alloc::KBox; 44 + /// # use kernel::types::ForeignOwnable; 45 + /// # use kernel::sync::{LockClassKey, SpinLock}; 46 + /// 47 + /// let key = KBox::pin_init(LockClassKey::new_dynamic(), GFP_KERNEL)?; 48 + /// let key_ptr = key.into_foreign(); 49 + /// 50 + /// { 51 + /// stack_pin_init!(let num: SpinLock<u32> = SpinLock::new( 52 + /// 0, 53 + /// c_str!("my_spinlock"), 54 + /// // SAFETY: `key_ptr` is returned by the above `into_foreign()`, whose 55 + /// // `from_foreign()` has not yet been called. 56 + /// unsafe { <Pin<KBox<LockClassKey>> as ForeignOwnable>::borrow(key_ptr) } 57 + /// )); 58 + /// } 59 + /// 60 + /// // SAFETY: We dropped `num`, the only use of the key, so the result of the previous 61 + /// // `borrow` has also been dropped. Thus, it's safe to use from_foreign. 62 + /// unsafe { drop(<Pin<KBox<LockClassKey>> as ForeignOwnable>::from_foreign(key_ptr)) }; 63 + /// 64 + /// # Ok::<(), Error>(()) 65 + /// ``` 66 + pub fn new_dynamic() -> impl PinInit<Self> { 67 + pin_init!(Self { 68 + // SAFETY: lockdep_register_key expects an uninitialized block of memory 69 + inner <- Opaque::ffi_init(|slot| unsafe { bindings::lockdep_register_key(slot) }) 70 + }) 71 + } 72 + 35 73 pub(crate) fn as_ptr(&self) -> *mut bindings::lock_class_key { 36 - self.0.get() 74 + self.inner.get() 75 + } 76 + } 77 + 78 + #[pinned_drop] 79 + impl PinnedDrop for LockClassKey { 80 + fn drop(self: Pin<&mut Self>) { 81 + // SAFETY: self.as_ptr was registered with lockdep and self is pinned, so the address 82 + // hasn't changed. Thus, it's safe to pass to unregister. 83 + unsafe { bindings::lockdep_unregister_key(self.as_ptr()) } 37 84 } 38 85 } 39 86 ··· 95 44 // SAFETY: lockdep expects uninitialized memory when it's handed a statically allocated 96 45 // lock_class_key 97 46 unsafe { ::core::mem::MaybeUninit::uninit().assume_init() }; 98 - &CLASS 47 + $crate::prelude::Pin::static_ref(&CLASS) 99 48 }}; 100 49 } 101 50
+24 -4
rust/kernel/sync/condvar.rs
··· 11 11 init::PinInit, 12 12 pin_init, 13 13 str::CStr, 14 - task::{MAX_SCHEDULE_TIMEOUT, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE}, 14 + task::{ 15 + MAX_SCHEDULE_TIMEOUT, TASK_FREEZABLE, TASK_INTERRUPTIBLE, TASK_NORMAL, TASK_UNINTERRUPTIBLE, 16 + }, 15 17 time::Jiffies, 16 18 types::Opaque, 17 19 }; 18 - use core::marker::PhantomPinned; 19 - use core::ptr; 20 + use core::{marker::PhantomPinned, pin::Pin, ptr}; 20 21 use macros::pin_data; 21 22 22 23 /// Creates a [`CondVar`] initialiser with the given name and a newly-created lock class. ··· 102 101 103 102 impl CondVar { 104 103 /// Constructs a new condvar initialiser. 105 - pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { 104 + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self> { 106 105 pin_init!(Self { 107 106 _pin: PhantomPinned, 108 107 // SAFETY: `slot` is valid while the closure is called and both `name` and `key` have ··· 157 156 #[must_use = "wait_interruptible returns if a signal is pending, so the caller must check the return value"] 158 157 pub fn wait_interruptible<T: ?Sized, B: Backend>(&self, guard: &mut Guard<'_, T, B>) -> bool { 159 158 self.wait_internal(TASK_INTERRUPTIBLE, guard, MAX_SCHEDULE_TIMEOUT); 159 + crate::current!().signal_pending() 160 + } 161 + 162 + /// Releases the lock and waits for a notification in interruptible and freezable mode. 163 + /// 164 + /// The process is allowed to be frozen during this sleep. No lock should be held when calling 165 + /// this function, and there is a lockdep assertion for this. Freezing a task that holds a lock 166 + /// can trivially deadlock vs another task that needs that lock to complete before it too can 167 + /// hit freezable. 168 + #[must_use = "wait_interruptible_freezable returns if a signal is pending, so the caller must check the return value"] 169 + pub fn wait_interruptible_freezable<T: ?Sized, B: Backend>( 170 + &self, 171 + guard: &mut Guard<'_, T, B>, 172 + ) -> bool { 173 + self.wait_internal( 174 + TASK_INTERRUPTIBLE | TASK_FREEZABLE, 175 + guard, 176 + MAX_SCHEDULE_TIMEOUT, 177 + ); 160 178 crate::current!().signal_pending() 161 179 } 162 180
+32 -3
rust/kernel/sync/lock.rs
··· 12 12 str::CStr, 13 13 types::{NotThreadSafe, Opaque, ScopeGuard}, 14 14 }; 15 - use core::{cell::UnsafeCell, marker::PhantomPinned}; 15 + use core::{cell::UnsafeCell, marker::PhantomPinned, pin::Pin}; 16 16 use macros::pin_data; 17 17 18 18 pub mod mutex; ··· 129 129 130 130 impl<T, B: Backend> Lock<T, B> { 131 131 /// Constructs a new lock initialiser. 132 - pub fn new(t: T, name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { 132 + pub fn new(t: T, name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self> { 133 133 pin_init!(Self { 134 134 data: UnsafeCell::new(t), 135 135 _pin: PhantomPinned, ··· 199 199 // SAFETY: `Guard` is sync when the data protected by the lock is also sync. 200 200 unsafe impl<T: Sync + ?Sized, B: Backend> Sync for Guard<'_, T, B> {} 201 201 202 - impl<T: ?Sized, B: Backend> Guard<'_, T, B> { 202 + impl<'a, T: ?Sized, B: Backend> Guard<'a, T, B> { 203 + /// Returns the lock that this guard originates from. 204 + /// 205 + /// # Examples 206 + /// 207 + /// The following example shows how to use [`Guard::lock_ref()`] to assert the corresponding 208 + /// lock is held. 209 + /// 210 + /// ``` 211 + /// # use kernel::{new_spinlock, stack_pin_init, sync::lock::{Backend, Guard, Lock}}; 212 + /// 213 + /// fn assert_held<T, B: Backend>(guard: &Guard<'_, T, B>, lock: &Lock<T, B>) { 214 + /// // Address-equal means the same lock. 215 + /// assert!(core::ptr::eq(guard.lock_ref(), lock)); 216 + /// } 217 + /// 218 + /// // Creates a new lock on the stack. 219 + /// stack_pin_init!{ 220 + /// let l = new_spinlock!(42) 221 + /// } 222 + /// 223 + /// let g = l.lock(); 224 + /// 225 + /// // `g` originates from `l`. 226 + /// assert_held(&g, &l); 227 + /// ``` 228 + pub fn lock_ref(&self) -> &'a Lock<T, B> { 229 + self.lock 230 + } 231 + 203 232 pub(crate) fn do_unlocked<U>(&mut self, cb: impl FnOnce() -> U) -> U { 204 233 // SAFETY: The caller owns the lock, so it is safe to unlock it. 205 234 unsafe { B::unlock(self.lock.state.get(), &self.state) };
+3 -2
rust/kernel/sync/lock/global.rs
··· 13 13 use core::{ 14 14 cell::UnsafeCell, 15 15 marker::{PhantomData, PhantomPinned}, 16 + pin::Pin, 16 17 }; 17 18 18 19 /// Trait implemented for marker types for global locks. ··· 27 26 /// The backend used for this global lock. 28 27 type Backend: Backend + 'static; 29 28 /// The class for this global lock. 30 - fn get_lock_class() -> &'static LockClassKey; 29 + fn get_lock_class() -> Pin<&'static LockClassKey>; 31 30 } 32 31 33 32 /// Type used for global locks. ··· 271 270 type Item = $valuety; 272 271 type Backend = $crate::global_lock_inner!(backend $kind); 273 272 274 - fn get_lock_class() -> &'static $crate::sync::LockClassKey { 273 + fn get_lock_class() -> Pin<&'static $crate::sync::LockClassKey> { 275 274 $crate::static_lock_class!() 276 275 } 277 276 }
+1 -1
rust/kernel/sync/poll.rs
··· 89 89 90 90 impl PollCondVar { 91 91 /// Constructs a new condvar initialiser. 92 - pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> { 92 + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self> { 93 93 pin_init!(Self { 94 94 inner <- CondVar::new(name, key), 95 95 })
+2
rust/kernel/task.rs
··· 23 23 pub const TASK_INTERRUPTIBLE: c_int = bindings::TASK_INTERRUPTIBLE as c_int; 24 24 /// Bitmask for tasks that are sleeping in an uninterruptible state. 25 25 pub const TASK_UNINTERRUPTIBLE: c_int = bindings::TASK_UNINTERRUPTIBLE as c_int; 26 + /// Bitmask for tasks that are sleeping in a freezable state. 27 + pub const TASK_FREEZABLE: c_int = bindings::TASK_FREEZABLE as c_int; 26 28 /// Convenience constant for waking up tasks regardless of whether they are in interruptible or 27 29 /// uninterruptible sleep. 28 30 pub const TASK_NORMAL: c_uint = bindings::TASK_NORMAL as c_uint;
+1 -1
rust/kernel/workqueue.rs
··· 369 369 impl<T: ?Sized, const ID: u64> Work<T, ID> { 370 370 /// Creates a new instance of [`Work`]. 371 371 #[inline] 372 - pub fn new(name: &'static CStr, key: &'static LockClassKey) -> impl PinInit<Self> 372 + pub fn new(name: &'static CStr, key: Pin<&'static LockClassKey>) -> impl PinInit<Self> 373 373 where 374 374 T: WorkItem<ID>, 375 375 {