Skip to contentSkip to conversation

Remove spin_unlock_wait()

RFC PATCH Superseded Paul E. McKenney 11 participants 124 messages Jun 29, 2017 – Jul 10, 2017 1 message not in the archive

Subsystems (20)

v1 of 5 Superseded Latest is v5 →

lore
Share & tools
lore
#0 lore raw

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(-)
#1 lore raw

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.

Cc Pablo Neira Ayuso <[email protected]>
Cc Jozsef Kadlecsik <[email protected]>
Cc Florian Westphal <[email protected]>
Cc "David S. Miller" <[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 +8 -18
index e847dbaa0c6b..9f997859d160 100644
@@ -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]

#3 lore raw

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

Cc Pablo Neira Ayuso<[email protected]>
Cc Jozsef Kadlecsik<[email protected]>
Cc Florian Westphal<[email protected]>
Cc "David S. Miller"<[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 -0
index e847dbaa0c6b..9f997859d160 100644

net/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 -5
 		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:

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

--
Manfred

Quoted 1 line

From 453e7a77f3756d939c754031b092cbdfbd149559 Mon Sep 17 00:00:00 2001

From: Manfred Spraul <[email protected]>
Date: Sun, 21 Aug 2016 07:17:55 +0200
Subject: [PATCH] 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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
#4 lore raw

Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair

In reply to a message not in the archive
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:
- 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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]);
 	}
 }
#5 lore raw

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:
- 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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.

Quoted 5 lines

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.

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

#6 lore raw

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:
- 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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 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().

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 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().

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

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)

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

#7 lore raw

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 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().

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);
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 a smp_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

#8 lore raw

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 like nf_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 b549e0281b66124b62aa94543f91b0e616abaf52 Mon 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
index 947dc23..75a4358 100644
@@ -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
#9 lore raw

Re: [PATCH RFC 01/26] netfilter: Replace spin_unlock_wait() with lock/unlock pair

In reply to Paul E. McKenney
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:
- 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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.

Quoted 5 lines

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.

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)

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

#10 lore raw

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:
- 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().

Fixes b16c29191dc8
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
index e847dba..1193565 100644
@@ -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.

Quoted 5 lines

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.

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)

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

#11 lore raw

RFC PATCH 2/26 task_work: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Oleg Nesterov <[email protected]>
Cc Andrew Morton <[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]>
---
 kernel/task_work.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/task_work.c +2 -1
index d513051fcca2..b9b428832229 100644
@@ -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
#12 lore raw

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.

#13 lore raw

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 think unlock_wait have 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

#14 lore raw

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.

#15 lore raw

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.

#16 lore raw

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.

#17 lore raw

Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair

In reply to Paul E. McKenney
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 ;)

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.

#18 lore raw

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.

#19 lore raw

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 use synchronize_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.

#20 lore raw

Re: [PATCH RFC 02/26] task_work: Replace spin_unlock_wait() with lock/unlock pair

In reply to Oleg Nesterov
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 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

#21 lore raw

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 by spin_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
index d513051fcca2..836a72a66fba 100644
@@ -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;
#22 lore raw

RFC PATCH 3/26 sched: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[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]>
---
 kernel/sched/core.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/sched/core.c +2 -1
index e91138fcde86..6dea3d9728c8 100644
@@ -3461,7 +3461,8 @@ void __noreturn do_task_dead(void)
 	 * is held by try_to_wake_up()
 	 */
 	smp_mb();
-	raw_spin_unlock_wait(&current->pi_lock);
+	raw_spin_lock(&current->pi_lock);
+	raw_spin_unlock(&current->pi_lock);
 
 	/* Causes final put_task_struct in finish_task_switch(): */
 	__set_current_state(TASK_DEAD);
Signature · 1 line
2.5.2
maintainer
#23 lore raw

