Skip to content

Commit 6512276

Browse files
wildea01Ingo Molnar
authored andcommitted
locking/qspinlock: Bound spinning on pending->locked transition in slowpath
If a locker taking the qspinlock slowpath reads a lock value indicating that only the pending bit is set, then it will spin whilst the concurrent pending->locked transition takes effect. Unfortunately, there is no guarantee that such a transition will ever be observed since concurrent lockers could continuously set pending and hand over the lock amongst themselves, leading to starvation. Whilst this would probably resolve in practice, it means that it is not possible to prove liveness properties about the lock and means that lock acquisition time is unbounded. Rather than removing the pending->locked spinning from the slowpath altogether (which has been shown to heavily penalise a 2-threaded locking stress test on x86), this patch replaces the explicit spinning with a call to atomic_cond_read_relaxed and allows the architecture to provide a bound on the number of spins. For architectures that can respond to changes in cacheline state in their smp_cond_load implementation, it should be sufficient to use the default bound of 1. Suggested-by: Waiman Long <longman@redhat.com> Signed-off-by: Will Deacon <will.deacon@arm.com> Acked-by: Peter Zijlstra (Intel) <peterz@infradead.org> Acked-by: Waiman Long <longman@redhat.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: boqun.feng@gmail.com Cc: linux-arm-kernel@lists.infradead.org Cc: paulmck@linux.vnet.ibm.com Link: http://lkml.kernel.org/r/1524738868-31318-4-git-send-email-will.deacon@arm.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 625e88b commit 6512276

File tree

1 file changed

+17
-3
lines changed

1 file changed

+17
-3
lines changed

kernel/locking/qspinlock.c

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,18 @@
7676
#define MAX_NODES 4
7777
#endif
7878

79+
/*
80+
* The pending bit spinning loop count.
81+
* This heuristic is used to limit the number of lockword accesses
82+
* made by atomic_cond_read_relaxed when waiting for the lock to
83+
* transition out of the "== _Q_PENDING_VAL" state. We don't spin
84+
* indefinitely because there's no guarantee that we'll make forward
85+
* progress.
86+
*/
87+
#ifndef _Q_PENDING_LOOPS
88+
#define _Q_PENDING_LOOPS 1
89+
#endif
90+
7991
/*
8092
* Per-CPU queue node structures; we can never have more than 4 nested
8193
* contexts: task, softirq, hardirq, nmi.
@@ -266,13 +278,15 @@ void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val)
266278
return;
267279

268280
/*
269-
* wait for in-progress pending->locked hand-overs
281+
* Wait for in-progress pending->locked hand-overs with a bounded
282+
* number of spins so that we guarantee forward progress.
270283
*
271284
* 0,1,0 -> 0,0,1
272285
*/
273286
if (val == _Q_PENDING_VAL) {
274-
while ((val = atomic_read(&lock->val)) == _Q_PENDING_VAL)
275-
cpu_relax();
287+
int cnt = _Q_PENDING_LOOPS;
288+
val = atomic_cond_read_relaxed(&lock->val,
289+
(VAL != _Q_PENDING_VAL) || !cnt--);
276290
}
277291

278292
/*

0 commit comments

Comments
 (0)