smp_cond_load_acquire + cmpwait
Subsystems (5)
Peter Zijlstra
maintainercover smp_cond_load_acquire + cmpwait
As Will reminded me last week; we still had some pending changes.
A number of patches that allow arm64 to use a monitor/mwait like construct to avoid most spin waiting.
Compile and boot tested on x86_64, but I seem to have misplaced my arm64 compiler so that part is still a rough sketch mostly.
Peter Zijlstra
maintainerRFC PATCH 1/3 locking: Replace smp_cond_acquire with smp_cond_load_acquire
This new form allows using hardware assisted waiting.
--- include/linux/compiler.h | 25 +++++++++++++++++++------ kernel/locking/qspinlock.c | 12 ++++++------ kernel/sched/core.c | 8 ++++---- kernel/sched/sched.h | 2 +- kernel/smp.c | 2 +- 5 files changed, 31 insertions(+), 18 deletions(-)
include/linux/compiler.h +19 -6
@@ -305,21 +305,34 @@ static __always_inline void __write_once })/** -* smp_cond_acquire() - Spin wait for cond with ACQUIRE ordering +* smp_cond_load_acquire() - (Spin) wait for cond with ACQUIRE ordering +* @ptr: pointer to the variable to wait on * @cond: boolean expression to wait for * * Equivalent to using smp_load_acquire() on the condition variable but employs * the control dependency of the wait to reduce the barrier on many platforms. * +* Due to C lacking lambda expressions we load the value of *ptr into a +* pre-named variable @VAL to be used in @cond. +* * The control dependency provides a LOAD->STORE order, the additional RMB * provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order, * aka. ACQUIRE. */ -#define smp_cond_acquire(cond) do { \ - while (!(cond)) \ - cpu_relax(); \ - smp_rmb(); /* ctrl + rmb := acquire */ \ -} while (0) +#ifndef smp_cond_load_acquire +#define smp_cond_load_acquire (ptr, cond_expr) ({ \ + typeof(ptr) __PTR = (ptr); \ + typeof(*ptr) VAL; \ + for (;;) { \ + VAL = READ_ONCE(*__PTR); \ + if (cond_expr) \ + break; \ + cpu_relax(); \ + } \ + smp_rmb();/* ctrl + rmb := acquire */ \ +VAL ; \ +}) +#endif #endif /* __KERNEL__ */
kernel/locking/qspinlock.c +6 -6
@@ -358,7 +358,7 @@ void queued_spin_lock_slowpath(struct qs* sequentiality; this is because not all clear_pending_set_locked() * implementations imply full barriers. */ -smp_cond_acquire (!(atomic_read (&lock->val) & _Q_LOCKED_MASK)); +smp_cond_load_acquire (&lock->val.counter, !(VAL & _Q_LOCKED_MASK));/* * take ownership and clear the pending bit.
@@ -434,7 +434,7 @@ void queued_spin_lock_slowpath(struct qs* * The PV pv_wait_head_or_lock function, if active, will acquire * the lock and return a non-zero value. So we have to skip the -* smp_cond_acquire() call. As the next PV queue head hasn't been +* smp_cond_load_acquire() call. As the next PV queue head hasn't been * designated yet, there is no way for the locked value to become * _Q_SLOW_VAL. So both the set_locked() and the * atomic_cmpxchg_relaxed() calls will be safe.
@@ -445,7 +445,7 @@ void queued_spin_lock_slowpath(struct qsif ((val =pv_wait_head_or_lock (lock, node))) goto locked; -smp_cond_acquire (!((val =atomic_read (&lock->val)) & _Q_LOCKED_PENDING_MASK)); + val =smp_cond_load_acquire (&lock->val.counter, !(VAL & _Q_LOCKED_PENDING_MASK)); locked:/*
@@ -465,9 +465,9 @@ void queued_spin_lock_slowpath(struct qsbreak ; }/* -* The smp_cond_acquire() call above has provided the necessary -* acquire semantics required for locking. At most two -* iterations of this loop may be ran. +* The smp_cond_load_acquire() call above has provided the +* necessary acquire semantics required for locking. At most +* two iterations of this loop may be ran. */ old =atomic_cmpxchg_relaxed (&lock->val, val, _Q_LOCKED_VAL);if (old == val)
kernel/sched/core.c +4 -4
@@ -1843,7 +1843,7 @@ static void ttwu_queue(struct task_struc* chain to provide order. Instead we do: * * 1) smp_store_release(X->on_cpu, 0) -* 2) smp_cond_acquire(!X->on_cpu) +* 2) smp_cond_load_acquire(!X->on_cpu) * * Example: *
@@ -1854,7 +1854,7 @@ static void ttwu_queue(struct task_struc* sched-out X * smp_store_release(X->on_cpu, 0); * -* smp_cond_acquire(!X->on_cpu); +* smp_cond_load_acquire(&X->on_cpu, !VAL); * X->state = WAKING * set_task_cpu(X,2) *
@@ -1880,7 +1880,7 @@ static void ttwu_queue(struct task_struc* This means that any means of doing remote wakeups must order the CPU doing * the wakeup against the CPU the task is going to end up running on. This, * however, is already required for the regular Program-Order guarantee above, -* since the waking CPU is the one issueing the ACQUIRE (smp_cond_acquire). +* since the waking CPU is the one issueing the ACQUIRE (smp_cond_load_acquire). * */
@@ -1953,7 +1953,7 @@ try_to_wake_up(struct task_struct *p, un* This ensures that tasks getting woken will be fully ordered against * their previous state and preserve Program Order. */ -smp_cond_acquire (!p->on_cpu); +smp_cond_load_acquire (&p->on_cpu, !VAL ); p->sched_contributes_to_load = !!task_contributes_to_load (p); p->state =TASK_WAKING ;
kernel/sched/sched.h +1 -1
@@ -1104,7 +1104,7 @@ static inline void finish_lock_switch(st* In particular, the load of prev->state in finish_task_switch() must * happen before this. * -* Pairs with the smp_cond_acquire() in try_to_wake_up(). +* Pairs with the smp_cond_load_acquire() in try_to_wake_up(). */ smp_store_release (&prev->on_cpu,0 );#endif
kernel/smp.c +1 -1
@@ -107,7 +107,7 @@ void __init call_function_init(void)*/ static __always_inline void csd_lock_wait(struct call_single_data *csd) { -smp_cond_acquire (!(csd->flags &CSD_FLAG_LOCK )); +smp_cond_load_acquire (&csd->flags, !(VAL &CSD_FLAG_LOCK )); } static __always_inline void csd_lock(struct call_single_data *csd)
Re: [RFC][PATCH 1/3] locking: Replace smp_cond_acquire with smp_cond_load_acquire
Quoted 43 lines · Peter Zijlstra
This new form allows using hardware assisted waiting.
Requested-by Will Deacon<[email protected]>Suggested-by Linus Torvalds<[email protected]>Signed-off-by Peter Zijlstra (Intel)<[email protected]>--- include/linux/compiler.h | 25 +++++++++++++++++++------ kernel/locking/qspinlock.c | 12 ++++++------ kernel/sched/core.c | 8 ++++---- kernel/sched/sched.h | 2 +- kernel/smp.c | 2 +- 5 files changed, 31 insertions(+), 18 deletions(-)include/linux/compiler.h +9
-6@@ -305,21 +305,34 @@ static __always_inline void __write_once })/** -* smp_cond_acquire() - Spin wait for cond with ACQUIRE ordering +* smp_cond_load_acquire() - (Spin) wait for cond with ACQUIRE ordering +* @ptr: pointer to the variable to wait on * @cond: boolean expression to wait for * * Equivalent to using smp_load_acquire() on the condition variable but employs * the control dependency of the wait to reduce the barrier on many platforms. * +* Due to C lacking lambda expressions we load the value of *ptr into a +* pre-named variable @VAL to be used in @cond. +* * The control dependency provides a LOAD->STORE order, the additional RMB * provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order, * aka. ACQUIRE. */ -#define smp_cond_acquire (cond) do { \ - while (!(cond)) \ - cpu_relax(); \ - smp_rmb();/* ctrl + rmb := acquire */ \ -}while (0 ) +#ifndef smp_cond_load_acquire +#define smp_cond_load_acquire (ptr, cond_expr) ({ \ + typeof(ptr) __PTR = (ptr); \ + typeof(*ptr) VAL; \
include/linux/compiler.h static __always_inline void __write_once · line 335
Quoted 10 lines
include/linux/compiler.h +10
-0+for (;;) { \ +VAL =READ_ONCE (*__PTR); \ +if (cond_expr) \ +break ; \ +cpu_relax (); \ + } \ +smp_rmb ();/* ctrl + rmb := acquire */ \ +VAL ; \ +}) +#endif
Using a predefined VAR seems a bit awkward as a reader of the code may not know where VAR comes from. How about passing in a variable to hold the latest value of (*ptr), e.g.
#ifndef smp_cond_load_acquire
#define smp_cond_load_acquire(ptr, var, cond_expr) do { \
typeof(ptr) __PTR = (ptr); \
for (;;) { \
var = READ_ONCE(*__PTR); \
if (cond_expr) \
break; \
cpu_relax(); \
} \
smp_rmb(); /* ctrl + rmb := acquire */ \} while (0)
#endif
Cheers,
Longman
Peter Zijlstra
maintainerRFC PATCH 2/3 locking/qrwlock: Use smp_cond_load_acquire()
In reply to Peter ZijlstraUse smp_cond_load_acquire() to make better use of the hardware assisted 'spin' wait on arm64.
Arguably the second hunk is the more horrid abuse possible, but avoids having to use cmpwait (see next patch) directly. Also, this makes 'clever' (ab)use of the cond+rmb acquire to omit the acquire from cmpxchg().
--- kernel/locking/qrwlock.c | 18 ++++-------------- 1 file changed, 4 insertions(+), 14 deletions(-)
kernel/locking/qrwlock.c +4 -14
@@ -53,10 +53,7 @@ struct __qrwlock { static __always_inline voidrspin_until_writer_unlock (struct qrwlock *lock,u32 cnts) { -while ((cnts & _QW_WMASK) == _QW_LOCKED) { -cpu_relax_lowlatency (); - cnts =atomic_read_acquire (&lock->cnts); - } +smp_cond_load_acquire (&lock->cnts.counter, (VAL & _QW_WMASK) != _QW_LOCKED); }/**
@@ -109,8 +106,6 @@ EXPORT_SYMBOL(queued_read_lock_slowpath)*/ void queued_write_lock_slowpath (struct qrwlock *lock) { -u32 cnts; -/* Put the writer into the wait queue */ arch_spin_lock (&lock->wait_lock);
@@ -134,15 +129,10 @@ void queued_write_lock_slowpath(struct q }/* When no more readers, set the locked flag */ -for (;;) { - cnts =atomic_read (&lock->cnts); -if ((cnts == _QW_WAITING) && - (atomic_cmpxchg_acquire (&lock->cnts, _QW_WAITING, - _QW_LOCKED) == _QW_WAITING)) -break ; +smp_cond_load_acquire (&lock->cnts.counter, + (VAL == _QW_WAITING) && +atomic_cmpxchg_relaxed (&lock->cnts, _QW_WAITING, _QW_LOCKED) == _QW_WAITING); -cpu_relax_lowlatency (); - } unlock:arch_spin_unlock (&lock->wait_lock); }
Re: [RFC][PATCH 2/3] locking/qrwlock: Use smp_cond_load_acquire()
Quoted 41 lines · Peter Zijlstra
Use
smp_cond_load_acquire()to make better use of the hardware assisted 'spin' wait on arm64.Arguably the second hunk is the more horrid abuse possible, but avoids having to use cmpwait (see next patch) directly. Also, this makes 'clever' (ab)use of the cond+rmb acquire to omit the acquire from
cmpxchg().Signed-off-by Peter Zijlstra (Intel) <[email protected]>---
kernel/locking/qrwlock.c| 18 ++++--------------1 file changed, 4 insertions(+), 14 deletions(-)kernel/locking/qrwlock.c +0
-0@@ -53,10 +53,7 @@ struct __qrwlock {static
__always_inlinevoidrspin_until_writer_unlock(struct qrwlock *lock, u32 cnts){kernel/locking/qrwlock.c +1
-4-while ((cnts & _QW_WMASK) == _QW_LOCKED) { -cpu_relax_lowlatency (); - cnts =atomic_read_acquire (&lock->cnts); - } +smp_cond_load_acquire (&lock->cnts.counter, (VAL & _QW_WMASK) != _QW_LOCKED);}
/**
kernel/locking/qrwlock.c +0
-3@@ -109,8 +106,6 @@ EXPORT_SYMBOL(queued_read_lock_slowpath)*/ void queued_write_lock_slowpath (struct qrwlock *lock) { -u32 cnts; -/* Put the writer into the wait queue */ arch_spin_lock (&lock->wait_lock);@@ -134,15 +129,10 @@ void queued_write_lock_slowpath(struct q }/* When no more readers, set the locked flag */ -for (;;) {
kernel/locking/qrwlock.c void queued_write_lock_slowpath(struct q · line 144
Quoted 10 lines
kernel/locking/qrwlock.c +3
-6- cnts =atomic_read (&lock->cnts); -if ((cnts == _QW_WAITING) && - (atomic_cmpxchg_acquire (&lock->cnts, _QW_WAITING, - _QW_LOCKED) == _QW_WAITING)) -break ; +smp_cond_load_acquire (&lock->cnts.counter, + (VAL == _QW_WAITING) && +atomic_cmpxchg_relaxed (&lock->cnts, _QW_WAITING, _QW_LOCKED) == _QW_WAITING); -cpu_relax_lowlatency ();
You would need some variant for cpu_relax_lowlatency otherwise you'll be hurting s390, no? fwiw back when I was looking at this, I recall thinking about possibly introducing smp_cond_acquire_lowlatency but never got around to it.
Thanks,
Davidlohr
Re: [RFC][PATCH 2/3] locking/qrwlock: Use smp_cond_load_acquire()
Quoted 54 lines · Davidlohr Bueso
Quoted 52 lines · Peter Zijlstra
Use
smp_cond_load_acquire()to make better use of the hardware assisted 'spin' wait on arm64.Arguably the second hunk is the more horrid abuse possible, but avoids having to use cmpwait (see next patch) directly. Also, this makes 'clever' (ab)use of the cond+rmb acquire to omit the acquire from
cmpxchg().Signed-off-by Peter Zijlstra (Intel) <[email protected]>---
kernel/locking/qrwlock.c| 18 ++++--------------1 file changed, 4 insertions(+), 14 deletions(-)kernel/locking/qrwlock.c +0
-0@@ -53,10 +53,7 @@ struct __qrwlock {static
__always_inlinevoidrspin_until_writer_unlock(struct qrwlock *lock, u32 cnts){kernel/locking/qrwlock.c +1
-4-while ((cnts & _QW_WMASK) == _QW_LOCKED) { -cpu_relax_lowlatency (); - cnts =atomic_read_acquire (&lock->cnts); - } + smp_cond_load_acquire(&lock->cnts.counter, (VAL & _QW_WMASK) !=
_QW_LOCKED);
}/**
kernel/locking/qrwlock.c +3
-8@@ -109,8 +106,6 @@ EXPORT_SYMBOL(queued_read_lock_slowpath)*/ void queued_write_lock_slowpath (struct qrwlock *lock) { -u32 cnts; -/* Put the writer into the wait queue */ arch_spin_lock (&lock->wait_lock);@@ -134,15 +129,10 @@ void queued_write_lock_slowpath(struct q }/* When no more readers, set the locked flag */ -for (;;) { - cnts =atomic_read (&lock->cnts); -if ((cnts == _QW_WAITING) && - (atomic_cmpxchg_acquire (&lock->cnts, _QW_WAITING, - _QW_LOCKED) == _QW_WAITING)) -break ; +smp_cond_load_acquire (&lock->cnts.counter, + (VAL == _QW_WAITING) && +atomic_cmpxchg_relaxed (&lock->cnts, _QW_WAITING, _QW_LOCKED) == _QW_WAITING);
Quoted 8 lines
kernel/locking/qrwlock.c void queued_write_lock_slowpath(struct q · line 145
Quoted 1 line
kernel/locking/qrwlock.c +0
-1-cpu_relax_lowlatency ();You would need some variant for
cpu_relax_lowlatencyotherwise you'll be hurting s390, no? fwiw back when I was looking at this, I recall thinking about possibly introducingsmp_cond_acquire_lowlatencybut never got around to it.Thanks,
Davidlohr
The qrwlock is currently only used on x86 architecture. We can also come back to revisit this issue when other architectures that need the lowlatency variants are going to use qrwlock.
Cheers,
Longman
Peter Zijlstra
maintainerRFC PATCH 3/3 locking,arm64: Introduce cmpwait()
In reply to Peter ZijlstraProvide the cmpwait() primitive, which will 'spin' wait for a variable to change. Use it to implement smp_cond_load_acquire() and provide an ARM64 implementation.
The ARM64 implementation uses LDXR+WFE to avoid most spinning by letting the hardware go idle while waiting for the exclusive load of the variable to be cancelled (as anybody changing the value must).
I've misplaced my arm64 compiler, so this is not even compile tested.
--- arch/arm64/include/asm/cmpxchg.h | 36 +++++++++++++++++++++++++++++ include/linux/atomic.h | 47 +++++++++++++++++++++++++++++++++++++++ include/linux/compiler.h | 30 ------------------------ 3 files changed, 83 insertions(+), 30 deletions(-)
arch/arm64/include/asm/cmpxchg.h +36 -0
@@ -224,4 +224,40 @@ __CMPXCHG_GEN(_mb) __ret; \ }) +#define __CMPWAIT_GEN (w, sz, name) \ +void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +{ \ + unsigned long tmp; \ + \ + asm volatile( \ + " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ + " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ + " cbnz %" #w "[tmp], 1f\n" \ + " wfe\n" \ + "1:" \ + : [tmp] "=&r" (tmp), [val] "=&r" (val), \ + [v] "+Q" (*(unsigned long *)ptr)); \ +} + +__CMPWAIT_GEN (w, b,1 ); +__CMPWAIT_GEN (w, h,2 ); +__CMPWAIT_GEN(w, , 4); +__CMPWAIT_GEN( , , 8); + +static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) +{ +switch (size) { +case 1 :return __cmpwait_case_1 (ptr, val); +case 2 :return __cmpwait_case_2 (ptr, val); +case 4 :return __cmpwait_case_4 (ptr, val); +case 8 :return __cmpwait_case_8 (ptr, val); +default :BUILD_BUG (); + } + +unreachable (); +} + +#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr))) +#endif /* __ASM_CMPXCHG_H */
include/linux/atomic.h +47 -0
53 lines not on this page · raw message
include/linux/compiler.h +0 -30
@@ -304,36 +304,6 @@ static __always_inline void __write_once __u.__val; \ }) -/** -* smp_cond_load_acquire() - (Spin) wait for cond with ACQUIRE ordering -* @ptr: pointer to the variable to wait on -* @cond: boolean expression to wait for -* -* Equivalent to using smp_load_acquire() on the condition variable but employs -* the control dependency of the wait to reduce the barrier on many platforms. -* -* Due to C lacking lambda expressions we load the value of *ptr into a -* pre-named variable @VAL to be used in @cond. -* -* The control dependency provides a LOAD->STORE order, the additional RMB -* provides LOAD->LOAD order, together they provide LOAD->{LOAD,STORE} order, -* aka. ACQUIRE. -*/ -#ifndef smp_cond_load_acquire -#define smp_cond_load_acquire (ptr, cond_expr) ({ \ - typeof(ptr) __PTR = (ptr); \ - typeof(*ptr) VAL; \ - for (;;) { \ - VAL = READ_ONCE(*__PTR); \ - if (cond_expr) \ - break; \ - cpu_relax(); \ - } \ - smp_rmb();/* ctrl + rmb := acquire */ \ -VAL ; \ -}) -#endif -#endif /* __KERNEL__ */ #endif /* __ASSEMBLY__ */
Peter Zijlstra
maintainerRe: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 1 line · Peter Zijlstra
+#define
__CMPWAIT_GEN(w, sz, name)\
+static inline \
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 239
Quoted 12 lines
arch/arm64/include/asm/cmpxchg.h +12
-0+void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +{ \ + unsigned long tmp; \ + \ + asm volatile( \ + " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ + " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ + " cbnz %" #w "[tmp], 1f\n" \ + " wfe\n" \ + "1:" \ + : [tmp] "=&r" (tmp), [val] "=&r" (val), \ + [v] "+Q" (*(unsigned long *)ptr)); \
And this probably wants a "memory" clobber to force reload values after this returns.
Quoted 1 line
+}
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
In reply to Peter ZijlstraHi Peter,
Thanks for looking at this!
Quoted 9 lines · Peter Zijlstra
Provide the
cmpwait()primitive, which will 'spin' wait for a variable to change. Use it to implementsmp_cond_load_acquire()and provide an ARM64 implementation.The ARM64 implementation uses LDXR+WFE to avoid most spinning by letting the hardware go idle while waiting for the exclusive load of the variable to be cancelled (as anybody changing the value must).
I've misplaced my arm64 compiler, so this is not even compile tested.
Guess what? ;)
init/do_mounts_initrd.o: In function `__cmpwait_case_1':/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:242: multiple definition of __cmpwait_case_1' init/do_mounts.o:/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:242: first defined here init/do_mounts_initrd.o: In function __cmpwait_case_2':/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:243: multiple definition of __cmpwait_case_2' init/do_mounts.o:/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:243: first defined here init/do_mounts_initrd.o: In function __cmpwait_case_4':/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:244: multiple definition of __cmpwait_case_4' init/do_mounts.o:/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:244: first defined here init/do_mounts_initrd.o: In function __cmpwait_case_8':/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:245: multiple definition of `__cmpwait_case_8' init/do_mounts.o:/home/will/work/aarch32/linux/linux/./arch/arm64/include/asm/cmpxchg.h:245: first defined here make[1]: *** [init/mounts.o] Error 1
make: *** [init] Error 2
make: *** Waiting for unfinished jobs....
(and lot of similar errors).
Looks like you're just missing an #undef in cmpxchg.h.
FWIW, you can pick up arm64 toolchain binaries from:
https://releases.linaro.org/components/toolchain/binaries/latest-5/
Quoted 13 lines
Suggested-by Will Deacon <[email protected]>Signed-off-by Peter Zijlstra (Intel) <[email protected]>--- arch/arm64/include/asm/cmpxchg.h | 36 +++++++++++++++++++++++++++++ include/linux/atomic.h | 47 +++++++++++++++++++++++++++++++++++++++ include/linux/compiler.h | 30 ------------------------ 3 files changed, 83 insertions(+), 30 deletions(-)arch/arm64/include/asm/cmpxchg.h +0
-0@@ -224,4 +224,40 @@ __CMPXCHG_GEN(_mb) __ret; \ })
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 235
Quoted 10 lines
arch/arm64/include/asm/cmpxchg.h +9
-0+#define __CMPWAIT_GEN (w, sz, name) \ +void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +{ \ + unsigned long tmp; \ + \ + asm volatile( \ + " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ + " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ + " cbnz %" #w "[tmp], 1f\n" \
Shouldn't this be cbz? (i.e. branch over the wfe if the value is equal to what we wanted?).
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 238
Quoted 3 lines
arch/arm64/include/asm/cmpxchg.h +3
-0+" wfe\n" \ +"1:" \ + : [tmp] "=&r" (tmp), [val] "=&r" (val), \
We only read val, so it can be an input operand, no?
Quoted 13 lines
arch/arm64/include/asm/cmpxchg.h +13
-0+ [v] "+Q" (*(unsigned long *)ptr)); \ +} + +__CMPWAIT_GEN(w, b,1 ); +__CMPWAIT_GEN (w, h,2 ); +__CMPWAIT_GEN(w, , 4); +__CMPWAIT_GEN( , , 8); + +static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) +{ +switch (size) { +case 1 :return __cmpwait_case_1 (ptr, val); +case 2 :return __cmpwait_case_2 (ptr, val);
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 261
Quoted 10 lines
arch/arm64/include/asm/cmpxchg.h +10
-0+case 4 :return __cmpwait_case_4 (ptr, val); +case 8 :return __cmpwait_case_8 (ptr, val); +default :BUILD_BUG (); + } + +unreachable (); +} + +#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))
We might want to call this cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make it cmpwait_acquire and remove the smp_rmb() from smp_cond_load_acquire(). Dunno.
Will
Peter Zijlstra
maintainerRe: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 1 line · Will Deacon
Thanks for looking at this!
n/p, had to se what it would look like etc.. :-)
Quoted 8 lines
Quoted 1 line
I've misplaced my arm64 compiler, so this is not even compile tested.
Guess what? ;)
make: *** [init] Error 2
make: *** Waiting for unfinished jobs....
Quoted 7 lines
(and lot of similar errors).
Looks like you're just missing an #undef in
cmpxchg.h.FWIW, you can pick up arm64 toolchain binaries from:
https://releases.linaro.org/components/toolchain/binaries/latest-5/
Ah, I usually build a whole set from sources; for some reason arm64 didn't build in the latest run. I'll have to kick it.
Quoted 4 lines
Quoted 4 lines
arch/arm64/include/asm/cmpxchg.h +4
-0+#define __CMPWAIT_GEN (w, sz, name) \ +void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +{ \ + unsigned long tmp; \
Quoted 8 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 235
Quoted 5 lines
arch/arm64/include/asm/cmpxchg.h +5
-0+ \ + asm volatile( \ + " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ + " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ + " cbnz %" #w "[tmp], 1f\n" \Shouldn't this be cbz? (i.e. branch over the wfe if the value is equal to what we wanted?).
Indeed so.
Quoted 5 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 238
Quoted 3 lines
arch/arm64/include/asm/cmpxchg.h +3
-0+" wfe\n" \ +"1:" \ + : [tmp] "=&r" (tmp), [val] "=&r" (val), \We only read val, so it can be an input operand, no?
True.. :-)
Quoted 6 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 261
Quoted 2 lines
arch/arm64/include/asm/cmpxchg.h +2
-0+#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))We might want to call this
cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make itcmpwait_acquireand remove thesmp_rmb()fromsmp_cond_load_acquire(). Dunno.
This is something I'll very much leave up to you. I have no idea on the tradeoffs involved here.
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 54 lines · Peter Zijlstra
Quoted 1 line · Will Deacon
Thanks for looking at this!
n/p, had to se what it would look like etc.. :-)
Quoted 8 lines
Quoted 1 line
I've misplaced my arm64 compiler, so this is not even compile tested.
Guess what? ;)
make: *** [init] Error 2
make: *** Waiting for unfinished jobs....Quoted 7 lines
(and lot of similar errors).
Looks like you're just missing an #undef in
cmpxchg.h.FWIW, you can pick up arm64 toolchain binaries from:
https://releases.linaro.org/components/toolchain/binaries/latest-5/
Ah, I usually build a whole set from sources; for some reason arm64 didn't build in the latest run. I'll have to kick it.
Quoted 4 lines
Quoted 4 lines
(quoted diff) +4
-0+#define __CMPWAIT_GEN(w, sz, name) \ +void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +{ \ + unsigned long tmp; \Quoted 8 lines
Review
Quoted 5 lines
(quoted diff) +5
-0+ \ + asm volatile( \ + " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ + " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ + " cbnz %" #w "[tmp], 1f\n" \Shouldn't this be cbz? (i.e. branch over the wfe if the value is equal to what we wanted?).
Indeed so.
Quoted 5 lines
Review
Quoted 3 lines
(quoted diff) +3
-0+ " wfe\n" \ + "1:" \ + : [tmp] "=&r" (tmp), [val] "=&r" (val), \We only read val, so it can be an input operand, no?
True.. :-)
Quoted 6 lines
Review
Quoted 2 lines
(quoted diff) +2
-0+#define cmpwait(ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))We might want to call this
cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make itcmpwait_acquireand remove thesmp_rmb()fromsmp_cond_load_acquire(). Dunno.
Quoted 2 lines
This is something I'll very much leave up to you. I have no idea on the tradeoffs involved here.
FWIW, here's a fixup patch to get this patch building and running. I also noticed some missing casts for the subword cases.
Will
--->8
Quoted 1 line
From
5aa5750d5eeb6e3a42f5547f094dc803f89793bbMon Sep 17 00:00:00 2001
From: Will Deacon <[email protected]>
Date: Tue, 26 Apr 2016 17:31:53 +0100
Subject: [PATCH] fixup! locking,arm64: Introduce cmpwait()
--- arch/arm64/include/asm/cmpxchg.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)
arch/arm64/include/asm/cmpxchg.h +9 -6
@@ -225,18 +225,19 @@ __CMPXCHG_GEN(_mb) })#define __CMPWAIT_GEN (w, sz, name) \ -void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \ +static inline void __cmpwait_case_##name(volatile void *ptr, \ + unsigned long val) \ { \ unsigned long tmp; \ \ asm volatile( \ " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ - " cbnz %" #w "[tmp], 1f\n" \ + " cbz %" #w "[tmp], 1f\n" \ " wfe\n" \ "1:" \ - : [tmp] "=&r" (tmp), [val] "=&r" (val), \ - [v] "+Q" (*(unsigned long *)ptr)); \ + : [tmp] "=&r" (tmp), [v] "+Q" (*(unsigned long *)ptr) \ + : [val] "r" (val)); \ }__CMPWAIT_GEN (w, b,1 );
@@ -244,11 +245,13 @@ __CMPWAIT_GEN(w, h, 2); __CMPWAIT_GEN(w, , 4); __CMPWAIT_GEN( , , 8); +#undef __CMPWAIT_GEN +static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) {switch (size) { -case 1 :return __cmpwait_case_1 (ptr, val); -case 2 :return __cmpwait_case_2 (ptr, val); +case 1 :return __cmpwait_case_1 (ptr, (u8 )val); +case 2 :return __cmpwait_case_2 (ptr, (u16 )val);case 4 :return __cmpwait_case_4 (ptr, val);case 8 :return __cmpwait_case_8 (ptr, val);default :BUILD_BUG ();
Signature · 1 line
2.1.4
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 19 lines · Will Deacon
From
5aa5750d5eeb6e3a42f5547f094dc803f89793bbMon Sep 17 00:00:00 2001
From: Will Deacon <[email protected]>
Date: Tue, 26 Apr 2016 17:31:53 +0100
Subject: [PATCH] fixup! locking,arm64: Introducecmpwait()Signed-off-by Will Deacon <[email protected]>--- arch/arm64/include/asm/cmpxchg.h | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-)arch/arm64/include/asm/cmpxchg.h +0
-1@@ -225,18 +225,19 @@ __CMPXCHG_GEN(_mb) })#define __CMPWAIT_GEN (w, sz, name) \ -void __cmpwait_case_##name(volatile void *ptr, unsigned long val) \
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 236
Quoted 10 lines
arch/arm64/include/asm/cmpxchg.h +3
-1+static inline void __cmpwait_case_##name(volatile void *ptr, \ + unsigned long val) \ { \ unsigned long tmp; \ \ asm volatile( \ " ldxr" #sz "\t%" #w "[tmp], %[v]\n" \ " eor %" #w "[tmp], %" #w "[tmp], %" #w "[val]\n" \ - " cbnz %" #w "[tmp], 1f\n" \ + " cbz %" #w "[tmp], 1f\n" \
Actually, you're right with cbnz. I only noticed when I came to implement my own version of smp_cond_load_acquire. *sigh*
I have fixups applied locally, so maybe the best thing is for me to send you an arm64 series on top of whatever you post next?
Will
Peter Zijlstra
maintainerRe: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 5 lines · Will Deacon
Actually, you're right with cbnz. I only noticed when I came to implement my own version of
smp_cond_load_acquire. *sigh*I have fixups applied locally, so maybe the best thing is for me to send you an arm64 series on top of whatever you post next?
Sure; lemme rip out all the AARGH64 bits so you can do whatever you wants :-)
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
In reply to Will DeaconOn Tue, Apr 12, 2016 at 05:59:41PM +0100, Will Deacon wrote:
[...]
Quoted 11 lines
Quoted 11 lines
arch/arm64/include/asm/cmpxchg.h +11
-0+static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) +{ +switch (size) { +case 1 :return __cmpwait_case_1 (ptr, val); +case 2 :return __cmpwait_case_2 (ptr, val); +case 4 :return __cmpwait_case_4 (ptr, val); +case 8 :return __cmpwait_case_8 (ptr, val); +default :BUILD_BUG (); + } + +unreachable ();
Quoted 9 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 261
Quoted 4 lines
arch/arm64/include/asm/cmpxchg.h +4
-0+} + +#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))We might want to call this
cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make itcmpwait_acquireand remove thesmp_rmb()fromsmp_cond_load_acquire(). Dunno.
How about replace smp_rmb() with a smp_acquire_barrier__after_cmpwait()? This barrier is designed to provide an ACQUIRE ordering when combining a cmpwait() .
And cmpwait() only has minimal ordering guarantee, but if it is actually an ACQUIRE, then the corresponding smp_acquire_barrier__after_cmpwait() is just empty.
We might need this special barrier on ppc, because we can implement it with "isync" given that cmpwait() has control dependency and ctrl+isync is ACQUIRE on ppc.
Thoughts?
Regards,
Boqun
Quoted 1 line
Will
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 23 lines · Boqun Feng
On Tue, Apr 12, 2016 at 05:59:41PM +0100, Will Deacon wrote:
[...]Quoted 11 lines
Quoted 11 lines
arch/arm64/include/asm/cmpxchg.h +11
-0+static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) +{ +switch (size) { +case 1 :return __cmpwait_case_1 (ptr, val); +case 2 :return __cmpwait_case_2 (ptr, val); +case 4 :return __cmpwait_case_4 (ptr, val); +case 8 :return __cmpwait_case_8 (ptr, val); +default :BUILD_BUG (); + } + +unreachable ();Quoted 9 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 261
Quoted 4 lines
arch/arm64/include/asm/cmpxchg.h +4
-0+} + +#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))We might want to call this
cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make itcmpwait_acquireand remove thesmp_rmb()fromsmp_cond_load_acquire(). Dunno.
Quoted 7 lines
How about replace
smp_rmb()with asmp_acquire_barrier__after_cmpwait()? This barrier is designed to provide an ACQUIRE ordering when combining acmpwait().And
cmpwait()only has minimal ordering guarantee, but if it is actually an ACQUIRE, then the correspondingsmp_acquire_barrier__after_cmpwait()is just empty.
Maybe, but that makes it difficult for me to use a load-acquire instruction for the ACQUIRE case.
Will
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 28 lines · Will Deacon
Quoted 27 lines · Boqun Feng
On Tue, Apr 12, 2016 at 05:59:41PM +0100, Will Deacon wrote:
[...]Quoted 11 lines
Quoted 11 lines
arch/arm64/include/asm/cmpxchg.h +11
-0+static inline void __cmpwait (volatile void *ptr,unsigned long val,int size) +{ +switch (size) { +case 1 :return __cmpwait_case_1 (ptr, val); +case 2 :return __cmpwait_case_2 (ptr, val); +case 4 :return __cmpwait_case_4 (ptr, val); +case 8 :return __cmpwait_case_8 (ptr, val); +default :BUILD_BUG (); + } + +unreachable ();Quoted 9 lines
arch/arm64/include/asm/cmpxchg.h __CMPXCHG_GEN(_mb) · line 261
Quoted 4 lines
arch/arm64/include/asm/cmpxchg.h +4
-0+} + +#define cmpwait (ptr, val) \ + __cmpwait((ptr), (unsigned long)(val), sizeof(*(ptr)))We might want to call this
cmpwait_relaxed, in case we decide to add fenced versions in the future. Or just make itcmpwait_acquireand remove thesmp_rmb()fromsmp_cond_load_acquire(). Dunno.How about replace
smp_rmb()with asmp_acquire_barrier__after_cmpwait()? This barrier is designed to provide an ACQUIRE ordering when combining acmpwait().
Quoted 7 lines
Quoted 3 lines
And
cmpwait()only has minimal ordering guarantee, but if it is actually an ACQUIRE, then the correspondingsmp_acquire_barrier__after_cmpwait()is just empty.Maybe, but that makes it difficult for me to use a load-acquire instruction for the ACQUIRE case.
You're right. I was missting that point. Please ignore this proposal.
So I think having a cmpwait_relaxed makes more sense for the people knowing they could only rely on the control dependency? Or we actually don't want to encourage this kind of people ;-)
Regards,
Boqun
Quoted 1 line
Will
Peter Zijlstra
maintainerRe: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
In reply to Boqun FengQuoted 3 lines · Boqun Feng
How about replace
smp_rmb()with asmp_acquire_barrier__after_cmpwait()? This barrier is designed to provide an ACQUIRE ordering when combining acmpwait().
That's a horrible name for a barrier :-)
Quoted 9 lines
And
cmpwait()only has minimal ordering guarantee, but if it is actually an ACQUIRE, then the correspondingsmp_acquire_barrier__after_cmpwait()is just empty.We might need this special barrier on ppc, because we can implement it with "isync" given that
cmpwait()has control dependency and ctrl+isync is ACQUIRE on ppc.Thoughts?
Provide a PPC specific smp_cond_load_acquire() using ISYNC ?
Re: [RFC][PATCH 3/3] locking,arm64: Introduce cmpwait()
Quoted 7 lines · Peter Zijlstra
Quoted 3 lines · Boqun Feng
How about replace
smp_rmb()with asmp_acquire_barrier__after_cmpwait()? This barrier is designed to provide an ACQUIRE ordering when combining acmpwait().That's a horrible name for a barrier :-)
Indeed, and having another special barrier is a pain.
Quoted 11 lines
Quoted 9 lines
And
cmpwait()only has minimal ordering guarantee, but if it is actually an ACQUIRE, then the correspondingsmp_acquire_barrier__after_cmpwait()is just empty.We might need this special barrier on ppc, because we can implement it with "isync" given that
cmpwait()has control dependency and ctrl+isync is ACQUIRE on ppc.Thoughts?
Provide a PPC specific
smp_cond_load_acquire()using ISYNC ?
That works, but I should do more investigation on that, I brought this up because ISYNC may be better than smp_rmb() in two cases, IIUC:
1. for old systems without LWSYNC, smp_rmb() is actually SYNC,
which is heavier than ISYNC.
2. for new systems, isync may be a little faster than lwsync,
according to Michael Ellerman: http://lkml.kernel.org/g/[email protected]
but I doubt these two cases will have any observed performance impact, so the current version of smp_cond_load_acquire() in your patch is fine, and we can switch to a specific one in the future we want to ;-)
Regards,
Boqun