Patch "locking/qspinlock: Fix spin_unlock_wait() some more" has been added to the 4.6-stable tree
Subsystems (3)
This is a note to let you know that I've just added the patch titled
locking/qspinlock: Fix spin_unlock_wait() some more
to the 4.6-stable tree which can be found at:
http://www.kernel.org/git/?p=linux/kernel/git/stable/stable-queue.git;a=summary
The filename of the patch is:
locking-qspinlock-fix-spin_unlock_wait-some-more.patch
and it can be found in the queue-4.6 subdirectory.
If you, or anyone else, feels it should not be added to the stable tree, please let <[email protected]> know about it.
Quoted 1 line
From
2c610022711675ee908b903d242f0b90e1db661fMon Sep 17 00:00:00 2001
From: Peter Zijlstra <[email protected]>
Date: Wed, 8 Jun 2016 10:19:51 +0200
Subject: locking/qspinlock: Fix spin_unlock_wait() some more
From: Peter Zijlstra <[email protected]>
commit 2c610022711675ee908b903d242f0b90e1db661f upstream.
While this prior commit:
54cf809b9512 ("locking,qspinlock: Fix spin_is_locked() and spin_unlock_wait()")
... fixes spin_is_locked() and spin_unlock_wait() for the usage in ipc/sem and netfilter, it does not in fact work right for the usage in task_work and futex.
So while the 2 locks crossed problem:
spin_lock(A) spin_lock(B) if (!spin_is_locked(B)) spin_unlock_wait(A) foo() foo();
... works with the smp_mb() injected by both spin_is_locked() and spin_unlock_wait(), this is not sufficient for:
flag = 1; smp_mb(); spin_lock() spin_unlock_wait() if (!flag) // add to lockless list // iterate lockless list
... because in this scenario, the store from spin_lock() can be delayed past the load of flag, uncrossing the variables and loosing the guarantee.
This patch reworks spin_is_locked() and spin_unlock_wait() to work in both cases by exploiting the observation that while the lock byte store can be delayed, the contender must have registered itself visibly in other state contained in the word.
It also allows for architectures to override both functions, as PPC and ARM64 have an additional issue for which we currently have no generic solution.
--- include/asm-generic/qspinlock.h | 53 +++++++++++------------------------ kernel/locking/qspinlock.c | 60 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 77 insertions(+), 36 deletions(-)
include/asm-generic/qspinlock.h +17 -36
@@ -22,37 +22,33 @@#include <asm-generic/qspinlock_types.h> /** +* queued_spin_unlock_wait - wait until the _current_ lock holder releases the lock +* @lock : Pointer to queued spinlock structure +* +* There is a very slight possibility of live-lock if the lockers keep coming +* and the waiter is just unfortunate enough to not see any unlock state. +*/ +#ifndef queued_spin_unlock_wait +extern void queued_spin_unlock_wait (struct qspinlock *lock); +#endif + +/** * queued_spin_is_locked - is the spinlock locked? * @lock: Pointer to queued spinlock structure * Return: 1 if it is locked, 0 otherwise */ +#ifndef queued_spin_is_locked static __always_inline int queued_spin_is_locked(struct qspinlock *lock) {/* -* queued_spin_lock_slowpath() can ACQUIRE the lock before -* issuing the unordered store that sets _Q_LOCKED_VAL. -* -* See both smp_cond_acquire() sites for more detail. -* -* This however means that in code like: -* -* spin_lock(A) spin_lock(B) -* spin_unlock_wait(B) spin_is_locked(A) -* do_something() do_something() -* -* Both CPUs can end up running do_something() because the store -* setting _Q_LOCKED_VAL will pass through the loads in -* spin_unlock_wait() and/or spin_is_locked(). +* See queued_spin_unlock_wait(). * -* Avoid this by issuing a full memory barrier between the spin_lock() -* and the loads in spin_unlock_wait() and spin_is_locked(). -* -* Note that regular mutual exclusion doesn't care about this -* delayed store. +* Any !0 state indicates it is locked, even if _Q_LOCKED_VAL +* isn't immediately observable. */ -smp_mb (); -return atomic_read (&lock->val) & _Q_LOCKED_MASK; +return atomic_read (&lock->val); } +#endif /** * queued_spin_value_unlocked - is the spinlock structure unlocked?
@@ -122,21 +118,6 @@ static __always_inline void queued_spin_ }#endif -/** -* queued_spin_unlock_wait - wait until current lock holder releases the lock -* @lock : Pointer to queued spinlock structure -* -* There is a very slight possibility of live-lock if the lockers keep coming -* and the waiter is just unfortunate enough to not see any unlock state. -*/ -static inline void queued_spin_unlock_wait (struct qspinlock *lock) -{ -/* See queued_spin_is_locked() */ -smp_mb (); -while (atomic_read (&lock->val) & _Q_LOCKED_MASK) -cpu_relax (); -} -#ifndef virt_spin_lock static __always_inline bool virt_spin_lock(struct qspinlock *lock) {
kernel/locking/qspinlock.c +60 -0
66 lines not on this page · raw message
Patches currently in stable-queue which might be from [email protected] are
queue-4.6/futex-calculate-the-futex-key-based-on-a-tail-page-for-file-based-futexes.patch queue-4.6/kprobes-x86-clear-tf-bit-in-fault-on-single-stepping.patch queue-4.6/x86-amd_nb-fix-boot-crash-on-non-amd-systems.patch queue-4.6/perf-x86-fix-32-bit-perf-user-callgraph-collection.patch queue-4.6/locking-qspinlock-fix-spin_unlock_wait-some-more.patch queue-4.6/locking-static_key-fix-concurrent-static_key_slow_inc.patch queue-4.6/perf-x86-intel-rapl-fix-pmus-free-during-cleanup.patch queue-4.6/locking-ww_mutex-report-recursive-ww_mutex-locking-early.patch