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

sched/wake_q: Add helper to call wake_up_q after unlock with preemption disabled

A common pattern seen when wake_qs are used to defer a wakeup
until after a lock is released is something like:
preempt_disable();
raw_spin_unlock(lock);
wake_up_q(wake_q);
preempt_enable();

So create some raw_spin_unlock*_wake() helper functions to clean
this up.

Applies on top of the fix I submitted here:
https://lore.kernel.org/lkml/20241212222138.2400498-1-jstultz@google.com/

NOTE: I recognise the unlock()/unlock_irq()/unlock_irqrestore()
variants creates its own duplication, which we could use a macro
to generate the similar functions, but I often dislike how those
generation macros making finding the actual implementation
harder, so I left the three functions as is. If folks would
prefer otherwise, let me know and I'll switch it.

Suggested-by: Peter Zijlstra <peterz@infradead.org>
Signed-off-by: John Stultz <jstultz@google.com>
Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org>
Link: https://lkml.kernel.org/r/20241217040803.243420-1-jstultz@google.com

authored by

John Stultz and committed by
Peter Zijlstra
abfdccd6 c2db11a7

+44 -43
+34
include/linux/sched/wake_q.h
··· 63 63 extern void wake_q_add_safe(struct wake_q_head *head, struct task_struct *task); 64 64 extern void wake_up_q(struct wake_q_head *head); 65 65 66 + /* Spin unlock helpers to unlock and call wake_up_q with preempt disabled */ 67 + static inline 68 + void raw_spin_unlock_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q) 69 + { 70 + guard(preempt)(); 71 + raw_spin_unlock(lock); 72 + if (wake_q) { 73 + wake_up_q(wake_q); 74 + wake_q_init(wake_q); 75 + } 76 + } 77 + 78 + static inline 79 + void raw_spin_unlock_irq_wake(raw_spinlock_t *lock, struct wake_q_head *wake_q) 80 + { 81 + guard(preempt)(); 82 + raw_spin_unlock_irq(lock); 83 + if (wake_q) { 84 + wake_up_q(wake_q); 85 + wake_q_init(wake_q); 86 + } 87 + } 88 + 89 + static inline 90 + void raw_spin_unlock_irqrestore_wake(raw_spinlock_t *lock, unsigned long flags, 91 + struct wake_q_head *wake_q) 92 + { 93 + guard(preempt)(); 94 + raw_spin_unlock_irqrestore(lock, flags); 95 + if (wake_q) { 96 + wake_up_q(wake_q); 97 + wake_q_init(wake_q); 98 + } 99 + } 66 100 #endif /* _LINUX_SCHED_WAKE_Q_H */
+1 -4
kernel/futex/pi.c
··· 1020 1020 * it sees the futex_q::pi_state. 1021 1021 */ 1022 1022 ret = __rt_mutex_start_proxy_lock(&q.pi_state->pi_mutex, &rt_waiter, current, &wake_q); 1023 - preempt_disable(); 1024 - raw_spin_unlock_irq(&q.pi_state->pi_mutex.wait_lock); 1025 - wake_up_q(&wake_q); 1026 - preempt_enable(); 1023 + raw_spin_unlock_irq_wake(&q.pi_state->pi_mutex.wait_lock, &wake_q); 1027 1024 1028 1025 if (ret) { 1029 1026 if (ret == 1)
+4 -12
kernel/locking/mutex.c
··· 657 657 goto err; 658 658 } 659 659 660 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 661 - /* Make sure we do wakeups before calling schedule */ 662 - wake_up_q(&wake_q); 663 - wake_q_init(&wake_q); 660 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 664 661 665 662 schedule_preempt_disabled(); 666 663 ··· 707 710 if (ww_ctx) 708 711 ww_mutex_lock_acquired(ww, ww_ctx); 709 712 710 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 711 - wake_up_q(&wake_q); 713 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 712 714 preempt_enable(); 713 715 return 0; 714 716 ··· 716 720 __mutex_remove_waiter(lock, &waiter); 717 721 err_early_kill: 718 722 trace_contention_end(lock, ret); 719 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 723 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 720 724 debug_mutex_free_waiter(&waiter); 721 725 mutex_release(&lock->dep_map, ip); 722 - wake_up_q(&wake_q); 723 726 preempt_enable(); 724 727 return ret; 725 728 } ··· 930 935 if (owner & MUTEX_FLAG_HANDOFF) 931 936 __mutex_handoff(lock, next); 932 937 933 - preempt_disable(); 934 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 935 - wake_up_q(&wake_q); 936 - preempt_enable(); 938 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 937 939 } 938 940 939 941 #ifndef CONFIG_DEBUG_LOCK_ALLOC
+5 -27
kernel/locking/rtmutex.c
··· 1292 1292 */ 1293 1293 get_task_struct(owner); 1294 1294 1295 - preempt_disable(); 1296 - raw_spin_unlock_irq(&lock->wait_lock); 1297 - /* wake up any tasks on the wake_q before calling rt_mutex_adjust_prio_chain */ 1298 - wake_up_q(wake_q); 1299 - wake_q_init(wake_q); 1300 - preempt_enable(); 1301 - 1295 + raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); 1302 1296 1303 1297 res = rt_mutex_adjust_prio_chain(owner, chwalk, lock, 1304 1298 next_lock, waiter, task); ··· 1636 1642 owner = rt_mutex_owner(lock); 1637 1643 else 1638 1644 owner = NULL; 1639 - preempt_disable(); 1640 - raw_spin_unlock_irq(&lock->wait_lock); 1641 - if (wake_q) { 1642 - wake_up_q(wake_q); 1643 - wake_q_init(wake_q); 1644 - } 1645 - preempt_enable(); 1645 + raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); 1646 1646 1647 1647 if (!owner || !rtmutex_spin_on_owner(lock, waiter, owner)) 1648 1648 rt_mutex_schedule(); ··· 1787 1799 */ 1788 1800 raw_spin_lock_irqsave(&lock->wait_lock, flags); 1789 1801 ret = __rt_mutex_slowlock_locked(lock, ww_ctx, state, &wake_q); 1790 - preempt_disable(); 1791 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 1792 - wake_up_q(&wake_q); 1793 - preempt_enable(); 1802 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 1794 1803 rt_mutex_post_schedule(); 1795 1804 1796 1805 return ret; ··· 1845 1860 owner = rt_mutex_owner(lock); 1846 1861 else 1847 1862 owner = NULL; 1848 - preempt_disable(); 1849 - raw_spin_unlock_irq(&lock->wait_lock); 1850 - wake_up_q(wake_q); 1851 - wake_q_init(wake_q); 1852 - preempt_enable(); 1863 + raw_spin_unlock_irq_wake(&lock->wait_lock, wake_q); 1853 1864 1854 1865 if (!owner || !rtmutex_spin_on_owner(lock, &waiter, owner)) 1855 1866 schedule_rtlock(); ··· 1874 1893 1875 1894 raw_spin_lock_irqsave(&lock->wait_lock, flags); 1876 1895 rtlock_slowlock_locked(lock, &wake_q); 1877 - preempt_disable(); 1878 - raw_spin_unlock_irqrestore(&lock->wait_lock, flags); 1879 - wake_up_q(&wake_q); 1880 - preempt_enable(); 1896 + raw_spin_unlock_irqrestore_wake(&lock->wait_lock, flags, &wake_q); 1881 1897 } 1882 1898 1883 1899 #endif /* RT_MUTEX_BUILD_SPINLOCKS */