Remove spin_unlock_wait()
Subsystems (20)
cover Remove spin_unlock_wait()
Hello!
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removes spin_unlock_wait() and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:
1-7. Change uses of spin_unlock_wait() and raw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
8. Remove core-kernel definitions for spin_unlock_wait() and
raw_spin_unlock_wait().
9-26. Remove arch-specific definitions of arch_spin_unlock_wait().
Thoughts?
Thanx, Paul
------------------------------------------------------------------------
arch/alpha/include/asm/spinlock.h | 5 - arch/arc/include/asm/spinlock.h | 5 - arch/arm/include/asm/spinlock.h | 16 ---- arch/arm64/include/asm/spinlock.h | 58 +---------------- arch/blackfin/include/asm/spinlock.h | 5 - arch/hexagon/include/asm/spinlock.h | 5 - arch/ia64/include/asm/spinlock.h | 21 ------ arch/m32r/include/asm/spinlock.h | 5 - arch/metag/include/asm/spinlock.h | 5 - arch/mips/include/asm/spinlock.h | 16 ---- arch/mn10300/include/asm/spinlock.h | 5 - arch/parisc/include/asm/spinlock.h | 7 -- arch/powerpc/include/asm/spinlock.h | 33 --------- arch/s390/include/asm/spinlock.h | 7 -- arch/sh/include/asm/spinlock-cas.h | 5 - arch/sh/include/asm/spinlock-llsc.h | 5 - arch/sparc/include/asm/spinlock_32.h | 5 - arch/sparc/include/asm/spinlock_64.h | 5 - arch/tile/include/asm/spinlock_32.h | 2 arch/tile/include/asm/spinlock_64.h | 2 arch/tile/lib/spinlock_32.c | 23 ------ arch/tile/lib/spinlock_64.c | 22 ------ arch/xtensa/include/asm/spinlock.h | 5 - drivers/ata/libata-eh.c | 8 -- include/asm-generic/qspinlock.h | 14 ---- include/linux/spinlock.h | 31 --------- include/linux/spinlock_up.h | 6 - ipc/sem.c | 3 kernel/exit.c | 3 kernel/locking/qspinlock.c | 117 ----------------------------------- kernel/sched/completion.c | 9 -- kernel/sched/core.c | 3 kernel/task_work.c | 3 net/netfilter/nf_conntrack_core.c | 26 ++----- 34 files changed, 26 insertions(+), 464 deletions(-)
RFC PATCH 1/26 netfilter: Replace spin_unlock_wait() with lock/unlock pair
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() calls in nf_conntrack_lock() and nf_conntrack_all_lock() with spin_lock() followed immediately by spin_unlock(). These functions do not appear to be invoked on any fastpaths.
--- net/netfilter/nf_conntrack_core.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-)
net/netfilter/nf_conntrack_core.c +8 -18
@@ -99,15 +99,11 @@ void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)spin_lock (lock);while (unlikely (nf_conntrack_locks_all)) {spin_unlock (lock); - -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); +/* Wait for nf_conntrack_locks_all_lock holder to release ... */ +spin_lock (&nf_conntrack_locks_all_lock); +spin_unlock (&nf_conntrack_locks_all_lock);spin_lock (lock); +/* ... and retry. */ } }EXPORT_SYMBOL_GPL (nf_conntrack_lock);
@@ -150,17 +146,11 @@ static void nf_conntrack_all_lock(void)spin_lock (&nf_conntrack_locks_all_lock); nf_conntrack_locks_all = true; - -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +/* Wait for any current holder to release lock. */ +spin_lock (&nf_conntrack_locks[i]); +spin_unlock (&nf_conntrack_locks[i]); +/* Next acquisition will see nf_conntrack_locks_all == true. */ } }
Signature · 1 line
2.5.2
A message that is not in the archive (sent off-list or not yet received): [email protected]
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Quoted 54 lines · Manfred Spraul
Hi Paul,
Quoted 29 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the
spin_unlock_wait()calls innf_conntrack_lock()andnf_conntrack_all_lock()withspin_lock()followed immediately byspin_unlock(). These functions do not appear to be invoked on any fastpaths.Signed-off-by Paul E. McKenney<[email protected]>Cc Pablo Neira Ayuso<[email protected]>Cc Jozsef Kadlecsik<[email protected]>Cc Florian Westphal<[email protected]>Cc "David S. Miller"<[email protected]>Cc <[email protected]>Cc <[email protected]>Cc <[email protected]>Cc Will Deacon<[email protected]>Cc Peter Zijlstra<[email protected]>Cc Alan Stern<[email protected]>Cc Andrea Parri<[email protected]>Cc Linus Torvalds<[email protected]>--- net/netfilter/nf_conntrack_core.c | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-)net/netfilter/nf_conntrack_core.c +0
-0net/netfilter/nf_conntrack_core.c void nf_conntrack_lock(spinlock_t *lock) __acquires(lock) · line 100
Quoted 2 lines
net/netfilter/nf_conntrack_core.c +0
-0@@ -99,15 +99,11 @@ void nf_conntrack_lock(spinlock_t *lock) __acquires(lock)spin_lock (lock);while (unlikely (nf_conntrack_locks_all)) {I think here an ACQUIRE is missing.
Quoted 6 lines
net/netfilter/nf_conntrack_core.c +0
-5spin_unlock (lock); - -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: net/netfilter/nf_conntrack_core.c void nf_conntrack_lock(spinlock_t *lock) __acquires(lock) · line 108
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +4
-3-*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); +/* Wait for nf_conntrack_locks_all_lock holder to release ... */ +spin_lock (&nf_conntrack_locks_all_lock); +spin_unlock (&nf_conntrack_locks_all_lock);spin_lock (lock); +/* ... and retry. */ } }As far as I see, nf_conntrack_locks[] nests inside
nf_conntrack_lock_all_lock.
So
Quoted 7 lines
spin_lock(&nf_conntrack_locks_all_lock); spin_lock(lock); spin_unlock(&nf_conntrack_locks_all_lock);can replace the retry logic.
Correct? Then what about the attached patch?
At first glance, it looks correct to me, thank you! I have replaced my patch with this one for testing and further review.
Thanx, Paul
Quoted 101 lines
--
ManfredQuoted 1 line
From
453e7a77f3756d939c754031b092cbdfbd149559Mon Sep 17 00:00:00 2001From: Manfred Spraul <[email protected]>
Date: Sun, 21 Aug 2016 07:17:55 +0200
Subject: [PATCH]net/netfilter/nf_conntrack_core: Fixnet_conntrack_lock()As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +24
-20@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ; + +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);@@ -149,18 +154,17 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }Signature · 1 line
2.9.4
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
In reply to a message not in the archiveQuoted 36 lines · Manfred Spraul
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +1
-4@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the
net/netfilter/nf_conntrack_core.c static struct conntrack_gc_work conntrack_gc_work; · line 104
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +3
-7-* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ;
As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
(1) We read nf_conntrack_locks_all == false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock in nf_conntrack_all_lock().
(2) We read nf_conntrack_locks_all == true, and this routine's critical section for nf_conntrack_locks_all_lock runs before the critical section in nf_conntrack_all_lock().
In fact, neither one can happen even if smp_load_acquire() is replaced with READ_ONCE(). The reason is simple enough, using this property of spinlocks:
If critical section CS1 runs before critical section CS2 (for the same lock) then: (a) every write coming before CS1's spin_unlock() will be visible to any read coming after CS2's spin_lock(), and (b) no write coming after CS2's spin_lock() will be visible to any read coming before CS1's spin_unlock().
Thus for (1), assuming the critical sections run in the order mentioned above, since nf_conntrack_all_lock() writes to nf_conntrack_locks_all before releasing nf_conntrack_locks[i], and since nf_conntrack_lock() acquires nf_conntrack_locks[i] before reading nf_conntrack_locks_all, by (a) the read will always see the write.
Similarly for (2), since nf_conntrack_all_lock() acquires nf_conntrack_locks_all_lock before writing to nf_conntrack_locks_all, and since nf_conntrack_lock() reads nf_conntrack_locks_all before releasing nf_conntrack_locks_all_lock, by (b) the read cannot see the write.
Alan Stern
Quoted 41 lines
net/netfilter/nf_conntrack_core.c +20
-9+ +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);@@ -149,18 +154,17 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Quoted 79 lines · Alan Stern
Quoted 36 lines · Manfred Spraul
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +1
-4@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the net/netfilter/nf_conntrack_core.c static struct conntrack_gc_work conntrack_gc_work; · line 104
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +3
-7-* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ;As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
(1) We read
nf_conntrack_locks_all== false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock innf_conntrack_all_lock().(2) We read
nf_conntrack_locks_all== true, and this routine's critical section fornf_conntrack_locks_all_lockruns before the critical section innf_conntrack_all_lock().In fact, neither one can happen even if
smp_load_acquire()is replaced withREAD_ONCE(). The reason is simple enough, using this property of spinlocks:If critical section CS1 runs before critical section CS2 (for the same lock) then: (a) every write coming before CS1's
spin_unlock()will be visible to any read coming after CS2'sspin_lock(), and (b) no write coming after CS2'sspin_lock()will be visible to any read coming before CS1'sspin_unlock().Thus for (1), assuming the critical sections run in the order mentioned above, since
nf_conntrack_all_lock()writes tonf_conntrack_locks_allbefore releasing nf_conntrack_locks[i], and sincenf_conntrack_lock()acquires nf_conntrack_locks[i] before readingnf_conntrack_locks_all, by (a) the read will always see the write.
Quoted 5 lines
Similarly for (2), since
nf_conntrack_all_lock()acquiresnf_conntrack_locks_all_lockbefore writing tonf_conntrack_locks_all, and sincenf_conntrack_lock()readsnf_conntrack_locks_allbefore releasingnf_conntrack_locks_all_lock, by (b) the read cannot see the write.
And the Linux kernel memory model (https://lwn.net/Articles/718628/
and https://lwn.net/Articles/720550/) agrees with Alan. Here is a litmus test, which emulates spin_lock() with xchg_acquire() and spin_unlock() with smp_store_release():
------------------------------------------------------------------------
C C-ManfredSpraul-L1G1xchgnr.litmus
(* Expected result: Never. *)
{
}
P0(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held)
{
/* Acquire local lock. */
r10 = xchg_acquire(lcl, 1);
r1 = READ_ONCE(*nfcla);
if (r1) {
smp_store_release(lcl, 0);
r11 = xchg_acquire(gbl, 1);
r12 = xchg_acquire(lcl, 1);
smp_store_release(gbl, 0);
}
r2 = READ_ONCE(*gbl_held);
WRITE_ONCE(*lcl_held, 1);
WRITE_ONCE(*lcl_held, 0);
smp_store_release(lcl, 0);
}P1(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held)
{
/* Acquire global lock. */
r10 = xchg_acquire(gbl, 1);
WRITE_ONCE(*nfcla, 1);
r11 = xchg_acquire(lcl, 1);
smp_store_release(lcl, 0);
r2 = READ_ONCE(*lcl_held);
WRITE_ONCE(*gbl_held, 1);
WRITE_ONCE(*gbl_held, 0);
smp_store_release(gbl, 0);
}exists
((0:r2=1 \/ 1:r2=1) /\ 0:r10=0 /\ 0:r11=0 /\ 0:r12=0 /\ 1:r10=0 /\ 1:r11=0)
------------------------------------------------------------------------
The memory model says that the forbidden state does not happen:
------------------------------------------------------------------------
States 25
0:r10=0; 0:r11=0; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=0; 0:r11=0; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=1; 1:r2=0;
0:r10=0; 0:r11=0; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=1; 1:r2=1;
0:r10=0; 0:r11=0; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=1; 1:r2=0;
0:r10=0; 0:r11=0; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=1; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=1; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=1; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=1; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=1; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=1; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=1; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=0; 0:r11=1; 0:r12=1; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=0; 0:r11=1; 0:r12=1; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=1; 0:r11=0; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=1; 0:r11=0; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=1; 0:r11=0; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=1; 0:r11=0; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=1; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=1; 0:r11=1; 0:r12=0; 0:r2=0; 1:r10=0; 1:r11=0; 1:r2=1;
0:r10=1; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=0;
0:r10=1; 0:r11=1; 0:r12=0; 0:r2=1; 1:r10=0; 1:r11=0; 1:r2=1;
No
Witnesses
Positive: 0 Negative: 260
Condition exists ((0:r2=1 \/ 1:r2=1) /\ 0:r10=0 /\ 0:r11=0 /\ 0:r12=0 /\ 1:r10=0 /\ 1:r11=0) Observation C-ManfredSpraul-L1G1xchgnr Never 0 260
------------------------------------------------------------------------
(Note the line "Positive: 0 Negative: 260", in other words, there were no scenarios that matched the "exists" clause and 260 that did not match.)
Of course, testing is also required. ;-)
Manfred, any objections to my changing your patch as Alan suggests?
Thanx, Paul
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Quoted 54 lines · Paul E. McKenney
Quoted 53 lines · Alan Stern
Quoted 36 lines · Manfred Spraul
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +1
-4@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the net/netfilter/nf_conntrack_core.c static struct conntrack_gc_work conntrack_gc_work; · line 104
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +3
-7-* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ;As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
Quoted 8 lines
Quoted 8 lines
(1) We read
nf_conntrack_locks_all== false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock innf_conntrack_all_lock().(2) We read
nf_conntrack_locks_all== true, and this routine's critical section fornf_conntrack_locks_all_lockruns before the critical section innf_conntrack_all_lock().
I was looking at nf_conntrack_all_unlock:
There is a smp_store_release() - which memory barrier does this pair with?
nf_conntrack_all_unlock()
<arbitrary writes>
smp_store_release(a, false)
spin_unlock(b);nf_conntrack_lock()
spin_lock(c);
xx=read_once(a)
if (xx==false)
return
<arbitrary read>Quoted 9 lines
Quoted 9 lines
In fact, neither one can happen even if
smp_load_acquire()is replaced withREAD_ONCE(). The reason is simple enough, using this property of spinlocks:If critical section CS1 runs before critical section CS2 (for the same lock) then: (a) every write coming before CS1's
spin_unlock()will be visible to any read coming after CS2'sspin_lock(), and (b) no write coming after CS2'sspin_lock()will be visible to any read coming before CS1'sspin_unlock().
Does this apply? The locks are different.
Quoted 43 lines
Quoted 11 lines
Thus for (1), assuming the critical sections run in the order mentioned above, since
nf_conntrack_all_lock()writes tonf_conntrack_locks_allbefore releasing nf_conntrack_locks[i], and sincenf_conntrack_lock()acquires nf_conntrack_locks[i] before readingnf_conntrack_locks_all, by (a) the read will always see the write.Similarly for (2), since
nf_conntrack_all_lock()acquiresnf_conntrack_locks_all_lockbefore writing tonf_conntrack_locks_all, and sincenf_conntrack_lock()readsnf_conntrack_locks_allbefore releasingnf_conntrack_locks_all_lock, by (b) the read cannot see the write.And the Linux kernel memory model (https://lwn.net/Articles/718628/
and https://lwn.net/Articles/720550/) agrees with Alan. Here is a litmus test, which emulatesspin_lock()withxchg_acquire()andspin_unlock()withsmp_store_release():------------------------------------------------------------------------
C C-ManfredSpraul-L1G1xchgnr.litmus
(* Expected result: Never. *)
{
}
P0(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held){ /* Acquire local lock. */ r10 = xchg_acquire(lcl, 1); r1 = READ_ONCE(*nfcla); if (r1) { smp_store_release(lcl, 0); r11 = xchg_acquire(gbl, 1); r12 = xchg_acquire(lcl, 1); smp_store_release(gbl, 0); } r2 = READ_ONCE(*gbl_held); WRITE_ONCE(*lcl_held, 1); WRITE_ONCE(*lcl_held, 0); smp_store_release(lcl, 0); }
P1(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held)
Quoted 9 lines
{
/* Acquire global lock. */
r10 = xchg_acquire(gbl, 1);
WRITE_ONCE(*nfcla, 1);
r11 = xchg_acquire(lcl, 1);
smp_store_release(lcl, 0);
r2 = READ_ONCE(*lcl_held);
WRITE_ONCE(*gbl_held, 1);
WRITE_ONCE(*gbl_held, 0);Where is the write that resets nfcla=0?
Quoted 9 lines
smp_store_release(gbl, 0); }exists
((0:r2=1 \/ 1:r2=1) /\ 0:r10=0 /\ 0:r11=0 /\ 0:r12=0 /\ 1:r10=0 /\ 1:r11=0)------------------------------------------------------------------------
The memory model says that the forbidden state does not happen:
[...]
Quoted 1 line
Manfred, any objections to my changing your patch as Alan suggests?
I tried to pair the memory barriers:nf_conntrack_all_unlock() contains a smp_store_release(). What does that pair with?
--
Manfred
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Quoted 24 lines · Manfred Spraul
Quoted 8 lines
Quoted 8 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ /* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ + if (likely(smp_load_acquire(&nf_conntrack_locks_all) == false)) + return;As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
Quoted 8 lines
Quoted 8 lines
(1) We read
nf_conntrack_locks_all== false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock innf_conntrack_all_lock().(2) We read
nf_conntrack_locks_all== true, and this routine's critical section fornf_conntrack_locks_all_lockruns before the critical section innf_conntrack_all_lock().I was looking at
nf_conntrack_all_unlock:
There is asmp_store_release()- which memory barrier does this pair with?
nf_conntrack_all_unlock()<arbitrary writes> smp_store_release(a, false) spin_unlock(b);
Quoted 6 lines
nf_conntrack_lock()spin_lock(c); xx=read_once(a) if (xx==false) return <arbitrary read>
Ah, I see your point. Yes, I did wonder about what would happen when nf_conntrack_locks_all was set back to false. But I didn't think about it any further, because the relevant code wasn't in your patch.
Quoted 3 lines
I tried to pair the memory barriers:
nf_conntrack_all_unlock()contains asmp_store_release(). What does that pair with?
You are right, this does need to be smp_load_acquire() after all. Perhaps the preceding comment should mention that it pairs with the smp_store_release() from an earlier invocation of nf_conntrack_all_unlock().
(Alternatively, you could make nf_conntrack_all_unlock() do a lock+unlock on all the locks in the array, just like nf_conntrack_all_lock(). But of course, that would be a lot less efficient.)
Alan Stern
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Hi Alan,
Quoted 5 lines · Alan Stern
(Alternatively, you could make
nf_conntrack_all_unlock()do a lock+unlock on all the locks in the array, just likenf_conntrack_all_lock(). But of course, that would be a lot less efficient.)
Hmmmm.
Someone with a weakly ordered system who can test this? semop() has a very short hotpath.
Either with aim9.shared_memory.ops_per_sec or
#sem-scalebench -t 10 -m 0
https://github.com/manfred-colorfu/ipcscale/blob/master/sem-scalebench.cpp
--
Manfred
Quoted 1 line
From
b549e0281b66124b62aa94543f91b0e616abaf52Mon Sep 17 00:00:00 2001
From: Manfred Spraul <[email protected]>
Date: Thu, 6 Jul 2017 20:05:44 +0200
Subject: [PATCH 2/2] ipc/sem.c: avoid smp_load_acuqire() in the hot-path
Alan Stern came up with an interesting idea:
If we perform a spin_lock()/spin_unlock() pair in the slow path, then we can skip the smp_load_acquire() in the hot path.
What do you think?
* When we removed the smp_mb() from the hot path, it was a user space visible speed-up of 11%:
https://lists.01.org/pipermail/lkp/2017-February/005520.html
* On x86, there is no improvement - as smp_load_acquire is READ_ONCE().
* Slowing down the slow path should not hurt:
Due to the hysteresis code, the slow path is at least factor 10 rarer than it was before.
Especially: Who is able to test it?
--- ipc/sem.c | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-)
ipc/sem.c +19 -14
@@ -186,16 +186,15 @@ static int sysvipc_sem_proc_show(struct seq_file *s, void *it);* * either local or global sem_lock() for read. * * Memory ordering: -* Most ordering is enforced by using spin_lock() and spin_unlock(). +* All ordering is enforced by using spin_lock() and spin_unlock(). * The special case is use_global_lock: * Setting it from non-zero to 0 is a RELEASE, this is ensured by -* using smp_store_release(). -* Testing if it is non-zero is an ACQUIRE, this is ensured by using -* smp_load_acquire(). -* Setting it from 0 to non-zero must be ordered with regards to -* this smp_load_acquire(), this is guaranteed because the smp_load_acquire() -* is inside a spin_lock() and after a write from 0 to non-zero a -* spin_lock()+spin_unlock() is done. +* performing spin_lock()/spin_lock() on every semaphore before setting to +* non-zero. +* Setting it from 0 to non-zero is an ACQUIRE, this is ensured by +* performing spin_lock()/spin_lock() on every semaphore after setting to +* non-zero. +* Testing if it is non-zero is within spin_lock(), no need for a barrier. */ #define sc_semmsl sem_ctls[0]
@@ -325,13 +324,20 @@ static void complexmode_tryleave(struct sem_array *sma)return ; }if (sma->use_global_lock ==1 ) { +int i; +struct sem *sem;/* * Immediately after setting use_global_lock to 0, -* a simple op can start. Thus: all memory writes -* performed by the current operation must be visible -* before we set use_global_lock to 0. +* a simple op can start. +* Perform a full lock/unlock, to guarantee memory +* ordering. */ -smp_store_release (&sma->use_global_lock,0 ); +for (i =0 ; i < sma->sem_nsems; i++) { + sem = sma->sem_base + i; +spin_lock (&sem->lock); +spin_unlock (&sem->lock); + } + sma->use_global_lock =0 ; }else { sma->use_global_lock--; }
@@ -379,8 +385,7 @@ static inline int sem_lock(struct sem_array *sma, struct sembuf *sops,*/ spin_lock (&sem->lock); -/* pairs with smp_store_release() */ -if (!smp_load_acquire (&sma->use_global_lock)) { +if (!sma->use_global_lock) {/* fast path successful! */ return sops->sem_num; }
Signature · 1 line
2.9.4
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyQuoted 118 lines · Paul E. McKenney
Quoted 79 lines · Alan Stern
Quoted 36 lines · Manfred Spraul
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +1
-4@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the net/netfilter/nf_conntrack_core.c static struct conntrack_gc_work conntrack_gc_work; · line 104
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +3
-7-* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ;As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
(1) We read
nf_conntrack_locks_all== false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock innf_conntrack_all_lock().(2) We read
nf_conntrack_locks_all== true, and this routine's critical section fornf_conntrack_locks_all_lockruns before the critical section innf_conntrack_all_lock().In fact, neither one can happen even if
smp_load_acquire()is replaced withREAD_ONCE(). The reason is simple enough, using this property of spinlocks:If critical section CS1 runs before critical section CS2 (for the same lock) then: (a) every write coming before CS1's
spin_unlock()will be visible to any read coming after CS2'sspin_lock(), and (b) no write coming after CS2'sspin_lock()will be visible to any read coming before CS1'sspin_unlock().Thus for (1), assuming the critical sections run in the order mentioned above, since
nf_conntrack_all_lock()writes tonf_conntrack_locks_allbefore releasing nf_conntrack_locks[i], and sincenf_conntrack_lock()acquires nf_conntrack_locks[i] before readingnf_conntrack_locks_all, by (a) the read will always see the write.Quoted 5 lines
Similarly for (2), since
nf_conntrack_all_lock()acquiresnf_conntrack_locks_all_lockbefore writing tonf_conntrack_locks_all, and sincenf_conntrack_lock()readsnf_conntrack_locks_allbefore releasingnf_conntrack_locks_all_lock, by (b) the read cannot see the write.And the Linux kernel memory model (https://lwn.net/Articles/718628/
and https://lwn.net/Articles/720550/) agrees with Alan. Here is a litmus test, which emulatesspin_lock()withxchg_acquire()andspin_unlock()withsmp_store_release():------------------------------------------------------------------------
C C-ManfredSpraul-L1G1xchgnr.litmus
(* Expected result: Never. *)
{
}
P0(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held){ /* Acquire local lock. */ r10 = xchg_acquire(lcl, 1); r1 = READ_ONCE(*nfcla); if (r1) { smp_store_release(lcl, 0); r11 = xchg_acquire(gbl, 1); r12 = xchg_acquire(lcl, 1); smp_store_release(gbl, 0); } r2 = READ_ONCE(*gbl_held); WRITE_ONCE(*lcl_held, 1); WRITE_ONCE(*lcl_held, 0); smp_store_release(lcl, 0); }
P1(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held)
Quoted 8 lines
{
/* Acquire global lock. */
r10 = xchg_acquire(gbl, 1);
WRITE_ONCE(*nfcla, 1);
r11 = xchg_acquire(lcl, 1);
smp_store_release(lcl, 0);
r2 = READ_ONCE(*lcl_held);
WRITE_ONCE(*gbl_held, 1);This litmus test is incomplete, because it omits the assignment setting nf_conntrack_locks_all back to false when the global lock is released. You should insert
smp_store_release(*nfcla, 0);
right here.
Quoted 6 lines
WRITE_ONCE(*gbl_held, 0); smp_store_release(gbl, 0); }exists
((0:r2=1 \/ 1:r2=1) /\ 0:r10=0 /\ 0:r11=0 /\ 0:r12=0 /\ 1:r10=0 /\ 1:r11=0)
With that addition, the litmus test fails unless the read of nfcla in P0 is an smp_load_acquire. So Manfred's patch should not be changed.
Alan Stern
Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair
Quoted 141 lines · Alan Stern
Quoted 118 lines · Paul E. McKenney
Quoted 79 lines · Alan Stern
Quoted 36 lines · Manfred Spraul
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)net/netfilter/nf_conntrack_core.c +1
-4@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the net/netfilter/nf_conntrack_core.c static struct conntrack_gc_work conntrack_gc_work; · line 104
Quoted 10 lines
net/netfilter/nf_conntrack_core.c +3
-7-* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ;As far as I can tell, this read does not need to have ACQUIRE semantics.
You need to guarantee that two things can never happen:
(1) We read
nf_conntrack_locks_all== false, and this routine's critical section for nf_conntrack_locks[i] runs after the (empty) critical section for that lock innf_conntrack_all_lock().(2) We read
nf_conntrack_locks_all== true, and this routine's critical section fornf_conntrack_locks_all_lockruns before the critical section innf_conntrack_all_lock().In fact, neither one can happen even if
smp_load_acquire()is replaced withREAD_ONCE(). The reason is simple enough, using this property of spinlocks:If critical section CS1 runs before critical section CS2 (for the same lock) then: (a) every write coming before CS1's
spin_unlock()will be visible to any read coming after CS2'sspin_lock(), and (b) no write coming after CS2'sspin_lock()will be visible to any read coming before CS1'sspin_unlock().Thus for (1), assuming the critical sections run in the order mentioned above, since
nf_conntrack_all_lock()writes tonf_conntrack_locks_allbefore releasing nf_conntrack_locks[i], and sincenf_conntrack_lock()acquires nf_conntrack_locks[i] before readingnf_conntrack_locks_all, by (a) the read will always see the write.Quoted 5 lines
Similarly for (2), since
nf_conntrack_all_lock()acquiresnf_conntrack_locks_all_lockbefore writing tonf_conntrack_locks_all, and sincenf_conntrack_lock()readsnf_conntrack_locks_allbefore releasingnf_conntrack_locks_all_lock, by (b) the read cannot see the write.And the Linux kernel memory model (https://lwn.net/Articles/718628/
and https://lwn.net/Articles/720550/) agrees with Alan. Here is a litmus test, which emulatesspin_lock()withxchg_acquire()andspin_unlock()withsmp_store_release():------------------------------------------------------------------------
C C-ManfredSpraul-L1G1xchgnr.litmus
(* Expected result: Never. *)
{
}
P0(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held){ /* Acquire local lock. */ r10 = xchg_acquire(lcl, 1); r1 = READ_ONCE(*nfcla); if (r1) { smp_store_release(lcl, 0); r11 = xchg_acquire(gbl, 1); r12 = xchg_acquire(lcl, 1); smp_store_release(gbl, 0); } r2 = READ_ONCE(*gbl_held); WRITE_ONCE(*lcl_held, 1); WRITE_ONCE(*lcl_held, 0); smp_store_release(lcl, 0); }
P1(int *nfcla, spinlock_t *gbl, int *gbl_held, spinlock_t *lcl, int *lcl_held)Quoted 8 lines
{ /* Acquire global lock. */ r10 = xchg_acquire(gbl, 1); WRITE_ONCE(*nfcla, 1); r11 = xchg_acquire(lcl, 1); smp_store_release(lcl, 0); r2 = READ_ONCE(*lcl_held); WRITE_ONCE(*gbl_held, 1);This litmus test is incomplete, because it omits the assignment setting
nf_conntrack_locks_allback to false when the global lock is released. You should insertsmp_store_release(*nfcla, 0);right here.
Quoted 4 lines
WRITE_ONCE(*gbl_held, 0); smp_store_release(gbl, 0); }
Quoted 5 lines
Quoted 2 lines
exists
((0:r2=1 \/ 1:r2=1) /\ 0:r10=0 /\ 0:r11=0 /\ 0:r12=0 /\ 1:r10=0 /\ 1:r11=0)With that addition, the litmus test fails unless the read of nfcla in P0 is an
smp_load_acquire. So Manfred's patch should not be changed.
Very good! Aside from updating litmus tests, my work is done! ;-)
Thanx, Paul
RFC PATCH 2/26 task_work: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in task_work_run() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because calls to the other side of the race, task_work_cancel(), should be rare.
--- kernel/task_work.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/task_work.c +2 -1
@@ -109,7 +109,8 @@ void task_work_run(void)* the first entry == work, cmpxchg(task_works) should * fail, but it can play with *work and other entries. */ -raw_spin_unlock_wait (&task->pi_lock); +raw_spin_lock (&task->pi_lock); +raw_spin_unlock (&task->pi_lock); do { next = work->next;
Signature · 1 line
2.5.2
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
kernel/task_work.c void task_work_run(void) · line 113
Quoted 10 lines · Paul E. McKenney
kernel/task_work.c +2
-1@@ -109,7 +109,8 @@ void task_work_run(void)* the first entry == work, cmpxchg(task_works) should * fail, but it can play with *work and other entries. */ -raw_spin_unlock_wait (&task->pi_lock); +raw_spin_lock (&task->pi_lock); +raw_spin_unlock (&task->pi_lock);
Well, bit the you need spin_lock_irq(). And this is one of the reasons why I personally think unlock_wait have some sense...
Oleg.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 14 lines · Oleg Nesterov
kernel/task_work.c void task_work_run(void) · line 113
Quoted 10 lines · Paul E. McKenney
kernel/task_work.c +2
-1@@ -109,7 +109,8 @@ void task_work_run(void)* the first entry == work, cmpxchg(task_works) should * fail, but it can play with *work and other entries. */ -raw_spin_unlock_wait (&task->pi_lock); +raw_spin_lock (&task->pi_lock); +raw_spin_unlock (&task->pi_lock);Well, bit the you need
spin_lock_irq(). And this is one of the reasons why I personally thinkunlock_waithave some sense...
Good catch, and I clearly need to double-check the other commits for any need to disable interrupts. Anyway, like this, with the addition of a flags variable, correct?
Quoted 2 lines
kernel/task_work.c void task_work_run(void) · line 115
Quoted 2 lines
kernel/task_work.c +2
-0+raw_spin_lock_irq (&task->pi_lock); +raw_spin_unlock_irq (&task->pi_lock);
I agree that the spin_unlock_wait() implementations would avoid the deadlock with an acquisition from an interrupt handler, while also avoiding the need to momentarily disable interrupts. The ->pi_lock is a per-task lock, so I am assuming (perhaps naively) that contention is not a problem. So is the overhead of interrupt disabling likely to be noticeable here?
Thanx, Paul
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 10 lines · Paul E. McKenney
Quoted 3 lines
Review
Quoted 3 lines
(quoted diff) +2
-0+ raw_spin_lock_irq(&task->pi_lock); + raw_spin_unlock_irq(&task->pi_lock);I agree that the
spin_unlock_wait()implementations would avoid the deadlock with an acquisition from an interrupt handler, while also avoiding the need to momentarily disable interrupts. The ->pi_lock is a per-task lock, so I am assuming (perhaps naively) that contention is not a problem. So is the overhead of interrupt disabling likely to be noticeable here?
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to unlock_wait. Yes I agree, it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it unlock_wait2 ;)
If not, we should probably change this code more:
kernel/task_work.c +2 -6
@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;
performance-wise this is almost the same, and if we do not really care about overhead we can simplify the code: this way it is obvious that we can't race with task_work_cancel().
Oleg.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 14 lines · Oleg Nesterov
Quoted 10 lines · Paul E. McKenney
Quoted 3 lines
kernel/task_work.c void task_work_run(void) · line 105
Quoted 3 lines
kernel/task_work.c +2
-0+raw_spin_lock_irq (&task->pi_lock); +raw_spin_unlock_irq (&task->pi_lock);I agree that the
spin_unlock_wait()implementations would avoid the deadlock with an acquisition from an interrupt handler, while also avoiding the need to momentarily disable interrupts. The ->pi_lock is a per-task lock, so I am assuming (perhaps naively) that contention is not a problem. So is the overhead of interrupt disabling likely to be noticeable here?I do not think the overhead will be noticeable in this particular case.
Quoted 7 lines
But I am not sure I understand why do we want to
unlock_wait. Yes I agree, it has some problems, but still...The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Thanx, Paul
Quoted 32 lines
kernel/task_work.c +2
-6@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;performance-wise this is almost the same, and if we do not really care about overhead we can simplify the code: this way it is obvious that we can't race with
task_work_cancel().Oleg.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 13 lines · Paul E. McKenney
Quoted 12 lines · Oleg Nesterov
Quoted 10 lines · Paul E. McKenney
Quoted 3 lines
kernel/task_work.c void task_work_run(void) · line 105
Quoted 3 lines
kernel/task_work.c +2
-0+raw_spin_lock_irq (&task->pi_lock); +raw_spin_unlock_irq (&task->pi_lock);I agree that the
spin_unlock_wait()implementations would avoid the deadlock with an acquisition from an interrupt handler, while also avoiding the need to momentarily disable interrupts. The ->pi_lock is a per-task lock, so I am assuming (perhaps naively) that contention is not a problem. So is the overhead of interrupt disabling likely to be noticeable here?
Quoted 4 lines
Quoted 4 lines
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree, it has some problems, but still...
Well, I tried documenting exactly what it did and did not do, which got an ack from Peter.
https://marc.info/?l=linux-kernel&m=149575078313105
However, my later pull request spawned a bit of discussion:
https://marc.info/?l=linux-kernel&m=149730349001044
This discussion led me to propose strengthening spin_unlock_wait() to act as a lock/unlock pair. This can be implemented on x86 as an smp_mb() followed by a read-only spinloop, as shown on branch spin_unlock_wait.2017.06.23a on my -rcu tree.
Linus was not amused, and said that if we were going to make spin_unlock_wait() have the semantics of lock+unlock, we should just open-code that, especially given that there are way more definitions of spin_unlock_wait() than there are uses. He also suggested making spin_unlock_wait() have only acquire semantics (x86 spin loop with no memory-barrier instructions) and add explicit barriers where required.
https://marc.info/?l=linux-kernel&m=149860012913036
I did a series for this which may be found on branch spin_unlock_wait.2017.06.27a on my -rcu tree.
This approach was not loved by others (see later on the above thread), and Linus's reply (which reiterated his opposition to lock+unlock semantics) suggested the possibility of removing spin_unlock_wait() entirely.
https://marc.info/?l=linux-kernel&m=149869476911620
So I figured, in for a penny, in for a pound, and therefore did the series that includes this patch. The most recent update (which does not yet include your improved version) is on branch spin_unlock_wait.2017.06.30b of my -rcu tree.
Hey, you asked! ;-)
Thanx, Paul
Quoted 41 lines
Quoted 4 lines
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Thanx, Paul
Quoted 32 lines
kernel/task_work.c +2
-6@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;performance-wise this is almost the same, and if we do not really care about overhead we can simplify the code: this way it is obvious that we can't race with
task_work_cancel().Oleg.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyQuoted 6 lines · Paul E. McKenney
Quoted 4 lines · Oleg Nesterov
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree,
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
if it was not clear, I tried to say "why do we want to _remove_ unlock_wait".
Quoted 8 lines
Quoted 6 lines
it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Only if you promise to replace all RCU flavors with a single simple implementation based on rwlock ;)
Seriously, of course I won't argue, and it seems that nobody except me likes this primitive, but to me spin_unlock_wait() looks like synchronize_rcu(() and sometimes it makes sense.
Including this particular case. task_work_run() is going to flush/destroy the ->task_works list, so it needs to wait until all currently executing "readers" (task_work_cancel()'s which have started before ->task_works was updated) have completed.
Oleg.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 18 lines · Oleg Nesterov
Quoted 6 lines · Paul E. McKenney
Quoted 4 lines · Oleg Nesterov
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^if it was not clear, I tried to say "why do we want to _remove_
unlock_wait".Quoted 7 lines
Quoted 6 lines
it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)If not, we should probably change this code more:
Quoted 8 lines
Quoted 1 line
This looks -much- better than my patch! May I have your Signed-off-by?
Only if you promise to replace all RCU flavors with a single simple implementation based on rwlock ;)
Seriously, of course I won't argue, and it seems that nobody except me likes this primitive, but to me
spin_unlock_wait()looks like synchronize_rcu(() and sometimes it makes sense.
If it looks like synchronize_rcu(), why not actually use synchronize_rcu()?
Alan Stern
Quoted 4 lines
Including this particular case.
task_work_run()is going to flush/destroy the ->task_works list, so it needs to wait until all currently executing "readers" (task_work_cancel()'s which have started before ->task_works was updated) have completed.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 25 lines · Alan Stern
Quoted 23 lines · Oleg Nesterov
Quoted 6 lines · Paul E. McKenney
Quoted 4 lines · Oleg Nesterov
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^if it was not clear, I tried to say "why do we want to _remove_
unlock_wait".Quoted 8 lines
Quoted 6 lines
it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Only if you promise to replace all RCU flavors with a single simple implementation based on rwlock ;)
Quoted 6 lines
Quoted 3 lines
Seriously, of course I won't argue, and it seems that nobody except me likes this primitive, but to me
spin_unlock_wait()looks like synchronize_rcu(() and sometimes it makes sense.If it looks like
synchronize_rcu(), why not actually usesynchronize_rcu()?
My guess is that the latencies of synchronize_rcu() don't suit his needs. When the lock is not held, spin_unlock_wait() is quite fast, even compared to expedited grace periods.
Thanx, Paul
Quoted 7 lines
Alan Stern
Quoted 4 lines
Including this particular case.
task_work_run()is going to flush/destroy the ->task_works list, so it needs to wait until all currently executing "readers" (task_work_cancel()'s which have started before ->task_works was updated) have completed.
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
In reply to Oleg NesterovQuoted 16 lines · Oleg Nesterov
Quoted 6 lines · Paul E. McKenney
Quoted 4 lines · Oleg Nesterov
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^if it was not clear, I tried to say "why do we want to _remove_
unlock_wait".Quoted 5 lines
Quoted 5 lines
it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)
Quoted 6 lines
Quoted 3 lines
Quoted 1 line
If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Only if you promise to replace all RCU flavors with a single simple implementation based on rwlock ;)
;-) ;-) ;-)
Here you go:
https://github.com/pramalhe/ConcurrencyFreaks/blob/master/papers/poormanurcu-2015.pdf
Quoted 3 lines
Seriously, of course I won't argue, and it seems that nobody except me likes this primitive, but to me
spin_unlock_wait()looks like synchronize_rcu(() and sometimes it makes sense.
Well, that analogy was what led me to propose that its semantics be defined as spin_lock() immediately followed by spin_unlock(). But that didn't go over well.
Quoted 4 lines
Including this particular case.
task_work_run()is going to flush/destroy the ->task_works list, so it needs to wait until all currently executing "readers" (task_work_cancel()'s which have started before ->task_works was updated) have completed.
Understood!
Thanx, Paul
Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair
Quoted 38 lines · Paul E. McKenney
Quoted 16 lines · Oleg Nesterov
Quoted 6 lines · Paul E. McKenney
Quoted 4 lines · Oleg Nesterov
I do not think the overhead will be noticeable in this particular case.
But I am not sure I understand why do we want to
unlock_wait. Yes I agree,^^^^^^^^^^^^^^^^^^^^^^^^^^^^^if it was not clear, I tried to say "why do we want to _remove_
unlock_wait".Quoted 5 lines
Quoted 5 lines
it has some problems, but still...
The code above looks strange for me. If we are going to repeat this pattern the perhaps we should add a helper for lock+unlock and name it
unlock_wait2;)Quoted 6 lines
Quoted 3 lines
Quoted 1 line
If not, we should probably change this code more:
This looks -much- better than my patch! May I have your Signed-off-by?
Only if you promise to replace all RCU flavors with a single simple implementation based on rwlock ;)
;-) ;-) ;-)
Here you go:
https://github.com/pramalhe/ConcurrencyFreaks/blob/master/papers/poormanurcu-2015.pdf
Quoted 3 lines
Seriously, of course I won't argue, and it seems that nobody except me likes this primitive, but to me
spin_unlock_wait()looks like synchronize_rcu(() and sometimes it makes sense.Well, that analogy was what led me to propose that its semantics be defined as
spin_lock()immediately followed byspin_unlock(). But that didn't go over well.
Quoted 6 lines
Quoted 4 lines
Including this particular case.
task_work_run()is going to flush/destroy the ->task_works list, so it needs to wait until all currently executing "readers" (task_work_cancel()'s which have started before ->task_works was updated) have completed.Understood!
And please see below for the resulting patch and commit log. Please let me know if I broke something.
Thanx, Paul
------------------------------------------------------------------------
commit 6c0801c9ab19fc2f4c1e2436eb1b72e0af9a317b
Author: Oleg Nesterov <[email protected]> Date: Fri Jun 30 13:13:59 2017 -0700
task_work: Replace spin_unlock_wait() with lock/unlock pair
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in task_work_run() with a spin_lock_irq() and a spin_unlock_irq() aruond the cmpxchg() dequeue loop. This should be safe from a performance perspective because ->pi_lock is local to the task and because calls to the other side of the race, task_work_cancel(), should be rare.
kernel/task_work.c +2 -6
@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;
RFC PATCH 3/26 sched: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_task_dead() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is this tasks ->pi_lock, and this is called only after the task exits.
--- kernel/sched/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/sched/core.c +2 -1
@@ -3461,7 +3461,8 @@ void __noreturn do_task_dead(void)* is held by try_to_wake_up() */ smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +raw_spin_lock (¤t->pi_lock); +raw_spin_unlock (¤t->pi_lock);/* Causes final put_task_struct in finish_task_switch(): */ __set_current_state (TASK_DEAD );
Signature · 1 line
2.5.2
Arnd Bergmann
maintainerRe: [PATCH RFC 03/26] sched: Replace spin_unlock_wait() with lock/unlock pair
Quoted 12 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the
spin_unlock_wait()call indo_task_dead()withspin_lock()followed immediately byspin_unlock(). This should be safe from a performance perspective because the lock is this tasks ->pi_lock, and this is called only after the task exits.kernel/sched/core.c +0
-0
kernel/sched/core.c void __noreturn do_task_dead(void) · line 3465
Quoted 6 lines
kernel/sched/core.c +2
-1@@ -3461,7 +3461,8 @@ void __noreturn do_task_dead(void)* is held by try_to_wake_up() */ smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +raw_spin_lock (¤t->pi_lock); +raw_spin_unlock (¤t->pi_lock);
Does the raw_spin_lock()/raw_spin_unlock() imply an smp_mb() or stronger? Maybe it would be clearer to remove the extra barrier if so.
Arnd
Re: [PATCH RFC 03/26] sched: Replace spin_unlock_wait() with lock/unlock pair
Quoted 9 lines · Arnd Bergmann
Quoted 7 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the
spin_unlock_wait()call indo_task_dead()withspin_lock()followed immediately byspin_unlock(). This should be safe from a performance perspective because the lock is this tasks ->pi_lock, and this is called only after the task exits.
Quoted 14 lines
kernel/sched/core.c void __noreturn do_task_dead(void) · line 3465
Quoted 11 lines
kernel/sched/core.c +2
-1@@ -3461,7 +3461,8 @@ void __noreturn do_task_dead(void)* is held by try_to_wake_up() */ smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +raw_spin_lock (¤t->pi_lock); +raw_spin_unlock (¤t->pi_lock);Does the raw_spin_lock()/raw_spin_unlock() imply an
smp_mb()or stronger? Maybe it would be clearer to remove the extra barrier if so.
No, it does not in general, but it does on most architectures, and there are ways to allow those architectures to gain the benefit of their stronger locks. For example, would this work?
Quoted 7 lines
kernel/sched/core.c void __noreturn do_task_dead(void) · line 3465
Quoted 7 lines
kernel/sched/core.c +3
-2* is held by try_to_wake_up() */ -smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +smp_mb__before_spinlock (); +raw_spin_lock (¤t->pi_lock); +raw_spin_unlock (¤t->pi_lock);
Thanx, Paul
RFC PATCH 4/26 completion: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in completion_done() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock will be held only the wakeup happens really quickly.
--- kernel/sched/completion.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
@@ -307,14 +307,9 @@ bool completion_done(struct completion *x)* If ->done, we need to wait for complete() to release ->wait.lock * otherwise we can end up freeing the completion before complete() * is done referencing it. -* -* The RMB pairs with complete()'s RELEASE of ->wait.lock and orders -* the loads of ->done and ->wait.lock such that we cannot observe -* the lock before complete() acquires it while observing the ->done -* after it's acquired the lock. */ -smp_rmb (); -spin_unlock_wait (&x->wait.lock); +spin_lock (&x->wait.lock); +spin_unlock (&x->wait.lock);return true; }EXPORT_SYMBOL (completion_done);
Signature · 1 line
2.5.2
RFC PATCH 5/26 exit: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_exit() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is a per-task lock, and this is happening only at task-exit time.
--- kernel/exit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
@@ -832,7 +832,8 @@ void __noreturn do_exit(long code)* Ensure that we must observe the pi_state in exit_mm() -> * mm_release() -> exit_pi_state_list(). */ -raw_spin_unlock_wait (&tsk->pi_lock); +raw_spin_lock (&tsk->pi_lock); +raw_spin_unlock (&tsk->pi_lock);if (unlikely (in_atomic ())) { pr_info("note: %s[%d] exited with preempt_count %d\n",
Signature · 1 line
2.5.2
RFC PATCH 6/26 ipc: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in exit_sem() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because exit_sem() is rarely invoked in production.
--- ipc/sem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
@@ -2096,7 +2096,8 @@ void exit_sem(struct task_struct *tsk)* possibility where we exit while freeary() didn't * finish unlocking sem_undo_list. */ -spin_unlock_wait (&ulp->lock); +spin_lock (&ulp->lock); +spin_unlock (&ulp->lock);rcu_read_unlock ();break ; }
Signature · 1 line
2.5.2
Re: [PATCH RFC 06/26] ipc: Replace spin_unlock_wait() with lock/unlock pair
Quoted 7 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the
spin_unlock_wait()call inexit_sem()withspin_lock()followed immediately byspin_unlock(). This should be safe from a performance perspective becauseexit_sem()is rarely invoked in production.
Quoted 9 lines
Signed-off-by Paul E. McKenney <[email protected]>Cc Andrew Morton <[email protected]>Cc Davidlohr Bueso <[email protected]>Cc Manfred Spraul <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>
Quoted 18 lines
--- ipc/sem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)ipc/sem.c +2
-1@@ -2096,7 +2096,8 @@ void exit_sem(struct task_struct *tsk)* possibility where we exit while freeary() didn't * finish unlocking sem_undo_list. */ -spin_unlock_wait (&ulp->lock); +spin_lock (&ulp->lock); +spin_unlock (&ulp->lock);rcu_read_unlock ();break ; }
Paul E. McKenney
AppliedRe: [PATCH RFC 06/26] ipc: Replace spin_unlock_wait() with lock/unlock pair
Quoted 17 lines · Manfred Spraul
Quoted 7 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the
spin_unlock_wait()call inexit_sem()withspin_lock()followed immediately byspin_unlock(). This should be safe from a performance perspective becauseexit_sem()is rarely invoked in production.Quoted 9 lines
Signed-off-by Paul E. McKenney <[email protected]>Cc Andrew Morton <[email protected]>Cc Davidlohr Bueso <[email protected]>Cc Manfred Spraul <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>
Quoted 1 line
Acked-by Manfred Spraul <[email protected]>
Applied, thank you!
Thanx, Paul
Quoted 20 lines
Quoted 18 lines
--- ipc/sem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)ipc/sem.c +2
-1@@ -2096,7 +2096,8 @@ void exit_sem(struct task_struct *tsk)* possibility where we exit while freeary() didn't * finish unlocking sem_undo_list. */ -spin_unlock_wait (&ulp->lock); +spin_lock (&ulp->lock); +spin_unlock (&ulp->lock);rcu_read_unlock ();break ; }
RFC PATCH 7/26 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore eliminates the spin_unlock_wait() call and associated else-clause and hoists the then-clause's lock and unlock out of the "if" statement. This should be safe from a performance perspective because according to Tejun there should be few if any drivers that don't set their own error handler.
--- drivers/ata/libata-eh.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
@@ -645,12 +645,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,* completions are honored. A scmd is determined to have * timed out iff its associated qc is active and not failed. */ +spin_lock_irqsave (ap->lock, flags);if (ap->ops->error_handler) {struct scsi_cmnd *scmd, *tmp;int nr_timedout =0 ; -spin_lock_irqsave (ap->lock, flags); -/* This must occur under the ap->lock as we don't want a polled recovery to race the real interrupt handler
@@ -700,12 +699,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,if (nr_timedout)__ata_port_freeze (ap); -spin_unlock_irqrestore (ap->lock, flags);/* initialize eh_tries */ ap->eh_tries =ATA_EH_MAX_TRIES ; - }else -spin_unlock_wait (ap->lock); + } +spin_unlock_irqrestore (ap->lock, flags); }EXPORT_SYMBOL (ata_scsi_cmd_error_handler);
Signature · 1 line
2.5.2
RFC PATCH 8/26 locking: Remove spin_unlock_wait() generic definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes spin_unlock_wait() and related definitions from core code.
--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)
include/asm-generic/qspinlock.h +0 -14
@@ -22,17 +22,6 @@#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
@@ -41,8 +30,6 @@ extern void queued_spin_unlock_wait(struct qspinlock *lock); static __always_inline int queued_spin_is_locked(struct qspinlock *lock) {/* -* See queued_spin_unlock_wait(). -* * Any !0 state indicates it is locked, even if _Q_LOCKED_VAL * isn't immediately observable. */
@@ -135,6 +122,5 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock)#define arch_spin_trylock (l) queued_spin_trylock(l)#define arch_spin_unlock (l) queued_spin_unlock(l)#define arch_spin_lock_flags (l, f) queued_spin_lock(l) -#define arch_spin_unlock_wait (l) queued_spin_unlock_wait(l)#endif /* __ASM_GENERIC_QSPINLOCK_H */
include/linux/spinlock.h +0 -31
@@ -130,12 +130,6 @@ do { \#define smp_mb__before_spinlock () smp_wmb()#endif -/** -* raw_spin_unlock_wait - wait until the spinlock gets unlocked -* @lock: the spinlock in question. -*/ -#define raw_spin_unlock_wait (lock) arch_spin_unlock_wait(&(lock)->raw_lock) -#ifdef CONFIG_DEBUG_SPINLOCK extern void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock);#define do_raw_spin_lock_flags (lock, flags) do_raw_spin_lock(lock)
@@ -369,31 +363,6 @@ static __always_inline int spin_trylock_irq(spinlock_t *lock)raw_spin_trylock_irqsave (spinlock_check (lock), flags); \ }) -/** -* spin_unlock_wait - Interpose between successive critical sections -* @lock: the spinlock whose critical sections are to be interposed. -* -* Semantically this is equivalent to a spin_lock() immediately -* followed by a spin_unlock(). However, most architectures have -* more efficient implementations in which the spin_unlock_wait() -* cannot block concurrent lock acquisition, and in some cases -* where spin_unlock_wait() does not write to the lock variable. -* Nevertheless, spin_unlock_wait() can have high overhead, so if -* you feel the need to use it, please check to see if there is -* a better way to get your job done. -* -* The ordering guarantees provided by spin_unlock_wait() are: -* -* 1. All accesses preceding the spin_unlock_wait() happen before -* any accesses in later critical sections for this same lock. -* 2. All accesses following the spin_unlock_wait() happen after -* any accesses in earlier critical sections for this same lock. -*/ -static __always_inline void spin_unlock_wait(spinlock_t *lock) -{ -raw_spin_unlock_wait (&lock->rlock); -} - static __always_inline int spin_is_locked(spinlock_t *lock) {return raw_spin_is_locked (&lock->rlock);
include/linux/spinlock_up.h +0 -6
@@ -26,11 +26,6 @@#ifdef CONFIG_DEBUG_SPINLOCK #define arch_spin_is_locked (x) ((x)->slock == 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { lock->slock =0 ;
@@ -73,7 +68,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)#else /* DEBUG_SPINLOCK */ #define arch_spin_is_locked (lock) ((void)(lock), 0) -#define arch_spin_unlock_wait (lock) do { barrier(); (void)(lock); } while (0)/* for sched/core.c and kernel_lock.c: */ # define arch_spin_lock (lock) do { barrier(); (void)(lock); } while (0)# define arch_spin_lock_flags (lock, flags) do { barrier(); (void)(lock); } while (0)
kernel/locking/qspinlock.c +0 -117
123 lines not on this page · raw message
Signature · 1 line
2.5.2
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 19 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes
spin_unlock_wait()and related definitions from core code.Signed-off-by Paul E. McKenney <[email protected]>Cc Arnd Bergmann <[email protected]>Cc Ingo Molnar <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)
[...]
Quoted 33 lines
kernel/locking/qspinlock.c +0
-25@@ -267,123 +267,6 @@ static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath#endif -/* -* Various notes on spin_is_locked() and spin_unlock_wait(), which are -* 'interesting' functions: -* -* PROBLEM: some architectures have an interesting issue with atomic ACQUIRE -* operations in that the ACQUIRE applies to the LOAD _not_ the STORE (ARM64, -* PPC). Also qspinlock has a similar issue per construction, the setting of -* the locked byte can be unordered acquiring the lock proper. -* -* This gets to be 'interesting' in the following cases, where the /should/s -* end up false because of this issue. -* -* -* CASE 1: -* -* So the spin_is_locked() correctness issue comes from something like: -* -* CPU0 CPU1 -* -* global_lock(); local_lock(i) -* spin_lock(&G) spin_lock(&L[i]) -* for (i) if (!spin_is_locked(&G)) { -* spin_unlock_wait(&L[i]); smp_acquire__after_ctrl_dep(); -* return; -* }
kernel/locking/qspinlock.c static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock, · line 304
Quoted 10 lines
kernel/locking/qspinlock.c +0
-10-* // deal with fail -* -* Where it is important CPU1 sees G locked or CPU0 sees L[i] locked such -* that there is exclusion between the two critical sections. -* -* The load from spin_is_locked(&G) /should/ be constrained by the ACQUIRE from -* spin_lock(&L[i]), and similarly the load(s) from spin_unlock_wait(&L[i]) -* /should/ be constrained by the ACQUIRE from spin_lock(&G). -* -* Similarly, later stuff is constrained by the ACQUIRE from CTRL+RMB.
Might be worth keeping this comment about spin_is_locked, since we're not removing that guy just yet!
Will
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 61 lines · Will Deacon
Quoted 19 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes
spin_unlock_wait()and related definitions from core code.Signed-off-by Paul E. McKenney <[email protected]>Cc Arnd Bergmann <[email protected]>Cc Ingo Molnar <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)[...]
Quoted 38 lines
kernel/locking/qspinlock.c +0
-30@@ -267,123 +267,6 @@ static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath#endif -/* -* Various notes on spin_is_locked() and spin_unlock_wait(), which are -* 'interesting' functions: -* -* PROBLEM: some architectures have an interesting issue with atomic ACQUIRE -* operations in that the ACQUIRE applies to the LOAD _not_ the STORE (ARM64, -* PPC). Also qspinlock has a similar issue per construction, the setting of -* the locked byte can be unordered acquiring the lock proper. -* -* This gets to be 'interesting' in the following cases, where the /should/s -* end up false because of this issue. -* -* -* CASE 1: -* -* So the spin_is_locked() correctness issue comes from something like: -* -* CPU0 CPU1 -* -* global_lock(); local_lock(i) -* spin_lock(&G) spin_lock(&L[i]) -* for (i) if (!spin_is_locked(&G)) { -* spin_unlock_wait(&L[i]); smp_acquire__after_ctrl_dep(); -* return; -* } -* // deal with fail -* -* Where it is important CPU1 sees G locked or CPU0 sees L[i] locked such -* that there is exclusion between the two critical sections. -*
Quoted 8 lines
kernel/locking/qspinlock.c static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock, · line 304
Quoted 5 lines
kernel/locking/qspinlock.c +0
-5-* The load from spin_is_locked(&G) /should/ be constrained by the ACQUIRE from -* spin_lock(&L[i]), and similarly the load(s) from spin_unlock_wait(&L[i]) -* /should/ be constrained by the ACQUIRE from spin_lock(&G). -* -* Similarly, later stuff is constrained by the ACQUIRE from CTRL+RMB. Might be worth keeping this comment about
spin_is_locked, since we're not removing that guy just yet!
Ah, all the examples had spin_unlock_wait() in them. So what I need to do is to create a spin_unlock_wait()-free example to illustrate the text starting with "The load from spin_is_locked(", correct?
I also need to check all uses of spin_is_locked(). There might no longer be any that rely on any particular ordering...
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 71 lines · Paul E. McKenney
Quoted 61 lines · Will Deacon
Quoted 19 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes
spin_unlock_wait()and related definitions from core code.Signed-off-by Paul E. McKenney <[email protected]>Cc Arnd Bergmann <[email protected]>Cc Ingo Molnar <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)[...]
Quoted 38 lines
kernel/locking/qspinlock.c +0
-30@@ -267,123 +267,6 @@ static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath#endif -/* -* Various notes on spin_is_locked() and spin_unlock_wait(), which are -* 'interesting' functions: -* -* PROBLEM: some architectures have an interesting issue with atomic ACQUIRE -* operations in that the ACQUIRE applies to the LOAD _not_ the STORE (ARM64, -* PPC). Also qspinlock has a similar issue per construction, the setting of -* the locked byte can be unordered acquiring the lock proper. -* -* This gets to be 'interesting' in the following cases, where the /should/s -* end up false because of this issue. -* -* -* CASE 1: -* -* So the spin_is_locked() correctness issue comes from something like: -* -* CPU0 CPU1 -* -* global_lock(); local_lock(i) -* spin_lock(&G) spin_lock(&L[i]) -* for (i) if (!spin_is_locked(&G)) { -* spin_unlock_wait(&L[i]); smp_acquire__after_ctrl_dep(); -* return; -* } -* // deal with fail -* -* Where it is important CPU1 sees G locked or CPU0 sees L[i] locked such -* that there is exclusion between the two critical sections. -* Quoted 8 lines
kernel/locking/qspinlock.c static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock, · line 304
Quoted 5 lines
kernel/locking/qspinlock.c +0
-5-* The load from spin_is_locked(&G) /should/ be constrained by the ACQUIRE from -* spin_lock(&L[i]), and similarly the load(s) from spin_unlock_wait(&L[i]) -* /should/ be constrained by the ACQUIRE from spin_lock(&G). -* -* Similarly, later stuff is constrained by the ACQUIRE from CTRL+RMB. Might be worth keeping this comment about
spin_is_locked, since we're not removing that guy just yet!
Quoted 3 lines
Ah, all the examples had
spin_unlock_wait()in them. So what I need to do is to create a spin_unlock_wait()-free example to illustrate the text starting with "The load from spin_is_locked(", correct?
Yeah, I think so.
Quoted 2 lines
I also need to check all uses of
spin_is_locked(). There might no longer be any that rely on any particular ordering...
Right. I think we're looking for the "insane case" as per 38b850a73034 (which was apparently used by ipc/sem.c at the time, but no longer).
There's a usage in kernel/debug/debug_core.c, but it doesn't fill me with joy.
Will
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 78 lines · Will Deacon
Quoted 71 lines · Paul E. McKenney
Quoted 61 lines · Will Deacon
Quoted 19 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes
spin_unlock_wait()and related definitions from core code.Signed-off-by Paul E. McKenney <[email protected]>Cc Arnd Bergmann <[email protected]>Cc Ingo Molnar <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)[...]
Quoted 38 lines
kernel/locking/qspinlock.c +0
-30@@ -267,123 +267,6 @@ static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock,#define queued_spin_lock_slowpath native_queued_spin_lock_slowpath#endif -/* -* Various notes on spin_is_locked() and spin_unlock_wait(), which are -* 'interesting' functions: -* -* PROBLEM: some architectures have an interesting issue with atomic ACQUIRE -* operations in that the ACQUIRE applies to the LOAD _not_ the STORE (ARM64, -* PPC). Also qspinlock has a similar issue per construction, the setting of -* the locked byte can be unordered acquiring the lock proper. -* -* This gets to be 'interesting' in the following cases, where the /should/s -* end up false because of this issue. -* -* -* CASE 1: -* -* So the spin_is_locked() correctness issue comes from something like: -* -* CPU0 CPU1 -* -* global_lock(); local_lock(i) -* spin_lock(&G) spin_lock(&L[i]) -* for (i) if (!spin_is_locked(&G)) { -* spin_unlock_wait(&L[i]); smp_acquire__after_ctrl_dep(); -* return; -* } -* // deal with fail -* -* Where it is important CPU1 sees G locked or CPU0 sees L[i] locked such -* that there is exclusion between the two critical sections. -* Quoted 8 lines
kernel/locking/qspinlock.c static __always_inline u32 __pv_wait_head_or_lock(struct qspinlock *lock, · line 304
Quoted 5 lines
kernel/locking/qspinlock.c +0
-5-* The load from spin_is_locked(&G) /should/ be constrained by the ACQUIRE from -* spin_lock(&L[i]), and similarly the load(s) from spin_unlock_wait(&L[i]) -* /should/ be constrained by the ACQUIRE from spin_lock(&G). -* -* Similarly, later stuff is constrained by the ACQUIRE from CTRL+RMB. Might be worth keeping this comment about
spin_is_locked, since we're not removing that guy just yet!Quoted 3 lines
Ah, all the examples had
spin_unlock_wait()in them. So what I need to do is to create a spin_unlock_wait()-free example to illustrate the text starting with "The load from spin_is_locked(", correct?Yeah, I think so.
Quoted 8 lines
Quoted 2 lines
I also need to check all uses of
spin_is_locked(). There might no longer be any that rely on any particular ordering...Right. I think we're looking for the "insane case" as per
38b850a73034(which was apparently used byipc/sem.cat the time, but no longer).There's a usage in
kernel/debug/debug_core.c, but it doesn't fill me with joy.
That is indeed an interesting one... But my first round will be what semantics the implementations seem to provide:
Acquire courtesy of TSO: s390, sparc, x86.
Acquire: ia64 (in reality fully ordered).
Control dependency: alpha, arc, arm, blackfin, hexagon, m32r, mn10300, tile,
xtensa.
Control dependency plus leading full barrier: arm64, powerpc.
UP-only: c6x, cris, frv, h8300, m68k, microblaze nios2, openrisc, um, unicore32.
Special cases:
metag: Acquire if !CONFIG_METAG_SMP_WRITE_REORDERING.
Otherwise control dependency?
mips: Control dependency, acquire if CONFIG_CPU_CAVIUM_OCTEON.
parisc: Acquire courtesy of TSO, but why barrier in smp_load_acquire?
sh: Acquire if one of SH4A, SH5, or J2, otherwise acquire? UP-only?
Are these correct, or am I missing something with any of them?
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 21 lines · Paul E. McKenney
Quoted 9 lines · Will Deacon
Quoted 2 lines · Paul E. McKenney
I also need to check all uses of
spin_is_locked(). There might no longer be any that rely on any particular ordering...Right. I think we're looking for the "insane case" as per
38b850a73034(which was apparently used byipc/sem.cat the time, but no longer).There's a usage in
kernel/debug/debug_core.c, but it doesn't fill me with joy.That is indeed an interesting one... But my first round will be what semantics the implementations seem to provide:
Acquire courtesy of TSO: s390, sparc, x86.
Acquire: ia64 (in reality fully ordered).
Control dependency: alpha, arc, arm, blackfin, hexagon, m32r, mn10300, tile,
xtensa.
Control dependency plus leading full barrier: arm64, powerpc.
UP-only: c6x, cris, frv, h8300, m68k, microblaze nios2, openrisc, um, unicore32.
Quoted 8 lines
Special cases:
metag: Acquire if !CONFIG_METAG_SMP_WRITE_REORDERING.
Otherwise control dependency?
mips: Control dependency, acquire ifCONFIG_CPU_CAVIUM_OCTEON.
parisc: Acquire courtesy of TSO, but why barrier insmp_load_acquire?
sh: Acquire if one of SH4A, SH5, or J2, otherwise acquire? UP-only?Are these correct, or am I missing something with any of them?
That looks about right but, at least on ARM, I think we have to consider the semantics of spin_is_locked with respect to the other spin_* functions, rather than in isolation.
For example, ARM only has a control dependency, but spin_lock has a trailing smp_mb() and spin_unlock has both leading and trailing smp_mb().
Will
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 29 lines · Will Deacon
Quoted 28 lines · Paul E. McKenney
Quoted 9 lines · Will Deacon
Quoted 2 lines · Paul E. McKenney
I also need to check all uses of
spin_is_locked(). There might no longer be any that rely on any particular ordering...Right. I think we're looking for the "insane case" as per
38b850a73034(which was apparently used byipc/sem.cat the time, but no longer).There's a usage in
kernel/debug/debug_core.c, but it doesn't fill me with joy.That is indeed an interesting one... But my first round will be what semantics the implementations seem to provide:
Acquire courtesy of TSO: s390, sparc, x86.
Acquire: ia64 (in reality fully ordered).
Control dependency: alpha, arc, arm, blackfin, hexagon, m32r, mn10300, tile,
xtensa.
Control dependency plus leading full barrier: arm64, powerpc.
UP-only: c6x, cris, frv, h8300, m68k, microblaze nios2, openrisc, um, unicore32.Special cases:
metag: Acquire if !CONFIG_METAG_SMP_WRITE_REORDERING.
Otherwise control dependency?
mips: Control dependency, acquire ifCONFIG_CPU_CAVIUM_OCTEON.
parisc: Acquire courtesy of TSO, but why barrier insmp_load_acquire?
sh: Acquire if one of SH4A, SH5, or J2, otherwise acquire? UP-only?
Quoted 8 lines
Quoted 1 line
Are these correct, or am I missing something with any of them?
That looks about right but, at least on ARM, I think we have to consider the semantics of
spin_is_lockedwith respect to the other spin_* functions, rather than in isolation.For example, ARM only has a control dependency, but
spin_lockhas a trailingsmp_mb()andspin_unlockhas both leading and trailingsmp_mb().
Agreed, and my next step is to look at spin_lock() followed by spin_is_locked(), not necessarily the same lock.
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 3 lines · Paul E. McKenney
Agreed, and my next step is to look at
spin_lock()followed byspin_is_locked(), not necessarily the same lock.
Hmm. Most (all?) "spin_is_locked()" really should be about the same thread that took the lock (ie it's about asserts and lock debugging).
The optimistic ABBA avoidance pattern for spinlocks *should* be
spin_lock(inner)
...
if (!try_lock(outer)) {
spin_unlock(inner);
.. do them in the right order ..so I don't think spin_is_locked() should have any memory barriers.
In fact, the core function for spin_is_locked() is arguably arch_spin_value_unlocked() which doesn't even do the access itself.
Linus
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 17 lines · Linus Torvalds
Quoted 3 lines · Paul E. McKenney
Agreed, and my next step is to look at
spin_lock()followed byspin_is_locked(), not necessarily the same lock.Hmm. Most (all?) "
spin_is_locked()" really should be about the same thread that took the lock (ie it's about asserts and lock debugging).The optimistic ABBA avoidance pattern for spinlocks *should* be
spin_lock(inner) ... if (!try_lock(outer)) { spin_unlock(inner); .. do them in the right order ..
Quoted 4 lines
so I don't think
spin_is_locked()should have any memory barriers.In fact, the core function for
spin_is_locked()is arguablyarch_spin_value_unlocked()which doesn't even do the access itself.
Yeah, but there's some spaced-out stuff going on in kgdb_cpu_enter where it looks to me like raw_spin_is_locked is used for synchronization. My eyes are hurting looking at it, though.
Will
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 18 lines · Will Deacon
Quoted 17 lines · Linus Torvalds
Quoted 3 lines · Paul E. McKenney
Agreed, and my next step is to look at
spin_lock()followed byspin_is_locked(), not necessarily the same lock.Hmm. Most (all?) "
spin_is_locked()" really should be about the same thread that took the lock (ie it's about asserts and lock debugging).The optimistic ABBA avoidance pattern for spinlocks *should* be
spin_lock(inner) ... if (!try_lock(outer)) { spin_unlock(inner); .. do them in the right order ..
Quoted 8 lines
Quoted 4 lines
so I don't think
spin_is_locked()should have any memory barriers.In fact, the core function for
spin_is_locked()is arguablyarch_spin_value_unlocked()which doesn't even do the access itself.Yeah, but there's some spaced-out stuff going on in
kgdb_cpu_enterwhere it looks to me likeraw_spin_is_lockedis used for synchronization. My eyes are hurting looking at it, though.
That certainly is one interesting function, isn't it? I wonder what happens if you replace the raw_spin_is_locked() calls with an unlock under a trylock check? ;-)
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 4 lines · Paul E. McKenney
That certainly is one interesting function, isn't it? I wonder what happens if you replace the
raw_spin_is_locked()calls with an unlock under a trylock check? ;-)
Deadlock due to interrupts again?
Didn't your spin_unlock_wait() patches teach you anything? Checking state is fundamentally different from taking the lock. Even a trylock.
I guess you could try with the irqsave versions. But no, we're not doing that.
Linus
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 8 lines · Linus Torvalds
Quoted 4 lines · Paul E. McKenney
That certainly is one interesting function, isn't it? I wonder what happens if you replace the
raw_spin_is_locked()calls with an unlock under a trylock check? ;-)Deadlock due to interrupts again?
Unless I am missing something subtle, the kgdb_cpu_enter() function in question has a local_irq_save() over the "interesting" portion of its workings, so interrupt-handler self-deadlock should not happen.
Quoted 2 lines
Didn't your
spin_unlock_wait()patches teach you anything? Checking state is fundamentally different from taking the lock. Even a trylock.
That was an embarrassing bug, no two ways about it. :-/
Quoted 1 line
I guess you could try with the irqsave versions. But no, we're not doing that.
Again, no need in this case.
But I agree with Will's assessment of this function...
The raw_spin_is_locked() looks to be asking if -any- CPU holds the dbg_slave_lock, and the answer could of course change immediately on return from raw_spin_is_locked(). Perhaps the theory is that if other CPU holds the lock, this CPU is supposed to be subjected to kgdb_roundup_cpus(). Except that the CPU that held dbg_slave_lock might be just about to release that lock. Odd.
Seems like there should be a get_online_cpus() somewhere, but maybe that constraint is to be manually enforced.
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
Quoted 32 lines · Paul E. McKenney
Quoted 8 lines · Linus Torvalds
Quoted 4 lines · Paul E. McKenney
That certainly is one interesting function, isn't it? I wonder what happens if you replace the
raw_spin_is_locked()calls with an unlock under a trylock check? ;-)Deadlock due to interrupts again?
Unless I am missing something subtle, the
kgdb_cpu_enter()function in question has alocal_irq_save()over the "interesting" portion of its workings, so interrupt-handler self-deadlock should not happen.Quoted 2 lines
Didn't your
spin_unlock_wait()patches teach you anything? Checking state is fundamentally different from taking the lock. Even a trylock.That was an embarrassing bug, no two ways about it. :-/
Quoted 1 line
I guess you could try with the irqsave versions. But no, we're not doing that.
Again, no need in this case.
But I agree with Will's assessment of this function...
The
raw_spin_is_locked()looks to be asking if -any- CPU holds thedbg_slave_lock, and the answer could of course change immediately on return fromraw_spin_is_locked(). Perhaps the theory is that if other CPU holds the lock, this CPU is supposed to be subjected tokgdb_roundup_cpus(). Except that the CPU that helddbg_slave_lockmight be just about to release that lock. Odd.
Quoted 2 lines
Seems like there should be a
get_online_cpus()somewhere, but maybe that constraint is to be manually enforced.
Except that invoking get_online_cpus() from an exception handler would be of course be a spectacularly bad idea. I would feel better if the num_online_cpus() was under the local_irq_save(), but perhaps this code is relying on the stop_machine(). Except that it appears we could deadlock with offline waiting for stop_machine() to complete and kdbg waiting for all CPUs to report, including those in stop_machine().
Looks like the current situation is "Don't use kdbg if there is any possibility of CPU-hotplug operations." Not necessarily an unreasonable restriction.
But I need to let me eyes heal a bit before looking at this more.
Thanx, Paul
Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions
In reply to Linus TorvaldsQuoted 8 lines · Linus Torvalds
Quoted 3 lines · Paul E. McKenney
Agreed, and my next step is to look at
spin_lock()followed byspin_is_locked(), not necessarily the same lock.Hmm. Most (all?) "
spin_is_locked()" really should be about the same thread that took the lock (ie it's about asserts and lock debugging).
Good to know, that does make things easier. ;-)
I am not certain that it is feasible to automatically recognize non-assert/non-debugging use cases of spin_is_locked(), but there is aways manual inspection.
Quoted 8 lines
The optimistic ABBA avoidance pattern for spinlocks *should* be
spin_lock(inner) ... if (!try_lock(outer)) { spin_unlock(inner); .. do them in the right order ..
Quoted 4 lines
so I don't think
spin_is_locked()should have any memory barriers.In fact, the core function for
spin_is_locked()is arguablyarch_spin_value_unlocked()which doesn't even do the access itself.
OK, so we should rework any cases where people are relying on acquisition of one spin_lock() being ordered with a later spin_is_locked() on some other lock by that same thread.
Thanx, Paul
RFC PATCH 9/26 alpha: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/alpha/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/alpha/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_spin_is_locked (x) ((x)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_spin_value_unlocked (arch_spinlock_t lock) {return lock.lock ==0 ;
Signature · 1 line
2.5.2
RFC PATCH 10/26 arc: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/arc/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/arc/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_is_locked (x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#ifdef CONFIG_ARC_HAS_LLSC static inline void arch_spin_lock (arch_spinlock_t *lock)
Signature · 1 line
2.5.2
RFC PATCH 11/26 arm: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/arm/include/asm/spinlock.h | 16 ---------------- 1 file changed, 16 deletions(-)
arch/arm/include/asm/spinlock.h +0 -16
@@ -52,22 +52,6 @@ static inline void dsb_sev(void)* memory. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->tickets.owner); - -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.tickets.owner == tmp.tickets.next || - tmp.tickets.owner != owner) -break ; - -wfe (); - } -smp_acquire__after_ctrl_dep (); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
Signature · 1 line
2.5.2
RFC PATCH 12/26 arm64: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/arm64/include/asm/spinlock.h | 58 ++++----------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-)
arch/arm64/include/asm/spinlock.h +5 -53
@@ -26,58 +26,6 @@* The memory barriers are implicit with the load-acquire and store-release * instructions. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -unsigned int tmp; -arch_spinlock_t lockval; -u32 owner; - -/* -* Ensure prior spin_lock operations to other locks have completed -* on this CPU before we test whether "lock" is locked. -*/ -smp_mb (); - owner =READ_ONCE (lock->owner) <<16 ; - - asmvolatile ( -" sevl\n" -"1: wfe\n" -"2: ldaxr %w0, %2\n" -/* Is the lock free? */ -" eor %w1, %w0, %w0, ror #16\n" -" cbz %w1, 3f\n" -/* Lock taken -- has there been a subsequent unlock->lock transition? */ -" eor %w1, %w3, %w0, lsl #16\n" -" cbz %w1, 1b\n" -/* -* The owner has been updated, so there was an unlock->lock -* transition that we missed. That means we can rely on the -* store-release of the unlock operation paired with the -* load-acquire of the lock operation to publish any of our -* previous stores to the new lock owner and therefore don't -* need to bother with the writeback below. -*/ -" b 4f\n" -"3:\n" -/* -* Serialise against any concurrent lockers by writing back the -* unlocked lock value -*/ - ARM64_LSE_ATOMIC_INSN( -/* LL/SC */ -" stxr %w1, %w0, %2\n" - __nops(2 ), -/* LSE atomics */ -" mov %w1, %w0\n" -" cas %w0, %w0, %2\n" -" eor %w1, %w1, %w0\n" ) - /* Somebody else wrote to the lock, GOTO 10 and reload the value */ -" cbnz %w1, 2b\n" -"4:" - : "=&r" (lockval), "=&r" (tmp), "+Q" (*lock) - : "r" (owner) - : "memory"); -}#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)
@@ -176,7 +124,11 @@ static inline int arch_spin_value_unlocked(arch_spinlock_t lock)static inline int arch_spin_is_locked (arch_spinlock_t *lock) { -smp_mb ();/* See arch_spin_unlock_wait */ +/* +* Ensure prior spin_lock operations to other locks have completed +* on this CPU before we test whether "lock" is locked. +*/ +smp_mb ();/* ^^^ */ return !arch_spin_value_unlocked (READ_ONCE (*lock)); }
Signature · 1 line
2.5.2
Re: [PATCH RFC 12/26] arm64: Remove spin_unlock_wait() arch-specific definitions
Quoted 13 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().Signed-off-by Paul E. McKenney <[email protected]>Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Cc <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>
Quoted 3 lines
--- arch/arm64/include/asm/spinlock.h | 58 ++++----------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-)
I'm going to miss this code.
Will
Paul E. McKenney
AppliedRe: [PATCH RFC 12/26] arm64: Remove spin_unlock_wait() arch-specific definitions
Quoted 18 lines · Will Deacon
Quoted 13 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().Signed-off-by Paul E. McKenney <[email protected]>Cc Catalin Marinas <[email protected]>Cc Will Deacon <[email protected]>Cc <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>Quoted 3 lines
--- arch/arm64/include/asm/spinlock.h | 58 ++++----------------------------------- 1 file changed, 5 insertions(+), 53 deletions(-)
Quoted 1 line
I'm going to miss this code.
;-) ;-) ;-)
Quoted 1 line
Acked-by Will Deacon <[email protected]>
Applied, thank you!
Thanx, Paul
RFC PATCH 13/26 blackfin: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/blackfin/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/blackfin/include/asm/spinlock.h +0 -5
@@ -48,11 +48,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__raw_spin_unlock_asm (&lock->lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_read_can_lock (arch_rwlock_t *rw) {return __raw_uncached_fetch_asm (&rw->lock) >0 ;
Signature · 1 line
2.5.2
RFC PATCH 14/26 hexagon: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/hexagon/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/hexagon/include/asm/spinlock.h +0 -5
@@ -179,11 +179,6 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)*/ #define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_is_locked (x) ((x)->lock != 0)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
Signature · 1 line
2.5.2
RFC PATCH 15/26 ia64: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/ia64/include/asm/spinlock.h | 21 --------------------- 1 file changed, 21 deletions(-)
arch/ia64/include/asm/spinlock.h +0 -21
@@ -76,22 +76,6 @@ static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)ACCESS_ONCE (*p) = (tmp +2 ) & ~1 ; } -static __always_inline void __ticket_spin_unlock_wait(arch_spinlock_t *lock) -{ -int *p = (int *)&lock->lock, ticket; - -ia64_invala (); - -for (;;) { - asmvolatile ("ld4.c.nc %0=[%1]" :"=r" (ticket) :"r" (p) :"memory" ); -if (!(((ticket >>TICKET_SHIFT ) ^ ticket) &TICKET_MASK )) -return ; -cpu_relax (); - } - -smp_acquire__after_ctrl_dep (); -} -static inline int __ticket_spin_is_locked (arch_spinlock_t *lock) {long tmp =ACCESS_ONCE (lock->lock);
@@ -143,11 +127,6 @@ static __always_inline void arch_spin_lock_flags(arch_spinlock_t *lock,arch_spin_lock (lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -__ticket_spin_unlock_wait (lock); -} -#define arch_read_can_lock (rw) (*(volatile int *)(rw) >= 0)#define arch_write_can_lock (rw) (*(volatile int *)(rw) == 0)
Signature · 1 line
2.5.2
RFC PATCH 16/26 m32r: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/m32r/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/m32r/include/asm/spinlock.h +0 -5
@@ -30,11 +30,6 @@#define arch_spin_is_locked (x) (*(volatile int *)(&(x)->slock) <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL >0 ); -} -/** * arch_spin_trylock - Try spin lock and return a result * @lock: Pointer to the lock variable
Signature · 1 line
2.5.2
RFC PATCH 17/26 metag: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/metag/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/metag/include/asm/spinlock.h +0 -5
@@ -15,11 +15,6 @@* locked. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
Signature · 1 line
2.5.2
RFC PATCH 18/26 mips: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/mips/include/asm/spinlock.h | 16 ---------------- 1 file changed, 16 deletions(-)
arch/mips/include/asm/spinlock.h +0 -16
@@ -50,22 +50,6 @@ static inline int arch_spin_value_unlocked(arch_spinlock_t lock)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->h.serving_now); -smp_rmb (); -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.h.serving_now == tmp.h.ticket || - tmp.h.serving_now != owner) -break ; - -cpu_relax (); - } -smp_acquire__after_ctrl_dep (); -} -static inline int arch_spin_is_contended (arch_spinlock_t *lock) {u32 counters =ACCESS_ONCE (lock->lock);
Signature · 1 line
2.5.2
RFC PATCH 19/26 mn10300: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/mn10300/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/mn10300/include/asm/spinlock.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (x) (*(volatile signed char *)(&(x)->slock) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -static inline void arch_spin_unlock (arch_spinlock_t *lock) { asm volatile(
Signature · 1 line
2.5.2
RFC PATCH 20/26 parisc: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/parisc/include/asm/spinlock.h | 7 ------- 1 file changed, 7 deletions(-)
arch/parisc/include/asm/spinlock.h +0 -7
@@ -14,13 +14,6 @@ static inline int arch_spin_is_locked(arch_spinlock_t *x)#define arch_spin_lock (lock) arch_spin_lock_flags(lock, 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *x) -{ -volatile unsigned int *a =__ldcw_align (x); - -smp_cond_load_acquire (a,VAL ); -} -static inline void arch_spin_lock_flags (arch_spinlock_t *x,unsigned long flags) {
Signature · 1 line
2.5.2
RFC PATCH 21/26 powerpc: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/powerpc/include/asm/spinlock.h | 33 --------------------------------- 1 file changed, 33 deletions(-)
arch/powerpc/include/asm/spinlock.h +0 -33
@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
Signature · 1 line
2.5.2
Re: [PATCH RFC 21/26] powerpc: Remove spin_unlock_wait() arch-specific definitions
Quoted 5 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().
Quoted 10 lines
Signed-off-by Paul E. McKenney <[email protected]>Cc Benjamin Herrenschmidt <[email protected]>Cc Paul Mackerras <[email protected]>Cc Michael Ellerman <[email protected]>Cc <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>
Regards,
Boqun
Quoted 51 lines
--- arch/powerpc/include/asm/spinlock.h | 33 --------------------------------- 1 file changed, 33 deletions(-)arch/powerpc/include/asm/spinlock.h +0
-33@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer. Signature · 1 line
2.5.2
Re: [PATCH RFC 21/26] powerpc: Remove spin_unlock_wait() arch-specific definitions
Quoted 17 lines · Boqun Feng
Quoted 5 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().Quoted 10 lines
Signed-off-by Paul E. McKenney <[email protected]>Cc Benjamin Herrenschmidt <[email protected]>Cc Paul Mackerras <[email protected]>Cc Michael Ellerman <[email protected]>Cc <[email protected]>Cc Will Deacon <[email protected]>Cc Peter Zijlstra <[email protected]>Cc Alan Stern <[email protected]>Cc Andrea Parri <[email protected]>Cc Linus Torvalds <[email protected]>
Quoted 1 line
Acked-by Boqun Feng <[email protected]>
And finally applied in preparation for v2 of the patch series.
Thank you!!!
Thanx, Paul
Quoted 54 lines
Regards,
BoqunQuoted 51 lines
--- arch/powerpc/include/asm/spinlock.h | 33 --------------------------------- 1 file changed, 33 deletions(-)arch/powerpc/include/asm/spinlock.h +0
-33@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer. Signature · 1 line
2.5.2
RFC PATCH 22/26 s390: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/s390/include/asm/spinlock.h | 7 ------- 1 file changed, 7 deletions(-)
arch/s390/include/asm/spinlock.h +0 -7
@@ -98,13 +98,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) : "cc", "memory"); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -while (arch_spin_is_locked (lock)) -arch_spin_relax (lock); -smp_acquire__after_ctrl_dep (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
Signature · 1 line
2.5.2
RFC PATCH 23/26 sh: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/sh/include/asm/spinlock-cas.h | 5 ----- arch/sh/include/asm/spinlock-llsc.h | 5 ----- 2 files changed, 10 deletions(-)
arch/sh/include/asm/spinlock-cas.h +0 -5
@@ -29,11 +29,6 @@ static inline unsigned __sl_cas(volatile unsigned *p, unsigned old, unsigned new#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {while (!__sl_cas (&lock->lock,1 ,0 ));
arch/sh/include/asm/spinlock-llsc.h +0 -5
@@ -21,11 +21,6 @@#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -/* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not.
Signature · 1 line
2.5.2
RFC PATCH 24/26 sparc: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/sparc/include/asm/spinlock_32.h | 5 ----- arch/sparc/include/asm/spinlock_64.h | 5 ----- 2 files changed, 10 deletions(-)
arch/sparc/include/asm/spinlock_32.h +0 -5
@@ -14,11 +14,6 @@#define arch_spin_is_locked (lock) (*((volatile unsigned char *)(lock)) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { __asm__ __volatile__(
arch/sparc/include/asm/spinlock_64.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (lp) ((lp)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {unsigned long tmp;
Signature · 1 line
2.5.2
RFC PATCH 25/26 tile: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/tile/include/asm/spinlock_32.h | 2 -- arch/tile/include/asm/spinlock_64.h | 2 -- arch/tile/lib/spinlock_32.c | 23 ----------------------- arch/tile/lib/spinlock_64.c | 22 ---------------------- 4 files changed, 49 deletions(-)
arch/tile/include/asm/spinlock_32.h +0 -2
@@ -64,8 +64,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->current_ticket = old_ticket +TICKET_QUANTUM ; } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/tile/include/asm/spinlock_64.h +0 -2
@@ -58,8 +58,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__insn_fetchadd4 (&lock->lock,1U << __ARCH_SPIN_CURRENT_SHIFT); } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -void arch_spin_lock_slow (arch_spinlock_t *lock,u32 val);/* Grab the "next" ticket number and bump it atomically.
arch/tile/lib/spinlock_32.c +0 -23
@@ -62,29 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -int curr =READ_ONCE (lock->current_ticket); -int next =READ_ONCE (lock->next_ticket); - -/* Return immediately if unlocked. */ -if (next == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (READ_ONCE (lock->current_ticket) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait); -/* * The low byte is always reserved to be the marker for a "tns" operation * since the low bit is set to "1" by a tns. The next seven bits are
arch/tile/lib/spinlock_64.c +0 -22
@@ -62,28 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -u32 val =READ_ONCE (lock->lock); -u32 curr =arch_spin_current (val); - -/* Return immediately if unlocked. */ -if (arch_spin_next (val) == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (arch_spin_current (READ_ONCE (lock->lock)) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait);/* * If the read lock fails due to a writer, we retry periodically
Signature · 1 line
2.5.2
Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions
Quoted 4 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().
Please don't make this one commit fopr every architecture.
Once something gets removed, it gets removed. There's no point in "remove it from architecture X". If there are no more users, we're done with it, and making it be 25 patches with the same commit message instead of just one doesn't help anybody.
Linus
Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions
Quoted 7 lines · Linus Torvalds
Quoted 4 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().
Quoted 6 lines
Please don't make this one commit fopr every architecture.
Once something gets removed, it gets removed. There's no point in "remove it from architecture X". If there are no more users, we're done with it, and making it be 25 patches with the same commit message instead of just one doesn't help anybody.
Apologies, I will merge them.
Thanx, Paul
Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions
Quoted 8 lines · Paul E. McKenney
Quoted 7 lines · Linus Torvalds
Quoted 4 lines · Paul E. McKenney
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific
arch_spin_unlock_wait().
Quoted 8 lines
Quoted 6 lines
Please don't make this one commit fopr every architecture.
Once something gets removed, it gets removed. There's no point in "remove it from architecture X". If there are no more users, we're done with it, and making it be 25 patches with the same commit message instead of just one doesn't help anybody.
Apologies, I will merge them.
I suppose that I could have sent the merged patch... Please see below.
Thanx, Paul
------------------------------------------------------------------------
commit e81efa4219e9b07e476448572aafe8f9c4ad28c8
Author: Paul E. McKenney <[email protected]> Date: Thu Jun 29 15:53:02 2017 -0700
arch: Remove spin_unlock_wait() arch-specific definitions
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait() for all architectures providing them.
arch/alpha/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_spin_is_locked (x) ((x)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_spin_value_unlocked (arch_spinlock_t lock) {return lock.lock ==0 ;
arch/arc/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_is_locked (x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#ifdef CONFIG_ARC_HAS_LLSC static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm/include/asm/spinlock.h +0 -16
@@ -52,22 +52,6 @@ static inline void dsb_sev(void)* memory. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->tickets.owner); - -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.tickets.owner == tmp.tickets.next || - tmp.tickets.owner != owner) -break ; - -wfe (); - } -smp_acquire__after_ctrl_dep (); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm64/include/asm/spinlock.h +5 -53
70 lines not on this page · raw message
arch/blackfin/include/asm/spinlock.h +0 -5
@@ -48,11 +48,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__raw_spin_unlock_asm (&lock->lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_read_can_lock (arch_rwlock_t *rw) {return __raw_uncached_fetch_asm (&rw->lock) >0 ;
arch/hexagon/include/asm/spinlock.h +0 -5
@@ -179,11 +179,6 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)*/ #define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_is_locked (x) ((x)->lock != 0)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/ia64/include/asm/spinlock.h +0 -21
@@ -76,22 +76,6 @@ static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)ACCESS_ONCE (*p) = (tmp +2 ) & ~1 ; } -static __always_inline void __ticket_spin_unlock_wait(arch_spinlock_t *lock) -{ -int *p = (int *)&lock->lock, ticket; - -ia64_invala (); - -for (;;) { - asmvolatile ("ld4.c.nc %0=[%1]" :"=r" (ticket) :"r" (p) :"memory" ); -if (!(((ticket >>TICKET_SHIFT ) ^ ticket) &TICKET_MASK )) -return ; -cpu_relax (); - } - -smp_acquire__after_ctrl_dep (); -} -static inline int __ticket_spin_is_locked (arch_spinlock_t *lock) {long tmp =ACCESS_ONCE (lock->lock);
@@ -143,11 +127,6 @@ static __always_inline void arch_spin_lock_flags(arch_spinlock_t *lock,arch_spin_lock (lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -__ticket_spin_unlock_wait (lock); -} -#define arch_read_can_lock (rw) (*(volatile int *)(rw) >= 0)#define arch_write_can_lock (rw) (*(volatile int *)(rw) == 0)
arch/m32r/include/asm/spinlock.h +0 -5
@@ -30,11 +30,6 @@#define arch_spin_is_locked (x) (*(volatile int *)(&(x)->slock) <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL >0 ); -} -/** * arch_spin_trylock - Try spin lock and return a result * @lock: Pointer to the lock variable
arch/metag/include/asm/spinlock.h +0 -5
@@ -15,11 +15,6 @@* locked. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/mips/include/asm/spinlock.h +0 -16
@@ -50,22 +50,6 @@ static inline int arch_spin_value_unlocked(arch_spinlock_t lock)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->h.serving_now); -smp_rmb (); -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.h.serving_now == tmp.h.ticket || - tmp.h.serving_now != owner) -break ; - -cpu_relax (); - } -smp_acquire__after_ctrl_dep (); -} -static inline int arch_spin_is_contended (arch_spinlock_t *lock) {u32 counters =ACCESS_ONCE (lock->lock);
arch/mn10300/include/asm/spinlock.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (x) (*(volatile signed char *)(&(x)->slock) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -static inline void arch_spin_unlock (arch_spinlock_t *lock) { asm volatile(
arch/parisc/include/asm/spinlock.h +0 -7
@@ -14,13 +14,6 @@ static inline int arch_spin_is_locked(arch_spinlock_t *x)#define arch_spin_lock (lock) arch_spin_lock_flags(lock, 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *x) -{ -volatile unsigned int *a =__ldcw_align (x); - -smp_cond_load_acquire (a,VAL ); -} -static inline void arch_spin_lock_flags (arch_spinlock_t *x,unsigned long flags) {
arch/powerpc/include/asm/spinlock.h +0 -33
@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/s390/include/asm/spinlock.h +0 -7
@@ -98,13 +98,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) : "cc", "memory"); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -while (arch_spin_is_locked (lock)) -arch_spin_relax (lock); -smp_acquire__after_ctrl_dep (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/sh/include/asm/spinlock-cas.h +0 -5
@@ -29,11 +29,6 @@ static inline unsigned __sl_cas(volatile unsigned *p, unsigned old, unsigned new#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {while (!__sl_cas (&lock->lock,1 ,0 ));
arch/sh/include/asm/spinlock-llsc.h +0 -5
@@ -21,11 +21,6 @@#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -/* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not.
arch/sparc/include/asm/spinlock_32.h +0 -5
@@ -14,11 +14,6 @@#define arch_spin_is_locked (lock) (*((volatile unsigned char *)(lock)) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { __asm__ __volatile__(
arch/sparc/include/asm/spinlock_64.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (lp) ((lp)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {unsigned long tmp;
arch/tile/include/asm/spinlock_32.h +0 -2
@@ -64,8 +64,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->current_ticket = old_ticket +TICKET_QUANTUM ; } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/tile/include/asm/spinlock_64.h +0 -2
@@ -58,8 +58,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__insn_fetchadd4 (&lock->lock,1U << __ARCH_SPIN_CURRENT_SHIFT); } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -void arch_spin_lock_slow (arch_spinlock_t *lock,u32 val);/* Grab the "next" ticket number and bump it atomically.
arch/tile/lib/spinlock_32.c +0 -23
@@ -62,29 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -int curr =READ_ONCE (lock->current_ticket); -int next =READ_ONCE (lock->next_ticket); - -/* Return immediately if unlocked. */ -if (next == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (READ_ONCE (lock->current_ticket) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait); -/* * The low byte is always reserved to be the marker for a "tns" operation * since the low bit is set to "1" by a tns. The next seven bits are
arch/tile/lib/spinlock_64.c +0 -22
@@ -62,28 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -u32 val =READ_ONCE (lock->lock); -u32 curr =arch_spin_current (val); - -/* Return immediately if unlocked. */ -if (arch_spin_next (val) == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (arch_spin_current (READ_ONCE (lock->lock)) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait);/* * If the read lock fails due to a writer, we retry periodically
arch/xtensa/include/asm/spinlock.h +0 -5
@@ -33,11 +33,6 @@#define arch_spin_is_locked (x) ((x)->slock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions
In reply to Linus TorvaldsQuoted 7 lines · Linus Torvalds
Please don't make this one commit fopr every architecture.
Once something gets removed, it gets removed. There's no point in "remove it from architecture X". If there are no more users, we're done with it, and making it be 25 patches with the same commit message instead of just one doesn't help anybody.
Just to clarify: I think the actual *users* are worth doing one by one, particularly if there are user-specific explanations of what that particular code wanted, and why spin_unlock_wait() doesn't really help.
And I think that you actually have those per-user insights by now, after looking at the long thread.
So I'm not saying "do one patch for the whole series". One patch per removal of use is fine - in fact preferred.
But once there are no actual more users, just remove all the architecture definitions in one go, because explaining the same thing several times doesn't actually help anything.
In fact, *if* we end up ever resurrecting that thing, it's good if we can resurrect it in one go. Then we can resurrect the one or two users that turned out to matter after all and could come up with why some particular ordering was ok too.
Linus
Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions
Quoted 10 lines · Linus Torvalds
Quoted 7 lines · Linus Torvalds
Please don't make this one commit fopr every architecture.
Once something gets removed, it gets removed. There's no point in "remove it from architecture X". If there are no more users, we're done with it, and making it be 25 patches with the same commit message instead of just one doesn't help anybody.
Quoted 4 lines
Just to clarify: I think the actual *users* are worth doing one by one, particularly if there are user-specific explanations of what that particular code wanted, and why
spin_unlock_wait()doesn't really help.
Got it, and I did merge -only- the arch-specific definition removals into one commit. Should I also merge the core-code definition removals into that same commit, or is it OK to remove the core-code definitions with one commit and the arch-specific definitions with another.
(My guess is that you would prefer I removed -all- definitions with one commit, including the core-kernel definitions, but at this point I figure I should just ask.)
Quoted 2 lines
And I think that you actually have those per-user insights by now, after looking at the long thread.
One Acked-by thus far, so some progress!
Quoted 2 lines
So I'm not saying "do one patch for the whole series". One patch per removal of use is fine - in fact preferred.
Got it. It allows the developers and maintainers to tell me where my analysis is wrong, for one thing. ;-)
Quoted 8 lines
But once there are no actual more users, just remove all the architecture definitions in one go, because explaining the same thing several times doesn't actually help anything.
In fact, *if* we end up ever resurrecting that thing, it's good if we can resurrect it in one go. Then we can resurrect the one or two users that turned out to matter after all and could come up with why some particular ordering was ok too.
Understood!
Thanx, Paul
RFC PATCH 26/26 xtensa: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait().
--- arch/xtensa/include/asm/spinlock.h | 5 ----- 1 file changed, 5 deletions(-)
arch/xtensa/include/asm/spinlock.h +0 -5
@@ -33,11 +33,6 @@#define arch_spin_is_locked (x) ((x)->slock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
Signature · 1 line
2.5.2
v2 cover Remove spin_unlock_wait()
In reply to Paul E. McKenneyHello!
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removes spin_unlock_wait() and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:
1-7. Change uses of spin_unlock_wait() and raw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
8. Remove core-kernel definitions for spin_unlock_wait() and
raw_spin_unlock_wait().
9. Remove arch-specific definitions of arch_spin_unlock_wait().
Changes since v1:
o Disable interrupts where needed, thus avoiding embarrassing
interrupt-based self-deadlocks.
o Substitute Manfred's patch for contrack_lock (#1 above).
o Substitute Oleg's patch for task_work (#2 above).
o Use more efficient barrier based on Arnd Bergmann feedback.
Thanx, Paul
------------------------------------------------------------------------
arch/alpha/include/asm/spinlock.h | 5 - arch/arc/include/asm/spinlock.h | 5 - arch/arm/include/asm/spinlock.h | 16 ---- arch/arm64/include/asm/spinlock.h | 58 +---------------- arch/blackfin/include/asm/spinlock.h | 5 - arch/hexagon/include/asm/spinlock.h | 5 - arch/ia64/include/asm/spinlock.h | 21 ------ arch/m32r/include/asm/spinlock.h | 5 - arch/metag/include/asm/spinlock.h | 5 - arch/mips/include/asm/spinlock.h | 16 ---- arch/mn10300/include/asm/spinlock.h | 5 - arch/parisc/include/asm/spinlock.h | 7 -- arch/powerpc/include/asm/spinlock.h | 33 --------- arch/s390/include/asm/spinlock.h | 7 -- arch/sh/include/asm/spinlock-cas.h | 5 - arch/sh/include/asm/spinlock-llsc.h | 5 - arch/sparc/include/asm/spinlock_32.h | 5 - arch/sparc/include/asm/spinlock_64.h | 5 - arch/tile/include/asm/spinlock_32.h | 2 arch/tile/include/asm/spinlock_64.h | 2 arch/tile/lib/spinlock_32.c | 23 ------ arch/tile/lib/spinlock_64.c | 22 ------ arch/xtensa/include/asm/spinlock.h | 5 - drivers/ata/libata-eh.c | 8 -- include/asm-generic/qspinlock.h | 14 ---- include/linux/spinlock.h | 31 --------- include/linux/spinlock_up.h | 6 - ipc/sem.c | 3 kernel/exit.c | 3 kernel/locking/qspinlock.c | 117 ----------------------------------- kernel/sched/completion.c | 9 -- kernel/sched/core.c | 5 - kernel/task_work.c | 8 -- net/netfilter/nf_conntrack_core.c | 44 +++++++------ 34 files changed, 43 insertions(+), 472 deletions(-)
PATCH v2 1/9 net/netfilter/nf_conntrack_core: Fix net_conntrack_lock()
From: Manfred Spraul <[email protected]>
As we want to remove spin_unlock_wait() and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.
In addition:
- Reading nf_conntrack_locks_all needs ACQUIRE memory ordering.
- The new code avoids the backwards loop.
Only slightly tested, I did not manage to trigger calls to nf_conntrack_all_lock().
--- net/netfilter/nf_conntrack_core.c | 44 +++++++++++++++++++++------------------ 1 file changed, 24 insertions(+), 20 deletions(-)
net/netfilter/nf_conntrack_core.c +24 -20
@@ -96,19 +96,24 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics */ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ; + +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);
@@ -149,18 +154,17 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }
Signature · 1 line
2.5.2
Re: [PATCH v2 1/9] net/netfilter/nf_conntrack_core: Fix net_conntrack_lock()
Hi Paul,
Quoted 6 lines · Paul E. McKenney
From: Manfred Spraul <[email protected]>
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.
Quoted 6 lines
In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().
If you want:
Attached would be V2, with adapted comments.
--
Manfred
From e3562faa1bc96e883108505e05deecaf38c87a26 Mon Sep 17 00:00:00 2001
From: Manfred Spraul <[email protected]>
Date: Sun, 21 Aug 2016 07:17:55 +0200
Subject: [PATCH 1/2] net/netfilter/nf_conntrack_core: Fix net_conntrack_lock()
As we want to remove spin_unlock_wait() and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.
In addition:
- Reading nf_conntrack_locks_all needs ACQUIRE memory ordering.
- The new code avoids the backwards loop.
Only slightly tested, I did not manage to trigger calls to nf_conntrack_all_lock().
V2: With improved comments, to clearly show how the barriers
pair.
--- net/netfilter/nf_conntrack_core.c | 52 ++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-)
net/netfilter/nf_conntrack_core.c +29 -23
@@ -96,19 +96,26 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics +* It pairs with the smp_store_release() in nf_conntrack_all_unlock() +*/ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ; + +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);
@@ -149,28 +156,27 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }static void nf_conntrack_all_unlock (void ) { -/* -* All prior stores must be complete before we clear +/* All prior stores must be complete before we clear * 'nf_conntrack_locks_all'. Otherwise nf_conntrack_lock() * might observe the false value but not the entire -* critical section: +* critical section. +* It pairs with the smp_load_acquire() in nf_conntrack_lock() */ smp_store_release (&nf_conntrack_locks_all, false);spin_unlock (&nf_conntrack_locks_all_lock);
Signature · 1 line
2.9.4
Re: [PATCH v2 1/9] net/netfilter/nf_conntrack_core: Fix net_conntrack_lock()
Quoted 13 lines · Manfred Spraul
Hi Paul,
Quoted 10 lines · Paul E. McKenney
From: Manfred Spraul <[email protected]>
As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.
Quoted 5 lines
Quoted 2 lines
Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().If you want:
Attached would be V2, with adapted comments.
I do like the improved comments, thank you! Queued, and will be part of a later v3 of the series.
Thanx, Paul
Quoted 118 lines
--
ManfredQuoted 1 line
From
e3562faa1bc96e883108505e05deecaf38c87a26Mon Sep 17 00:00:00 2001From: Manfred Spraul <[email protected]>
Date: Sun, 21 Aug 2016 07:17:55 +0200
Subject: [PATCH 1/2]net/netfilter/nf_conntrack_core: Fixnet_conntrack_lock()As we want to remove
spin_unlock_wait()and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.In addition:
- Readingnf_conntrack_locks_allneeds ACQUIRE memory ordering.
- The new code avoids the backwards loop.Only slightly tested, I did not manage to trigger calls to
nf_conntrack_all_lock().V2: With improved comments, to clearly show how the barriers
pair.Fixes b16c29191dc8Signed-off-by Manfred Spraul <[email protected]>Cc <[email protected]>Cc Alan Stern <[email protected]>Cc Sasha Levin <[email protected]>Cc Pablo Neira Ayuso <[email protected]>--- net/netfilter/nf_conntrack_core.c | 52 ++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-)net/netfilter/nf_conntrack_core.c +29
-23@@ -96,19 +96,26 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics +* It pairs with the smp_store_release() in nf_conntrack_all_unlock() +*/ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ; + +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);@@ -149,28 +156,27 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }static void nf_conntrack_all_unlock (void ) { -/* -* All prior stores must be complete before we clear +/* All prior stores must be complete before we clear * 'nf_conntrack_locks_all'. Otherwise nf_conntrack_lock() * might observe the false value but not the entire -* critical section: +* critical section. +* It pairs with the smp_load_acquire() in nf_conntrack_lock() */ smp_store_release (&nf_conntrack_locks_all, false);spin_unlock (&nf_conntrack_locks_all_lock);Signature · 1 line
2.9.4
PATCH v2 2/9 task_work: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyFrom: Oleg Nesterov <[email protected]>
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in task_work_run() with a spin_lock_irq() and a spin_unlock_irq() aruond the cmpxchg() dequeue loop. This should be safe from a performance perspective because ->pi_lock is local to the task and because calls to the other side of the race, task_work_cancel(), should be rare.
--- kernel/task_work.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
kernel/task_work.c +2 -6
@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;
Signature · 1 line
2.5.2
PATCH v2 3/9 sched: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_task_dead() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is this tasks ->pi_lock, and this is called only after the task exits.
[ paulmck: Replace leading smp_mb() with smp_mb__before_spinlock(),
courtesy of Arnd Bergmann's noting its odd location. ]
--- kernel/sched/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/sched/core.c +3 -2
@@ -3460,8 +3460,9 @@ void __noreturn do_task_dead(void)* To avoid it, we have to wait for releasing tsk->pi_lock which * is held by try_to_wake_up() */ -smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +smp_mb__before_spinlock (); +raw_spin_lock_irq (¤t->pi_lock); +raw_spin_unlock_irq (¤t->pi_lock);/* Causes final put_task_struct in finish_task_switch(): */ __set_current_state (TASK_DEAD );
Signature · 1 line
2.5.2
PATCH v2 4/9 completion: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in completion_done() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock will be held only the wakeup happens really quickly.
--- kernel/sched/completion.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
@@ -307,14 +307,9 @@ bool completion_done(struct completion *x)* If ->done, we need to wait for complete() to release ->wait.lock * otherwise we can end up freeing the completion before complete() * is done referencing it. -* -* The RMB pairs with complete()'s RELEASE of ->wait.lock and orders -* the loads of ->done and ->wait.lock such that we cannot observe -* the lock before complete() acquires it while observing the ->done -* after it's acquired the lock. */ -smp_rmb (); -spin_unlock_wait (&x->wait.lock); +spin_lock_irq (&x->wait.lock); +spin_unlock_irq (&x->wait.lock);return true; }EXPORT_SYMBOL (completion_done);
Signature · 1 line
2.5.2
PATCH v2 5/9 exit: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_exit() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is a per-task lock, and this is happening only at task-exit time.
--- kernel/exit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
@@ -832,7 +832,8 @@ void __noreturn do_exit(long code)* Ensure that we must observe the pi_state in exit_mm() -> * mm_release() -> exit_pi_state_list(). */ -raw_spin_unlock_wait (&tsk->pi_lock); +raw_spin_lock_irq (&tsk->pi_lock); +raw_spin_unlock_irq (&tsk->pi_lock);if (unlikely (in_atomic ())) { pr_info("note: %s[%d] exited with preempt_count %d\n",
Signature · 1 line
2.5.2
PATCH v2 6/9 ipc: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in exit_sem() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because exit_sem() is rarely invoked in production.
--- ipc/sem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
@@ -2096,7 +2096,8 @@ void exit_sem(struct task_struct *tsk)* possibility where we exit while freeary() didn't * finish unlocking sem_undo_list. */ -spin_unlock_wait (&ulp->lock); +spin_lock (&ulp->lock); +spin_unlock (&ulp->lock);rcu_read_unlock ();break ; }
Signature · 1 line
2.5.2
PATCH v2 7/9 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore eliminates the spin_unlock_wait() call and associated else-clause and hoists the then-clause's lock and unlock out of the "if" statement. This should be safe from a performance perspective because according to Tejun there should be few if any drivers that don't set their own error handler.
--- drivers/ata/libata-eh.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
@@ -645,12 +645,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,* completions are honored. A scmd is determined to have * timed out iff its associated qc is active and not failed. */ +spin_lock_irqsave (ap->lock, flags);if (ap->ops->error_handler) {struct scsi_cmnd *scmd, *tmp;int nr_timedout =0 ; -spin_lock_irqsave (ap->lock, flags); -/* This must occur under the ap->lock as we don't want a polled recovery to race the real interrupt handler
@@ -700,12 +699,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,if (nr_timedout)__ata_port_freeze (ap); -spin_unlock_irqrestore (ap->lock, flags);/* initialize eh_tries */ ap->eh_tries =ATA_EH_MAX_TRIES ; - }else -spin_unlock_wait (ap->lock); + } +spin_unlock_irqrestore (ap->lock, flags); }EXPORT_SYMBOL (ata_scsi_cmd_error_handler);
Signature · 1 line
2.5.2
PATCH v2 8/9 locking: Remove spin_unlock_wait() generic definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes spin_unlock_wait() and related definitions from core code.
--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)
include/asm-generic/qspinlock.h +0 -14
@@ -22,17 +22,6 @@#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
@@ -41,8 +30,6 @@ extern void queued_spin_unlock_wait(struct qspinlock *lock); static __always_inline int queued_spin_is_locked(struct qspinlock *lock) {/* -* See queued_spin_unlock_wait(). -* * Any !0 state indicates it is locked, even if _Q_LOCKED_VAL * isn't immediately observable. */
@@ -135,6 +122,5 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock)#define arch_spin_trylock (l) queued_spin_trylock(l)#define arch_spin_unlock (l) queued_spin_unlock(l)#define arch_spin_lock_flags (l, f) queued_spin_lock(l) -#define arch_spin_unlock_wait (l) queued_spin_unlock_wait(l)#endif /* __ASM_GENERIC_QSPINLOCK_H */
include/linux/spinlock.h +0 -31
@@ -130,12 +130,6 @@ do { \#define smp_mb__before_spinlock () smp_wmb()#endif -/** -* raw_spin_unlock_wait - wait until the spinlock gets unlocked -* @lock: the spinlock in question. -*/ -#define raw_spin_unlock_wait (lock) arch_spin_unlock_wait(&(lock)->raw_lock) -#ifdef CONFIG_DEBUG_SPINLOCK extern void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock);#define do_raw_spin_lock_flags (lock, flags) do_raw_spin_lock(lock)
@@ -369,31 +363,6 @@ static __always_inline int spin_trylock_irq(spinlock_t *lock)raw_spin_trylock_irqsave (spinlock_check (lock), flags); \ }) -/** -* spin_unlock_wait - Interpose between successive critical sections -* @lock: the spinlock whose critical sections are to be interposed. -* -* Semantically this is equivalent to a spin_lock() immediately -* followed by a spin_unlock(). However, most architectures have -* more efficient implementations in which the spin_unlock_wait() -* cannot block concurrent lock acquisition, and in some cases -* where spin_unlock_wait() does not write to the lock variable. -* Nevertheless, spin_unlock_wait() can have high overhead, so if -* you feel the need to use it, please check to see if there is -* a better way to get your job done. -* -* The ordering guarantees provided by spin_unlock_wait() are: -* -* 1. All accesses preceding the spin_unlock_wait() happen before -* any accesses in later critical sections for this same lock. -* 2. All accesses following the spin_unlock_wait() happen after -* any accesses in earlier critical sections for this same lock. -*/ -static __always_inline void spin_unlock_wait(spinlock_t *lock) -{ -raw_spin_unlock_wait (&lock->rlock); -} - static __always_inline int spin_is_locked(spinlock_t *lock) {return raw_spin_is_locked (&lock->rlock);
include/linux/spinlock_up.h +0 -6
@@ -26,11 +26,6 @@#ifdef CONFIG_DEBUG_SPINLOCK #define arch_spin_is_locked (x) ((x)->slock == 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { lock->slock =0 ;
@@ -73,7 +68,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)#else /* DEBUG_SPINLOCK */ #define arch_spin_is_locked (lock) ((void)(lock), 0) -#define arch_spin_unlock_wait (lock) do { barrier(); (void)(lock); } while (0)/* for sched/core.c and kernel_lock.c: */ # define arch_spin_lock (lock) do { barrier(); (void)(lock); } while (0)# define arch_spin_lock_flags (lock, flags) do { barrier(); (void)(lock); } while (0)
kernel/locking/qspinlock.c +0 -117
123 lines not on this page · raw message
Signature · 1 line
2.5.2
PATCH v2 9/9 arch: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait() for all architectures providing them.
--- arch/alpha/include/asm/spinlock.h | 5 ---- arch/arc/include/asm/spinlock.h | 5 ---- arch/arm/include/asm/spinlock.h | 16 ---------- arch/arm64/include/asm/spinlock.h | 58 ++++-------------------------------- arch/blackfin/include/asm/spinlock.h | 5 ---- arch/hexagon/include/asm/spinlock.h | 5 ---- arch/ia64/include/asm/spinlock.h | 21 ------------- arch/m32r/include/asm/spinlock.h | 5 ---- arch/metag/include/asm/spinlock.h | 5 ---- arch/mips/include/asm/spinlock.h | 16 ---------- arch/mn10300/include/asm/spinlock.h | 5 ---- arch/parisc/include/asm/spinlock.h | 7 ----- arch/powerpc/include/asm/spinlock.h | 33 -------------------- arch/s390/include/asm/spinlock.h | 7 ----- arch/sh/include/asm/spinlock-cas.h | 5 ---- arch/sh/include/asm/spinlock-llsc.h | 5 ---- arch/sparc/include/asm/spinlock_32.h | 5 ---- arch/sparc/include/asm/spinlock_64.h | 5 ---- arch/tile/include/asm/spinlock_32.h | 2 -- arch/tile/include/asm/spinlock_64.h | 2 -- arch/tile/lib/spinlock_32.c | 23 -------------- arch/tile/lib/spinlock_64.c | 22 -------------- arch/xtensa/include/asm/spinlock.h | 5 ---- 23 files changed, 5 insertions(+), 262 deletions(-)
arch/alpha/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_spin_is_locked (x) ((x)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_spin_value_unlocked (arch_spinlock_t lock) {return lock.lock ==0 ;
arch/arc/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_is_locked (x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#ifdef CONFIG_ARC_HAS_LLSC static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm/include/asm/spinlock.h +0 -16
@@ -52,22 +52,6 @@ static inline void dsb_sev(void)* memory. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->tickets.owner); - -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.tickets.owner == tmp.tickets.next || - tmp.tickets.owner != owner) -break ; - -wfe (); - } -smp_acquire__after_ctrl_dep (); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm64/include/asm/spinlock.h +5 -53
70 lines not on this page · raw message
arch/blackfin/include/asm/spinlock.h +0 -5
@@ -48,11 +48,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__raw_spin_unlock_asm (&lock->lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_read_can_lock (arch_rwlock_t *rw) {return __raw_uncached_fetch_asm (&rw->lock) >0 ;
arch/hexagon/include/asm/spinlock.h +0 -5
@@ -179,11 +179,6 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)*/ #define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_is_locked (x) ((x)->lock != 0)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/ia64/include/asm/spinlock.h +0 -21
@@ -76,22 +76,6 @@ static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)ACCESS_ONCE (*p) = (tmp +2 ) & ~1 ; } -static __always_inline void __ticket_spin_unlock_wait(arch_spinlock_t *lock) -{ -int *p = (int *)&lock->lock, ticket; - -ia64_invala (); - -for (;;) { - asmvolatile ("ld4.c.nc %0=[%1]" :"=r" (ticket) :"r" (p) :"memory" ); -if (!(((ticket >>TICKET_SHIFT ) ^ ticket) &TICKET_MASK )) -return ; -cpu_relax (); - } - -smp_acquire__after_ctrl_dep (); -} -static inline int __ticket_spin_is_locked (arch_spinlock_t *lock) {long tmp =ACCESS_ONCE (lock->lock);
@@ -143,11 +127,6 @@ static __always_inline void arch_spin_lock_flags(arch_spinlock_t *lock,arch_spin_lock (lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -__ticket_spin_unlock_wait (lock); -} -#define arch_read_can_lock (rw) (*(volatile int *)(rw) >= 0)#define arch_write_can_lock (rw) (*(volatile int *)(rw) == 0)
arch/m32r/include/asm/spinlock.h +0 -5
@@ -30,11 +30,6 @@#define arch_spin_is_locked (x) (*(volatile int *)(&(x)->slock) <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL >0 ); -} -/** * arch_spin_trylock - Try spin lock and return a result * @lock: Pointer to the lock variable
arch/metag/include/asm/spinlock.h +0 -5
@@ -15,11 +15,6 @@* locked. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/mips/include/asm/spinlock.h +0 -16
@@ -50,22 +50,6 @@ static inline int arch_spin_value_unlocked(arch_spinlock_t lock)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->h.serving_now); -smp_rmb (); -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.h.serving_now == tmp.h.ticket || - tmp.h.serving_now != owner) -break ; - -cpu_relax (); - } -smp_acquire__after_ctrl_dep (); -} -static inline int arch_spin_is_contended (arch_spinlock_t *lock) {u32 counters =ACCESS_ONCE (lock->lock);
arch/mn10300/include/asm/spinlock.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (x) (*(volatile signed char *)(&(x)->slock) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -static inline void arch_spin_unlock (arch_spinlock_t *lock) { asm volatile(
arch/parisc/include/asm/spinlock.h +0 -7
@@ -14,13 +14,6 @@ static inline int arch_spin_is_locked(arch_spinlock_t *x)#define arch_spin_lock (lock) arch_spin_lock_flags(lock, 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *x) -{ -volatile unsigned int *a =__ldcw_align (x); - -smp_cond_load_acquire (a,VAL ); -} -static inline void arch_spin_lock_flags (arch_spinlock_t *x,unsigned long flags) {
arch/powerpc/include/asm/spinlock.h +0 -33
@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/s390/include/asm/spinlock.h +0 -7
@@ -98,13 +98,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) : "cc", "memory"); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -while (arch_spin_is_locked (lock)) -arch_spin_relax (lock); -smp_acquire__after_ctrl_dep (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/sh/include/asm/spinlock-cas.h +0 -5
@@ -29,11 +29,6 @@ static inline unsigned __sl_cas(volatile unsigned *p, unsigned old, unsigned new#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {while (!__sl_cas (&lock->lock,1 ,0 ));
arch/sh/include/asm/spinlock-llsc.h +0 -5
@@ -21,11 +21,6 @@#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -/* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not.
arch/sparc/include/asm/spinlock_32.h +0 -5
@@ -14,11 +14,6 @@#define arch_spin_is_locked (lock) (*((volatile unsigned char *)(lock)) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { __asm__ __volatile__(
arch/sparc/include/asm/spinlock_64.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (lp) ((lp)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {unsigned long tmp;
arch/tile/include/asm/spinlock_32.h +0 -2
@@ -64,8 +64,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->current_ticket = old_ticket +TICKET_QUANTUM ; } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/tile/include/asm/spinlock_64.h +0 -2
@@ -58,8 +58,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__insn_fetchadd4 (&lock->lock,1U << __ARCH_SPIN_CURRENT_SHIFT); } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -void arch_spin_lock_slow (arch_spinlock_t *lock,u32 val);/* Grab the "next" ticket number and bump it atomically.
arch/tile/lib/spinlock_32.c +0 -23
@@ -62,29 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -int curr =READ_ONCE (lock->current_ticket); -int next =READ_ONCE (lock->next_ticket); - -/* Return immediately if unlocked. */ -if (next == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (READ_ONCE (lock->current_ticket) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait); -/* * The low byte is always reserved to be the marker for a "tns" operation * since the low bit is set to "1" by a tns. The next seven bits are
arch/tile/lib/spinlock_64.c +0 -22
@@ -62,28 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -u32 val =READ_ONCE (lock->lock); -u32 curr =arch_spin_current (val); - -/* Return immediately if unlocked. */ -if (arch_spin_next (val) == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (arch_spin_current (READ_ONCE (lock->lock)) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait);/* * If the read lock fails due to a writer, we retry periodically
arch/xtensa/include/asm/spinlock.h +0 -5
@@ -33,11 +33,6 @@#define arch_spin_is_locked (x) ((x)->slock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
Signature · 1 line
2.5.2
From: Paul E. McKenney
Quoted 7 lines
Sent: 06 July 2017 00:30
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removesspin_unlock_wait()and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:
Quoted 5 lines
1-7. Change uses of
spin_unlock_wait()andraw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
I can't help feeling that it might be better to have a spin_lock_sync() call that is equivalent to a spin_lock/spin_unlock pair.
The default implementation being an inline function that does exactly that. This would let an architecture implement a more efficient version.
It might even be that this is the defined semantics of spin_unlock_wait().
Note that it can only be useful to do a spin_lock/unlock pair if it is impossible for another code path to try to acquire the lock. (Or, at least, the code can't care if the lock is acquired just after.) So if it can de determined that the lock isn't held (a READ_ONCE() might be enough) the lock itself need not be acquired (with the associated slow bus cycles).
David
Quoted 14 lines · David Laight
From: Paul E. McKenney
Quoted 7 lines
Sent: 06 July 2017 00:30
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removesspin_unlock_wait()and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:Quoted 5 lines
1-7. Change uses of
spin_unlock_wait()andraw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
Quoted 6 lines
I can't help feeling that it might be better to have a
spin_lock_sync()call that is equivalent to a spin_lock/spin_unlock pair.
The default implementation being an inline function that does exactly that. This would let an architecture implement a more efficient version.It might even be that this is the defined semantics of
spin_unlock_wait().
That was in fact my first proposal, see the comment header in current mainline for spin_unlock_wait() in include/linux/spinlock.h. But Linus quite rightly pointed out that if spin_unlock_wait() was to be defined in this way, we should get rid of spin_unlock_wait() entirely, especially given that there are not very many calls to spin_unlock_wait() and also given that none of them are particularly performance critical. Hence the current patch set, which does just that.
Quoted 3 lines
Note that it can only be useful to do a spin_lock/unlock pair if it is impossible for another code path to try to acquire the lock. (Or, at least, the code can't care if the lock is acquired just after.)
Indeed! As Oleg Nesterov pointed out, a spin_lock()/spin_unlock() pair is sort of like synchronize_rcu() for a given lock, where that lock's critical sections play the role of RCU read-side critical sections. So anything before the pair is visible to later critical sections, and anything in prior critical sections is visible to anything after the pair. But again, as Linus pointed out, if we are going to have these semantics, just do spin_lock() immediately followed by spin_unlock().
Quoted 3 lines
So if it can de determined that the lock isn't held (a
READ_ONCE()might be enough) the lock itself need not be acquired (with the associated slow bus cycles).
If you want the full semantics of a spin_lock()/spin_unlock() pair, you need a full memory barrier before the READ_ONCE(), even on x86. Without that memory barrier, you don't get the effect of the release implicit in spin_unlock().
For weaker architectures, such as PowerPC and ARM, a READ_ONCE() does -not- suffice, not at all, even with smp_mb() before and after. I encourage you to take a look at arch_spin_unlock_wait() in arm64 and powerpc if youi are interested. There were also some lengthy LKML threads discussing this about 18 months ago that could be illuminating.
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current arch_spin_unlock_wait() implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.
Thanx, Paul
Peter Zijlstra
maintainerQuoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.
I do in fact contended there are any optimizations for the exact lock+unlock semantics.
The current spin_unlock_wait() is weaker. Most notably it will not (with exception of ARM64/PPC for other reasons) cause waits on other CPUs.
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
Quoted 2 lines
The current
spin_unlock_wait()is weaker. Most notably it will not (with exception of ARM64/PPC for other reasons) cause waits on other CPUs.
Agreed, weaker semantics allow more optimizations. So use cases needing only the weaker semantics should more readily show performance benefits. But either way, we need compelling use cases, and I do not believe that any of the existing spin_unlock_wait() calls are compelling. Perhaps I am confused, but I am not seeing it for any of them.
Thanx, Paul
Peter Zijlstra
maintainerQuoted 8 lines · Paul E. McKenney
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.
Quoted 4 lines
Quoted 2 lines
I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
For the exact semantics you'd have to fully participate in the fairness protocol. You have to in fact acquire the lock in order to have the other contending CPUs wait (otherwise my earlier case 3 will fail).
At that point I'm not sure there is much actual code you can leave out.
What actual optimization is there left at that point?
Quoted 14 lines · Peter Zijlstra
Quoted 8 lines · Paul E. McKenney
Quoted 7 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.Quoted 4 lines
Quoted 2 lines
I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
Quoted 7 lines
For the exact semantics you'd have to fully participate in the fairness protocol. You have to in fact acquire the lock in order to have the other contending CPUs wait (otherwise my earlier case 3 will fail).
At that point I'm not sure there is much actual code you can leave out.
What actual optimization is there left at that point?
Got it. It was just that I was having a hard time parsing your sentence. You were contending that there are no optimizations for all implementations for the full semantics.
Thanx, Paul
Quoted 13 lines · Paul E. McKenney
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
Quoted 8 lines
Quoted 2 lines
The current
spin_unlock_wait()is weaker. Most notably it will not (with exception of ARM64/PPC for other reasons) cause waits on other CPUs.Agreed, weaker semantics allow more optimizations. So use cases needing only the weaker semantics should more readily show performance benefits. But either way, we need compelling use cases, and I do not believe that any of the existing
spin_unlock_wait()calls are compelling. Perhaps I am confused, but I am not seeing it for any of them.
If somebody really wants the full spin_unlock_wait semantics and doesn't want to interfere with other CPUs, wouldn't synchronize_sched() or something similar do the job? It wouldn't be as efficient as lock+unlock, but it also wouldn't affect other CPUs.
Alan Stern
Peter Zijlstra
maintainerQuoted 24 lines · Alan Stern
Quoted 13 lines · Paul E. McKenney
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
Quoted 8 lines
Quoted 2 lines
The current
spin_unlock_wait()is weaker. Most notably it will not (with exception of ARM64/PPC for other reasons) cause waits on other CPUs.Agreed, weaker semantics allow more optimizations. So use cases needing only the weaker semantics should more readily show performance benefits. But either way, we need compelling use cases, and I do not believe that any of the existing
spin_unlock_wait()calls are compelling. Perhaps I am confused, but I am not seeing it for any of them.
Quoted 4 lines
If somebody really wants the full
spin_unlock_waitsemantics and doesn't want to interfere with other CPUs, wouldn'tsynchronize_sched()or something similar do the job? It wouldn't be as efficient as lock+unlock, but it also wouldn't affect other CPUs.
So please don't do that. That'll create massive pain for RT. Also I don't think it works. The whole point was that spin_unlock_wait() is _cheaper_ than lock()+unlock(). If it gets to be more expensive there is absolutely no point in using it.
Quoted 30 lines · Peter Zijlstra
Quoted 24 lines · Alan Stern
Quoted 13 lines · Paul E. McKenney
Quoted 9 lines · Peter Zijlstra
Quoted 5 lines · Paul E. McKenney
And yes, there are architecture-specific optimizations for an empty spin_lock()/spin_unlock() critical section, and the current
arch_spin_unlock_wait()implementations show some of these optimizations. But I expect that performance benefits would need to be demonstrated at the system level.I do in fact contended there are any optimizations for the exact lock+unlock semantics.
You lost me on this one.
Quoted 8 lines
Quoted 2 lines
The current
spin_unlock_wait()is weaker. Most notably it will not (with exception of ARM64/PPC for other reasons) cause waits on other CPUs.Agreed, weaker semantics allow more optimizations. So use cases needing only the weaker semantics should more readily show performance benefits. But either way, we need compelling use cases, and I do not believe that any of the existing
spin_unlock_wait()calls are compelling. Perhaps I am confused, but I am not seeing it for any of them.Quoted 4 lines
If somebody really wants the full
spin_unlock_waitsemantics and doesn't want to interfere with other CPUs, wouldn'tsynchronize_sched()or something similar do the job? It wouldn't be as efficient as lock+unlock, but it also wouldn't affect other CPUs.
Quoted 4 lines
So please don't do that. That'll create massive pain for RT. Also I don't think it works. The whole point was that
spin_unlock_wait()is _cheaper_ than lock()+unlock(). If it gets to be more expensive there is absolutely no point in using it.
Of course; that is obvious.
I was making a rhetorical point: You should not try to justify spin_unlock_wait() on the basis that it doesn't cause waits on other CPUs.
Alan Stern
Peter Zijlstra
maintainerQuoted 14 lines · David Laight
From: Paul E. McKenney
Quoted 7 lines
Sent: 06 July 2017 00:30
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removesspin_unlock_wait()and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:Quoted 5 lines
1-7. Change uses of
spin_unlock_wait()andraw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
Quoted 4 lines
I can't help feeling that it might be better to have a
spin_lock_sync()call that is equivalent to a spin_lock/spin_unlock pair.
The default implementation being an inline function that does exactly that. This would let an architecture implement a more efficient version.
So that has the IRQ inversion issue found earlier in this patch-set. Not actually doing the acquire though breaks other guarantees. See later.
Quoted 1 line
It might even be that this is the defined semantics of
spin_unlock_wait().
As is, spin_unlock_wait() is somewhat ill defined. IIRC it grew from an optimization by Oleg and subsequently got used elsewhere. And it being the subtle bugger it is, there were bugs.
But part of the problem with that definition is fairness. For fair locks, spin_lock()+spin_unlock() partakes in the fairness protocol. Many of the things that would make spin_lock_sync() cheaper preclude it doing that.
(with the exception of ticket locks, those could actually do this).
But I think we can argue we don't in fact want that, all we really need is to ensure the completion of the _current_ lock. But then you've violated that equivalent thing.
As is, this is all a bit up in the air -- some of the ticket lock variants are fair or minimal, the qspinlock on is prone to starvation (although I think I can in fact fix that for qspinlock).
Quoted 6 lines
Note that it can only be useful to do a spin_lock/unlock pair if it is impossible for another code path to try to acquire the lock. (Or, at least, the code can't care if the lock is acquired just after.) So if it can de determined that the lock isn't held (a
READ_ONCE()might be enough) the lock itself need not be acquired (with the associated slow bus cycles).
Now look at the ARM64/PPC implementations that do explicit stores.
READ_ONCE() can only ever be sufficient on strongly ordered architectures, but given many performance critical architectures are in fact weakly ordered, you've opened up the exact can of worms we want to get rid of the thing for.
So given:
(1)
spin_lock()
spin_unlock()
does not in fact provide memory ordering. Any prior/subsequent load/stores can leak into the section and cross there.
What the thing does do however is serialize against other critical sections in that:
(2)
CPU0 CPU1
spin_lock() spin_lock() X = 5 spin_unlock()
spin_unlock()
r = X;
we must have r == 5 (due to address dependency on the lock; CPU1 does the store to X, then a store-release to the lock. CPU0 then does a load-acquire on the lock and that fully orders the subsequent load of X to the prior store of X).
The other way around is more difficult though:
(3)
CPU0 CPU1
X=5
spin_lock()
spin_lock() spin_unlock() r = X; spin_unlock()
Where the above will in fact observe r == 5, this will be very difficult to achieve with anything that will not let CPU1 wait. Which was the entire premise of the original optimization by Oleg.
One of the later fixes we did to spin_unlock_wait() is to give it ACQUIRE semantics to deal with (2) but even that got massively tricky, see for example the ARM64 / PPC implementations.
In short, I doubt there is any real optimization possible if you want to retain exact lock+unlock semantics.
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that spin_unlock_wait() so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.
Thanx, Paul
Peter Zijlstra
maintainerQuoted 6 lines · Paul E. McKenney
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 8 lines
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that
spin_unlock_wait()so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.
Ha!, but what would constitute 'good semantics' ?
The current thing is something along the lines of:
"Waits for the currently observed critical section
to complete with ACQUIRE ordering such that it will observe whatever state was left by said critical section."
With the 'obvious' benefit of limited interference on those actually wanting to acquire the lock, and a shorter wait time on our side too, since we only need to wait for completion of the current section, and not for however many contender are before us.
Not sure I have an actual (micro) benchmark that shows a difference though.
Is this all good enough to retain the thing, I dunno. Like I said, I'm conflicted on the whole thing. On the one hand its a nice optimization, on the other hand I don't want to have to keep fixing these bugs.
Quoted 29 lines · Peter Zijlstra
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 8 lines
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that
spin_unlock_wait()so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.Ha!, but what would constitute 'good semantics' ?
The current thing is something along the lines of:
"Waits for the currently observed critical section
to complete with ACQUIRE ordering such that it will observe whatever state was left by said critical section."With the 'obvious' benefit of limited interference on those actually wanting to acquire the lock, and a shorter wait time on our side too, since we only need to wait for completion of the current section, and not for however many contender are before us.
Quoted 8 lines
Not sure I have an actual (micro) benchmark that shows a difference though.
Is this all good enough to retain the thing, I dunno. Like I said, I'm conflicted on the whole thing. On the one hand its a nice optimization, on the other hand I don't want to have to keep fixing these bugs.
As I've said, I'd be keen to see us drop this and bring it back if/when we get a compelling use-case along with performance numbers. At that point, we'd be in a better position to define the semantics anyway, knowing what exactly is expected by the use-case.
Will
Quoted 35 lines · Will Deacon
Quoted 34 lines · Peter Zijlstra
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 8 lines
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that
spin_unlock_wait()so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.Ha!, but what would constitute 'good semantics' ?
The current thing is something along the lines of:
"Waits for the currently observed critical section
to complete with ACQUIRE ordering such that it will observe whatever state was left by said critical section."With the 'obvious' benefit of limited interference on those actually wanting to acquire the lock, and a shorter wait time on our side too, since we only need to wait for completion of the current section, and not for however many contender are before us.
Not sure I have an actual (micro) benchmark that shows a difference though.
Quoted 8 lines
Quoted 3 lines
Is this all good enough to retain the thing, I dunno. Like I said, I'm conflicted on the whole thing. On the one hand its a nice optimization, on the other hand I don't want to have to keep fixing these bugs.
As I've said, I'd be keen to see us drop this and bring it back if/when we get a compelling use-case along with performance numbers. At that point, we'd be in a better position to define the semantics anyway, knowing what exactly is expected by the use-case.
Hear, hear!!! ;-)
Thanx, Paul
Quoted 11 lines · Peter Zijlstra
Quoted 10 lines · Paul E. McKenney
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
Quoted 6 lines
Quoted 4 lines
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that
spin_unlock_wait()so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.Ha!, but what would constitute 'good semantics' ?
At this point, it beats the heck out of me! ;-)
Quoted 11 lines
The current thing is something along the lines of:
"Waits for the currently observed critical section
to complete with ACQUIRE ordering such that it will observe whatever state was left by said critical section."With the 'obvious' benefit of limited interference on those actually wanting to acquire the lock, and a shorter wait time on our side too, since we only need to wait for completion of the current section, and not for however many contender are before us.
Quoted 8 lines
Not sure I have an actual (micro) benchmark that shows a difference though.
Is this all good enough to retain the thing, I dunno. Like I said, I'm conflicted on the whole thing. On the one hand its a nice optimization, on the other hand I don't want to have to keep fixing these bugs.
Yeah, if I had seen a compelling use case... Oleg's task_work case was closest, but given that it involved a task-local lock that shouldn't be all -that- heavily contended, it is hard to see there being all that much difference.
But maybe I am missing something here? Wouldn't be the first time...
Thanx, Paul
Quoted 20 lines · Peter Zijlstra
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines · Peter Zijlstra
Quoted 1 line · David Laight
From: Paul E. McKenney
[ . . . ]
Quoted 8 lines
Quoted 3 lines
Now on the one hand I feel like Oleg that it would be a shame to loose the optimization, OTOH this thing is really really tricky to use, and has lead to a number of bugs already.
I do agree, it is a bit sad to see these optimizations go. So, should this make mainline, I will be tagging the commits that
spin_unlock_wait()so that they can be easily reverted should someone come up with good semantics and a compelling use case with compelling performance benefits.Ha!, but what would constitute 'good semantics' ?
The current thing is something along the lines of:
Quoted 8 lines
"Waits for the currently observed critical section
to complete with ACQUIRE ordering such that it will observe whatever state was left by said critical section."With the 'obvious' benefit of limited interference on those actually wanting to acquire the lock, and a shorter wait time on our side too, since we only need to wait for completion of the current section, and not for however many contender are before us.
There's another, probably just as significant advantage: queued_spin_unlock_wait() is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - if spin_unlock_wait() is ever used in a performance critical code path.
Quoted 2 lines
Not sure I have an actual (micro) benchmark that shows a difference though.
It should be pretty obvious from pretty much any profile, the actual lock+unlock sequence that modifies the lock cache line is essentially a global cacheline bounce.
Quoted 3 lines
Is this all good enough to retain the thing, I dunno. Like I said, I'm conflicted on the whole thing. On the one hand its a nice optimization, on the other hand I don't want to have to keep fixing these bugs.
So on one hand it's _obvious_ that spin_unlock_wait() is both faster on the local _and_ the remote CPUs for any sort of use case where performance matters - I don't even understand how that can be argued otherwise.
The real question, does any use-case (we care about) exist.
Here's a quick list of all the use cases:
net/netfilter/nf_conntrack_core.c:
- This is I believe the 'original', historic spin_unlock_wait() usecase that still exists in the kernel. spin_unlock_wait() is only used in a rare case, when the netfilter hash is resized via nf_conntrack_hash_resize() - which is a very heavy operation to begin with. It will no doubt get slower with the proposed changes, but it probably does not matter. A networking person Acked-by would be nice though.
drivers/ata/libata-eh.c:
- Locking of the ATA port in ata_scsi_cmd_error_handler(), presumably this can race with IRQs and ioctls() on other CPUs. Very likely not performance sensitive in any fashion, on IO errors things stop for many seconds anyway.
ipc/sem.c:
- A rare race condition branch in the SysV IPC semaphore freeing code in exit_sem() - where even the main code flow is not performance sensitive, because typical database workloads get their semaphore arrays during startup and don't ever do heavy runtime allocation/freeing of them.
kernel/sched/completion.c:
- completion_done(). This is actually a (comparatively) rarely used completion API call - almost all the upstream usecases are in drivers, plus two in filesystems - neither usecase seems in a performance critical hot path. Completions typically involve scheduling and context switching, so in the worst case the proposed change adds overhead to a scheduling slow path.
So I'd argue that unless there's some surprising performance aspect of a completion_done() user, the proposed changes should not cause any performance trouble.
In fact I'd argue that any future high performance spin_unlock_wait() user is probably better off open coding the unlock-wait poll loop (and possibly thinking hard about eliminating it altogether). If such patterns pop up in the kernel we can think about consolidating them into a single read-only primitive again.
I.e. I think the proposed changes are doing no harm, and the unavailability of a generic primitive does not hinder future optimizations either in any significant fashion.
Thanks,
Ingo
Peter Zijlstra
maintainerQuoted 24 lines · Ingo Molnar
Here's a quick list of all the use cases:
net/netfilter/nf_conntrack_core.c:- This is I believe the 'original', historic
spin_unlock_wait()usecase that still exists in the kernel.spin_unlock_wait()is only used in a rare case, when the netfilter hash is resized vianf_conntrack_hash_resize()- which is a very heavy operation to begin with. It will no doubt get slower with the proposed changes, but it probably does not matter. A networking person Acked-by would be nice though.
drivers/ata/libata-eh.c:- Locking of the ATA port in
ata_scsi_cmd_error_handler(), presumably this can race with IRQs andioctls()on other CPUs. Very likely not performance sensitive in any fashion, on IO errors things stop for many seconds anyway.
ipc/sem.c:- A rare race condition branch in the SysV IPC semaphore freeing code in
exit_sem()- where even the main code flow is not performance sensitive, because typical database workloads get their semaphore arrays during startup and don't ever do heavy runtime allocation/freeing of them.
Quoted 8 lines
kernel/sched/completion.c:-
completion_done(). This is actually a (comparatively) rarely used completion API call - almost all the upstream usecases are in drivers, plus two in filesystems - neither usecase seems in a performance critical hot path. Completions typically involve scheduling and context switching, so in the worst case the proposed change adds overhead to a scheduling slow path.
You missed the one in do_exit(), which I thought was the original one.
Quoted 1 line · Peter Zijlstra
You missed the one in
do_exit(), which I thought was the original one.
Indeed, it's raw_spin_unlock_wait() which my git grep pattern missed.
But it's not the original spin_unlock_wait(): the pi_lock and priority inheritance is a newfangled invention that Linus (rightfully) resisted for years.
The original spin_unlock_wait() was for the global scheduler_lock, long gone.
Here's the full history of the original spin_unlock_wait() usecase in the do_exit() path, for the historically interested:
[1997/04] v2.1.36:
the spin_unlock_wait() primitive gets introduced as part of release()
[1998/08] v2.1.114:
the release() usecase gets converted to an open coded spin_lock()+unlock() poll loop over scheduler_lock
[1999/05] v2.3.11pre3:
open coded loop is changed over to poll p->has_cpu
[1999/07] v2.3.12pre6:
->has_cpu loop poll loop is converted to a spin_lock()+unlock() poll loop over runqueue_lock
[2000/06] 2.4.0-test6pre4:
combined open coded p->has_cpu poll loop is added back, in addition to the
lock()+unlock() loop[2000/11] 2.4.0-test12pre4:
lock+unlock loop is changed from scheduler_lock to task_lock
[2001/11] v2.4.14.9:
->has_cpu gets renamed to ->cpus_runnable
[2001/12] v2.5.1.10:
poll loop is factored out from exit()'s release() function to the scheduler's new wait_task_inactive() function
...
[2017/07] v4.12:
wait_task_inactive() is still alive and kicking. Its poll loop has increased in complexity, but it still does not use spin_unlock_wait()
So it was always a mess, and we relatively early flipped from the clever spin_unlock_wait() implementation to an open coded lock+unlock poll loop.
TL;DR: The original do_exit() usecase is gone, it does not use spin_unlock_wait(),
since 1998.
Thanks,
Ingo
Peter Zijlstra
maintainerQuoted 3 lines · Ingo Molnar
[1997/04] v2.1.36:
the
spin_unlock_wait()primitive gets introduced as part ofrelease()
Whee, that goes _way_ further back than I thought it did :-)
Quoted 5 lines
[2017/07] v4.12:
wait_task_inactive()is still alive and kicking. Its poll loop has increased in complexity, but it still does not usespin_unlock_wait()
I've tried 'fixing' that wait_task_inactive() thing a number of times, but always failed :/ That is very nasty code indeed.
On Fri, Jul 07, 2017 at 10:31:28AM +0200, Ingo Molnar wrote:
[ . . . ]
Quoted 4 lines
In fact I'd argue that any future high performance
spin_unlock_wait()user is probably better off open coding the unlock-wait poll loop (and possibly thinking hard about eliminating it altogether). If such patterns pop up in the kernel we can think about consolidating them into a single read-only primitive again.
I would like any reintroduction to include a header comment saying exactly what the consolidated primitive actually does and does not do. ;-)
Quoted 3 lines
I.e. I think the proposed changes are doing no harm, and the unavailability of a generic primitive does not hinder future optimizations either in any significant fashion.
I will have a v3 with updated comments from Manfred. Thoughts on when/where to push this?
The reason I ask is if this does not go in during this merge window, I need to fix the header comment on spin_unlock_wait().
Thanx, Paul
Quoted 12 lines · Paul E. McKenney
On Fri, Jul 07, 2017 at 10:31:28AM +0200, Ingo Molnar wrote:
[ . . . ]
Quoted 4 lines
In fact I'd argue that any future high performance
spin_unlock_wait()user is probably better off open coding the unlock-wait poll loop (and possibly thinking hard about eliminating it altogether). If such patterns pop up in the kernel we can think about consolidating them into a single read-only primitive again.I would like any reintroduction to include a header comment saying exactly what the consolidated primitive actually does and does not do. ;-)
Quoted 6 lines
Quoted 3 lines
I.e. I think the proposed changes are doing no harm, and the unavailability of a generic primitive does not hinder future optimizations either in any significant fashion.
I will have a v3 with updated comments from Manfred. Thoughts on when/where to push this?
Once everyone agrees I can apply it to the locking tree. I think PeterZ's was the only objection?
Quoted 2 lines
The reason I ask is if this does not go in during this merge window, I need to fix the header comment on
spin_unlock_wait().
Can try it next week after some testing - let's see how busy things get for Linus in the merge window?
Thanks,
Ingo
Quoted 19 lines · Ingo Molnar
Quoted 16 lines · Paul E. McKenney
On Fri, Jul 07, 2017 at 10:31:28AM +0200, Ingo Molnar wrote:
[ . . . ]
Quoted 4 lines
In fact I'd argue that any future high performance
spin_unlock_wait()user is probably better off open coding the unlock-wait poll loop (and possibly thinking hard about eliminating it altogether). If such patterns pop up in the kernel we can think about consolidating them into a single read-only primitive again.I would like any reintroduction to include a header comment saying exactly what the consolidated primitive actually does and does not do. ;-)
Quoted 3 lines
I.e. I think the proposed changes are doing no harm, and the unavailability of a generic primitive does not hinder future optimizations either in any significant fashion.
Quoted 5 lines
Quoted 2 lines
I will have a v3 with updated comments from Manfred. Thoughts on when/where to push this?
Once everyone agrees I can apply it to the locking tree. I think PeterZ's was the only objection?
Oleg wasn't all that happy, either, but he did supply the relevant patch.
Quoted 5 lines
Quoted 2 lines
The reason I ask is if this does not go in during this merge window, I need to fix the header comment on
spin_unlock_wait().Can try it next week after some testing - let's see how busy things get for Linus in the merge window?
Sounds good! Either way is fine with me.
Thanx, Paul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove a smp_mb() in the hot path.
So for sem_lock(), I either need a primitive that dirties the cacheline or sem_lock() must continue to use spin_lock()/spin_unlock().
--
Manfred
Quoted 8 lines · Manfred Spraul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.
Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().
Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on spin_unlock() and get very close to the slow path performance of a pure cacheline-dirtying behavior.
But adding something like spin_barrier(), which purely dirties the lock cacheline, would be even faster, right?
Thanks,
Ingo
Quoted 17 lines · Ingo Molnar
Quoted 8 lines · Manfred Spraul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().
Quoted 6 lines
Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on
spin_unlock()and get very close to the slow path performance of a pure cacheline-dirtying behavior.But adding something like
spin_barrier(), which purely dirties the lock cacheline, would be even faster, right?
Interestingly enough, the arm64 and powerpc implementations of spin_unlock_wait() were very close to what it sounds like you are describing.
Thanx, Paul
Quoted 22 lines · Paul E. McKenney
Quoted 21 lines · Ingo Molnar
Quoted 8 lines · Manfred Spraul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on
spin_unlock()and get very close to the slow path performance of a pure cacheline-dirtying behavior.
Quoted 6 lines
Quoted 2 lines
But adding something like
spin_barrier(), which purely dirties the lock cacheline, would be even faster, right?Interestingly enough, the arm64 and powerpc implementations of
spin_unlock_wait()were very close to what it sounds like you are describing.
So could we perhaps solve all our problems by defining the generic version thusly:
void spin_unlock_wait(spinlock_t *lock)
{
if (spin_trylock(lock))
spin_unlock(lock);
}... and perhaps rename it to spin_barrier() [or whatever proper name there would be]?
Architectures can still optimize it, to remove the small window where the lock is held locally - as long as the ordering is at least as strong as the generic version.
This would have various advantages:
- semantics are well-defined
- the generic implementation is already pretty well optimized (no spinning)
- it would make it usable for the IPC performance optimization
- architectures could still optimize it to eliminate the window where the lock is held locally - if there's such instructions available.
Was this proposed before, or am I missing something?
Thanks,
Ingo
Quoted 34 lines · Ingo Molnar
Quoted 22 lines · Paul E. McKenney
Quoted 21 lines · Ingo Molnar
Quoted 8 lines · Manfred Spraul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on
spin_unlock()and get very close to the slow path performance of a pure cacheline-dirtying behavior.Quoted 6 lines
Quoted 2 lines
But adding something like
spin_barrier(), which purely dirties the lock cacheline, would be even faster, right?Interestingly enough, the arm64 and powerpc implementations of
spin_unlock_wait()were very close to what it sounds like you are describing.So could we perhaps solve all our problems by defining the generic version thusly:
Quoted 8 lines
void
spin_unlock_wait(spinlock_t *lock){ if (spin_trylock(lock)) spin_unlock(lock); }... and perhaps rename it to
spin_barrier()[or whatever proper name there would be]?
As lockdep, 0day Test Robot, Linus Torvalds, and several others let me know in response to my original (thankfully RFC!) patch series, this needs to disable irqs to work in the general case. For example, if the lock in question is an irq-disabling lock, you take an interrupt just after a successful spin_trylock(), and that interrupt acquires the same lock, the actuarial statistics of your kernel degrade sharply and suddenly.
What I get for sending out untested patches! :-/
Quoted 8 lines
Architectures can still optimize it, to remove the small window where the lock is held locally - as long as the ordering is at least as strong as the generic version.
This would have various advantages:
- semantics are well-defined
Quoted 8 lines
- the generic implementation is already pretty well optimized (no spinning)
- it would make it usable for the IPC performance optimization
- architectures could still optimize it to eliminate the window where the lock is held locally - if there's such instructions available.
Was this proposed before, or am I missing something?
It was sort of proposed...
https://marc.info/?l=linux-arch&m=149912878628355&w=2
But do we have a situation where normal usage of spin_lock() and spin_unlock() is causing performance or scalability trouble?
(We do have at least one situation in fnic that appears to be buggy use of spin_is_locked(), and proposing a patch for that case in on my todo list.)
Thanx, Paul
Pardon me for barging in, but I found this whole interchange extremely confusing...
Quoted 14 lines · Ingo Molnar
Quoted 12 lines · Paul E. McKenney
Quoted 11 lines · Ingo Molnar
Quoted 8 lines · Manfred Spraul
Hi Ingo,
Quoted 5 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().
This statement doesn't seem to make sense. Did Manfred mean to write "smp_mb()" instead of "spin_lock()/spin_unlock()"?
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on
spin_unlock()and get very close to the slow path performance of a pure cacheline-dirtying behavior.
This is even more confusing. Did Ingo mean to suggest using "spin_trylock()+spin_unlock()" in place of "spin_lock()+spin_unlock()" could provide the desired ordering guarantee without delaying other CPUs that may try to acquire the lock? That seems highly questionable.
Quoted 7 lines
Quoted 6 lines
Quoted 2 lines
But adding something like
spin_barrier(), which purely dirties the lock cacheline, would be even faster, right?Interestingly enough, the arm64 and powerpc implementations of
spin_unlock_wait()were very close to what it sounds like you are describing.
Quoted 7 lines
So could we perhaps solve all our problems by defining the generic version thusly:
void
spin_unlock_wait(spinlock_t *lock){ if (spin_trylock(lock)) spin_unlock(lock); }
How could this possibly be a generic version of spin_unlock_wait()? It does nothing at all (with no ordering properties) if some other CPU currently holds the lock, whereas the real spin_unlock_wait() would wait until the other CPU released the lock (or possibly longer).
And if no other CPU currently holds the lock, this has exactly the same performance properties as spin_lock()+spin_unlock(), so what's the advantage?
Alan Stern
Quoted 23 lines
... and perhaps rename it to
spin_barrier()[or whatever proper name there would be]?Architectures can still optimize it, to remove the small window where the lock is held locally - as long as the ordering is at least as strong as the generic version.
This would have various advantages:
- semantics are well-defined
- the generic implementation is already pretty well optimized (no spinning)
- it would make it usable for the IPC performance optimization
- architectures could still optimize it to eliminate the window where the lock is held locally - if there's such instructions available.
Was this proposed before, or am I missing something?
Thanks,
Ingo
Hi Alan,
Quoted 17 lines · Alan Stern
Pardon me for barging in, but I found this whole interchange extremely confusing...
Quoted 12 lines · Ingo Molnar
Quoted 10 lines · Paul E. McKenney
Quoted 9 lines · Ingo Molnar
Quoted 7 lines · Manfred Spraul
Hi Ingo,
Quoted 4 lines · Ingo Molnar
There's another, probably just as significant advantage:
queued_spin_unlock_wait()is 'read-only', while spin_lock()+spin_unlock() dirties the lock cache line. On any bigger system this should make a very measurable difference - ifspin_unlock_wait()is ever used in a performance critical code path.
Quoted 7 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
Quoted 5 lines
At least for ipc/sem:
Dirtying the cacheline (in the slow path) allows to remove asmp_mb()in the hot path.
So forsem_lock(), I either need a primitive that dirties the cacheline orsem_lock()must continue to use spin_lock()/spin_unlock().This statement doesn't seem to make sense. Did Manfred mean to write "
smp_mb()" instead of "spin_lock()/spin_unlock()"?
Option 1:
fastpath:
spin_lock(local_lock)
smp_mb(); [[1]]
smp_load_acquire(global_flag);
slow path:
global_flag = 1;
smp_mb();
<spin_unlock_wait_without_cacheline_dirtying>Option 2:
fastpath:
spin_lock(local_lock);
smp_load_acquire(global_flag)
slow path:
global_flag = 1;
spin_lock(local_lock);spin_unlock(local_lock).Rational:
The ACQUIRE from spin_lock is at the read of local_lock, not at the write. i.e.: Without the smp_mb() at [[1]], the CPU can do:
read local_lock;
read global_flag;
write local_lock;For Option 2, the smp_mb() is not required, because fast path and slow path acquire the same lock.
Quoted 7 lines
Quoted 3 lines
Quoted 3 lines
Quoted 3 lines
Technically you could use spin_trylock()+spin_unlock() and avoid the lock acquire spinning on
spin_unlock()and get very close to the slow path performance of a pure cacheline-dirtying behavior.This is even more confusing. Did Ingo mean to suggest using "spin_trylock()+spin_unlock()" in place of "spin_lock()+spin_unlock()" could provide the desired ordering guarantee without delaying other CPUs that may try to acquire the lock? That seems highly questionable.
I agree :-)
--
Manfred
Quoted 5 lines · Peter Zijlstra
Quoted 1 line
It might even be that this is the defined semantics of
spin_unlock_wait().As is,
spin_unlock_wait()is somewhat ill defined. IIRC it grew from an optimization by Oleg and subsequently got used elsewhere. And it being the subtle bugger it is, there were bugs.
I believe the historical, original spin_unlock_wait() came from early SMP optimizations of the networking code - and then spread elsewhere, step by step. All but one of the networking uses went away since then - so I don't think there's any original usecase left.
Thanks,
Ingo
Quoted 5 lines · Ingo Molnar
Quoted 2 lines · Peter Zijlstra
Quoted 1 line
It might even be that this is the defined semantics of
spin_unlock_wait().
Quoted 8 lines
Quoted 3 lines
As is,
spin_unlock_wait()is somewhat ill defined. IIRC it grew from an optimization by Oleg and subsequently got used elsewhere. And it being the subtle bugger it is, there were bugs.I believe the historical, original
spin_unlock_wait()came from early SMP optimizations of the networking code - and then spread elsewhere, step by step. All but one of the networking uses went away since then - so I don't think there's any original usecase left.
No - the original usecase was task teardown: I still remembered that but didn't find the commit - but it's there in very old Linux kernel patches, done by DaveM originally in v2.1.36 (!):
kernel/exit.c +6 -0
@@ -136,6 +136,12 @@ void release(struct task_struct * p) }for (i=1 ; i<NR_TASKS ; i++)if (task[i] == p) { +#ifdef __SMP__ +/* FIXME! Cheesy, but kills the window... -DaveM */ +while (p->processor !=NO_PROC_ID ) +barrier (); +spin_unlock_wait (&scheduler_lock); +#endif
Other code learned to use spin_unlock_wait(): the original version of [hard]irq_enter() was the second user, net_family_read_lock() was the third user, followed by more uses in networking. All but one of those are not present in the current upstream kernel anymore.
This task-teardown FIXME was fixed in v2.1.114 (was replaced by an open coded poll loop), but the spin_unlock_wait() primitive remained.
The rest is history! ;-)
Thanks,
Ingo
v3 cover Remove spin_unlock_wait()
In reply to Paul E. McKenneyHello!
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This series therefore removes spin_unlock_wait() and changes its users to instead use a lock/unlock pair. The commits are as follows, in three groups:
1-7. Change uses of spin_unlock_wait() and raw_spin_unlock_wait()
to instead use a spin_lock/spin_unlock pair. These may be applied in any order, but must be applied before any later commits in this series. The commit logs state why I believe that these commits won't noticeably degrade performance.
8. Remove core-kernel definitions for spin_unlock_wait() and
raw_spin_unlock_wait().
9. Remove arch-specific definitions of arch_spin_unlock_wait().
Changes since v2:
o Add comment-only update from Manfred.
Changes since v1:
o Disable interrupts where needed, thus avoiding embarrassing
interrupt-based self-deadlocks.
o Substitute Manfred's patch for contrack_lock (#1 above).
o Substitute Oleg's patch for task_work (#2 above).
o Use more efficient barrier based on Arnd Bergmann feedback.
o Merge the arch commits.
Thanx, Paul
------------------------------------------------------------------------
arch/alpha/include/asm/spinlock.h | 5 - arch/arc/include/asm/spinlock.h | 5 - arch/arm/include/asm/spinlock.h | 16 ---- arch/arm64/include/asm/spinlock.h | 58 +---------------- arch/blackfin/include/asm/spinlock.h | 5 - arch/hexagon/include/asm/spinlock.h | 5 - arch/ia64/include/asm/spinlock.h | 21 ------ arch/m32r/include/asm/spinlock.h | 5 - arch/metag/include/asm/spinlock.h | 5 - arch/mips/include/asm/spinlock.h | 16 ---- arch/mn10300/include/asm/spinlock.h | 5 - arch/parisc/include/asm/spinlock.h | 7 -- arch/powerpc/include/asm/spinlock.h | 33 --------- arch/s390/include/asm/spinlock.h | 7 -- arch/sh/include/asm/spinlock-cas.h | 5 - arch/sh/include/asm/spinlock-llsc.h | 5 - arch/sparc/include/asm/spinlock_32.h | 5 - arch/sparc/include/asm/spinlock_64.h | 5 - arch/tile/include/asm/spinlock_32.h | 2 arch/tile/include/asm/spinlock_64.h | 2 arch/tile/lib/spinlock_32.c | 23 ------ arch/tile/lib/spinlock_64.c | 22 ------ arch/xtensa/include/asm/spinlock.h | 5 - drivers/ata/libata-eh.c | 8 -- include/asm-generic/qspinlock.h | 14 ---- include/linux/spinlock.h | 31 --------- include/linux/spinlock_up.h | 6 - ipc/sem.c | 3 kernel/exit.c | 3 kernel/locking/qspinlock.c | 117 ----------------------------------- kernel/sched/completion.c | 9 -- kernel/sched/core.c | 5 - kernel/task_work.c | 8 -- net/netfilter/nf_conntrack_core.c | 52 ++++++++------- 34 files changed, 48 insertions(+), 475 deletions(-)
PATCH v3 1/9 net/netfilter/nf_conntrack_core: Fix net_conntrack_lock()
From: Manfred Spraul <[email protected]>
As we want to remove spin_unlock_wait() and replace it with explicit spin_lock()/spin_unlock() calls, we can use this to simplify the locking.
In addition:
- Reading nf_conntrack_locks_all needs ACQUIRE memory ordering.
- The new code avoids the backwards loop.
Only slightly tested, I did not manage to trigger calls to nf_conntrack_all_lock().
V2: With improved comments, to clearly show how the barriers
pair.
--- net/netfilter/nf_conntrack_core.c | 52 ++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-)
net/netfilter/nf_conntrack_core.c +29 -23
@@ -96,19 +96,26 @@ static struct conntrack_gc_work conntrack_gc_work;void nf_conntrack_lock (spinlock_t *lock)__acquires (lock) { +/* 1) Acquire the lock */ spin_lock (lock); -while (unlikely (nf_conntrack_locks_all)) { -spin_unlock (lock); -/* -* Order the 'nf_conntrack_locks_all' load vs. the -* spin_unlock_wait() loads below, to ensure -* that 'nf_conntrack_locks_all_lock' is indeed held: -*/ -smp_rmb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ -spin_unlock_wait (&nf_conntrack_locks_all_lock); -spin_lock (lock); - } +/* 2) read nf_conntrack_locks_all, with ACQUIRE semantics +* It pairs with the smp_store_release() in nf_conntrack_all_unlock() +*/ +if (likely (smp_load_acquire (&nf_conntrack_locks_all) == false)) +return ; + +/* fast path failed, unlock */ +spin_unlock (lock); + +/* Slow path 1) get global lock */ +spin_lock (&nf_conntrack_locks_all_lock); + +/* Slow path 2) get the lock we want */ +spin_lock (lock); + +/* Slow path 3) release the global lock */ +spin_unlock (&nf_conntrack_locks_all_lock); }EXPORT_SYMBOL_GPL (nf_conntrack_lock);
@@ -149,28 +156,27 @@ static void nf_conntrack_all_lock(void)int i;spin_lock (&nf_conntrack_locks_all_lock); - nf_conntrack_locks_all = true; -/* -* Order the above store of 'nf_conntrack_locks_all' against -* the spin_unlock_wait() loads below, such that if -* nf_conntrack_lock() observes 'nf_conntrack_locks_all' -* we must observe nf_conntrack_locks[] held: -*/ -smp_mb ();/* spin_lock(&nf_conntrack_locks_all_lock) */ + nf_conntrack_locks_all = true;for (i =0 ; i <CONNTRACK_LOCKS ; i++) { -spin_unlock_wait (&nf_conntrack_locks[i]); +spin_lock (&nf_conntrack_locks[i]); + +/* This spin_unlock provides the "release" to ensure that +* nf_conntrack_locks_all==true is visible to everyone that +* acquired spin_lock(&nf_conntrack_locks[]). +*/ +spin_unlock (&nf_conntrack_locks[i]); } }static void nf_conntrack_all_unlock (void ) { -/* -* All prior stores must be complete before we clear +/* All prior stores must be complete before we clear * 'nf_conntrack_locks_all'. Otherwise nf_conntrack_lock() * might observe the false value but not the entire -* critical section: +* critical section. +* It pairs with the smp_load_acquire() in nf_conntrack_lock() */ smp_store_release (&nf_conntrack_locks_all, false);spin_unlock (&nf_conntrack_locks_all_lock);
Signature · 1 line
2.5.2
PATCH v3 2/9 task_work: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyFrom: Oleg Nesterov <[email protected]>
There is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in task_work_run() with a spin_lock_irq() and a spin_unlock_irq() aruond the cmpxchg() dequeue loop. This should be safe from a performance perspective because ->pi_lock is local to the task and because calls to the other side of the race, task_work_cancel(), should be rare.
--- kernel/task_work.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-)
kernel/task_work.c +2 -6
@@ -96,20 +96,16 @@ void task_work_run(void)* work->func() can do task_work_add(), do not set * work_exited unless the list is empty. */ +raw_spin_lock_irq (&task->pi_lock);do { work =READ_ONCE (task->task_works); head = !work && (task->flags &PF_EXITING ) ? &work_exited :NULL ; }while (cmpxchg (&task->task_works, work, head) != work); +raw_spin_unlock_irq (&task->pi_lock);if (!work)break ; -/* -* Synchronize with task_work_cancel(). It can't remove -* the first entry == work, cmpxchg(task_works) should -* fail, but it can play with *work and other entries. -*/ -raw_spin_unlock_wait (&task->pi_lock); do { next = work->next;
Signature · 1 line
2.5.2
PATCH v3 3/9 sched: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_task_dead() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is this tasks ->pi_lock, and this is called only after the task exits.
[ paulmck: Replace leading smp_mb() with smp_mb__before_spinlock(),
courtesy of Arnd Bergmann's noting its odd location. ]
--- kernel/sched/core.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-)
kernel/sched/core.c +3 -2
@@ -3460,8 +3460,9 @@ void __noreturn do_task_dead(void)* To avoid it, we have to wait for releasing tsk->pi_lock which * is held by try_to_wake_up() */ -smp_mb (); -raw_spin_unlock_wait (¤t->pi_lock); +smp_mb__before_spinlock (); +raw_spin_lock_irq (¤t->pi_lock); +raw_spin_unlock_irq (¤t->pi_lock);/* Causes final put_task_struct in finish_task_switch(): */ __set_current_state (TASK_DEAD );
Signature · 1 line
2.5.2
PATCH v3 4/9 completion: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in completion_done() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock will be held only the wakeup happens really quickly.
--- kernel/sched/completion.c | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
@@ -307,14 +307,9 @@ bool completion_done(struct completion *x)* If ->done, we need to wait for complete() to release ->wait.lock * otherwise we can end up freeing the completion before complete() * is done referencing it. -* -* The RMB pairs with complete()'s RELEASE of ->wait.lock and orders -* the loads of ->done and ->wait.lock such that we cannot observe -* the lock before complete() acquires it while observing the ->done -* after it's acquired the lock. */ -smp_rmb (); -spin_unlock_wait (&x->wait.lock); +spin_lock_irq (&x->wait.lock); +spin_unlock_irq (&x->wait.lock);return true; }EXPORT_SYMBOL (completion_done);
Signature · 1 line
2.5.2
PATCH v3 5/9 exit: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in do_exit() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because the lock is a per-task lock, and this is happening only at task-exit time.
--- kernel/exit.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
@@ -832,7 +832,8 @@ void __noreturn do_exit(long code)* Ensure that we must observe the pi_state in exit_mm() -> * mm_release() -> exit_pi_state_list(). */ -raw_spin_unlock_wait (&tsk->pi_lock); +raw_spin_lock_irq (&tsk->pi_lock); +raw_spin_unlock_irq (&tsk->pi_lock);if (unlikely (in_atomic ())) { pr_info("note: %s[%d] exited with preempt_count %d\n",
Signature · 1 line
2.5.2
PATCH v3 6/9 ipc: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore replaces the spin_unlock_wait() call in exit_sem() with spin_lock() followed immediately by spin_unlock(). This should be safe from a performance perspective because exit_sem() is rarely invoked in production.
--- ipc/sem.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
@@ -2096,7 +2096,8 @@ void exit_sem(struct task_struct *tsk)* possibility where we exit while freeary() didn't * finish unlocking sem_undo_list. */ -spin_unlock_wait (&ulp->lock); +spin_lock (&ulp->lock); +spin_unlock (&ulp->lock);rcu_read_unlock ();break ; }
Signature · 1 line
2.5.2
PATCH v3 7/9 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore eliminates the spin_unlock_wait() call and associated else-clause and hoists the then-clause's lock and unlock out of the "if" statement. This should be safe from a performance perspective because according to Tejun there should be few if any drivers that don't set their own error handler.
--- drivers/ata/libata-eh.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
@@ -645,12 +645,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,* completions are honored. A scmd is determined to have * timed out iff its associated qc is active and not failed. */ +spin_lock_irqsave (ap->lock, flags);if (ap->ops->error_handler) {struct scsi_cmnd *scmd, *tmp;int nr_timedout =0 ; -spin_lock_irqsave (ap->lock, flags); -/* This must occur under the ap->lock as we don't want a polled recovery to race the real interrupt handler
@@ -700,12 +699,11 @@ void ata_scsi_cmd_error_handler(struct Scsi_Host *host, struct ata_port *ap,if (nr_timedout)__ata_port_freeze (ap); -spin_unlock_irqrestore (ap->lock, flags);/* initialize eh_tries */ ap->eh_tries =ATA_EH_MAX_TRIES ; - }else -spin_unlock_wait (ap->lock); + } +spin_unlock_irqrestore (ap->lock, flags); }EXPORT_SYMBOL (ata_scsi_cmd_error_handler);
Signature · 1 line
2.5.2
PATCH v3 8/9 locking: Remove spin_unlock_wait() generic definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes spin_unlock_wait() and related definitions from core code.
--- include/asm-generic/qspinlock.h | 14 ----- include/linux/spinlock.h | 31 ----------- include/linux/spinlock_up.h | 6 --- kernel/locking/qspinlock.c | 117 ---------------------------------------- 4 files changed, 168 deletions(-)
include/asm-generic/qspinlock.h +0 -14
@@ -22,17 +22,6 @@#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
@@ -41,8 +30,6 @@ extern void queued_spin_unlock_wait(struct qspinlock *lock); static __always_inline int queued_spin_is_locked(struct qspinlock *lock) {/* -* See queued_spin_unlock_wait(). -* * Any !0 state indicates it is locked, even if _Q_LOCKED_VAL * isn't immediately observable. */
@@ -135,6 +122,5 @@ static __always_inline bool virt_spin_lock(struct qspinlock *lock)#define arch_spin_trylock (l) queued_spin_trylock(l)#define arch_spin_unlock (l) queued_spin_unlock(l)#define arch_spin_lock_flags (l, f) queued_spin_lock(l) -#define arch_spin_unlock_wait (l) queued_spin_unlock_wait(l)#endif /* __ASM_GENERIC_QSPINLOCK_H */
include/linux/spinlock.h +0 -31
@@ -130,12 +130,6 @@ do { \#define smp_mb__before_spinlock () smp_wmb()#endif -/** -* raw_spin_unlock_wait - wait until the spinlock gets unlocked -* @lock: the spinlock in question. -*/ -#define raw_spin_unlock_wait (lock) arch_spin_unlock_wait(&(lock)->raw_lock) -#ifdef CONFIG_DEBUG_SPINLOCK extern void do_raw_spin_lock(raw_spinlock_t *lock) __acquires(lock);#define do_raw_spin_lock_flags (lock, flags) do_raw_spin_lock(lock)
@@ -369,31 +363,6 @@ static __always_inline int spin_trylock_irq(spinlock_t *lock)raw_spin_trylock_irqsave (spinlock_check (lock), flags); \ }) -/** -* spin_unlock_wait - Interpose between successive critical sections -* @lock: the spinlock whose critical sections are to be interposed. -* -* Semantically this is equivalent to a spin_lock() immediately -* followed by a spin_unlock(). However, most architectures have -* more efficient implementations in which the spin_unlock_wait() -* cannot block concurrent lock acquisition, and in some cases -* where spin_unlock_wait() does not write to the lock variable. -* Nevertheless, spin_unlock_wait() can have high overhead, so if -* you feel the need to use it, please check to see if there is -* a better way to get your job done. -* -* The ordering guarantees provided by spin_unlock_wait() are: -* -* 1. All accesses preceding the spin_unlock_wait() happen before -* any accesses in later critical sections for this same lock. -* 2. All accesses following the spin_unlock_wait() happen after -* any accesses in earlier critical sections for this same lock. -*/ -static __always_inline void spin_unlock_wait(spinlock_t *lock) -{ -raw_spin_unlock_wait (&lock->rlock); -} - static __always_inline int spin_is_locked(spinlock_t *lock) {return raw_spin_is_locked (&lock->rlock);
include/linux/spinlock_up.h +0 -6
@@ -26,11 +26,6 @@#ifdef CONFIG_DEBUG_SPINLOCK #define arch_spin_is_locked (x) ((x)->slock == 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { lock->slock =0 ;
@@ -73,7 +68,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)#else /* DEBUG_SPINLOCK */ #define arch_spin_is_locked (lock) ((void)(lock), 0) -#define arch_spin_unlock_wait (lock) do { barrier(); (void)(lock); } while (0)/* for sched/core.c and kernel_lock.c: */ # define arch_spin_lock (lock) do { barrier(); (void)(lock); } while (0)# define arch_spin_lock_flags (lock, flags) do { barrier(); (void)(lock); } while (0)
kernel/locking/qspinlock.c +0 -117
123 lines not on this page · raw message
Signature · 1 line
2.5.2
PATCH v3 9/9 arch: Remove spin_unlock_wait() arch-specific definitions
In reply to Paul E. McKenneyThere is no agreed-upon definition of spin_unlock_wait()'s semantics, and it appears that all callers could do just as well with a lock/unlock pair. This commit therefore removes the underlying arch-specific arch_spin_unlock_wait() for all architectures providing them.
--- arch/alpha/include/asm/spinlock.h | 5 ---- arch/arc/include/asm/spinlock.h | 5 ---- arch/arm/include/asm/spinlock.h | 16 ---------- arch/arm64/include/asm/spinlock.h | 58 ++++-------------------------------- arch/blackfin/include/asm/spinlock.h | 5 ---- arch/hexagon/include/asm/spinlock.h | 5 ---- arch/ia64/include/asm/spinlock.h | 21 ------------- arch/m32r/include/asm/spinlock.h | 5 ---- arch/metag/include/asm/spinlock.h | 5 ---- arch/mips/include/asm/spinlock.h | 16 ---------- arch/mn10300/include/asm/spinlock.h | 5 ---- arch/parisc/include/asm/spinlock.h | 7 ----- arch/powerpc/include/asm/spinlock.h | 33 -------------------- arch/s390/include/asm/spinlock.h | 7 ----- arch/sh/include/asm/spinlock-cas.h | 5 ---- arch/sh/include/asm/spinlock-llsc.h | 5 ---- arch/sparc/include/asm/spinlock_32.h | 5 ---- arch/sparc/include/asm/spinlock_64.h | 5 ---- arch/tile/include/asm/spinlock_32.h | 2 -- arch/tile/include/asm/spinlock_64.h | 2 -- arch/tile/lib/spinlock_32.c | 23 -------------- arch/tile/lib/spinlock_64.c | 22 -------------- arch/xtensa/include/asm/spinlock.h | 5 ---- 23 files changed, 5 insertions(+), 262 deletions(-)
arch/alpha/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_spin_is_locked (x) ((x)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_spin_value_unlocked (arch_spinlock_t lock) {return lock.lock ==0 ;
arch/arc/include/asm/spinlock.h +0 -5
@@ -16,11 +16,6 @@#define arch_spin_is_locked (x) ((x)->slock != __ARCH_SPIN_LOCK_UNLOCKED__)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#ifdef CONFIG_ARC_HAS_LLSC static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm/include/asm/spinlock.h +0 -16
@@ -52,22 +52,6 @@ static inline void dsb_sev(void)* memory. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->tickets.owner); - -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.tickets.owner == tmp.tickets.next || - tmp.tickets.owner != owner) -break ; - -wfe (); - } -smp_acquire__after_ctrl_dep (); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
arch/arm64/include/asm/spinlock.h +5 -53
70 lines not on this page · raw message
arch/blackfin/include/asm/spinlock.h +0 -5
@@ -48,11 +48,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__raw_spin_unlock_asm (&lock->lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline int arch_read_can_lock (arch_rwlock_t *rw) {return __raw_uncached_fetch_asm (&rw->lock) >0 ;
arch/hexagon/include/asm/spinlock.h +0 -5
@@ -179,11 +179,6 @@ static inline unsigned int arch_spin_trylock(arch_spinlock_t *lock)*/ #define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_is_locked (x) ((x)->lock != 0)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/ia64/include/asm/spinlock.h +0 -21
@@ -76,22 +76,6 @@ static __always_inline void __ticket_spin_unlock(arch_spinlock_t *lock)ACCESS_ONCE (*p) = (tmp +2 ) & ~1 ; } -static __always_inline void __ticket_spin_unlock_wait(arch_spinlock_t *lock) -{ -int *p = (int *)&lock->lock, ticket; - -ia64_invala (); - -for (;;) { - asmvolatile ("ld4.c.nc %0=[%1]" :"=r" (ticket) :"r" (p) :"memory" ); -if (!(((ticket >>TICKET_SHIFT ) ^ ticket) &TICKET_MASK )) -return ; -cpu_relax (); - } - -smp_acquire__after_ctrl_dep (); -} -static inline int __ticket_spin_is_locked (arch_spinlock_t *lock) {long tmp =ACCESS_ONCE (lock->lock);
@@ -143,11 +127,6 @@ static __always_inline void arch_spin_lock_flags(arch_spinlock_t *lock,arch_spin_lock (lock); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -__ticket_spin_unlock_wait (lock); -} -#define arch_read_can_lock (rw) (*(volatile int *)(rw) >= 0)#define arch_write_can_lock (rw) (*(volatile int *)(rw) == 0)
arch/m32r/include/asm/spinlock.h +0 -5
@@ -30,11 +30,6 @@#define arch_spin_is_locked (x) (*(volatile int *)(&(x)->slock) <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock,VAL >0 ); -} -/** * arch_spin_trylock - Try spin lock and return a result * @lock: Pointer to the lock variable
arch/metag/include/asm/spinlock.h +0 -5
@@ -15,11 +15,6 @@* locked. */ -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)#define arch_read_lock_flags (lock, flags) arch_read_lock(lock)
arch/mips/include/asm/spinlock.h +0 -16
@@ -50,22 +50,6 @@ static inline int arch_spin_value_unlocked(arch_spinlock_t lock)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u16 owner =READ_ONCE (lock->h.serving_now); -smp_rmb (); -for (;;) { -arch_spinlock_t tmp =READ_ONCE (*lock); - -if (tmp.h.serving_now == tmp.h.ticket || - tmp.h.serving_now != owner) -break ; - -cpu_relax (); - } -smp_acquire__after_ctrl_dep (); -} -static inline int arch_spin_is_contended (arch_spinlock_t *lock) {u32 counters =ACCESS_ONCE (lock->lock);
arch/mn10300/include/asm/spinlock.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (x) (*(volatile signed char *)(&(x)->slock) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -static inline void arch_spin_unlock (arch_spinlock_t *lock) { asm volatile(
arch/parisc/include/asm/spinlock.h +0 -7
@@ -14,13 +14,6 @@ static inline int arch_spin_is_locked(arch_spinlock_t *x)#define arch_spin_lock (lock) arch_spin_lock_flags(lock, 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *x) -{ -volatile unsigned int *a =__ldcw_align (x); - -smp_cond_load_acquire (a,VAL ); -} -static inline void arch_spin_lock_flags (arch_spinlock_t *x,unsigned long flags) {
arch/powerpc/include/asm/spinlock.h +0 -33
@@ -170,39 +170,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->slock =0 ; } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -arch_spinlock_t lock_val; - -smp_mb (); - -/* -* Atomically load and store back the lock value (unchanged). This -* ensures that our observation of the lock value is ordered with -* respect to other lock operations. -*/ - __asm__ __volatile__( -"1: " PPC_LWARX(%0, 0, %2, 0) "\n" -" stwcx. %0, 0, %2\n" -" bne- 1b\n" - : "=&r" (lock_val), "+m" (*lock) - : "r" (lock) - : "cr0", "xer"); - - if (arch_spin_value_unlocked(lock_val)) - goto out; - -while (lock->slock) { -HMT_low (); -if (SHARED_PROCESSOR ) -__spin_yield (lock); - } -HMT_medium (); - -out: -smp_mb (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/s390/include/asm/spinlock.h +0 -7
@@ -98,13 +98,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lp) : "cc", "memory"); } -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -while (arch_spin_is_locked (lock)) -arch_spin_relax (lock); -smp_acquire__after_ctrl_dep (); -} -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/sh/include/asm/spinlock-cas.h +0 -5
@@ -29,11 +29,6 @@ static inline unsigned __sl_cas(volatile unsigned *p, unsigned old, unsigned new#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {while (!__sl_cas (&lock->lock,1 ,0 ));
arch/sh/include/asm/spinlock-llsc.h +0 -5
@@ -21,11 +21,6 @@#define arch_spin_is_locked (x) ((x)->lock <= 0)#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock,VAL >0 ); -} -/* * Simple spin lock operations. There are two variants, one clears IRQ's * on the local processor, one does not.
arch/sparc/include/asm/spinlock_32.h +0 -5
@@ -14,11 +14,6 @@#define arch_spin_is_locked (lock) (*((volatile unsigned char *)(lock)) != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) { __asm__ __volatile__(
arch/sparc/include/asm/spinlock_64.h +0 -5
@@ -26,11 +26,6 @@#define arch_spin_is_locked (lp) ((lp)->lock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->lock, !VAL ); -} -static inline void arch_spin_lock (arch_spinlock_t *lock) {unsigned long tmp;
arch/tile/include/asm/spinlock_32.h +0 -2
@@ -64,8 +64,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock) lock->current_ticket = old_ticket +TICKET_QUANTUM ; } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -/* * Read-write spinlocks, allowing multiple readers * but only one writer.
arch/tile/include/asm/spinlock_64.h +0 -2
@@ -58,8 +58,6 @@ static inline void arch_spin_unlock(arch_spinlock_t *lock)__insn_fetchadd4 (&lock->lock,1U << __ARCH_SPIN_CURRENT_SHIFT); } -void arch_spin_unlock_wait (arch_spinlock_t *lock); -void arch_spin_lock_slow (arch_spinlock_t *lock,u32 val);/* Grab the "next" ticket number and bump it atomically.
arch/tile/lib/spinlock_32.c +0 -23
@@ -62,29 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -int curr =READ_ONCE (lock->current_ticket); -int next =READ_ONCE (lock->next_ticket); - -/* Return immediately if unlocked. */ -if (next == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (READ_ONCE (lock->current_ticket) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait); -/* * The low byte is always reserved to be the marker for a "tns" operation * since the low bit is set to "1" by a tns. The next seven bits are
arch/tile/lib/spinlock_64.c +0 -22
@@ -62,28 +62,6 @@ int arch_spin_trylock(arch_spinlock_t *lock) }EXPORT_SYMBOL (arch_spin_trylock); -void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -u32 iterations =0 ; -u32 val =READ_ONCE (lock->lock); -u32 curr =arch_spin_current (val); - -/* Return immediately if unlocked. */ -if (arch_spin_next (val) == curr) -return ; - -/* Wait until the current locker has released the lock. */ -do { -delay_backoff (iterations++); - }while (arch_spin_current (READ_ONCE (lock->lock)) == curr); - -/* -* The TILE architecture doesn't do read speculation; therefore -* a control dependency guarantees a LOAD->{LOAD,STORE} order. -*/ -barrier (); -} -EXPORT_SYMBOL (arch_spin_unlock_wait);/* * If the read lock fails due to a writer, we retry periodically
arch/xtensa/include/asm/spinlock.h +0 -5
@@ -33,11 +33,6 @@#define arch_spin_is_locked (x) ((x)->slock != 0) -static inline void arch_spin_unlock_wait (arch_spinlock_t *lock) -{ -smp_cond_load_acquire (&lock->slock, !VAL ); -} -#define arch_spin_lock_flags (lock, flags) arch_spin_lock(lock)static inline void arch_spin_lock (arch_spinlock_t *lock)
Signature · 1 line
2.5.2