Re: [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 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 +0 -0
index e91138fcde86..6dea3d9728c8 100644

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(&current->pi_lock);
+       raw_spin_lock(&current->pi_lock);
+       raw_spin_unlock(&current->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

#24 lore raw

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

Quoted 14 lines

kernel/sched/core.c void __noreturn do_task_dead(void) · line 3465

Quoted 11 lines
kernel/sched/core.c +2 -1
index e91138fcde86..6dea3d9728c8 100644
@@ -3461,7 +3461,8 @@ void __noreturn do_task_dead(void)
         * is held by try_to_wake_up()
         */
        smp_mb();
-       raw_spin_unlock_wait(&current->pi_lock);
+       raw_spin_lock(&current->pi_lock);
+       raw_spin_unlock(&current->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(&current->pi_lock);
+       smp_mb__before_spinlock();
+       raw_spin_lock(&current->pi_lock);
+       raw_spin_unlock(&current->pi_lock);

Thanx, Paul

#25 lore raw

RFC PATCH 4/26 completion: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/sched/completion.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
index 53f9558fa925..d4b89a59629f 100644
@@ -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
#26 lore raw

RFC PATCH 5/26 exit: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/exit.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
index 516acdb0e0ec..1a976e47ddd1 100644
@@ -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
#27 lore raw

RFC PATCH 6/26 ipc: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

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]>
---
 ipc/sem.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
index 947dc2348271..e88d0749a929 100644
@@ -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
Acked-by on v1 6/26
#28 lore raw

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

Quoted 9 lines
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
index 947dc2348271..e88d0749a929 100644
@@ -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;
  		}
author Applied
#29 lore raw

Re: [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 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.

Quoted 9 lines
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

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
index 947dc2348271..e88d0749a929 100644
@@ -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;
 		}
#30 lore raw

RFC PATCH 7/26 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

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]>
---
 drivers/ata/libata-eh.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
index ef68232b5222..779f6f18c1f4 100644
@@ -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
#31 lore raw

RFC PATCH 8/26 locking: Remove spin_unlock_wait() generic definitions

In reply to 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.

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(-)
include/asm-generic/qspinlock.h +0 -14
index 9f0681bf1e87..66260777d644 100644
@@ -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
index d9510e8522d4..ef018a6e4985 100644
@@ -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
index 0d9848de677d..612fb530af41 100644
@@ -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
#32 lore raw

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.

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
index b2caec7315af..64a9051e4c2c 100644
@@ -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

#33 lore raw

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.

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
index b2caec7315af..64a9051e4c2c 100644
@@ -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

#34 lore raw

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.

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
index b2caec7315af..64a9051e4c2c 100644
@@ -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

#35 lore raw

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.

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
index b2caec7315af..64a9051e4c2c 100644
@@ -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 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.

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

#36 lore raw

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

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

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

#37 lore raw

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

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?

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_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().

Agreed, and my next step is to look at spin_lock() followed by spin_is_locked(), not necessarily the same lock.

Thanx, Paul

#38 lore raw

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 by spin_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

#39 lore raw

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 by spin_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 arguably arch_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

#40 lore raw

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 by spin_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 arguably arch_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.

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

#41 lore raw

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

#42 lore raw

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

#43 lore raw

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

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

#44 lore raw

Re: [PATCH RFC 08/26] locking: Remove spin_unlock_wait() generic definitions

In reply to Linus Torvalds
Quoted 8 lines · Linus Torvalds
Quoted 3 lines · Paul E. McKenney

Agreed, and my next step is to look at spin_lock() followed by spin_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 arguably arch_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

#45 lore raw

RFC PATCH 9/26 alpha: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Richard Henderson <[email protected]>
Cc Ivan Kokshaysky <[email protected]>
Cc Matt Turner <[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]>
---
 arch/alpha/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/alpha/include/asm/spinlock.h +0 -5
index a40b9fc0c6c3..718ac0b64adf 100644
@@ -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
#46 lore raw

RFC PATCH 10/26 arc: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Vineet Gupta <[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]>
---
 arch/arc/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/arc/include/asm/spinlock.h +0 -5
index 233d5ffe6ec7..a325e6a36523 100644
@@ -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
#47 lore raw

RFC PATCH 11/26 arm: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Russell King <[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]>
---
 arch/arm/include/asm/spinlock.h | 16 ----------------
 1 file changed, 16 deletions(-)
arch/arm/include/asm/spinlock.h +0 -16
index 4bec45442072..c030143c18c6 100644
@@ -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
#48 lore raw

RFC PATCH 12/26 arm64: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Catalin Marinas <[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]>
---
 arch/arm64/include/asm/spinlock.h | 58 ++++-----------------------------------
 1 file changed, 5 insertions(+), 53 deletions(-)
