Locking changes for v4.2
Subsystems (24)
Linus,
Please pull the latest locking-core-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking-core-for-linus
# HEAD: 68722101ec3a0e179408a13708dd020e04f54aab locking/lockdep: Remove hard coded array size dependency
The main changes are:
- 'qspinlock' support, enabled on x86: queued spinlocks - these are now the spinlock variant used by x86 as they outperform ticket spinlocks in every category. (Waiman Long)
- 'pvqspinlock' support on x86: paravirtualized variant of queued spinlocks. (Waiman Long, Peter Zijlstra)
- 'qrwlock' support, enabled on x86: queued rwlocks. Similar to queued spinlocks, they are now the variant used by x86:
CONFIG_ARCH_USE_QUEUED_SPINLOCKS=y
CONFIG_QUEUED_SPINLOCKS=y
CONFIG_ARCH_USE_QUEUED_RWLOCKS=y
CONFIG_QUEUED_RWLOCKS=y
- various lockdep fixlets
- various locking primitives cleanups, further WRITE_ONCE() propagation
Thanks,
Ingo
------------------>
Borislav Petkov (1):
lockdep: Do not break user-visible string
David Vrabel (1):
locking/pvqspinlock, x86: Enable PV qspinlock for Xen
George Beshers (1):
locking/lockdep: Remove hard coded array size dependency
Ingo Molnar (1):
locking/pvqspinlock: Rename QUEUED_SPINLOCK to QUEUED_SPINLOCKS
Peter Zijlstra (2):
locking/arch: Add WRITE_ONCE() to set_mb() locking/arch: Rename set_mb() to smp_store_mb()
Peter Zijlstra (Intel) (4):
locking/qspinlock: Add pending bit
locking/qspinlock: Optimize for smaller NR_CPUS locking/qspinlock: Revert to test-and-set on hypervisors locking/pvqspinlock, x86: Implement the paravirt qspinlock call patching
Preeti U Murthy (1):
kernel: Replace reference to ASSIGN_ONCE() with WRITE_ONCE() in comment
Sebastian Andrzej Siewior (1):
locking/rtmutex: Drop usage of __HAVE_ARCH_CMPXCHG
Thomas Gleixner (2):
arch: Remove __ARCH_HAVE_CMPXCHG
rtmutex: Warn if trylock is called from hard/softirq context
Waiman Long (10):
locking/rwsem: Reduce spinlock contention in wakeup after up_read()/up_write() locking/qspinlock: Introduce a simple generic 4-byte queued spinlock locking/qspinlock, x86: Enable x86-64 to use queued spinlocks locking/qspinlock: Extract out code snippets for the next patch locking/qspinlock: Use a simple write to grab the lock locking/pvqspinlock: Implement simple paravirt support for the qspinlock locking/pvqspinlock, x86: Enable PV qspinlock for KVM locking/pvqspinlock: Replace xchg() by the more descriptive set_mb() locking/qrwlock: Rename QUEUE_RWLOCK to QUEUED_RWLOCKS locking/qrwlock: Don't contend with readers when setting _QW_WAITING
Documentation/memory-barriers.txt | 6 +- arch/alpha/include/asm/cmpxchg.h | 2 - arch/arm/include/asm/barrier.h | 2 +- arch/arm64/include/asm/barrier.h | 2 +- arch/avr32/include/asm/cmpxchg.h | 2 - arch/hexagon/include/asm/cmpxchg.h | 1 - arch/ia64/include/asm/barrier.h | 7 +- arch/ia64/include/uapi/asm/cmpxchg.h | 2 - arch/m32r/include/asm/cmpxchg.h | 2 - arch/m68k/include/asm/cmpxchg.h | 1 - arch/metag/include/asm/barrier.h | 2 +- arch/metag/include/asm/cmpxchg.h | 2 - arch/mips/include/asm/barrier.h | 4 +- arch/mips/include/asm/cmpxchg.h | 2 - arch/parisc/include/asm/cmpxchg.h | 2 - arch/powerpc/include/asm/barrier.h | 2 +- arch/powerpc/include/asm/cmpxchg.h | 1 - arch/s390/include/asm/barrier.h | 2 +- arch/s390/include/asm/cmpxchg.h | 2 - arch/score/include/asm/cmpxchg.h | 2 - arch/sh/include/asm/barrier.h | 2 +- arch/sh/include/asm/cmpxchg.h | 2 - arch/sparc/include/asm/barrier_64.h | 4 +- arch/sparc/include/asm/cmpxchg_32.h | 1 - arch/sparc/include/asm/cmpxchg_64.h | 2 - arch/tile/include/asm/atomic_64.h | 3 - arch/x86/Kconfig | 5 +- arch/x86/include/asm/barrier.h | 4 +- arch/x86/include/asm/cmpxchg.h | 2 - arch/x86/include/asm/paravirt.h | 29 +- arch/x86/include/asm/paravirt_types.h | 10 + arch/x86/include/asm/qspinlock.h | 57 ++++ arch/x86/include/asm/qspinlock_paravirt.h | 6 + arch/x86/include/asm/spinlock.h | 5 + arch/x86/include/asm/spinlock_types.h | 4 + arch/x86/kernel/kvm.c | 43 +++ arch/x86/kernel/paravirt-spinlocks.c | 24 +- arch/x86/kernel/paravirt_patch_32.c | 22 +- arch/x86/kernel/paravirt_patch_64.c | 22 +- arch/x86/um/asm/barrier.h | 3 +- arch/x86/xen/spinlock.c | 64 +++- fs/select.c | 6 +- include/asm-generic/barrier.h | 4 +- include/asm-generic/cmpxchg.h | 3 - include/asm-generic/qspinlock.h | 139 +++++++++ include/asm-generic/qspinlock_types.h | 79 +++++ include/linux/compiler.h | 4 +- include/linux/lockdep.h | 4 +- include/linux/osq_lock.h | 5 + include/linux/sched.h | 8 +- kernel/Kconfig.locks | 13 +- kernel/futex.c | 2 +- kernel/locking/Makefile | 3 +- kernel/locking/lockdep.c | 3 +- kernel/locking/mcs_spinlock.h | 1 + kernel/locking/qrwlock.c | 30 +- kernel/locking/qspinlock.c | 473 ++++++++++++++++++++++++++++++ kernel/locking/qspinlock_paravirt.h | 325 ++++++++++++++++++++ kernel/locking/rtmutex.c | 13 +- kernel/locking/rwsem-xadd.c | 44 +++ kernel/sched/wait.c | 4 +- 61 files changed, 1423 insertions(+), 102 deletions(-) create mode 100644 arch/x86/include/asm/qspinlock.h create mode 100644 arch/x86/include/asm/qspinlock_paravirt.h create mode 100644 include/asm-generic/qspinlock.h create mode 100644 include/asm-generic/qspinlock_types.h create mode 100644 kernel/locking/qspinlock.c create mode 100644 kernel/locking/qspinlock_paravirt.h
Documentation/memory-barriers.txt +3 -3
@@ -1662,7 +1662,7 @@ CPU from reordering them. There are some more advanced barrier functions: - (*) set_mb(var, value) + (*) smp_store_mb(var, value) This assigns the value to the variable and then inserts a full memory barrier after it, depending on the function. It isn't guaranteed to
@@ -1975,7 +1975,7 @@ A general memory barrier is interpolated automatically by set_current_state() CPU 1 =============================== set_current_state(); - set_mb(); + smp_store_mb(); STORE current->state <general barrier> LOAD event_indicated
@@ -2016,7 +2016,7 @@ something up. The barrier occurs before the task state is cleared, and so sits CPU 1 CPU 2 =============================== =============================== set_current_state(); STORE event_indicated - set_mb(); wake_up(); + smp_store_mb(); wake_up(); STORE current->state <write barrier> <general barrier> STORE current->state LOAD event_indicated
arch/alpha/include/asm/cmpxchg.h +0 -2
@@ -66,6 +66,4 @@#undef __ASM__MB#undef ____cmpxchg -#define __HAVE_ARCH_CMPXCHG 1 -#endif /* _ALPHA_CMPXCHG_H */
arch/arm/include/asm/barrier.h +1 -1
@@ -81,7 +81,7 @@ do { \#define read_barrier_depends () do { } while(0)#define smp_read_barrier_depends () do { } while(0) -#define set_mb (var, value) do { var = value; smp_mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)#define smp_mb__before_atomic () smp_mb()#define smp_mb__after_atomic () smp_mb()
arch/arm64/include/asm/barrier.h +1 -1
@@ -114,7 +114,7 @@ do { \#define read_barrier_depends () do { } while(0)#define smp_read_barrier_depends () do { } while(0) -#define set_mb (var, value) do { var = value; smp_mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)#define nop () asm volatile("nop");#define smp_mb__before_atomic () smp_mb()
arch/avr32/include/asm/cmpxchg.h +0 -2
@@ -70,8 +70,6 @@ extern unsigned long __cmpxchg_u64_unsupported_on_32bit_kernels(if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer (void ); -#define __HAVE_ARCH_CMPXCHG 1 -static inline unsigned long __cmpxchg (volatile void *ptr,unsigned long old,unsigned long new,int size) {
arch/hexagon/include/asm/cmpxchg.h +0 -1
@@ -64,7 +64,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void *ptr,* looks just like atomic_cmpxchg on our arch currently with a bunch of * variable casting. */ -#define __HAVE_ARCH_CMPXCHG 1#define cmpxchg (ptr, old, new) \ ({ \
arch/ia64/include/asm/barrier.h +1 -6
@@ -77,12 +77,7 @@ do { \ ___p1; \ }) -/* -* XXX check on this ---I suspect what Linus really wants here is -* acquire vs release semantics but we can't discuss this stuff with -* Linus just yet. Grrr... -*/ -#define set_mb (var, value) do { (var) = (value); mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); mb(); } while (0)/* * The group barrier in front of the rsm & ssm are necessary to ensure
arch/ia64/include/uapi/asm/cmpxchg.h +0 -2
@@ -61,8 +61,6 @@ extern void ia64_xchg_called_with_bad_pointer(void);* indicated by comparing RETURN with OLD. */ -#define __HAVE_ARCH_CMPXCHG 1 -/* * This function doesn't exist, so you'll get a linker error * if something tries to do an invalid cmpxchg().
arch/m32r/include/asm/cmpxchg.h +0 -2
@@ -107,8 +107,6 @@ __xchg_local(unsigned long x, volatile void *ptr, int size) ((__typeof__ (*(ptr )))__xchg_local ((unsigned long )(x), (ptr), \ sizeof(*(ptr)))) -#define __HAVE_ARCH_CMPXCHG 1 -static inline unsigned long __cmpxchg_u32 (volatile unsigned int *p,unsigned int old,unsigned int new) {
arch/m68k/include/asm/cmpxchg.h +0 -1
@@ -90,7 +90,6 @@ extern unsigned long __invalid_cmpxchg_size(volatile void *,* indicated by comparing RETURN with OLD. */ #ifdef CONFIG_RMW_INSNS -#define __HAVE_ARCH_CMPXCHG 1static inline unsigned long __cmpxchg (volatile void *p,unsigned long old,unsigned long new,int size)
arch/metag/include/asm/barrier.h +1 -1
@@ -84,7 +84,7 @@ static inline void fence(void)#define read_barrier_depends () do { } while (0)#define smp_read_barrier_depends () do { } while (0) -#define set_mb (var, value) do { var = value; smp_mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); smp_mb(); } while (0)#define smp_store_release (p, v) \ do { \
arch/metag/include/asm/cmpxchg.h +0 -2
@@ -51,8 +51,6 @@ static inline unsigned long __cmpxchg(volatile void *ptr, unsigned long old,return old; } -#define __HAVE_ARCH_CMPXCHG 1 -#define cmpxchg (ptr, o, n) \ ({ \ __typeof__(*(ptr)) _o_ = (o); \
arch/mips/include/asm/barrier.h +2 -2
@@ -112,8 +112,8 @@#define __WEAK_LLSC_MB " \n"#endif -#define set_mb (var, value) \ - do { var = value; smp_mb(); } while (0) +#define smp_store_mb (var, value) \ + do { WRITE_ONCE(var, value); smp_mb(); } while (0)#define smp_llsc_mb () __asm__ __volatile__(__WEAK_LLSC_MB : : :"memory")
arch/mips/include/asm/cmpxchg.h +0 -2
@@ -138,8 +138,6 @@ static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int siz __xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))); \ }) -#define __HAVE_ARCH_CMPXCHG 1 -#define __cmpxchg_asm (ld, st, m, old, new) \ ({ \ __typeof(*(m)) __ret; \
arch/parisc/include/asm/cmpxchg.h +0 -2
@@ -46,8 +46,6 @@ __xchg(unsigned long x, __volatile__ void *ptr, int size)#define xchg (ptr, x) \ ((__typeof__(*(ptr)))__xchg((unsigned long)(x), (ptr), sizeof(*(ptr)))) -#define __HAVE_ARCH_CMPXCHG 1 -/* bug catcher for when unsupported size is used - won't link */ extern void __cmpxchg_called_with_bad_pointer (void );
arch/powerpc/include/asm/barrier.h +1 -1
@@ -34,7 +34,7 @@#define rmb () __asm__ __volatile__ ("sync" : : : "memory")#define wmb () __asm__ __volatile__ ("sync" : : : "memory") -#define set_mb (var, value) do { var = value; mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); mb(); } while (0)#ifdef __SUBARCH_HAS_LWSYNC# define SMPWMB LWSYNC
arch/powerpc/include/asm/cmpxchg.h +0 -1
@@ -144,7 +144,6 @@ __xchg_local(volatile void *ptr, unsigned long x, unsigned int size)* Compare and exchange - if *p == old, set it to new, * and return the old value of *p. */ -#define __HAVE_ARCH_CMPXCHG 1static __always_inline unsigned long __cmpxchg_u32 (volatile unsigned int *p,unsigned long old,unsigned long new)
arch/s390/include/asm/barrier.h +1 -1
@@ -36,7 +36,7 @@#define smp_mb__before_atomic () smp_mb()#define smp_mb__after_atomic () smp_mb() -#define set_mb (var, value) do { var = value; mb(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); mb(); } while (0)#define smp_store_release (p, v) \ do { \
arch/s390/include/asm/cmpxchg.h +0 -2
@@ -32,8 +32,6 @@ __old; \ }) -#define __HAVE_ARCH_CMPXCHG -#define __cmpxchg_double_op (p1, p2, o1, o2, n1, n2, insn) \ ({ \ register __typeof__(*(p1)) __old1 asm("2") = (o1); \
arch/score/include/asm/cmpxchg.h +0 -2
@@ -42,8 +42,6 @@ static inline unsigned long __cmpxchg(volatile unsigned long *m, (unsigned long)(o), \ (unsigned long)(n))) -#define __HAVE_ARCH_CMPXCHG 1 -#include <asm-generic/cmpxchg-local.h> #endif /* _ASM_SCORE_CMPXCHG_H */
arch/sh/include/asm/barrier.h +1 -1
@@ -32,7 +32,7 @@#define ctrl_barrier () __asm__ __volatile__ ("nop;nop;nop;nop;nop;nop;nop;nop")#endif -#define set_mb (var, value) do { (void)xchg(&var, value); } while (0) +#define smp_store_mb (var, value) do { (void)xchg(&var, value); } while (0)#include <asm-generic/barrier.h>
arch/sh/include/asm/cmpxchg.h +0 -2
@@ -46,8 +46,6 @@ extern void __xchg_called_with_bad_pointer(void);* if something tries to do an invalid cmpxchg(). */ extern void __cmpxchg_called_with_bad_pointer (void ); -#define __HAVE_ARCH_CMPXCHG 1 -static inline unsigned long __cmpxchg (volatile void * ptr,unsigned long old,unsigned long new,int size) {
arch/sparc/include/asm/barrier_64.h +2 -2
@@ -40,8 +40,8 @@ do { __asm__ __volatile__("ba,pt %%xcc, 1f\n\t" \#define dma_rmb () rmb()#define dma_wmb () wmb() -#define set_mb (__var, __value) \ - do { __var = __value; membar_safe("#StoreLoad"); } while(0) +#define smp_store_mb (__var, __value) \ + do { WRITE_ONCE(__var, __value); membar_safe("#StoreLoad"); } while(0)#ifdef CONFIG_SMP #define smp_mb () mb()
arch/sparc/include/asm/cmpxchg_32.h +0 -1
@@ -34,7 +34,6 @@ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr, int* * Cribbed from <asm-parisc/atomic.h> */ -#define __HAVE_ARCH_CMPXCHG 1/* bug catcher for when unsupported size is used - won't link */ void __cmpxchg_called_with_bad_pointer (void );
arch/sparc/include/asm/cmpxchg_64.h +0 -2
@@ -65,8 +65,6 @@ static inline unsigned long __xchg(unsigned long x, __volatile__ void * ptr,#include <asm-generic/cmpxchg-local.h> -#define __HAVE_ARCH_CMPXCHG 1 -static inline unsigned long __cmpxchg_u32 (volatile int *m,int old,int new) {
arch/tile/include/asm/atomic_64.h +0 -3
@@ -105,9 +105,6 @@ static inline long atomic64_add_unless(atomic64_t *v, long a, long u)#define atomic64_inc_not_zero (v) atomic64_add_unless((v), 1, 0) -/* Define this to indicate that cmpxchg is an efficient operation. */ -#define __HAVE_ARCH_CMPXCHG -#endif /* !__ASSEMBLY__ */ #endif /* _ASM_TILE_ATOMIC_64_H */
arch/x86/Kconfig +3 -2
@@ -127,7 +127,8 @@ config X86 select MODULES_USE_ELF_RELA if X86_64 select CLONE_BACKWARDS if X86_32 select ARCH_USE_BUILTIN_BSWAP - select ARCH_USE_QUEUE_RWLOCK + select ARCH_USE_QUEUED_SPINLOCKS + select ARCH_USE_QUEUED_RWLOCKS select OLD_SIGSUSPEND3 if X86_32 || IA32_EMULATION select OLD_SIGACTION if X86_32 select COMPAT_OLD_SIGACTION if IA32_EMULATION
@@ -666,7 +667,7 @@ config PARAVIRT_DEBUG config PARAVIRT_SPINLOCKS bool "Paravirtualization layer for spinlocks" depends on PARAVIRT && SMP - select UNINLINE_SPIN_UNLOCK + select UNINLINE_SPIN_UNLOCK if !QUEUED_SPINLOCKS ---help--- Paravirtualized spinlocks allow a pvops backend to replace the spinlock implementation with something virtualization-friendly
arch/x86/include/asm/barrier.h +2 -2
@@ -35,12 +35,12 @@#define smp_mb () mb()#define smp_rmb () dma_rmb()#define smp_wmb () barrier() -#define set_mb (var, value) do { (void)xchg(&var, value); } while (0) +#define smp_store_mb (var, value) do { (void)xchg(&var, value); } while (0)#else /* !SMP */ #define smp_mb () barrier()#define smp_rmb () barrier()#define smp_wmb () barrier() -#define set_mb (var, value) do { var = value; barrier(); } while (0) +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)#endif /* SMP */ #define read_barrier_depends () do { } while (0)
arch/x86/include/asm/cmpxchg.h +0 -2
@@ -4,8 +4,6 @@#include <linux/compiler.h> #include <asm/alternative.h> /* Provides LOCK_PREFIX */ -#define __HAVE_ARCH_CMPXCHG 1 -/* * Non-existant functions to indicate usage errors at link time * (or compile-time if the compiler implements __compiletime_error().
arch/x86/include/asm/paravirt.h +28 -1
@@ -712,6 +712,31 @@ static inline void __set_fixmap(unsigned /* enum fixed_addresses */ idx, #if defined(CONFIG_SMP) && defined(CONFIG_PARAVIRT_SPINLOCKS) +#ifdef CONFIG_QUEUED_SPINLOCKS + +static __always_inline void pv_queued_spin_lock_slowpath(struct qspinlock *lock, + u32 val) +{ + PVOP_VCALL2(pv_lock_ops.queued_spin_lock_slowpath, lock, val); +} + +static __always_inline void pv_queued_spin_unlock(struct qspinlock *lock) +{ + PVOP_VCALLEE1(pv_lock_ops.queued_spin_unlock, lock); +} + +static __always_inline void pv_wait(u8 *ptr, u8 val) +{ + PVOP_VCALL2(pv_lock_ops.wait, ptr, val); +} + +static __always_inline void pv_kick(int cpu) +{ + PVOP_VCALL1(pv_lock_ops.kick, cpu); +} + +#else /* !CONFIG_QUEUED_SPINLOCKS */ + static __always_inline void __ticket_lock_spinning(struct arch_spinlock *lock, __ticket_t ticket) {
@@ -724,7 +749,9 @@ static __always_inline void __ticket_unlock_kick(struct arch_spinlock *lock,PVOP_VCALL2 (pv_lock_ops.unlock_kick, lock, ticket); } -#endif +#endif /* CONFIG_QUEUED_SPINLOCKS */ + +#endif /* SMP && PARAVIRT_SPINLOCKS */ #ifdef CONFIG_X86_32 #define PV_SAVE_REGS "pushl %ecx; pushl %edx;"
arch/x86/include/asm/paravirt_types.h +10 -0
@@ -333,9 +333,19 @@ struct arch_spinlock;typedef u16 __ticket_t ;#endif +struct qspinlock ; +struct pv_lock_ops { +#ifdef CONFIG_QUEUED_SPINLOCKS +void (*queued_spin_lock_slowpath)(struct qspinlock *lock,u32 val); +struct paravirt_callee_save queued_spin_unlock; + +void (*wait)(u8 *ptr,u8 val); +void (*kick)(int cpu); +#else /* !CONFIG_QUEUED_SPINLOCKS */ struct paravirt_callee_save lock_spinning;void (*unlock_kick)(struct arch_spinlock *lock,__ticket_t ticket); +#endif /* !CONFIG_QUEUED_SPINLOCKS */ };/* This contains all the paravirt structures: we get a convenient
arch/x86/include/asm/qspinlock.h new file +57 -0
57 lines not on this page · raw message
arch/x86/include/asm/qspinlock_paravirt.h new file +6 -0
@@ -0,0 +1,6 @@ +#ifndef __ASM_QSPINLOCK_PARAVIRT_H +#define __ASM_QSPINLOCK_PARAVIRT_H + +PV_CALLEE_SAVE_REGS_THUNK (__pv_queued_spin_unlock); + +#endif
arch/x86/include/asm/spinlock.h +5 -0
@@ -42,6 +42,10 @@extern struct static_key paravirt_ticketlocks_enabled; static __always_inline bool static_key_false(struct static_key *key); +#ifdef CONFIG_QUEUED_SPINLOCKS +#include <asm/qspinlock.h> +#else +#ifdef CONFIG_PARAVIRT_SPINLOCKS static inline void __ticket_enter_slowpath (arch_spinlock_t *lock)
@@ -196,6 +200,7 @@ static inline void arch_spin_unlock_wait(arch_spinlock_t *lock) cpu_relax(); } } +#endif /* CONFIG_QUEUED_SPINLOCKS */ /* * Read-write spinlocks, allowing multiple readers
arch/x86/include/asm/spinlock_types.h +4 -0
@@ -23,6 +23,9 @@ typedef u32 __ticketpair_t;#define TICKET_SHIFT (sizeof(__ticket_t) * 8) +#ifdef CONFIG_QUEUED_SPINLOCKS +#include <asm-generic/qspinlock_types.h> +#else typedef struct arch_spinlock { union { __ticketpair_t head_tail;
@@ -33,6 +36,7 @@ typedef struct arch_spinlock { } arch_spinlock_t;#define __ARCH_SPIN_LOCK_UNLOCKED { { 0 } } +#endif /* CONFIG_QUEUED_SPINLOCKS */ #include <asm-generic/qrwlock_types.h>
arch/x86/kernel/kvm.c +43 -0
63 lines not on this page · raw message
arch/x86/kernel/paravirt-spinlocks.c +23 -1
@@ -8,11 +8,33 @@#include <asm/paravirt.h> +#ifdef CONFIG_QUEUED_SPINLOCKS +__visible void __native_queued_spin_unlock(struct qspinlock *lock) +{ +native_queued_spin_unlock (lock); +} + +PV_CALLEE_SAVE_REGS_THUNK (__native_queued_spin_unlock); + +bool pv_is_native_spin_unlock (void ) +{ +return pv_lock_ops.queued_spin_unlock.func == + __raw_callee_save___native_queued_spin_unlock; +} +#endif + struct pv_lock_ops pv_lock_ops = {#ifdef CONFIG_SMP +#ifdef CONFIG_QUEUED_SPINLOCKS + .queued_spin_lock_slowpath = native_queued_spin_lock_slowpath, + .queued_spin_unlock = PV_CALLEE_SAVE(__native_queued_spin_unlock), + .wait = paravirt_nop, + .kick = paravirt_nop, +#else /* !CONFIG_QUEUED_SPINLOCKS */ .lock_spinning = __PV_IS_CALLEE_SAVE(paravirt_nop), .unlock_kick = paravirt_nop, -#endif +#endif /* !CONFIG_QUEUED_SPINLOCKS */ +#endif /* SMP */ };EXPORT_SYMBOL (pv_lock_ops);
arch/x86/kernel/paravirt_patch_32.c +18 -4
@@ -12,6 +12,10 @@ DEF_NATIVE(pv_mmu_ops, read_cr3, "mov %cr3, %eax");DEF_NATIVE (pv_cpu_ops, clts,"clts" );DEF_NATIVE (pv_cpu_ops, read_tsc,"rdtsc" ); +#if defined(CONFIG_PARAVIRT_SPINLOCKS ) && defined(CONFIG_QUEUED_SPINLOCKS ) +DEF_NATIVE (pv_lock_ops, queued_spin_unlock,"movb $0, (%eax)" ); +#endif +unsigned paravirt_patch_ident_32 (void *insnbuf,unsigned len) {/* arg in %eax, return in %eax */
@@ -24,6 +28,8 @@ unsigned paravirt_patch_ident_64(void *insnbuf, unsigned len)return 0 ; } +extern bool pv_is_native_spin_unlock (void ); +unsigned native_patch (u8 type,u16 clobbers,void *ibuf,unsigned long addr,unsigned len) {
@@ -47,14 +53,22 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,PATCH_SITE (pv_mmu_ops, write_cr3);PATCH_SITE (pv_cpu_ops, clts);PATCH_SITE (pv_cpu_ops, read_tsc); - - patch_site: - ret =paravirt_patch_insns (ibuf, len, start, end); -break ; +#if defined(CONFIG_PARAVIRT_SPINLOCKS ) && defined(CONFIG_QUEUED_SPINLOCKS ) +case PARAVIRT_PATCH (pv_lock_ops.queued_spin_unlock): +if (pv_is_native_spin_unlock ()) { + start = start_pv_lock_ops_queued_spin_unlock; + end = end_pv_lock_ops_queued_spin_unlock; + goto patch_site; + } +#endif default : ret =paravirt_patch_default (type, clobbers, ibuf, addr, len);break ; + +patch_site: + ret =paravirt_patch_insns (ibuf, len, start, end); +break ; }#undef PATCH_SITEreturn ret;
arch/x86/kernel/paravirt_patch_64.c +18 -4
@@ -21,6 +21,10 @@ DEF_NATIVE(pv_cpu_ops, swapgs, "swapgs"); DEF_NATIVE(, mov32, "mov %edi, %eax"); DEF_NATIVE(, mov64, "mov %rdi, %rax"); +#if defined(CONFIG_PARAVIRT_SPINLOCKS ) && defined(CONFIG_QUEUED_SPINLOCKS ) +DEF_NATIVE (pv_lock_ops, queued_spin_unlock,"movb $0, (%rdi)" ); +#endif +unsigned paravirt_patch_ident_32 (void *insnbuf,unsigned len) { return paravirt_patch_insns(insnbuf, len,
@@ -33,6 +37,8 @@ unsigned paravirt_patch_ident_64(void *insnbuf, unsigned len) start__mov64, end__mov64); } +extern bool pv_is_native_spin_unlock (void ); +unsigned native_patch (u8 type,u16 clobbers,void *ibuf,unsigned long addr,unsigned len) {
@@ -59,14 +65,22 @@ unsigned native_patch(u8 type, u16 clobbers, void *ibuf,PATCH_SITE (pv_cpu_ops, clts);PATCH_SITE (pv_mmu_ops, flush_tlb_single);PATCH_SITE (pv_cpu_ops, wbinvd); - - patch_site: - ret =paravirt_patch_insns (ibuf, len, start, end); -break ; +#if defined(CONFIG_PARAVIRT_SPINLOCKS ) && defined(CONFIG_QUEUED_SPINLOCKS ) +case PARAVIRT_PATCH (pv_lock_ops.queued_spin_unlock): +if (pv_is_native_spin_unlock ()) { + start = start_pv_lock_ops_queued_spin_unlock; + end = end_pv_lock_ops_queued_spin_unlock; + goto patch_site; + } +#endif default : ret =paravirt_patch_default (type, clobbers, ibuf, addr, len);break ; + +patch_site: + ret =paravirt_patch_insns (ibuf, len, start, end); +break ; }#undef PATCH_SITEreturn ret;
arch/x86/um/asm/barrier.h +2 -1
@@ -39,7 +39,8 @@#define smp_mb () barrier()#define smp_rmb () barrier()#define smp_wmb () barrier() -#define set_mb (var, value) do { var = value; barrier(); } while (0) + +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); barrier(); } while (0)#define read_barrier_depends () do { } while (0)#define smp_read_barrier_depends () do { } while (0)
arch/x86/xen/spinlock.c +60 -4
99 lines not on this page · raw message
fs/select.c +3 -3
@@ -189,7 +189,7 @@ static int __pollwake(wait_queue_t *wait, unsigned mode, int sync, void *key)* doesn't imply write barrier and the users expect write * barrier semantics on wakeup functions. The following * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() -* and is paired with set_mb() in poll_schedule_timeout. +* and is paired with smp_store_mb() in poll_schedule_timeout. */ smp_wmb (); pwq->triggered =1 ;
@@ -244,7 +244,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,/* * Prepare for the next iteration. * -* The following set_mb() serves two purposes. First, it's +* The following smp_store_mb() serves two purposes. First, it's * the counterpart rmb of the wmb in pollwake() such that data * written before wake up is always visible after wake up. * Second, the full barrier guarantees that triggered clearing
@@ -252,7 +252,7 @@ int poll_schedule_timeout(struct poll_wqueues *pwq, int state,* this problem doesn't exist for the first iteration as * add_wait_queue() has full barrier semantics. */ -set_mb (pwq->triggered,0 ); +smp_store_mb (pwq->triggered,0 );return rc; }
include/asm-generic/barrier.h +2 -2
@@ -66,8 +66,8 @@#define smp_read_barrier_depends () do { } while (0)#endif -#ifndef set_mb -#define set_mb (var, value) do { (var) = (value); mb(); } while (0) +#ifndef smp_store_mb +#define smp_store_mb (var, value) do { WRITE_ONCE(var, value); mb(); } while (0)#endif #ifndef smp_mb__before_atomic
include/asm-generic/cmpxchg.h +0 -3
@@ -86,9 +86,6 @@ unsigned long __xchg(unsigned long x, volatile void *ptr, int size)/* * Atomic compare and exchange. -* -* Do not define __HAVE_ARCH_CMPXCHG because we want to use it to check whether -* a cmpxchg primitive faster than repeated local irq save/restore exists. */ #include <asm-generic/cmpxchg-local.h>
include/asm-generic/qspinlock.h new file +139 -0
139 lines not on this page · raw message
include/asm-generic/qspinlock_types.h new file +79 -0
79 lines not on this page · raw message
include/linux/compiler.h +2 -2
@@ -250,7 +250,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s ({ union { typeof(x) __val; char __c[1]; } __u; __read_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })#define WRITE_ONCE (x, val) \ - ({ typeof(x) __val = (val); __write_once_size(&(x), &__val, sizeof(__val)); __val; }) + ({ union { typeof(x) __val; char __c[1]; } __u = { .__val = (val) }; __write_once_size(&(x), __u.__c, sizeof(x)); __u.__val; })#endif /* __KERNEL__ */
@@ -450,7 +450,7 @@ static __always_inline void __write_once_size(volatile void *p, void *res, int s* with an explicit memory barrier or atomic instruction that provides the * required ordering. * -* If possible use READ_ONCE/ASSIGN_ONCE instead. +* If possible use READ_ONCE()/WRITE_ONCE() instead. */ #define __ACCESS_ONCE (x) ({ \ __maybe_unused typeof(x) __var = (__force typeof(x)) 0; \
include/linux/lockdep.h +2 -2
@@ -130,8 +130,8 @@ enum bounce_type { };struct lock_class_stats { -unsigned long contention_point[4 ]; -unsigned long contending_point[4 ]; +unsigned long contention_point[LOCKSTAT_POINTS ]; +unsigned long contending_point[LOCKSTAT_POINTS ];struct lock_time read_waittime;struct lock_time write_waittime;struct lock_time read_holdtime;
include/linux/osq_lock.h +5 -0
@@ -32,4 +32,9 @@ static inline void osq_lock_init(struct optimistic_spin_queue *lock)extern bool osq_lock (struct optimistic_spin_queue *lock);extern void osq_unlock (struct optimistic_spin_queue *lock); +static inline bool osq_is_locked (struct optimistic_spin_queue *lock) +{ +return atomic_read (&lock->tail) !=OSQ_UNLOCKED_VAL ; +} +#endif
include/linux/sched.h +4 -4
@@ -252,7 +252,7 @@ extern char ___assert_task_state[1 - 2*!!(#define set_task_state (tsk, state_value) \ do { \ (tsk)->task_state_change = _THIS_IP_; \ - set_mb((tsk)->state, (state_value)); \ + smp_store_mb((tsk)->state, (state_value)); \ } while (0)/*
@@ -274,7 +274,7 @@ extern char ___assert_task_state[1 - 2*!!(#define set_current_state (state_value) \ do { \ current->task_state_change = _THIS_IP_; \ - set_mb(current->state, (state_value)); \ + smp_store_mb(current->state, (state_value)); \ } while (0)#else
@@ -282,7 +282,7 @@ extern char ___assert_task_state[1 - 2*!!(#define __set_task_state (tsk, state_value) \ do { (tsk)->state = (state_value); } while (0)#define set_task_state (tsk, state_value) \ - set_mb((tsk)->state, (state_value)) + smp_store_mb((tsk)->state, (state_value))/* * set_current_state() includes a barrier so that the write of current->state
@@ -298,7 +298,7 @@ extern char ___assert_task_state[1 - 2*!!(#define __set_current_state (state_value) \ do { current->state = (state_value); } while (0)#define set_current_state (state_value) \ - set_mb(current->state, (state_value)) + smp_store_mb(current->state, (state_value))#endif
kernel/Kconfig.locks +10 -3
@@ -235,9 +235,16 @@ config LOCK_SPIN_ON_OWNER def_bool y depends on MUTEX_SPIN_ON_OWNER || RWSEM_SPIN_ON_OWNER -config ARCH_USE_QUEUE_RWLOCK +config ARCH_USE_QUEUED_SPINLOCKS bool -config QUEUE_RWLOCK - def_bool y if ARCH_USE_QUEUE_RWLOCK +config QUEUED_SPINLOCKS + def_bool y if ARCH_USE_QUEUED_SPINLOCKS + depends on SMP + +config ARCH_USE_QUEUED_RWLOCKS + bool + +config QUEUED_RWLOCKS + def_bool y if ARCH_USE_QUEUED_RWLOCKS depends on SMP
kernel/futex.c +1 -1
@@ -2055,7 +2055,7 @@ static void futex_wait_queue_me(struct futex_hash_bucket *hb, struct futex_q *q, {/* * The task state is guaranteed to be set before another task can -* wake it. set_current_state() is implemented using set_mb() and +* wake it. set_current_state() is implemented using smp_store_mb() and * queue_me() calls spin_unlock() upon completion, both serializing * access to the hash list and forcing another memory barrier. */
kernel/locking/Makefile +2 -1
@@ -17,6 +17,7 @@ obj-$(CONFIG_SMP) += spinlock.o obj-$(CONFIG_LOCK_SPIN_ON_OWNER) += osq_lock.o obj-$(CONFIG_SMP) += lglock.o obj-$(CONFIG_PROVE_LOCKING) += spinlock.o +obj-$(CONFIG_QUEUED_SPINLOCKS) += qspinlock.o obj-$(CONFIG_RT_MUTEXES) += rtmutex.o obj-$(CONFIG_DEBUG_RT_MUTEXES) += rtmutex-debug.o obj-$(CONFIG_RT_MUTEX_TESTER) += rtmutex-tester.o
@@ -25,5 +26,5 @@ obj-$(CONFIG_DEBUG_SPINLOCK) += spinlock_debug.o obj-$(CONFIG_RWSEM_GENERIC_SPINLOCK) += rwsem-spinlock.o obj-$(CONFIG_RWSEM_XCHGADD_ALGORITHM) += rwsem-xadd.o obj-$(CONFIG_PERCPU_RWSEM) += percpu-rwsem.o -obj-$(CONFIG_QUEUE_RWLOCK) += qrwlock.o +obj-$(CONFIG_QUEUED_RWLOCKS) += qrwlock.o obj-$(CONFIG_LOCK_TORTURE_TEST) += locktorture.o
kernel/locking/lockdep.c +1 -2
@@ -4066,8 +4066,7 @@ void __init lockdep_info(void)#ifdef CONFIG_DEBUG_LOCKDEP if (lockdep_init_error) { -printk ("WARNING: lockdep init error! lock-%s was acquired" -"before lockdep_init\n" , lock_init_error); +printk ("WARNING: lockdep init error: lock '%s' was acquired before lockdep_init().\n" , lock_init_error);printk ("Call stack leading to lockdep invocation was:\n" );print_stack_trace (&lockdep_init_trace,0 ); }
kernel/locking/mcs_spinlock.h +1 -0
@@ -17,6 +17,7 @@struct mcs_spinlock {struct mcs_spinlock *next;int locked;/* 1 if lock acquired */ +int count;/* nesting count, see qspinlock.c */ };#ifndef arch_mcs_spin_lock_contended
kernel/locking/qrwlock.c +25 -5
@@ -1,5 +1,5 @@/* -* Queue read/write lock +* Queued read/write locks * * 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
@@ -22,6 +22,26 @@#include <linux/hardirq.h> #include <asm/qrwlock.h> +/* +* This internal data structure is used for optimizing access to some of +* the subfields within the atomic_t cnts. +*/ +struct __qrwlock { +union { +atomic_t cnts; +struct { +#ifdef __LITTLE_ENDIAN +u8 wmode;/* Writer mode */ +u8 rcnts[3 ];/* Reader counts */ +#else +u8 rcnts[3 ];/* Reader counts */ +u8 wmode;/* Writer mode */ +#endif + }; + }; +arch_spinlock_t lock; +}; +/** * rspin_until_writer_unlock - inc reader count & spin until writer is gone * @lock : Pointer to queue rwlock structure
@@ -107,10 +127,10 @@ void queue_write_lock_slowpath(struct qrwlock *lock)* or wait for a previous writer to go away. */ for (;;) { - cnts =atomic_read (&lock->cnts); -if (!(cnts & _QW_WMASK) && - (atomic_cmpxchg (&lock->cnts, cnts, - cnts | _QW_WAITING) == cnts)) +struct __qrwlock *l = (struct __qrwlock *)lock; + +if (!READ_ONCE (l->wmode) && + (cmpxchg (&l->wmode,0 , _QW_WAITING) ==0 ))break ;cpu_relax_lowlatency ();
kernel/locking/qspinlock.c new file +473 -0
473 lines not on this page · raw message
kernel/locking/qspinlock_paravirt.h new file +325 -0
325 lines not on this page · raw message
kernel/locking/rtmutex.c +10 -3
@@ -70,10 +70,10 @@ static void fixup_rt_mutex_waiters(struct rt_mutex *lock) }/* -* We can speed up the acquire/release, if the architecture -* supports cmpxchg and if there's no debugging state to be set up +* We can speed up the acquire/release, if there's no debugging state to be +* set up. */ -#if defined(__HAVE_ARCH_CMPXCHG) && !defined(CONFIG_DEBUG_RT_MUTEXES ) +#ifndef CONFIG_DEBUG_RT_MUTEXES # define rt_mutex_cmpxchg (l,c,n) (cmpxchg(&l->owner, c, n) == c)static inline void mark_rt_mutex_waiters (struct rt_mutex *lock) {
@@ -1441,10 +1441,17 @@ EXPORT_SYMBOL_GPL(rt_mutex_timed_lock);* * @lock: the rt_mutex to be locked * +* This function can only be called in thread context. It's safe to +* call it from atomic regions, but not from hard interrupt or soft +* interrupt context. +* * Returns 1 on success and 0 on contention */ int __sched rt_mutex_trylock(struct rt_mutex *lock) { +if (WARN_ON (in_irq () ||in_nmi () ||in_serving_softirq ())) +return 0 ; +return rt_mutex_fasttrylock (lock, rt_mutex_slowtrylock); }EXPORT_SYMBOL_GPL (rt_mutex_trylock);
kernel/locking/rwsem-xadd.c +44 -0
62 lines not on this page · raw message
kernel/sched/wait.c +2 -2
@@ -341,7 +341,7 @@ long wait_woken(wait_queue_t *wait, unsigned mode, long timeout)* condition being true _OR_ WQ_FLAG_WOKEN such that we will not miss * an event. */ -set_mb (wait->flags, wait->flags & ~WQ_FLAG_WOKEN );/* B */ +smp_store_mb (wait->flags, wait->flags & ~WQ_FLAG_WOKEN );/* B */ return timeout; }
@@ -354,7 +354,7 @@ int woken_wake_function(wait_queue_t *wait, unsigned mode, int sync, void *key)* doesn't imply write barrier and the users expects write * barrier semantics on wakeup functions. The following * smp_wmb() is equivalent to smp_wmb() in try_to_wake_up() -* and is paired with set_mb() in wait_woken(). +* and is paired with smp_store_mb() in wait_woken(). */ smp_wmb ();/* C */ wait->flags |=WQ_FLAG_WOKEN ;
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in Please read the FAQ at http://www.tux.org/lkml/
Quoted 6 lines · Ingo Molnar
Linus,
Please pull the latest locking-core-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.gitlocking-core-for-linus
Quoted 7 lines
# HEAD:
68722101ec3a0e179408a13708dd020e04f54aablocking/lockdep: Remove hard coded array size dependencyThe main changes are:
- 'qspinlock' support, enabled on x86: queued spinlocks - these are now the spinlock variant used by x86 as they outperform ticket spinlocks in every category. (Waiman Long)
Very interesting.
While rebasing http://www.kernelhub.org/?msg=737214&p=2
I found that indeed qspinlock improved the scalability issue by a big factor.
linux-4.1
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 6977349, ops/sec 232578 6977349
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 7326280, ops/sec 244209 7326280
Current Linus tree :
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 15640802, ops/sec 521360 15640802
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 15045022, ops/sec 501500 15045022
Adding fd_install() patch then :
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 21471043, ops/sec 715701 21471043
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 21068501, ops/sec 702283 21068501
Quoted 54 lines · Eric Dumazet
Quoted 8 lines · Ingo Molnar
Linus,
Please pull the latest locking-core-for-linus git tree from:
git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip.git locking-core-for-linus # HEAD: 68722101ec3a0e179408a13708dd020e04f54aab locking/lockdep: Remove hard coded array size dependencyQuoted 5 lines
The main changes are:
- 'qspinlock' support, enabled on x86: queued spinlocks - these are now the spinlock variant used by x86 as they outperform ticket spinlocks in every category. (Waiman Long)
Very interesting.
While rebasing http://www.kernelhub.org/?msg=737214&p=2
I found that indeed qspinlock improved the scalability issue by a big factor.
linux-4.1
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 6977349, ops/sec 232578 6977349lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 7326280, ops/sec 244209 7326280Current Linus tree :
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 15640802, ops/sec 521360 15640802lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 15045022, ops/sec 501500 15045022Adding
fd_install()patch then :lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 21471043, ops/sec 715701 21471043
Quoted 4 lines
lpaa23:~# pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 21068501, ops/sec 702283 21068501
I am glad that the qspinlock patch helps. May I know what kind of machine (cpu type, # of sockets, etc) that you are running the test on? Usually the more sockets the machine has, the bigger the improvement you will see.
Cheers,
Longman
Quoted 4 lines · Waiman Long
I am glad that the qspinlock patch helps. May I know what kind of machine (cpu type, # of sockets, etc) that you are running the test on? Usually the more sockets the machine has, the bigger the improvement you will see.
This particular host had two Intel(R) Xeon(R) CPU E5-2696 v2 @ 2.50GHz
(12 cores per socket, total of 48 hyper threads)
Note that if pipebench uses 48 threads (instead of 16 as my previous mail did), there is almost no degradation.
lpaa23:~# ./pipebench -n 16 threads:16 sleep:30 pinned:1 total operations: 20228142, ops/sec 674271 20228142
lpaa23:~# ./pipebench -n 48 threads:48 sleep:30 pinned:1 total operations: 19763616, ops/sec 658787 19763616
Thanks