forked from torvalds/linux
-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
locking/qspinlock: Introduce a simple generic 4-byte queued spinlock
This patch introduces a new generic queued spinlock implementation that can serve as an alternative to the default ticket spinlock. Compared with the ticket spinlock, this queued spinlock should be almost as fair as the ticket spinlock. It has about the same speed in single-thread and it can be much faster in high contention situations especially when the spinlock is embedded within the data structure to be protected. Only in light to moderate contention where the average queue depth is around 1-3 will this queued spinlock be potentially a bit slower due to the higher slowpath overhead. This queued spinlock is especially suit to NUMA machines with a large number of cores as the chance of spinlock contention is much higher in those machines. The cost of contention is also higher because of slower inter-node memory traffic. Due to the fact that spinlocks are acquired with preemption disabled, the process will not be migrated to another CPU while it is trying to get a spinlock. Ignoring interrupt handling, a CPU can only be contending in one spinlock at any one time. Counting soft IRQ, hard IRQ and NMI, a CPU can only have a maximum of 4 concurrent lock waiting activities. By allocating a set of per-cpu queue nodes and used them to form a waiting queue, we can encode the queue node address into a much smaller 24-bit size (including CPU number and queue node index) leaving one byte for the lock. Please note that the queue node is only needed when waiting for the lock. Once the lock is acquired, the queue node can be released to be used later. Signed-off-by: Waiman Long <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Cc: Andrew Morton <[email protected]> Cc: Boris Ostrovsky <[email protected]> Cc: Borislav Petkov <[email protected]> Cc: Daniel J Blueman <[email protected]> Cc: David Vrabel <[email protected]> Cc: Douglas Hatch <[email protected]> Cc: H. Peter Anvin <[email protected]> Cc: Konrad Rzeszutek Wilk <[email protected]> Cc: Linus Torvalds <[email protected]> Cc: Oleg Nesterov <[email protected]> Cc: Paolo Bonzini <[email protected]> Cc: Paul E. McKenney <[email protected]> Cc: Peter Zijlstra <[email protected]> Cc: Raghavendra K T <[email protected]> Cc: Rik van Riel <[email protected]> Cc: Scott J Norton <[email protected]> Cc: Thomas Gleixner <[email protected]> Cc: [email protected] Cc: [email protected] Link: http://lkml.kernel.org/r/[email protected] Signed-off-by: Ingo Molnar <[email protected]>
- Loading branch information
Showing
6 changed files
with
408 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
/* | ||
* Queued spinlock | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. | ||
* | ||
* Authors: Waiman Long <[email protected]> | ||
*/ | ||
#ifndef __ASM_GENERIC_QSPINLOCK_H | ||
#define __ASM_GENERIC_QSPINLOCK_H | ||
|
||
#include <asm-generic/qspinlock_types.h> | ||
|
||
/** | ||
* queued_spin_is_locked - is the spinlock locked? | ||
* @lock: Pointer to queued spinlock structure | ||
* Return: 1 if it is locked, 0 otherwise | ||
*/ | ||
static __always_inline int queued_spin_is_locked(struct qspinlock *lock) | ||
{ | ||
return atomic_read(&lock->val); | ||
} | ||
|
||
/** | ||
* queued_spin_value_unlocked - is the spinlock structure unlocked? | ||
* @lock: queued spinlock structure | ||
* Return: 1 if it is unlocked, 0 otherwise | ||
* | ||
* N.B. Whenever there are tasks waiting for the lock, it is considered | ||
* locked wrt the lockref code to avoid lock stealing by the lockref | ||
* code and change things underneath the lock. This also allows some | ||
* optimizations to be applied without conflict with lockref. | ||
*/ | ||
static __always_inline int queued_spin_value_unlocked(struct qspinlock lock) | ||
{ | ||
return !atomic_read(&lock.val); | ||
} | ||
|
||
/** | ||
* queued_spin_is_contended - check if the lock is contended | ||
* @lock : Pointer to queued spinlock structure | ||
* Return: 1 if lock contended, 0 otherwise | ||
*/ | ||
static __always_inline int queued_spin_is_contended(struct qspinlock *lock) | ||
{ | ||
return atomic_read(&lock->val) & ~_Q_LOCKED_MASK; | ||
} | ||
/** | ||
* queued_spin_trylock - try to acquire the queued spinlock | ||
* @lock : Pointer to queued spinlock structure | ||
* Return: 1 if lock acquired, 0 if failed | ||
*/ | ||
static __always_inline int queued_spin_trylock(struct qspinlock *lock) | ||
{ | ||
if (!atomic_read(&lock->val) && | ||
(atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL) == 0)) | ||
return 1; | ||
return 0; | ||
} | ||
|
||
extern void queued_spin_lock_slowpath(struct qspinlock *lock, u32 val); | ||
|
||
/** | ||
* queued_spin_lock - acquire a queued spinlock | ||
* @lock: Pointer to queued spinlock structure | ||
*/ | ||
static __always_inline void queued_spin_lock(struct qspinlock *lock) | ||
{ | ||
u32 val; | ||
|
||
val = atomic_cmpxchg(&lock->val, 0, _Q_LOCKED_VAL); | ||
if (likely(val == 0)) | ||
return; | ||
queued_spin_lock_slowpath(lock, val); | ||
} | ||
|
||
#ifndef queued_spin_unlock | ||
/** | ||
* queued_spin_unlock - release a queued spinlock | ||
* @lock : Pointer to queued spinlock structure | ||
*/ | ||
static __always_inline void queued_spin_unlock(struct qspinlock *lock) | ||
{ | ||
/* | ||
* smp_mb__before_atomic() in order to guarantee release semantics | ||
*/ | ||
smp_mb__before_atomic_dec(); | ||
atomic_sub(_Q_LOCKED_VAL, &lock->val); | ||
} | ||
#endif | ||
|
||
/** | ||
* queued_spin_unlock_wait - wait until current lock holder releases the lock | ||
* @lock : Pointer to queued spinlock structure | ||
* | ||
* There is a very slight possibility of live-lock if the lockers keep coming | ||
* and the waiter is just unfortunate enough to not see any unlock state. | ||
*/ | ||
static inline void queued_spin_unlock_wait(struct qspinlock *lock) | ||
{ | ||
while (atomic_read(&lock->val) & _Q_LOCKED_MASK) | ||
cpu_relax(); | ||
} | ||
|
||
/* | ||
* Initializier | ||
*/ | ||
#define __ARCH_SPIN_LOCK_UNLOCKED { ATOMIC_INIT(0) } | ||
|
||
/* | ||
* Remapping spinlock architecture specific functions to the corresponding | ||
* queued spinlock functions. | ||
*/ | ||
#define arch_spin_is_locked(l) queued_spin_is_locked(l) | ||
#define arch_spin_is_contended(l) queued_spin_is_contended(l) | ||
#define arch_spin_value_unlocked(l) queued_spin_value_unlocked(l) | ||
#define arch_spin_lock(l) queued_spin_lock(l) | ||
#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 */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
/* | ||
* Queued spinlock | ||
* | ||
* This program is free software; you can redistribute it and/or modify | ||
* it under the terms of the GNU General Public License as published by | ||
* the Free Software Foundation; either version 2 of the License, or | ||
* (at your option) any later version. | ||
* | ||
* This program is distributed in the hope that it will be useful, | ||
* but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
* GNU General Public License for more details. | ||
* | ||
* (C) Copyright 2013-2015 Hewlett-Packard Development Company, L.P. | ||
* | ||
* Authors: Waiman Long <[email protected]> | ||
*/ | ||
#ifndef __ASM_GENERIC_QSPINLOCK_TYPES_H | ||
#define __ASM_GENERIC_QSPINLOCK_TYPES_H | ||
|
||
/* | ||
* Including atomic.h with PARAVIRT on will cause compilation errors because | ||
* of recursive header file incluson via paravirt_types.h. So don't include | ||
* it if PARAVIRT is on. | ||
*/ | ||
#ifndef CONFIG_PARAVIRT | ||
#include <linux/types.h> | ||
#include <linux/atomic.h> | ||
#endif | ||
|
||
typedef struct qspinlock { | ||
atomic_t val; | ||
} arch_spinlock_t; | ||
|
||
/* | ||
* Bitfields in the atomic value: | ||
* | ||
* 0- 7: locked byte | ||
* 8- 9: tail index | ||
* 10-31: tail cpu (+1) | ||
*/ | ||
#define _Q_SET_MASK(type) (((1U << _Q_ ## type ## _BITS) - 1)\ | ||
<< _Q_ ## type ## _OFFSET) | ||
#define _Q_LOCKED_OFFSET 0 | ||
#define _Q_LOCKED_BITS 8 | ||
#define _Q_LOCKED_MASK _Q_SET_MASK(LOCKED) | ||
|
||
#define _Q_TAIL_IDX_OFFSET (_Q_LOCKED_OFFSET + _Q_LOCKED_BITS) | ||
#define _Q_TAIL_IDX_BITS 2 | ||
#define _Q_TAIL_IDX_MASK _Q_SET_MASK(TAIL_IDX) | ||
|
||
#define _Q_TAIL_CPU_OFFSET (_Q_TAIL_IDX_OFFSET + _Q_TAIL_IDX_BITS) | ||
#define _Q_TAIL_CPU_BITS (32 - _Q_TAIL_CPU_OFFSET) | ||
#define _Q_TAIL_CPU_MASK _Q_SET_MASK(TAIL_CPU) | ||
|
||
#define _Q_LOCKED_VAL (1U << _Q_LOCKED_OFFSET) | ||
|
||
#endif /* __ASM_GENERIC_QSPINLOCK_TYPES_H */ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.