arch/arm64/include/asm/spinlock.h +5 -53
index cae331d553f8..f445bd7f2b9f 100644
@@ -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;
-
-	asm volatile(
-"	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
Acked-by on v1 12/26
#49 lore raw

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().

Cc Catalin Marinas <[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 3 lines
---
 arch/arm64/include/asm/spinlock.h | 58 ++++-----------------------------------
 1 file changed, 5 insertions(+), 53 deletions(-)

I'm going to miss this code.

Will

author Applied
#50 lore raw

Re: [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().

Cc Catalin Marinas <[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 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

Applied, thank you!

Thanx, Paul

#51 lore raw

RFC PATCH 13/26 blackfin: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Steven Miao <[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]>
---
 arch/blackfin/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/blackfin/include/asm/spinlock.h +0 -5
index c58f4a83ed6f..f6431439d15d 100644
@@ -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
#52 lore raw

RFC PATCH 14/26 hexagon: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Richard Kuo <[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]>
---
 arch/hexagon/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/hexagon/include/asm/spinlock.h +0 -5
index a1c55788c5d6..53a8d5885887 100644
@@ -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
#53 lore raw

RFC PATCH 15/26 ia64: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Tony Luck <[email protected]>
Cc Fenghua Yu <[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]>
---
 arch/ia64/include/asm/spinlock.h | 21 ---------------------
 1 file changed, 21 deletions(-)
arch/ia64/include/asm/spinlock.h +0 -21
index ca9e76149a4a..df2c121164b8 100644
@@ -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 (;;) {
-		asm volatile ("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
#54 lore raw

RFC PATCH 16/26 m32r: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

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]>
---
 arch/m32r/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/m32r/include/asm/spinlock.h +0 -5
index 323c7fc953cd..a56825592b90 100644
@@ -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
#55 lore raw

RFC PATCH 17/26 metag: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc James Hogan <[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]>
---
 arch/metag/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/metag/include/asm/spinlock.h +0 -5
index c0c7a22be1ae..ddf7fe5708a6 100644
@@ -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
#56 lore raw

RFC PATCH 18/26 mips: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Ralf Baechle <[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]>
---
 arch/mips/include/asm/spinlock.h | 16 ----------------
 1 file changed, 16 deletions(-)
arch/mips/include/asm/spinlock.h +0 -16
index a8df44d60607..81b4945031ee 100644
@@ -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
#57 lore raw

RFC PATCH 19/26 mn10300: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc David Howells <[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]>
---
 arch/mn10300/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/mn10300/include/asm/spinlock.h +0 -5
index 9c7b8f7942d8..fe413b41df6c 100644
@@ -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
#58 lore raw

RFC PATCH 20/26 parisc: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc "James E.J. Bottomley" <[email protected]>
Cc Helge Deller <[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]>
---
 arch/parisc/include/asm/spinlock.h | 7 -------
 1 file changed, 7 deletions(-)
arch/parisc/include/asm/spinlock.h +0 -7
index e32936cd7f10..55bfe4affca3 100644
@@ -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
#59 lore raw

RFC PATCH 21/26 powerpc: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Benjamin Herrenschmidt <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Michael Ellerman <[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]>
---
 arch/powerpc/include/asm/spinlock.h | 33 ---------------------------------
 1 file changed, 33 deletions(-)
arch/powerpc/include/asm/spinlock.h +0 -33
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
Acked-by on v1 21/26
#60 lore raw

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
Cc Benjamin Herrenschmidt <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Michael Ellerman <[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
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
#61 lore raw

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
Cc Benjamin Herrenschmidt <[email protected]>
Cc Paul Mackerras <[email protected]>
Cc Michael Ellerman <[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

And finally applied in preparation for v2 of the patch series.

Thank you!!!

Thanx, Paul

Quoted 54 lines

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
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
#62 lore raw

RFC PATCH 22/26 s390: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Martin Schwidefsky <[email protected]>
Cc Heiko Carstens <[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]>
---
 arch/s390/include/asm/spinlock.h | 7 -------
 1 file changed, 7 deletions(-)
arch/s390/include/asm/spinlock.h +0 -7
index f7838ecd83c6..217ee5210c32 100644
@@ -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
#63 lore raw

RFC PATCH 23/26 sh: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Yoshinori Sato <[email protected]>
Cc Rich Felker <[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]>
---
 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
index c46e8cc7b515..5ed7dbbd94ff 100644
@@ -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
index cec78143fa83..f77263aae760 100644
@@ -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
#64 lore raw

RFC PATCH 24/26 sparc: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc "David S. Miller" <[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]>
---
 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
index 8011e79f59c9..67345b2dc408 100644
@@ -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
index 07c9f2e9bf57..923d57f9b79d 100644
@@ -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
#65 lore raw

RFC PATCH 25/26 tile: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

Cc Chris Metcalf <[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]>
---
 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
index b14b1ba5bf9c..cba8ba9b8da6 100644
@@ -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
index b9718fb4e74a..9a2c2d605752 100644
@@ -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
index 076c6cc43113..db9333f2447c 100644
@@ -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
index a4b5b2cbce93..de414c22892f 100644
@@ -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
#66 lore raw

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

#67 lore raw

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

#68 lore raw

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.

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]>
arch/alpha/include/asm/spinlock.h +0 -5
index a40b9fc0c6c3..718ac0b64adf 100644
@@ -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
index 233d5ffe6ec7..a325e6a36523 100644
@@ -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
index 4bec45442072..c030143c18c6 100644
@@ -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
index c58f4a83ed6f..f6431439d15d 100644
@@ -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
index a1c55788c5d6..53a8d5885887 100644
@@ -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
index ca9e76149a4a..df2c121164b8 100644
@@ -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 (;;) {
-		asm volatile ("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
index 323c7fc953cd..a56825592b90 100644
@@ -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
index c0c7a22be1ae..ddf7fe5708a6 100644
@@ -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
index a8df44d60607..81b4945031ee 100644
@@ -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
index 9c7b8f7942d8..fe413b41df6c 100644
@@ -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
index e32936cd7f10..55bfe4affca3 100644
@@ -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
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
index f7838ecd83c6..217ee5210c32 100644
@@ -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
index c46e8cc7b515..5ed7dbbd94ff 100644
@@ -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
index cec78143fa83..f77263aae760 100644
@@ -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
index 8011e79f59c9..67345b2dc408 100644
@@ -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
index 07c9f2e9bf57..923d57f9b79d 100644
@@ -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
index b14b1ba5bf9c..cba8ba9b8da6 100644
@@ -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
index b9718fb4e74a..9a2c2d605752 100644
@@ -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
index 076c6cc43113..db9333f2447c 100644
@@ -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
index a4b5b2cbce93..de414c22892f 100644
@@ -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
index a36221cf6363..3bb49681ee24 100644
@@ -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)
#69 lore raw

Re: [PATCH RFC 25/26] tile: Remove spin_unlock_wait() arch-specific definitions

In reply to 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.

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

#70 lore raw

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

#71 lore raw

RFC PATCH 26/26 xtensa: Remove spin_unlock_wait() arch-specific definitions

In reply to 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().

---
 arch/xtensa/include/asm/spinlock.h | 5 -----
 1 file changed, 5 deletions(-)
arch/xtensa/include/asm/spinlock.h +0 -5
index a36221cf6363..3bb49681ee24 100644
@@ -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
#72 lore raw

v2 cover Remove spin_unlock_wait()

In reply to Paul E. McKenney

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. 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(-)
#73 lore raw

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
index e847dbaa0c6b..1193565c38ae 100644
@@ -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
#74 lore raw

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:
- 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().

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.

Fixes b16c29191dc8
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
index 9979f46..51390fe 100644
@@ -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
#75 lore raw

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:
- Reading nf_conntrack_locks_all needs 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

--
Manfred

Quoted 1 line

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.

Fixes b16c29191dc8
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
index 9979f46..51390fe 100644
@@ -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
#76 lore raw

PATCH v2 2/9 task_work: Replace spin_unlock_wait() with lock/unlock pair

In reply to Paul E. McKenney

From: 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
index d513051fcca2..836a72a66fba 100644
@@ -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
#77 lore raw

PATCH v2 3/9 sched: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>

[ 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
index e91138fcde86..48a8760fedf4 100644
@@ -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(&current->pi_lock);
+	smp_mb__before_spinlock();
+	raw_spin_lock_irq(&current->pi_lock);
+	raw_spin_unlock_irq(&current->pi_lock);
 
 	/* Causes final put_task_struct in finish_task_switch(): */
 	__set_current_state(TASK_DEAD);
Signature · 1 line
2.5.2
#78 lore raw

PATCH v2 4/9 completion: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/sched/completion.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
index 53f9558fa925..3e66712e1964 100644
@@ -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
#79 lore raw

PATCH v2 5/9 exit: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/exit.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
index 516acdb0e0ec..6d19c9090d43 100644
@@ -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
#80 lore raw

PATCH v2 6/9 ipc: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Andrew Morton <[email protected]>
Cc Davidlohr Bueso <[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]>
---
 ipc/sem.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
index 947dc2348271..e88d0749a929 100644
@@ -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
#81 lore raw

PATCH v2 7/9 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

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]>
---
 drivers/ata/libata-eh.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
index ef68232b5222..779f6f18c1f4 100644
@@ -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
#82 lore raw

PATCH v2 8/9 locking: Remove spin_unlock_wait() generic definitions

In reply to 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.

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(-)
include/asm-generic/qspinlock.h +0 -14
index 9f0681bf1e87..66260777d644 100644
@@ -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
index d9510e8522d4..ef018a6e4985 100644
@@ -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
index 0d9848de677d..612fb530af41 100644
@@ -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
#83 lore raw

PATCH v2 9/9 arch: Remove spin_unlock_wait() arch-specific definitions

In reply to 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() 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
index a40b9fc0c6c3..718ac0b64adf 100644
@@ -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
index 233d5ffe6ec7..a325e6a36523 100644
@@ -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
index 4bec45442072..c030143c18c6 100644
@@ -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
index c58f4a83ed6f..f6431439d15d 100644
@@ -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
index a1c55788c5d6..53a8d5885887 100644
@@ -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
index ca9e76149a4a..df2c121164b8 100644
@@ -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 (;;) {
-		asm volatile ("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
index 323c7fc953cd..a56825592b90 100644
@@ -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
index c0c7a22be1ae..ddf7fe5708a6 100644
@@ -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
index a8df44d60607..81b4945031ee 100644
@@ -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
index 9c7b8f7942d8..fe413b41df6c 100644
@@ -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
index e32936cd7f10..55bfe4affca3 100644
@@ -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
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
index f7838ecd83c6..217ee5210c32 100644
@@ -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
index c46e8cc7b515..5ed7dbbd94ff 100644
@@ -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
index cec78143fa83..f77263aae760 100644
@@ -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
index 8011e79f59c9..67345b2dc408 100644
@@ -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
index 07c9f2e9bf57..923d57f9b79d 100644
@@ -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
index b14b1ba5bf9c..cba8ba9b8da6 100644
@@ -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
index b9718fb4e74a..9a2c2d605752 100644
@@ -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
index 076c6cc43113..db9333f2447c 100644
@@ -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
index a4b5b2cbce93..de414c22892f 100644
@@ -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
index a36221cf6363..3bb49681ee24 100644
@@ -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
#84 lore raw
In reply to Paul E. McKenney

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 removes spin_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() 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.

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

#85 lore raw
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 removes spin_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() 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.

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

maintainer
#86 lore raw
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.

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.

#87 lore raw
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

maintainer
#88 lore raw
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.

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?

#89 lore raw
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

#90 lore raw
In reply to Paul E. McKenney
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

maintainer
#91 lore raw
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_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.

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.

#92 lore raw
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_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.

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

maintainer
#93 lore raw
In reply to David Laight
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 removes spin_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() 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.

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.

#94 lore raw
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

maintainer
#95 lore raw
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.

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.

#96 lore raw
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

#97 lore raw
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

#98 lore raw
In reply to Peter Zijlstra
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

#99 lore raw
In reply to Peter Zijlstra
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

maintainer
#100 lore raw
Quoted 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 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.

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.

#101 lore raw
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

maintainer
#102 lore raw
Quoted 3 lines · Ingo Molnar

[1997/04] v2.1.36:

the spin_unlock_wait() primitive gets introduced as part of release()

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 use spin_unlock_wait()

I've tried 'fixing' that wait_task_inactive() thing a number of times, but always failed :/ That is very nasty code indeed.

#103 lore raw
In reply to Ingo Molnar

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

#104 lore raw
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

#105 lore raw
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

#106 lore raw
In reply to Ingo Molnar

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 - if spin_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

#107 lore raw
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 - if spin_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 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().

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

#108 lore raw
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 - if spin_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 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().

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

#109 lore raw
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 - if spin_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 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().

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

#110 lore raw
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 - if spin_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 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().

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

#111 lore raw
In reply to Ingo Molnar

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 - if spin_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 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().

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

#112 lore raw

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 - if spin_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 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().

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

#113 lore raw
In reply to Peter Zijlstra
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

#114 lore raw
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

#115 lore raw

v3 cover Remove spin_unlock_wait()

In reply to Paul E. McKenney

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. 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(-)
#116 lore raw

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.

Fixes b16c29191dc8 ("netfilter: nf_conntrack: use safer way to lock all buckets")
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
index e847dbaa0c6b..e0560c41e371 100644
@@ -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
#117 lore raw

PATCH v3 2/9 task_work: Replace spin_unlock_wait() with lock/unlock pair

In reply to Paul E. McKenney

From: 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
index d513051fcca2..836a72a66fba 100644
@@ -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
#118 lore raw

PATCH v3 3/9 sched: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>

[ 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
index e91138fcde86..48a8760fedf4 100644
@@ -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(&current->pi_lock);
+	smp_mb__before_spinlock();
+	raw_spin_lock_irq(&current->pi_lock);
+	raw_spin_unlock_irq(&current->pi_lock);
 
 	/* Causes final put_task_struct in finish_task_switch(): */
 	__set_current_state(TASK_DEAD);
Signature · 1 line
2.5.2
#119 lore raw

PATCH v3 4/9 completion: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/sched/completion.c | 9 ++-------
 1 file changed, 2 insertions(+), 7 deletions(-)
kernel/sched/completion.c +2 -7
index 53f9558fa925..3e66712e1964 100644
@@ -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
#120 lore raw

PATCH v3 5/9 exit: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Ingo Molnar <[email protected]>
Cc Peter Zijlstra <[email protected]>
Cc Will Deacon <[email protected]>
Cc Alan Stern <[email protected]>
Cc Andrea Parri <[email protected]>
Cc Linus Torvalds <[email protected]>
---
 kernel/exit.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
kernel/exit.c +2 -1
index 516acdb0e0ec..6d19c9090d43 100644
@@ -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
#121 lore raw

PATCH v3 6/9 ipc: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

Cc Andrew Morton <[email protected]>
Cc Davidlohr Bueso <[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]>
---
 ipc/sem.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)
ipc/sem.c +2 -1
index 947dc2348271..e88d0749a929 100644
@@ -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
#122 lore raw

PATCH v3 7/9 drivers/ata: Replace spin_unlock_wait() with lock/unlock pair

In reply to 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 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.

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]>
---
 drivers/ata/libata-eh.c | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)
drivers/ata/libata-eh.c +3 -5
index ef68232b5222..779f6f18c1f4 100644
@@ -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
#123 lore raw

PATCH v3 8/9 locking: Remove spin_unlock_wait() generic definitions

In reply to 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.

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(-)
include/asm-generic/qspinlock.h +0 -14
index 9f0681bf1e87..66260777d644 100644
@@ -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
index d9510e8522d4..ef018a6e4985 100644
@@ -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
index 0d9848de677d..612fb530af41 100644
@@ -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
#124 lore raw

PATCH v3 9/9 arch: Remove spin_unlock_wait() arch-specific definitions

In reply to 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() 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
index a40b9fc0c6c3..718ac0b64adf 100644
@@ -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
index 233d5ffe6ec7..a325e6a36523 100644
@@ -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
index 4bec45442072..c030143c18c6 100644
@@ -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
index c58f4a83ed6f..f6431439d15d 100644
@@ -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
index a1c55788c5d6..53a8d5885887 100644
@@ -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
index ca9e76149a4a..df2c121164b8 100644
@@ -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 (;;) {
-		asm volatile ("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
index 323c7fc953cd..a56825592b90 100644
@@ -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
index c0c7a22be1ae..ddf7fe5708a6 100644
@@ -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
index a8df44d60607..81b4945031ee 100644
@@ -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
index 9c7b8f7942d8..fe413b41df6c 100644
@@ -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
index e32936cd7f10..55bfe4affca3 100644
@@ -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
index 8c1b913de6d7..d256e448ea49 100644
@@ -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
index f7838ecd83c6..217ee5210c32 100644
@@ -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
index c46e8cc7b515..5ed7dbbd94ff 100644
@@ -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
index cec78143fa83..f77263aae760 100644
@@ -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
index 8011e79f59c9..67345b2dc408 100644
@@ -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
index 07c9f2e9bf57..923d57f9b79d 100644
@@ -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
index b14b1ba5bf9c..cba8ba9b8da6 100644
@@ -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
index b9718fb4e74a..9a2c2d605752 100644
@@ -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
index 076c6cc43113..db9333f2447c 100644
@@ -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
index a4b5b2cbce93..de414c22892f 100644
@@ -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
index a36221cf6363..3bb49681ee24 100644
@@ -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