rwsem: performance optimizations
Subsystems (5)
v6 cover rwsem: performance optimizations
In reply to a message not in the archiveWe fixed a missing file and fixed various style issues for version 6 of this patchset. We will like to have it merged if there are no objections.
In this patchset, we introduce two categories of optimizations to read write semaphore. The first four patches from Alex Shi reduce cache bouncing of the sem->count field by doing a pre-read of the sem->count and avoid cmpxchg if possible.
The last two patches introduce similar optimistic spinning logic as the mutex code for the writer lock acquisition of rwsem. This addresses the general 'mutexes out perform writer-rwsems' situations that has been seen in more than one case. Users now need not worry about performance issues when choosing between these two locking mechanisms.
Without these optimizations, Davidlohr Bueso saw a -8% regression to aim7's shared and high_systime workloads when he switched i_mmap_mutex to rwsem. Tests were on 8 socket 80 cores system. With the patchset, he got significant improvements to the aim7 suite instead of regressions:
alltests (+16.3%), custom (+20%), disk (+19.5%), high_systime (+7%), shared (+18.4%) and short (+6.3%).
Tim Chen also got a +5% improvements to exim mail server workload on a 40 core system.
Thanks to Ingo Molnar, Peter Hurley and Peter Zijlstra for reviewing this patchset.
Regards,
Tim Chen
Changelog:
v6:
1. Fix missing mcslock.h file.
2. Fix various code style issues.
v5:
1. Try optimistic spinning before we put the writer on the wait queue to avoid bottlenecking at wait queue. This provides 5% boost to exim workload and between 2% to 8% boost to aim7.
2. Put MCS locking code into its own mcslock.h file for better reuse between mutex.c and rwsem.c
3. Remove the configuration RWSEM_SPIN_ON_WRITE_OWNER and make the operations default per Ingo's suggestions.
v4:
1. Fixed a bug in task_struct definition in rwsem_can_spin_on_owner
2. Fix another typo for RWSEM_SPIN_ON_WRITE_OWNER config option
v3:
1. Added ACCESS_ONCE to sem->count access in rwsem_can_spin_on_owner.
2. Fix typo bug for RWSEM_SPIN_ON_WRITE_OWNER option in init/Kconfig
v2:
1. Reorganize changes to down_write_trylock and do_wake into 4 patches and fixed a bug referencing &sem->count when sem->count is intended.
2. Fix unsafe sem->owner de-reference in rwsem_can_spin_on_owner.
the option to be on for more seasoning but can be turned off should it be detrimental.
3. Various patch comments update
Alex Shi (4):
rwsem: check the lock before cpmxchg in down_write_trylock
rwsem: remove 'out' label in do_wake
rwsem: remove try_reader_grant label do_wake rwsem/wake: check lock before do atomic update
Tim Chen (2):
MCS Lock: Restructure the MCS lock defines and locking code into its
own file
rwsem: do optimistic spinning for writer lock acquisition
include/asm-generic/rwsem.h | 8 +- include/linux/mcslock.h | 58 +++++++++++ include/linux/rwsem.h | 6 +- kernel/mutex.c | 58 ++---------- kernel/rwsem.c | 19 ++++- lib/rwsem.c | 228 +++++++++++++++++++++++++++++++++++++----- 6 files changed, 292 insertions(+), 85 deletions(-) create mode 100644 include/linux/mcslock.h
Signature · 1 line
1.7.4.4
PATCH v6 1/6 rwsem: check the lock before cpmxchg in down_write_trylock
In reply to a message not in the archiveCmpxchg will cause the cacheline bouning when do the value checking, that cause scalability issue in a large machine (like a 80 core box).
So a lock pre-read can relief this contention.
--- include/asm-generic/rwsem.h | 8 ++++---- 1 files changed, 4 insertions(+), 4 deletions(-)
include/asm-generic/rwsem.h +4 -4
@@ -70,11 +70,11 @@ static inline void __down_write(struct rw_semaphore *sem)static inline int __down_write_trylock (struct rw_semaphore *sem) { -long tmp; +if (unlikely (sem->count !=RWSEM_UNLOCKED_VALUE )) +return 0 ; - tmp =cmpxchg (&sem->count,RWSEM_UNLOCKED_VALUE , -RWSEM_ACTIVE_WRITE_BIAS ); -return tmp ==RWSEM_UNLOCKED_VALUE ; +return cmpxchg (&sem->count,RWSEM_UNLOCKED_VALUE , +RWSEM_ACTIVE_WRITE_BIAS ) ==RWSEM_UNLOCKED_VALUE ; }/*
Signature · 9 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v6 2/6 rwsem: remove 'out' label in do_wake
In reply to a message not in the archiveThat make code simple and more readable.
--- lib/rwsem.c | 5 ++--- 1 files changed, 2 insertions(+), 3 deletions(-)
lib/rwsem.c +2 -3
@@ -75,7 +75,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)* will block as they will notice the queued writer. */ wake_up_process (waiter->task); - goto out; +return sem; }/* Writers might steal the lock before we grant it to the next reader.
@@ -91,7 +91,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)/* A writer stole the lock. Undo our reader grant. */ if (rwsem_atomic_update (-adjustment, sem) &RWSEM_ACTIVE_MASK ) - goto out; +return sem;/* Last active locker left. Retry waking readers. */ goto try_reader_grant; }
@@ -136,7 +136,6 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type) sem->wait_list.next = next; next->prev = &sem->wait_list; - out:return sem; }
Signature · 9 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v6 3/6 rwsem: remove try_reader_grant label do_wake
In reply to a message not in the archiveThat make code simple and more readable
--- lib/rwsem.c | 12 +++++++----- 1 files changed, 7 insertions(+), 5 deletions(-)
lib/rwsem.c +7 -5
@@ -85,15 +85,17 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type) adjustment =0 ;if (wake_type !=RWSEM_WAKE_READ_OWNED ) { adjustment =RWSEM_ACTIVE_READ_BIAS ; - try_reader_grant: - oldcount =rwsem_atomic_update (adjustment, sem) - adjustment; -if (unlikely (oldcount <RWSEM_WAITING_BIAS )) { -/* A writer stole the lock. Undo our reader grant. */ +while (1 ) { + oldcount =rwsem_atomic_update (adjustment, sem) + - adjustment; +if (likely (oldcount >=RWSEM_WAITING_BIAS )) +break ; + +/* A writer stole the lock. Undo our reader grant. */ if (rwsem_atomic_update (-adjustment, sem) &RWSEM_ACTIVE_MASK )return sem;/* Last active locker left. Retry waking readers. */ - goto try_reader_grant; } }
Signature · 9 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v6 4/6 rwsem/wake: check lock before do atomic update
In reply to a message not in the archiveAtomic update lock and roll back will cause cache bouncing in large machine. A lock status pre-read can relieve this problem
--- lib/rwsem.c | 8 +++++++- 1 files changed, 7 insertions(+), 1 deletions(-)
lib/rwsem.c +7 -1
@@ -64,7 +64,7 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)struct rwsem_waiter *waiter;struct task_struct *tsk;struct list_head *next; -long oldcount, woken, loop, adjustment; +long woken, loop, adjustment; waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);if (waiter->type ==RWSEM_WAITING_FOR_WRITE ) {
@@ -86,6 +86,12 @@ __rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)if (wake_type !=RWSEM_WAKE_READ_OWNED ) { adjustment =RWSEM_ACTIVE_READ_BIAS ;while (1 ) { +long oldcount; + +/* A writer stole the lock. */ +if (unlikely (sem->count <RWSEM_WAITING_BIAS )) +return sem; + oldcount =rwsem_atomic_update (adjustment, sem) - adjustment;if (likely (oldcount >=RWSEM_WAITING_BIAS ))
Signature · 9 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v6 5/6 MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to a message not in the archiveWe will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from mutex.c and put into its own file allow us to reuse this code easily for rwsem.
--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.h
include/linux/mcslock.h new file +58 -0
@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* +* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb (); +} + +#endif
kernel/mutex.c +7 -51
95 lines not on this page · raw message
Signature · 9 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
PATCH v6 6/6 rwsem: do optimistic spinning for writer lock acquisition
In reply to a message not in the archiveWe want to add optimistic spinning to rwsems because
the writer rwsem does not perform as well as mutexes. Tim noticed that for exim (mail server) workloads, when reverting commit 4fc3f1d6 and Davidlohr noticed it when converting the i_mmap_mutex to a rwsem in some aim7 workloads. We've noticed that the biggest difference
is when we fail to acquire a mutex in the fastpath, optimistic spinning comes in to play and we can avoid a large amount of unnecessary sleeping and overhead of moving tasks in and out of wait queue.
Allowing optimistic spinning before putting the writer on the wait queue reduces wait queue contention and provided greater chance for the rwsem to get acquired. With these changes, rwsem is on par with mutex.
--- include/linux/rwsem.h | 6 +- kernel/rwsem.c | 19 +++++- lib/rwsem.c | 203 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 207 insertions(+), 21 deletions(-)
include/linux/rwsem.h +5 -1
@@ -26,6 +26,8 @@ struct rw_semaphore {long count;raw_spinlock_t wait_lock;struct list_head wait_list; +struct task_struct *owner;/* write owner */ +void *spin_mlock;#ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map;#endif
@@ -58,7 +60,9 @@ static inline int rwsem_is_locked(struct rw_semaphore *sem)#define __RWSEM_INITIALIZER (name) \ { RWSEM_UNLOCKED_VALUE, \ __RAW_SPIN_LOCK_UNLOCKED(name.wait_lock), \ - LIST_HEAD_INIT((name).wait_list) \ + LIST_HEAD_INIT((name).wait_list), \ + NULL, \ + NULL \ __RWSEM_DEP_MAP_INIT(name) }#define DECLARE_RWSEM (name) \
kernel/rwsem.c +18 -1
@@ -12,6 +12,16 @@#include <linux/atomic.h> +static inline void rwsem_set_owner (struct rw_semaphore *sem) +{ + sem->owner = current; +} + +static inline void rwsem_clear_owner (struct rw_semaphore *sem) +{ + sem->owner =NULL ; +} +/* * lock for reading */
@@ -48,6 +58,7 @@ void __sched down_write(struct rw_semaphore *sem)rwsem_acquire (&sem->dep_map,0 ,0 , _RET_IP_);LOCK_CONTENDED (sem, __down_write_trylock, __down_write); +rwsem_set_owner (sem); }EXPORT_SYMBOL (down_write);
@@ -59,8 +70,10 @@ int down_write_trylock(struct rw_semaphore *sem) {int ret =__down_write_trylock (sem); -if (ret ==1 ) +if (ret ==1 ) {rwsem_acquire (&sem->dep_map,0 ,1 , _RET_IP_); +rwsem_set_owner (sem); + }return ret; }
@@ -86,6 +99,7 @@ void up_write(struct rw_semaphore *sem)rwsem_release (&sem->dep_map,1 , _RET_IP_);__up_write (sem); +rwsem_clear_owner (sem); }EXPORT_SYMBOL (up_write);
@@ -100,6 +114,7 @@ void downgrade_write(struct rw_semaphore *sem)* dependency. */ __downgrade_write (sem); +rwsem_clear_owner (sem); }EXPORT_SYMBOL (downgrade_write);
@@ -122,6 +137,7 @@ void _down_write_nest_lock(struct rw_semaphore *sem, struct lockdep_map *nest)rwsem_acquire_nest (&sem->dep_map,0 ,0 , nest, _RET_IP_);LOCK_CONTENDED (sem, __down_write_trylock, __down_write); +rwsem_set_owner (sem); }EXPORT_SYMBOL (_down_write_nest_lock);
@@ -141,6 +157,7 @@ void down_write_nested(struct rw_semaphore *sem, int subclass)rwsem_acquire (&sem->dep_map, subclass,0 , _RET_IP_);LOCK_CONTENDED (sem, __down_write_trylock, __down_write); +rwsem_set_owner (sem); }EXPORT_SYMBOL (down_write_nested);
lib/rwsem.c +184 -19
249 lines not on this page · raw message
Signature · 8 lines
1.7.4.4 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 18 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +0
-0
include/linux/mcslock.h line 4
Quoted 4 lines
include/linux/mcslock.h new file +4
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock.
A (very) short blurb about what an MCS lock is would be nice here.
include/linux/mcslock.h line 12
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +};
The vertical alignment looks broken here.
Quoted 36 lines
include/linux/mcslock.h new file +36
-0+ +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* +* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node)
include/linux/mcslock.h line 58
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) + arch_mutex_cpu_relax(); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb (); +} + +#endif
We typically close header guards not via a plain #endif but like this:
#endif /* __LINUX_SPINLOCK_H */
#endif /* __LINUX_SPINLOCK_TYPES_H */
etc.
Thanks,
Ingo
Re: [PATCH v6 6/6] rwsem: do optimistic spinning for writer lock acquisition
In reply to Tim ChenQuoted 34 lines · Tim Chen
We want to add optimistic spinning to rwsems because
the writer rwsem does not perform as well as mutexes. Tim noticed that for exim (mail server) workloads, when reverting commit4fc3f1d6and Davidlohr noticed it when converting thei_mmap_mutexto a rwsem in some aim7 workloads. We've noticed that the biggest difference
is when we fail to acquire a mutex in the fastpath, optimistic spinning comes in to play and we can avoid a large amount of unnecessary sleeping and overhead of moving tasks in and out of wait queue.Allowing optimistic spinning before putting the writer on the wait queue reduces wait queue contention and provided greater chance for the rwsem to get acquired. With these changes, rwsem is on par with mutex.
Reviewed-by Ingo Molnar <[email protected]>Reviewed-by Peter Zijlstra <[email protected]>Reviewed-by Peter Hurley <[email protected]>Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/rwsem.h | 6 +- kernel/rwsem.c | 19 +++++- lib/rwsem.c | 203 ++++++++++++++++++++++++++++++++++++++++++++----- 3 files changed, 207 insertions(+), 21 deletions(-)include/linux/rwsem.h +2
-0@@ -26,6 +26,8 @@ struct rw_semaphore {long count;raw_spinlock_t wait_lock;struct list_head wait_list; +struct task_struct *owner;/* write owner */ +void *spin_mlock;
include/linux/rwsem.h struct rw_semaphore {
Quoted 10 lines
include/linux/rwsem.h +5
-0+#define MLOCK (rwsem) ((struct mcs_spin_node **)&((rwsem)->spin_mlock)) +mcs_spin_lock (MLOCK (sem), &node); +mcs_spin_unlock (MLOCK (sem), &node); +mcs_spin_unlock (MLOCK (sem), &node); +mcs_spin_unlock (MLOCK (sem), &node);
That forced type casting is ugly and fragile.
To avoid having to include mcslock.h into rwsem.h just add a forward struct declaration, before the struct rw_semaphore definition:
struct mcs_spin_node;
Then define spin_mlock with the right type:
struct mcs_spin_node *spin_mlock;
I'd also suggest renaming 'spin_mlock', to reduce unnecessary variants. If the lock type name is 'struct mcs_spin_node' then 'mcs_lock' would be a perfect field name, right?
While at it, renaming mcs_spin_node to mcs_spinlock might be wise as well, and the include file would be named mcs_spinlock.h.
Thanks,
Ingo
Peter Zijlstra
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Ingo MolnarQuoted 6 lines · Ingo Molnar
Quoted 4 lines
+/*
+ * MCS lock defines
+ *
+ * This file contains the main data structure and API definitions of MCS lock.A (very) short blurb about what an MCS lock is would be nice here.
A while back I suggested including a link to something like:
http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
Its a fairly concise write-up of the idea; only 6 pages. The sad part about linking to the web is that links tend to go dead after a while.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ingo Molnar
Quoted 4 lines
+/*
+ * MCS lock defines
+ *
+ * This file contains the main data structure and API definitions of MCS lock.
Quoted 8 lines
Quoted 1 line
A (very) short blurb about what an MCS lock is would be nice here.
A while back I suggested including a link to something like:
http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
Its a fairly concise write-up of the idea; only 6 pages. The sad part about linking to the web is that links tend to go dead after a while.
So what I wanted to see was to add just a few sentences summing up the concept - so that people blundering into this file in include/linux/ have an idea what it's all about!
Thanks,
Ingo
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Peter ZijlstraQuoted 6 lines · Peter Zijlstra
Quoted 5 lines · Ingo Molnar
Quoted 4 lines
+/*
+ * MCS lock defines
+ *
+ * This file contains the main data structure and API definitions of MCS lock.
Quoted 8 lines
Quoted 1 line
A (very) short blurb about what an MCS lock is would be nice here.
A while back I suggested including a link to something like:
http://www.cise.ufl.edu/tr/DOC/REP-1992-71.pdf
Its a fairly concise write-up of the idea; only 6 pages. The sad part about linking to the web is that links tend to go dead after a while.
Link rot is a problem. If I provide a few details about MCS lock I think people should be able to google for it.
Tim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{
include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;
If we don't spin on the local node, is it necessary to set this variable?
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +}
include/linux/mcslock.h line 48
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+ +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* +* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node)
And can we make this check likely()?
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 43 lines · Jason Low
Quoted 42 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +24
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ;
Quoted 8 lines
include/linux/mcslock.h line 30
Quoted 6 lines
include/linux/mcslock.h new file +6
-0+ node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
I don't follow, the whole idea is to spin on the local variable.
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 53 lines · Davidlohr Bueso
Quoted 43 lines · Jason Low
Quoted 42 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +24
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ;Quoted 8 lines
include/linux/mcslock.h line 30
Quoted 6 lines
include/linux/mcslock.h new file +6
-0+ node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set node->locked = 1.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.
Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();
The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
Thanks,
Davidlohr
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 66 lines · Davidlohr Bueso
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
Quoted 6 lines
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
hmm, others threads acquiring the lock will be spinning on their own local nodes, not this node's node->locked. And if prev == NULL, the current thread won't be reading it's node->lock either since we return. So what other thread is going to be reading this node's node->lock?
Thanks,
Jason
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 74 lines · Jason Low
Quoted 66 lines · Davidlohr Bueso
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
Quoted 6 lines
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
Quoted 7 lines
hmm, others threads acquiring the lock will be spinning on their own local nodes, not this node's
node->locked. And if prev == NULL, the current thread won't be reading it'snode->lockeither since we return. So what other thread is going to be reading this node'snode->lock?Thanks,
Jason
I think setting node->locked = 1 for the prev==NULL case is not necessary functionally, but was done for semantics consistency.
Tim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 71 lines · Jason Low
Quoted 70 lines · Davidlohr Bueso
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();
Quoted 5 lines
Quoted 2 lines
The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
hmm, others threads acquiring the lock will be spinning on their own local nodes, not this node's
node->locked.
But we're xchg'ing memory locations, not values - so after the xchg() call, when we modify node->locked, we're also changing lock, which isn't local.
Quoted 11 lines
And if prev == NULL, the
current thread won't be reading it'snode->lockeither since we return. So what other thread is going to be reading this node'snode->lock?Thanks,
Jason
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 80 lines · Tim Chen
Quoted 79 lines · Jason Low
Quoted 66 lines · Davidlohr Bueso
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
Quoted 6 lines
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
hmm, others threads acquiring the lock will be spinning on their own local nodes, not this node's
node->locked. And if prev == NULL, the current thread won't be reading it'snode->lockeither since we return. So what other thread is going to be reading this node'snode->lock?
Quoted 5 lines
Quoted 2 lines
Thanks,
JasonI think setting
node->locked= 1 for the prev==NULL case is not necessary functionally, but was done for semantics consistency.
Okay, that would makes sense for consistency because we always first set node->lock = 0 at the top of the function.
If we prefer to optimize this a bit though, perhaps we can first move the node->lock = 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock = 1" inside the code block.
static noinline
void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node)
{
struct mcs_spin_node *prev;
/* Init node */
node->next = NULL;
prev = xchg(lock, node);
if (likely(prev == NULL)) {
/* Lock acquired */
return;
}
node->locked = 0;
ACCESS_ONCE(prev->next) = node;
smp_wmb();
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
}Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 87 lines · Jason Low
Quoted 80 lines · Tim Chen
Quoted 79 lines · Jason Low
Quoted 66 lines · Davidlohr Bueso
Quoted 54 lines · Jason Low
Quoted 53 lines · Davidlohr Bueso
Quoted 51 lines · Jason Low
Quoted 38 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +20
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{include/linux/mcslock.h line 30
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ;If we don't spin on the local node, is it necessary to set this variable?
Quoted 6 lines
Quoted 1 line
I don't follow, the whole idea is to spin on the local variable.
If prev == NULL, doesn't that mean it won't proceed to spin on the variable because the lock is already free and we call return? In that case where we directly acquire the lock, I was wondering if it is necessary to set
node->locked= 1.Yes, that's true, but we need to flag the lock as acquired (the node's lock is initially set to unlocked), otherwise others trying to acquire the lock can spin forever:
Quoted 6 lines
/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax();The ->locked variable in this implementation refers to if the lock is acquired, and *not* to if busy-waiting is necessary.
hmm, others threads acquiring the lock will be spinning on their own local nodes, not this node's
node->locked. And if prev == NULL, the current thread won't be reading it'snode->lockeither since we return. So what other thread is going to be reading this node'snode->lock?Quoted 5 lines
Quoted 2 lines
Thanks,
JasonI think setting
node->locked= 1 for the prev==NULL case is not necessary functionally, but was done for semantics consistency.
Quoted 7 lines
Okay, that would makes sense for consistency because we always first set
node->lock= 0 at the top of the function.If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.
I suppose we can save one single assignment.
The gain is probably not noticeable as once we set node->next to NULL, node->locked is likely in local cache line and the assignment operation is cheap.
Regards,
Tim
Quoted 13 lines
static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ return;
Quoted 9 lines
}
node->locked = 0;
ACCESS_ONCE(prev->next) = node;
smp_wmb();
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 8 lines · Tim Chen
Quoted 4 lines
If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.I suppose we can save one single assignment. The gain is probably not noticeable as once we set
node->nextto NULL,node->lockedis likely in local cache line and the assignment operation is cheap.
Would be nice to have this as a separate, add-on patch. Every single instruction removal that has no downside is an upside!
You can add a comment that explains it.
Thanks,
Ingo
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 7 lines · Ingo Molnar
Quoted 5 lines · Tim Chen
Quoted 4 lines
If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.
Quoted 8 lines
Quoted 3 lines
I suppose we can save one single assignment. The gain is probably not noticeable as once we set
node->nextto NULL,node->lockedis likely in local cache line and the assignment operation is cheap.Would be nice to have this as a separate, add-on patch. Every single instruction removal that has no downside is an upside!
You can add a comment that explains it.
Yup, especially a spin lock (and one that I have found to be be used very frequently when running workloads on big machines).
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Peter Zijlstra
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Ingo MolnarQuoted 4 lines · Ingo Molnar
Would be nice to have this as a separate, add-on patch. Every single instruction removal that has no downside is an upside!
You can add a comment that explains it.
If someone is going to do add-on patches to the mcslock.h file, please also consider doing a patch that adds comments to the memory barriers in there.
Also, checkpatch.pl should really warn about that; and it appears there code in there for that; however:
# grep -C3 smp_mb scripts/checkpatch.pl
}
}
# check for memory barriers without a comment.
if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) {
if (!ctx_has_comment($first_line, $linenr)) {
CHK("MEMORY_BARRIER",
"memory barrier without comment\n" . $herecurr);
# grep -C3 smp_wmb kernel/mutex.c
return;
}
ACCESS_ONCE(prev->next) = node;
smp_wmb();
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
--
arch_mutex_cpu_relax();
}
ACCESS_ONCE(next->locked) = 1;
smp_wmb();
}/*
# scripts/checkpatch.pl -f kernel/mutex.c 2>&1 | grep memory #
so that appears to be completely broken :/
Joe, any clue what's up with that?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Joe Perches
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 4 lines · Peter Zijlstra
checkpatch.pl should really warn about that; and it appears there code in there for that; however:
# grep -C3 smp_mb scripts/checkpatch.pl
[]
Quoted 5 lines
# check for memory barriers without a comment.
if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) {
if (!ctx_has_comment($first_line, $linenr)) {
CHK("MEMORY_BARRIER",
"memory barrier without comment\n" . $herecurr);[]
Quoted 6 lines
# scripts/checkpatch.pl -f kernel/mutex.c 2>&1 | grep memory #so that appears to be completely broken :/
Joe, any clue what's up with that?
It's a CHK test, so it's only tested with --strict
$ scripts/checkpatch.pl -f --strict kernel/mutex.c 2>&1 | grep memory CHECK: memory barrier without comment CHECK: memory barrier without comment
It could be changed to WARN so it's always on.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Peter Zijlstra
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 7 lines · Joe Perches
It's a CHK test, so it's only tested with --strict
$ scripts/checkpatch.pl -f --strict kernel/mutex.c 2>&1 | grep memory CHECK: memory barrier without comment CHECK: memory barrier without commentIt could be changed to WARN so it's always on.
Yes please, we can't be too careful with memory barriers.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Joe Perches
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 10 lines · Peter Zijlstra
Quoted 7 lines · Joe Perches
It's a CHK test, so it's only tested with --strict
$ scripts/checkpatch.pl -f --strict kernel/mutex.c 2>&1 | grep memory CHECK: memory barrier without comment CHECK: memory barrier without commentIt could be changed to WARN so it's always on.
Yes please, we can't be too careful with memory barriers.
I'll send the patch separately.
It seems a pretty noisy test.
There are 13 hits just in arch/x86/kernel/
$ ./scripts/checkpatch.pl -f arch/x86/kernel/*.c | grep -A3 "memory barrier" WARNING: memory barrier without comment #685: FILE: x86/kernel/alternative.c:685: + smp_wmb();
--
WARNING: memory barrier without comment
#401: FILE: x86/kernel/kvm.c:401:
+ rmb();
WARNING: memory barrier without comment
#403: FILE: x86/kernel/kvm.c:403:
+ rmb();
--
WARNING: memory barrier without comment
#702: FILE: x86/kernel/kvm.c:702:
+ smp_wmb();
WARNING: memory barrier without comment
#704: FILE: x86/kernel/kvm.c:704:
+ smp_wmb();
--
WARNING: memory barrier without comment
#62: FILE: x86/kernel/ldt.c:62:
+ wmb();
WARNING: memory barrier without comment
#64: FILE: x86/kernel/ldt.c:64:
+ wmb();
--
WARNING: memory barrier without comment
#204: FILE: x86/kernel/smpboot.c:204:
+ wmb();
WARNING: memory barrier without comment
#265: FILE: x86/kernel/smpboot.c:265:
+ wmb();
--
WARNING: memory barrier without comment
#557: FILE: x86/kernel/smpboot.c:557:
+ mb();
--
WARNING: memory barrier without comment
#1065: FILE: x86/kernel/smpboot.c:1065:
+ mb();
--
WARNING: memory barrier without comment
#1321: FILE: x86/kernel/smpboot.c:1321:
+ mb();
WARNING: memory barrier without comment
#1399: FILE: x86/kernel/smpboot.c:1399:
+ mb();
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Joe Perches
maintainerPATCH 1/1 checkpatch: Make the memory barrier test noisier
In reply to Peter ZijlstraPeter Zijlstra prefers that comments be required near uses of memory barriers.
Change the message level for memory barrier uses from a --strict test only to a normal WARN so it's always emitted.
This might produce false positives around insertions of memory barriers when a comment is outside the patch context block.
And checkpatch is still stupid, it only looks for existence of any comment, not at the comment content.
--- scripts/checkpatch.pl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
scripts/checkpatch.pl +2 -2
@@ -3816,8 +3816,8 @@ sub string_find_replace { # check for memory barriers without a comment. if ($line =~ /\b(mb|rmb|wmb|read_barrier_depends|smp_mb|smp_rmb|smp_wmb|smp_read_barrier_depends)\(/) { if (!ctx_has_comment($first_line, $linenr)) { - CHK("MEMORY_BARRIER", - "memory barrier without comment\n" . $herecurr); + WARN("MEMORY_BARRIER", + "memory barrier without comment\n" . $herecurr); } } # check of hardware specific defines
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Peter Zijlstra
maintainerRe: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Joe PerchesQuoted 8 lines · Joe Perches
Quoted 7 lines · Peter Zijlstra
Quoted 6 lines · Joe Perches
It's a CHK test, so it's only tested with --strict
$ scripts/checkpatch.pl -f --strict kernel/mutex.c 2>&1 | grep memory CHECK: memory barrier without comment CHECK: memory barrier without comment
Quoted 8 lines
Quoted 3 lines
Quoted 1 line
It could be changed to WARN so it's always on.
Yes please, we can't be too careful with memory barriers.
I'll send the patch separately.
It seems a pretty noisy test.
There are 13 hits just inarch/x86/kernel/
Urgh. that wants fixing. We really need to stop getting more and that's where checkpatch is good at.
At very very bare minimum the comment should mention where the pairing barrier is; but ideally it should describe the actual ordering.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH] checkpatch: Make the memory barrier test noisier
In reply to Joe PerchesQuoted 9 lines · Joe Perches
Peter Zijlstra prefers that comments be required near uses of memory barriers.
Change the message level for memory barrier uses from a --strict test only to a normal WARN so it's always emitted.
This might produce false positives around insertions of memory barriers when a comment is outside the patch context block.
One would argue that in that case they're too far away in any case :-)
Quoted 2 lines
And checkpatch is still stupid, it only looks for existence of any comment, not at the comment content.
Could we try and alleviate this by giving a slightly more verbose warning?
Maybe something like:
memory barrier without comment; please refer to the pairing barrier and describe the ordering requirements.
Quoted 2 lines
Suggested-by Peter Zijlstra <[email protected]>Signed-off-by Joe Perches <[email protected]>
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Joe Perches
maintainerRe: [PATCH] checkpatch: Make the memory barrier test noisier
Quoted 16 lines · Peter Zijlstra
Quoted 9 lines · Joe Perches
Peter Zijlstra prefers that comments be required near uses of memory barriers.
Change the message level for memory barrier uses from a --strict test only to a normal WARN so it's always emitted.
This might produce false positives around insertions of memory barriers when a comment is outside the patch context block.
One would argue that in that case they're too far away in any case :-)
Quoted 2 lines
And checkpatch is still stupid, it only looks for existence of any comment, not at the comment content.
Quoted 7 lines
Could we try and alleviate this by giving a slightly more verbose warning?
Maybe something like:
memory barrier without comment; please refer to the pairing barrier and describe the ordering requirements.
That would make it seem as if all barriers are SMP no?
Maybe just refer to Documentation/memory-barriers.txt and/or say something like "please document appropriately"
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Peter Zijlstra
maintainerRe: [PATCH] checkpatch: Make the memory barrier test noisier
Quoted 1 line · Joe Perches
That would make it seem as if all barriers are SMP no?
I would think any memory barrier is ordering against someone else; if not smp then a device/hardware -- like for instance the hardware page table walker.
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
Quoted 2 lines
Maybe just refer to
Documentation/memory-barriers.txtand/or say something like "please document appropriately"
Documentation/memory-barriers.txt is always good; appropriately doesn't seem to quantify anything much at all. Someone might think:
/* */smp_mb();
appropriate...
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Paul E. McKenney
Acked-byRe: [PATCH] checkpatch: Make the memory barrier test noisier
Quoted 9 lines · Peter Zijlstra
Quoted 1 line · Joe Perches
That would make it seem as if all barriers are SMP no?
I would think any memory barrier is ordering against someone else; if not smp then a device/hardware -- like for instance the hardware page table walker.
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
Oddly enough, there is one exception that proves the rule... On Itanium, suppose we have the following code, with x initially equal to zero:
CPU 1: ACCESS_ONCE(x) = 1;
CPU 2: r1 = ACCESS_ONCE(x); r2 = ACCESS_ONCE(x);
Itanium architects have told me that it really is possible for CPU 2 to see r1==1 and r2==0. Placing a memory barrier between CPU 2's pair of fetches prevents this, but without any other memory barrier to pair with.
Quoted 10 lines
Quoted 2 lines
Maybe just refer to
Documentation/memory-barriers.txtand/or say something like "please document appropriately"
Documentation/memory-barriers.txtis always good; appropriately doesn't seem to quantify anything much at all. Someone might think:/* */
smp_mb();appropriate...
I end up doing this:
/* */smp_mb(); /* See above block comment. */
But it would be nice for the prior comment to be recognized as belonging to the memory barrier without the additional "See above" comment.
In any case, please feel free to add:
to the original checkpatch.pl patch.
Thanx, Paul
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 63 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +45
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/*
include/linux/mcslock.h line 55
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb ();
Shouldn't the memory barrier precede the "ACCESS_ONCE(next->locked) = 1;"? Maybe in an "else" clause of the prior "if" statement, given that the cmpxchg() does it otherwise.
Otherwise, in the case where the "if" conditionn is false, the critical section could bleed out past the unlock.
Thanx, Paul
Quoted 117 lines
include/linux/mcslock.h new file +3
-0+} + +#endif kernel/mutex.c +7
-51@@ -25,6 +25,7 @@#include <linux/spinlock.h> #include <linux/interrupt.h> #include <linux/debug_locks.h> +#include <linux/mcslock.h> /* * In the DEBUG case we are using the "NULL fastpath" for mutexes, @@ -111,54 +112,9 @@ EXPORT_SYMBOL(mutex_lock);* more or less simultaneously, the spinners need to acquire a MCS lock * first before spinning on the owner field. * -* We don't inline mspin_lock() so that perf can correctly account for the -* time spent in this lock function. */ -struct mspin_node { -struct mspin_node *next ; -int locked;/* 1 if lock acquired */ -}; -#define MLOCK (mutex) ((struct mspin_node **)&((mutex)->spin_mlock)) -static noinline -void mspin_lock(struct mspin_node **lock, struct mspin_node *node) -{ -struct mspin_node *prev; - -/* Init node */ - node->locked =0 ; - node->next =NULL ; - - prev =xchg (lock, node); -if (likely (prev ==NULL )) { -/* Lock acquired */ - node->locked =1 ; -return ; - } -ACCESS_ONCE (prev->next) = node; -smp_wmb (); -/* Wait until the lock holder passes the lock down */ -while (!ACCESS_ONCE (node->locked)) -arch_mutex_cpu_relax (); -} - -static void mspin_unlock (struct mspin_node **lock,struct mspin_node *node) -{ -struct mspin_node *next =ACCESS_ONCE (node->next); - -if (likely (!next)) { -/* -* Release the lock by setting it to NULL -*/ -if (cmpxchg (lock, node,NULL ) == node) -return ; -/* Wait until the next pointer is set */ -while (!(next =ACCESS_ONCE (node->next))) -arch_mutex_cpu_relax (); - } -ACCESS_ONCE (next->locked) =1 ; -smp_wmb (); -} +#define MLOCK (mutex) ((struct mcs_spin_node **)&((mutex)->spin_mlock))/* * Mutex spinning code migrated from kernel/sched/core.c @@ -448,7 +404,7 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,for (;;) {struct task_struct *owner; -struct mspin_node node; +struct mcs_spin_node node;if (!__builtin_constant_p (ww_ctx ==NULL ) && ww_ctx->acquired >0 ) {struct ww_mutex *ww;@@ -470,10 +426,10 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass,* If there's an owner, wait for it to either * release the lock or go to sleep. */ -mspin_lock (MLOCK (lock), &node); +mcs_spin_lock (MLOCK (lock), &node); owner =ACCESS_ONCE (lock->owner);if (owner && !mutex_spin_on_owner (lock, owner)) { -mspin_unlock (MLOCK (lock), &node); +mcs_spin_unlock (MLOCK (lock), &node); goto slowpath; }@@ -488,11 +444,11 @@ __mutex_lock_common(struct mutex *lock, long state, unsigned int subclass, }mutex_set_owner (lock); -mspin_unlock (MLOCK (lock), &node); +mcs_spin_unlock (MLOCK (lock), &node);preempt_enable ();return 0 ; } -mspin_unlock (MLOCK (lock), &node); +mcs_spin_unlock (MLOCK (lock), &node);/* * When there's no owner, we might have preempted between the Signature · 9 lines
1.7.4.4 -- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html Please read the FAQ at http://www.tux.org/lkml/
Peter Zijlstra
maintainerRe: [PATCH] checkpatch: Make the memory barrier test noisier
In reply to Paul E. McKenneyQuoted 6 lines · Paul E. McKenney
Quoted 2 lines
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
Oddly enough, there is one exception that proves the rule... On Itanium, suppose we have the following code, with x initially equal to zero:
Quoted 7 lines
CPU 1:
ACCESS_ONCE(x)= 1;CPU 2: r1 =
ACCESS_ONCE(x); r2 =ACCESS_ONCE(x);Itanium architects have told me that it really is possible for CPU 2 to see r1==1 and r2==0. Placing a memory barrier between CPU 2's pair of fetches prevents this, but without any other memory barrier to pair with.
Oh man.. its really past time to sink that itanic already.
I suppose it allows the cpu to reorder the reads in its pipeline and the memory barrier disallows this. Curious.. does our memory-barriers.txt file mention this 'fun' fact?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH] checkpatch: Make the memory barrier test noisier
Quoted 15 lines · Peter Zijlstra
Quoted 6 lines · Paul E. McKenney
Quoted 2 lines
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
Oddly enough, there is one exception that proves the rule... On Itanium, suppose we have the following code, with x initially equal to zero:
Quoted 7 lines
CPU 1:
ACCESS_ONCE(x)= 1;CPU 2: r1 =
ACCESS_ONCE(x); r2 =ACCESS_ONCE(x);Itanium architects have told me that it really is possible for CPU 2 to see r1=1 and r2=0. Placing a memory barrier between CPU 2's pair of fetches prevents this, but without any other memory barrier to pair with.
Quoted 5 lines
Oh man.. its really past time to sink that itanic already.
I suppose it allows the cpu to reorder the reads in its pipeline and the memory barrier disallows this. Curious.. does our memory-barriers.txt file mention this 'fun' fact?
Probably not. I was recently reminded of it by some people on the C++ standards committee. I had first heard of it about 5 years ago, but hadn't heard definitively until quite recently.
I defer to the Itanium maintainers to actually make the required changes, should they choose to do so. I suppose that one way to handle it in the Linux kernel would be to make ACCESS_ONCE() be architecture specific, with Itanium placing a memory barrier either before or after --- either would work. But since Itanium seems to run Linux reliably, I am guessing that the probability of misordering is quite low. But again, the ball is firmly in the Itanium maintainers' courts, and I have added them on CC.
Thanx, Paul
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Ingo MolnarQuoted 2 lines · Ingo Molnar
Would be nice to have this as a separate, add-on patch. Every single instruction removal that has no downside is an upside!
Okay, so here is a patch. Tim, would you like to add this to v7?
...
Subject: MCS lock: Remove and reorder unnecessary assignments in mcs_spin_lock()
In mcs_spin_lock(), if (likely(prev == NULL)) is true, then the lock is free and we won't spin on the local node. In that case, we don't have to assign node->locked because it won't be used. We can also move the node->locked = 0 assignment so that it occurs after the if (likely(prev == NULL)) check.
This might also help make it clearer as to how the node->locked variable is used in MCS locks.
--- include/linux/mcslock.h | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)
include/linux/mcslock.h +1 -2
@@ -21,15 +21,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node)struct mcs_spin_node *prev;/* Init node */ - node->locked =0 ; node->next =NULL ; prev =xchg (lock, node);if (likely (prev ==NULL )) {/* Lock acquired */ - node->locked =1 ;return ; } + node->locked =0 ;ACCESS_ONCE (prev->next) = node;smp_wmb ();/* Wait until the lock holder passes the lock down */
Signature · 8 lines
1.7.1 -- To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM, see: http://www.linux-mm.org/ . Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 5 lines · Jason Low
Quoted 2 lines · Ingo Molnar
Would be nice to have this as a separate, add-on patch. Every single instruction removal that has no downside is an upside!
Okay, so here is a patch. Tim, would you like to add this to v7?
Okay. Will do.
Tim
Quoted 28 lines
...
Subject: MCS lock: Remove and reorder unnecessary assignments inmcs_spin_lock()In
mcs_spin_lock(), if (likely(prev == NULL)) is true, then the lock is free and we won't spin on the local node. In that case, we don't have to assignnode->lockedbecause it won't be used. We can also move thenode->locked= 0 assignment so that it occurs after the if (likely(prev == NULL)) check.This might also help make it clearer as to how the
node->lockedvariable is used in MCS locks.Signed-off-by Jason Low <[email protected]>--- include/linux/mcslock.h | 3 +-- 1 files changed, 1 insertions(+), 2 deletions(-)include/linux/mcslock.h +0
-1@@ -21,15 +21,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node)struct mcs_spin_node *prev;/* Init node */ - node->locked =0 ; node->next =NULL ;
include/linux/mcslock.h void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) · line 34
Quoted 10 lines
include/linux/mcslock.h +1
-1prev =xchg (lock, node);if (likely (prev ==NULL )) {/* Lock acquired */ - node->locked =1 ;return ; } + node->locked =0 ;ACCESS_ONCE (prev->next) = node;smp_wmb ();/* Wait until the lock holder passes the lock down */
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Paul E. McKenneyQuoted 79 lines · Paul E. McKenney
Quoted 63 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +45
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* include/linux/mcslock.h line 55
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb ();Shouldn't the memory barrier precede the "
ACCESS_ONCE(next->locked)= 1;"? Maybe in an "else" clause of the prior "if" statement, given that thecmpxchg()does it otherwise.
Quoted 2 lines
Otherwise, in the case where the "if" conditionn is false, the critical section could bleed out past the unlock.
Yes, I agree with you that the smp_wmb should be moved before ACCESS_ONCE to prevent critical section from bleeding. Copying Waiman who is the original author of the mcs code to see if he has any comments on things we may have missed.
Tim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Paul E. McKenneyQuoted 45 lines · Paul E. McKenney
Quoted 44 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +26
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; +
Quoted 8 lines
include/linux/mcslock.h line 34
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+ prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb ();
BTW, is the above memory barrier necessary? It seems like the xchg instruction already provided a memory barrier.
Now if we made the changes that Jason suggested:
/* Init node */
- node->locked = 0;
node->next = NULL;
prev = xchg(lock, node);
if (likely(prev == NULL)) {
/* Lock acquired */
- node->locked = 1;
return;
}
+ node->locked = 0;
ACCESS_ONCE(prev->next) = node;
smp_wmb();We are probably still okay as other cpus do not read the value of node->locked, which is a local variable.
Tim
Quoted 31 lines
Quoted 11 lines
include/linux/mcslock.h new file +11
-0+/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* include/linux/mcslock.h line 55
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb ();Shouldn't the memory barrier precede the "
ACCESS_ONCE(next->locked)= 1;"? Maybe in an "else" clause of the prior "if" statement, given that thecmpxchg()does it otherwise.Otherwise, in the case where the "if" conditionn is false, the critical section could bleed out past the unlock.
Thanx, Paul
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 20 lines · Tim Chen
BTW, is the above memory barrier necessary? It seems like the xchg instruction already provided a memory barrier.
Now if we made the changes that Jason suggested:
/* Init node */(quoted diff) +1
-2- node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ - node->locked = 1; return; } + node->locked = 0; ACCESS_ONCE(prev->next) = node; smp_wmb();
Quoted 2 lines
We are probably still okay as other cpus do not read the value of
node->locked, which is a local variable.
Similarly, I was wondering if we should also move smp_wmb() so that it is before the ACCESS_ONCE(prev->next) = node and after the node->locked = 0. Would we want to guarantee that the node->locked gets set before it is added to the linked list where a previous thread calling mcs_spin_unlock() would potentially modify node->locked?
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 75 lines · Tim Chen
Quoted 45 lines · Paul E. McKenney
Quoted 44 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +26
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; +Quoted 8 lines
include/linux/mcslock.h line 34
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+ prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb ();BTW, is the above memory barrier necessary? It seems like the xchg instruction already provided a memory barrier.
Now if we made the changes that Jason suggested:
/* Init node */(quoted diff) +1
-2- node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ - node->locked = 1; return; } + node->locked = 0; ACCESS_ONCE(prev->next) = node; smp_wmb();
Quoted 2 lines
We are probably still okay as other cpus do not read the value of
node->locked, which is a local variable.
I don't immediately see the need for the smp_wmb() in either case.
Quoted 5 lines
Tim
Quoted 3 lines
include/linux/mcslock.h line 37
Quoted 3 lines
include/linux/mcslock.h new file +3
-0+/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax ();
However, you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section.
Thanx, Paul
Quoted 23 lines
Quoted 23 lines
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* include/linux/mcslock.h line 55
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb ();Shouldn't the memory barrier precede the "
ACCESS_ONCE(next->locked)= 1;"? Maybe in an "else" clause of the prior "if" statement, given that thecmpxchg()does it otherwise.
Quoted 7 lines
Quoted 5 lines
Otherwise, in the case where the "if" conditionn is false, the critical section could bleed out past the unlock.
Thanx, Paul
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 76 lines · Paul E. McKenney
Quoted 75 lines · Tim Chen
Quoted 45 lines · Paul E. McKenney
Quoted 44 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +26
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; +Quoted 8 lines
include/linux/mcslock.h line 34
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+ prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb ();BTW, is the above memory barrier necessary? It seems like the xchg instruction already provided a memory barrier.
Now if we made the changes that Jason suggested:
/* Init node */(quoted diff) +1
-2- node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ - node->locked = 1; return; } + node->locked = 0; ACCESS_ONCE(prev->next) = node; smp_wmb();
Quoted 4 lines
Quoted 2 lines
We are probably still okay as other cpus do not read the value of
node->locked, which is a local variable.I don't immediately see the need for the
smp_wmb()in either case.
Thinking a bit more, the following could happen in Jason's initial patch proposal. In this case variable "prev" referenced by CPU1 points to "node" referenced by CPU2
CPU 1 (calling lock) CPU 2 (calling unlock) ACCESS_ONCE(prev->next) = node *next = ACCESS_ONCE(node->next); ACCESS_ONCE(next->locked) = 1; node->locked = 0;
Then we will be spinning forever on CPU1 as we overwrite the lock passed from CPU2 before we check it. The original code assign "node->locked = 0" before xchg does not have this issue. Doing the following change of moving smp_wmb immediately after node->locked assignment (suggested by Jason)
node->locked = 0; smp_wmb(); ACCESS_ONCE(prev->next) = node;
could avoid the problem, but will need closer scrutiny to see if there are other pitfalls if wmb happen before
ACCESS_ONCE(prev->next) = node;
Quoted 7 lines
Quoted 4 lines
Quoted 4 lines
include/linux/mcslock.h line 37
Quoted 4 lines
include/linux/mcslock.h new file +3
-0+/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax ();However, you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section.
Is it necessary to add a memory barrier after acquiring the lock if the previous lock holder execute smp_wmb before passing the lock?
Thanks.
Tim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 99 lines · Tim Chen
Quoted 76 lines · Paul E. McKenney
Quoted 75 lines · Tim Chen
Quoted 45 lines · Paul E. McKenney
Quoted 44 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen <[email protected]>Signed-off-by Davidlohr Bueso <[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-) create mode 100644 include/linux/mcslock.hinclude/linux/mcslock.h new file +26
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; +Quoted 8 lines
include/linux/mcslock.h line 34
Quoted 8 lines
include/linux/mcslock.h new file +8
-0+ prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb ();BTW, is the above memory barrier necessary? It seems like the xchg instruction already provided a memory barrier.
Now if we made the changes that Jason suggested:
/* Init node */(quoted diff) +1
-2- node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ - node->locked = 1; return; } + node->locked = 0; ACCESS_ONCE(prev->next) = node; smp_wmb();Quoted 4 lines
Quoted 2 lines
We are probably still okay as other cpus do not read the value of
node->locked, which is a local variable.I don't immediately see the need for the
smp_wmb()in either case.Thinking a bit more, the following could happen in Jason's initial patch proposal. In this case variable "prev" referenced by CPU1 points to "node" referenced by CPU2
CPU 1 (calling lock) CPU 2 (calling unlock) ACCESS_ONCE(prev->next) = node *next = ACCESS_ONCE(node->next); ACCESS_ONCE(next->locked) = 1; node->locked = 0;Then we will be spinning forever on CPU1 as we overwrite the lock passed from CPU2 before we check it. The original code assign "
node->locked= 0" before xchg does not have this issue. Doing the following change of movingsmp_wmbimmediately afternode->lockedassignment (suggested by Jason)
Quoted 8 lines
node->locked = 0; smp_wmb(); ACCESS_ONCE(prev->next) = node;could avoid the problem, but will need closer scrutiny to see if there are other pitfalls if wmb happen before
ACCESS_ONCE(prev->next) = node;
I could believe that an smp_wmb() might be needed before the "ACCESS_ONCE(prev->next) = node;", just not after.
Quoted 10 lines
Quoted 6 lines
Quoted 3 lines
Quoted 3 lines
include/linux/mcslock.h line 37
Quoted 3 lines
include/linux/mcslock.h new file +3
-0+/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax ();However, you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section.
Is it necessary to add a memory barrier after acquiring the lock if the previous lock holder execute
smp_wmbbefore passing the lock?
Yep. The previous lock holder's smp_wmb() won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede the node->locked check, which would be very bad.
Thanx, Paul
Re: [PATCH] checkpatch: Make the memory barrier test noisier
In reply to Peter ZijlstraQuoted 9 lines · Peter Zijlstra
Quoted 1 line · Joe Perches
That would make it seem as if all barriers are SMP no?
I would think any memory barrier is ordering against someone else; if not smp then a device/hardware -- like for instance the hardware page table walker.
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
But not necessarily more than 1 kind of parties. It is perfectly possible to have a barrier against other threads running the same function.
Regards
Oliver
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Paul E. McKenneyQuoted 4 lines · Paul E. McKenney
Yep. The previous lock holder's
smp_wmb()won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede thenode->lockedcheck, which would be very bad.
Paul, Tim, Longman,
How would you like the proposed changes below?
---
Subject: [PATCH] MCS: optimizations and barrier corrections
Delete the node->locked = 1 assignment if the lock is free as it won't be used.
Delete the smp_wmb() in mcs_spin_lock() and add a full memory barrier at the end of the mcs_spin_lock() function. As Paul McKenney suggested, "you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section." And in the mcs_spin_unlock(), move the memory barrier so that it is before the "ACCESS_ONCE(next->locked) = 1;".
--- include/linux/mcslock.h | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)
include/linux/mcslock.h +0 -0
@@ -26,15 +26,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock,
struct mcs_spin_node *node)
prev = xchg(lock, node);
if (likely(prev == NULL)) {
- /* Lock acquired */
- node->locked = 1;
+ /* Lock acquired. No need to set node->locked since it
won't be used */
return;
}
ACCESS_ONCE(prev->next) = node;
- smp_wmb();
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();+ smp_mb();
}
static void mcs_spin_unlock(struct mcs_spin_node **lock, structmcs_spin_node *node)
@@ -51,8 +50,8 @@ static void mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *n
while (!(next = ACCESS_ONCE(node->next)))
arch_mutex_cpu_relax();
}
- ACCESS_ONCE(next->locked) = 1;
smp_wmb();+ ACCESS_ONCE(next->locked) = 1;
}
#endif
Signature · 1 line
1.7.1
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 7 lines · Jason Low
Quoted 4 lines · Paul E. McKenney
Yep. The previous lock holder's
smp_wmb()won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede thenode->lockedcheck, which would be very bad.
Quoted 6 lines
Paul, Tim, Longman,
How would you like the proposed changes below?
---
Subject: [PATCH] MCS: optimizations and barrier corrections
We *really* need to comment those barriers - explicitly that is :)
Quoted 28 lines
Delete the
node->locked= 1 assignment if the lock is free as it won't be used.Delete the
smp_wmb()inmcs_spin_lock()and add a full memory barrier at the end of themcs_spin_lock()function. As Paul McKenney suggested, "you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section." And in themcs_spin_unlock(), move the memory barrier so that it is before the "ACCESS_ONCE(next->locked)= 1;".Signed-off-by Jason Low <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Tim Chen <[email protected]>--- include/linux/mcslock.h | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)include/linux/mcslock.h +1
-2@@ -26,15 +26,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) prev =xchg (lock, node);if (likely (prev ==NULL )) { -/* Lock acquired */ - node->locked =1 ; +/* Lock acquired. No need to set node->locked since it
Quoted 1 line
won't be used */
Then, we need to explain/comment then the relationship between this situation and the locked being set in mspin_unlock(), passing the lock holder down the list.
Quoted 23 lines
return; } ACCESS_ONCE(prev->next) = node;include/linux/mcslock.h +1
-1-smp_wmb ();/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE (node->locked))arch_mutex_cpu_relax (); +smp_mb (); } static void mcs_spin_unlock(struct mcs_spin_node **lock, struct
mcs_spin_node*node)include/linux/mcslock.h +1
-1@@ -51,8 +50,8 @@ static void mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *nwhile (!(next =ACCESS_ONCE (node->next)))arch_mutex_cpu_relax (); } -ACCESS_ONCE (next->locked) =1 ;smp_wmb (); +ACCESS_ONCE (next->locked) =1 ; }#endif
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 10 lines · Jason Low
Quoted 4 lines · Paul E. McKenney
Yep. The previous lock holder's
smp_wmb()won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede thenode->lockedcheck, which would be very bad.Paul, Tim, Longman,
How would you like the proposed changes below?
Could you point me at what this applies to? I can find flaws looking at random pieces, given a little luck, but at some point I need to look at the whole thing. ;-)
Thanx, Paul
Quoted 57 lines
---
Subject: [PATCH] MCS: optimizations and barrier correctionsDelete the
node->locked= 1 assignment if the lock is free as it won't be used.Delete the
smp_wmb()inmcs_spin_lock()and add a full memory barrier at the end of themcs_spin_lock()function. As Paul McKenney suggested, "you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section." And in themcs_spin_unlock(), move the memory barrier so that it is before the "ACCESS_ONCE(next->locked)= 1;".Signed-off-by Jason Low <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Tim Chen <[email protected]>--- include/linux/mcslock.h | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)include/linux/mcslock.h +1
-2@@ -26,15 +26,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) prev =xchg (lock, node);if (likely (prev ==NULL )) { -/* Lock acquired */ - node->locked =1 ; +/* Lock acquired. No need to set node->locked since it won't be used */
return; } ACCESS_ONCE(prev->next) = node;include/linux/mcslock.h +1
-1-smp_wmb ();/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE (node->locked))arch_mutex_cpu_relax (); +smp_mb (); } static void mcs_spin_unlock(struct mcs_spin_node **lock, struct
mcs_spin_node*node)include/linux/mcslock.h +1
-1@@ -51,8 +50,8 @@ static void mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *nwhile (!(next =ACCESS_ONCE (node->next)))arch_mutex_cpu_relax (); } -ACCESS_ONCE (next->locked) =1 ;smp_wmb (); +ACCESS_ONCE (next->locked) =1 ; }#endif
Signature · 1 line
1.7.1
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 79 lines · Tim Chen
Quoted 78 lines · Paul E. McKenney
Quoted 63 lines · Tim Chen
We will need the MCS lock code for doing optimistic spinning for rwsem. Extracting the MCS code from
mutex.cand put into its own file allow us to reuse this code easily for rwsem.Signed-off-by Tim Chen<[email protected]>Signed-off-by Davidlohr Bueso<[email protected]>--- include/linux/mcslock.h | 58 +++++++++++++++++++++++++++++++++++++++++++++++ kernel/mutex.c | 58 +++++----------------------------------------- 2 files changed, 65 insertions(+), 51 deletions(-)create mode 100644
include/linux/mcslock.hinclude/linux/mcslock.h new file +45
-0@@ -0,0 +1,58 @@ +/* +* MCS lock defines +* +* This file contains the main data structure and API definitions of MCS lock. +*/ +#ifndef __LINUX_MCSLOCK_H +#define __LINUX_MCSLOCK_H + +struct mcs_spin_node { +struct mcs_spin_node *next; +int locked;/* 1 if lock acquired */ +}; + +/* +* We don't inline mcs_spin_lock() so that perf can correctly account for the +* time spent in this lock function. +*/ +static noinline +void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) +{ +struct mcs_spin_node *prev; + +/* Init node */ + node->locked =0 ; + node->next =NULL ; + + prev =xchg (lock, node); +if (likely (prev ==NULL )) { +/* Lock acquired */ + node->locked =1 ; +return ; + } +ACCESS_ONCE (prev->next) = node; +smp_wmb (); +/* Wait until the lock holder passes the lock down */ +while (!ACCESS_ONCE (node->locked)) +arch_mutex_cpu_relax (); +} + +static void mcs_spin_unlock (struct mcs_spin_node **lock,struct mcs_spin_node *node) +{ +struct mcs_spin_node *next =ACCESS_ONCE (node->next); + +if (likely (!next)) { +/* include/linux/mcslock.h line 55
Quoted 10 lines
include/linux/mcslock.h new file +10
-0+* Release the lock by setting it to NULL +*/ +if (cmpxchg (lock, node,NULL ) == node) +return ; +/* Wait until the next pointer is set */ +while (!(next =ACCESS_ONCE (node->next))) +arch_mutex_cpu_relax (); + } +ACCESS_ONCE (next->locked) =1 ; +smp_wmb ();Shouldn't the memory barrier precede the "
ACCESS_ONCE(next->locked)= 1;"? Maybe in an "else" clause of the prior "if" statement, given that thecmpxchg()does it otherwise.
Quoted 8 lines
Quoted 2 lines
Otherwise, in the case where the "if" conditionn is false, the critical section could bleed out past the unlock.
Yes, I agree with you that the
smp_wmbshould be moved beforeACCESS_ONCEto prevent critical section from bleeding. Copying Waiman who is the original author of the mcs code to see if he has any comments on things we may have missed.Tim
As a more general lock/unlock mechanism, I also agreed that we should move smp_wmb() before ACCESS_ONCE(). For the mutex case, it is used as a queuing mechanism rather than guarding critical section, so it doesn't really matter.
Regards,
Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Paul E. McKenneyQuoted 8 lines · Paul E. McKenney
Quoted 7 lines · Jason Low
Quoted 4 lines · Paul E. McKenney
Yep. The previous lock holder's
smp_wmb()won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede thenode->lockedcheck, which would be very bad.
Quoted 7 lines
Quoted 3 lines
Paul, Tim, Longman,
How would you like the proposed changes below?
Could you point me at what this applies to? I can find flaws looking at random pieces, given a little luck, but at some point I need to look at the whole thing. ;-)
Sure. Here is a link to the patch we are trying to modify:
https://lkml.org/lkml/2013/9/25/532
Also, below is what the mcs_spin_lock() and mcs_spin_unlock() functions would look like after applying the proposed changes.
static noinline
void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node)
{
struct mcs_spin_node *prev;
/* Init node */
node->locked = 0;
node->next = NULL;
prev = xchg(lock, node);
if (likely(prev == NULL)) {
/* Lock acquired. No need to set node->locked since it
won't be used */
return;
}
ACCESS_ONCE(prev->next) = node;
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
smp_mb();
}static void mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *node)
{
struct mcs_spin_node *next = ACCESS_ONCE(node->next);
if (likely(!next)) {
/*
* Release the lock by setting it to NULL
*/
if (cmpxchg(lock, node, NULL) == node)
return;
/* Wait until the next pointer is set */
while (!(next = ACCESS_ONCE(node->next)))
arch_mutex_cpu_relax();
}
smp_wmb();
ACCESS_ONCE(next->locked) = 1;
}--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Peter Zijlstra
maintainerRe: [PATCH] checkpatch: Make the memory barrier test noisier
In reply to Oliver NeukumQuoted 8 lines · Oliver Neukum
Quoted 7 lines · Peter Zijlstra
Quoted 1 line · Joe Perches
That would make it seem as if all barriers are SMP no?
I would think any memory barrier is ordering against someone else; if not smp then a device/hardware -- like for instance the hardware page table walker.
Quoted 6 lines
Quoted 2 lines
Barriers are fundamentally about order; and order only makes sense if there's more than 1 party to the game.
But not necessarily more than 1 kind of parties. It is perfectly possible to have a barrier against other threads running the same function.
Then that makes a good comment ;-)
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 12 lines · Jason Low
Quoted 12 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL;
Quoted 11 lines
Quoted 11 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();
I wonder if a memory barrier is really needed here.
Quoted 7 lines
Quoted 7 lines
}
static void
mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *next = ACCESS_ONCE(node->next);
Quoted 13 lines
Quoted 5 lines
if (likely(!next)) { /* * Release the lock by setting it to NULL */ if (cmpxchg(lock, node, NULL) == node)Quoted 8 lines
return; /* Wait until the next pointer is set */ while (!(next = ACCESS_ONCE(node->next))) arch_mutex_cpu_relax(); } smp_wmb(); ACCESS_ONCE(next->locked) = 1; }
Instead, I think what we need may be:
if (likely(!next)) {
....
} else
smp_mb();ACCESS_ONCE(next->locked) = 1;
That will ensure a memory barrier in the unlock path.
Regards,
Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 25 lines · Waiman Long
Quoted 12 lines · Jason Low
Quoted 12 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL;Quoted 11 lines
Quoted 11 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();
Quoted 1 line
I wonder if a memory barrier is really needed here.
If the compiler can reorder the while (!ACCESS_ONCE(node->locked)) check so that the check occurs after an instruction in the critical section, then the barrier may be necessary.
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Paul E. McKenneyQuoted 10 lines · Paul E. McKenney
Quoted 9 lines · Jason Low
Quoted 4 lines · Paul E. McKenney
Yep. The previous lock holder's
smp_wmb()won't keep either the compiler or the CPU from reordering things for the new lock holder. They could for example reorder the critical section to precede thenode->lockedcheck, which would be very bad.Paul, Tim, Longman,
Quoted 7 lines
Quoted 1 line
How would you like the proposed changes below?
Could you point me at what this applies to? I can find flaws looking at random pieces, given a little luck, but at some point I need to look at the whole thing. ;-)
Thanx, Paul
Jason's patch is on top of the following patchset:
https://lkml.org/lkml/2013/9/26/674
Thanks.
Tim
Quoted 64 lines
Quoted 54 lines
---
Subject: [PATCH] MCS: optimizations and barrier correctionsDelete the
node->locked= 1 assignment if the lock is free as it won't be used.Delete the
smp_wmb()inmcs_spin_lock()and add a full memory barrier at the end of themcs_spin_lock()function. As Paul McKenney suggested, "you do need a full memory barrier here in order to ensure that you see the effects of the previous lock holder's critical section." And in themcs_spin_unlock(), move the memory barrier so that it is before the "ACCESS_ONCE(next->locked)= 1;".Signed-off-by Jason Low <[email protected]>Signed-off-by Paul E. McKenney <[email protected]>Signed-off-by Tim Chen <[email protected]>--- include/linux/mcslock.h | 7 +++---- 1 files changed, 3 insertions(+), 4 deletions(-)include/linux/mcslock.h +1
-2@@ -26,15 +26,14 @@ void mcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node) prev =xchg (lock, node);if (likely (prev ==NULL )) { -/* Lock acquired */ - node->locked =1 ; +/* Lock acquired. No need to set node->locked since it won't be used */
return; } ACCESS_ONCE(prev->next) = node;include/linux/mcslock.h +1
-1-smp_wmb ();/* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE (node->locked))arch_mutex_cpu_relax (); +smp_mb (); } static void mcs_spin_unlock(struct mcs_spin_node **lock, struct
mcs_spin_node*node)include/linux/mcslock.h +1
-1@@ -51,8 +50,8 @@ static void mcs_spin_unlock(struct mcs_spin_node **lock, struct mcs_spin_node *nwhile (!(next =ACCESS_ONCE (node->next)))arch_mutex_cpu_relax (); } -ACCESS_ONCE (next->locked) =1 ;smp_wmb (); +ACCESS_ONCE (next->locked) =1 ; }Quoted 4 lines
#endif
Signature · 1 line
1.7.1--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 25 lines · Jason Low
Quoted 24 lines · Waiman Long
Quoted 23 lines · Jason Low
Quoted 23 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();
Quoted 5 lines
Quoted 1 line
I wonder if a memory barrier is really needed here.
If the compiler can reorder the while (!ACCESS_ONCE(node->locked)) check so that the check occurs after an instruction in the critical section, then the barrier may be necessary.
In that case, just a barrier() call should be enough.
-Longman
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 26 lines · Waiman Long
Quoted 25 lines · Jason Low
Quoted 24 lines · Waiman Long
Quoted 23 lines · Jason Low
Quoted 23 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();
Quoted 7 lines
Quoted 5 lines
Quoted 1 line
I wonder if a memory barrier is really needed here.
If the compiler can reorder the while (!ACCESS_ONCE(node->locked)) check so that the check occurs after an instruction in the critical section, then the barrier may be necessary.
In that case, just a
barrier()call should be enough.
The cpu could still be executing out of order load instruction from the critical section before checking node->locked? Probably smp_mb() is still needed.
Tim
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 32 lines · Tim Chen
Quoted 31 lines · Waiman Long
Quoted 30 lines · Jason Low
Quoted 24 lines · Waiman Long
Quoted 12 lines · Jason Low
Quoted 12 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL;Quoted 11 lines
Quoted 11 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();Quoted 1 line
I wonder if a memory barrier is really needed here.
If the compiler can reorder the while (!ACCESS_ONCE(node->locked)) check so that the check occurs after an instruction in the critical section, then the barrier may be necessary.
Quoted 6 lines
Quoted 1 line
In that case, just a
barrier()call should be enough.The cpu could still be executing out of order load instruction from the critical section before checking
node->locked? Probablysmp_mb()is still needed.Tim
But this is the lock function, a barrier() call should be enough to prevent the critical section from creeping up there. We certainly need some kind of memory barrier at the end of the unlock function.
-Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 38 lines · Waiman Long
Quoted 37 lines · Tim Chen
Quoted 26 lines · Waiman Long
Quoted 25 lines · Jason Low
Quoted 24 lines · Waiman Long
Quoted 23 lines · Jason Low
Quoted 23 lines
Also, below is what the
mcs_spin_lock()andmcs_spin_unlock()functions would look like after applying the proposed changes.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->locked = 0; node->next = NULL; prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired. No need to set node->locked since it won't be used */ return; } ACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();Quoted 6 lines
Quoted 5 lines
Quoted 1 line
I wonder if a memory barrier is really needed here.
If the compiler can reorder the while (!ACCESS_ONCE(node->locked)) check so that the check occurs after an instruction in the critical section, then the barrier may be necessary.
In that case, just a
barrier()call should be enough.The cpu could still be executing out of order load instruction from the critical section before checking
node->locked? Probablysmp_mb()is still needed.
Quoted 5 lines
Quoted 1 line
Tim
But this is the lock function, a
barrier()call should be enough to prevent the critical section from creeping up there. We certainly need some kind of memory barrier at the end of the unlock function.
I may be missing something. My understanding is that barrier only prevents the compiler from rearranging instructions, but not for cpu out of order execution (as in smp_mb). So cpu could read memory in the next critical section, before node->locked is true, (i.e. unlock has been completed). If we only have a simple barrier at end of mcs_lock, then say the code on CPU1 is
mcs_lock x = 1; ... x = 2; mcs_unlock
and CPU 2 is
mcs_lock y = x; ... mcs_unlock
We expect y to be 2 after the "y = x" assignment. But we we may execute the code as
CPU1 CPU2 x = 1; ... y = x; ( y=1, out of order load) x = 2 mcs_unlock Check node->locked==true continue executing critical section (y=1 when we expect y=2)
So we get y to be 1 when we expect that it should be 2. Adding smp_mb after the node->locked check in lock code
ACCESS_ONCE(prev->next) = node;
/* Wait until the lock holder passes the lock down */
while (!ACCESS_ONCE(node->locked))
arch_mutex_cpu_relax();
smp_mb();should prevent this scenario.
Thanks.
Tim
Quoted 7 lines
-Longman
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html
Please read the FAQ at http://www.tux.org/lkml/
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 51 lines · Tim Chen
Quoted 9 lines · Waiman Long
Quoted 6 lines
The cpu could still be executing out of order load instruction from the critical section before checking
node->locked? Probablysmp_mb()is still needed.Tim
But this is the lock function, a
barrier()call should be enough to prevent the critical section from creeping up there. We certainly need some kind of memory barrier at the end of the unlock function.I may be missing something. My understanding is that barrier only prevents the compiler from rearranging instructions, but not for cpu out of order execution (as in
smp_mb). So cpu could read memory in the next critical section, beforenode->lockedis true, (i.e. unlock has been completed). If we only have a simple barrier at end ofmcs_lock, then say the code on CPU1 ismcs_lock x = 1; ... x = 2; mcs_unlockand CPU 2 is
mcs_lock y = x; ... mcs_unlockWe expect y to be 2 after the "y = x" assignment. But we we may execute the code as
CPU1 CPU2 x = 1; ... y = x; ( y=1, out of order load) x = 2 mcs_unlock Check node->locked==true continue executing critical section (y=1 when we expect y=2)So we get y to be 1 when we expect that it should be 2. Adding
smp_mbafter thenode->lockedcheck in lock codeACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();
Quoted 4 lines
should prevent this scenario.
Thanks.
Tim
If the lock and unlock functions are done right, there should be no overlap of critical section. So it is job of the lock/unlock functions to make sure that critical section code won't leak out. There should be some kind of memory barrier at the beginning of the lock function and the end of the unlock function.
The critical section also likely to have branches. The CPU may speculatively execute code on the 2 branches, but one of them will be discarded once the branch condition is known. Also arch_mutex_cpu_relax() is a compiler barrier by itself. So we may not need a barrier() after all. The while statement is a branch instruction, any code after that can only be speculatively executed and cannot be committed until the branch is done.
In x86, the smp_mb() function translated to a mfence instruction which cost time. That is why I try to get rid of it if it is not necessary.
Regards,
Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 63 lines · Waiman Long
Quoted 51 lines · Tim Chen
Quoted 9 lines · Waiman Long
Quoted 6 lines
The cpu could still be executing out of order load instruction from the critical section before checking
node->locked? Probablysmp_mb()is still needed.Tim
But this is the lock function, a
barrier()call should be enough to prevent the critical section from creeping up there. We certainly need some kind of memory barrier at the end of the unlock function.I may be missing something. My understanding is that barrier only prevents the compiler from rearranging instructions, but not for cpu out of order execution (as in
smp_mb). So cpu could read memory in the next critical section, beforenode->lockedis true, (i.e. unlock has been completed). If we only have a simple barrier at end ofmcs_lock, then say the code on CPU1 ismcs_lock x = 1; ... x = 2; mcs_unlockand CPU 2 is
mcs_lock y = x; ... mcs_unlockWe expect y to be 2 after the "y = x" assignment. But we we may execute the code as
CPU1 CPU2 x = 1; ... y = x; ( y=1, out of order load) x = 2 mcs_unlock Check node->locked==true continue executing critical section (y=1 when we expect y=2)So we get y to be 1 when we expect that it should be 2. Adding
smp_mbafter thenode->lockedcheck in lock codeACCESS_ONCE(prev->next) = node; /* Wait until the lock holder passes the lock down */ while (!ACCESS_ONCE(node->locked)) arch_mutex_cpu_relax(); smp_mb();Quoted 4 lines
should prevent this scenario.
Thanks.
TimIf the lock and unlock functions are done right, there should be no overlap of critical section. So it is job of the lock/unlock functions to make sure that critical section code won't leak out. There should be some kind of memory barrier at the beginning of the lock function and the end of the unlock function.
Quoted 7 lines
The critical section also likely to have branches. The CPU may speculatively execute code on the 2 branches, but one of them will be discarded once the branch condition is known. Also
arch_mutex_cpu_relax()is a compiler barrier by itself. So we may not need abarrier()after all. The while statement is a branch instruction, any code after that can only be speculatively executed and cannot be committed until the branch is done.
But the condition code may be checked after speculative execution? The condition may not be true during speculative execution and only turns true when we check the condition, and take that branch?
The thing that bothers me is without memory barrier after the while statement, we could speculatively execute before affirming the lock is in acquired state. Then when we check the lock, the lock is set to acquired state in the mean time.
We could be loading some memory entry *before*
the node->locked has been set true. I think a smp_rmb (if not a smp_mb) should be set after the while statement.
At first I was also thinking that the memory barrier is not necessary but Paul convinced me otherwise in a previous email.
https://lkml.org/lkml/2013/9/27/523
Quoted 4 lines
In x86, the
smp_mb()function translated to a mfence instruction which cost time. That is why I try to get rid of it if it is not necessary.
I also hope that the memory barrier is not necessary and I am missing something obvious. But I haven't been able to persuade myself.
Tim
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 17 lines · Jason Low
Quoted 16 lines · Tim Chen
Okay, that would makes sense for consistency because we always first set
node->lock= 0 at the top of the function.If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->next = NULL;
Quoted 6 lines
Quoted 6 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ return; } node->locked = 0;
You can remove the locked flag setting statement inside if (prev == NULL), but you can't clear the locked flag after xchg(). In the interval between xchg() and locked=0, the previous lock owner may come in and set the flag. Now if your clear it, the thread will loop forever. You have to clear it before xchg().
-Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
Quoted 29 lines · Waiman Long
Quoted 20 lines · Jason Low
Quoted 18 lines · Tim Chen
Okay, that would makes sense for consistency because we always first set
node->lock= 0 at the top of the function.If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->next = NULL;Quoted 6 lines
Quoted 6 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ return; } node->locked = 0;
Quoted 5 lines
You can remove the locked flag setting statement inside if (prev == NULL), but you can't clear the locked flag after
xchg(). In the interval betweenxchg()and locked=0, the previous lock owner may come in and set the flag. Now if your clear it, the thread will loop forever. You have to clear it beforexchg().
Yes, in my most recent version, I left locked = 0 in its original place so that the xchg() can act as a barrier for it.
The other option would have been to put another barrier after locked =
0. I went with leaving locked = 0 in its original place so that we don't need that extra barrier.
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Tim ChenQuoted 19 lines · Tim Chen
Quoted 7 lines · Waiman Long
If the lock and unlock functions are done right, there should be no overlap of critical section. So it is job of the lock/unlock functions to make sure that critical section code won't leak out. There should be some kind of memory barrier at the beginning of the lock function and the end of the unlock function.
Quoted 7 lines
The critical section also likely to have branches. The CPU may speculatively execute code on the 2 branches, but one of them will be discarded once the branch condition is known. Also
arch_mutex_cpu_relax()is a compiler barrier by itself. So we may not need abarrier()after all. The while statement is a branch instruction, any code after that can only be speculatively executed and cannot be committed until the branch is done.But the condition code may be checked after speculative execution? The condition may not be true during speculative execution and only turns true when we check the condition, and take that branch?
Quoted 7 lines
The thing that bothers me is without memory barrier after the while statement, we could speculatively execute before affirming the lock is in acquired state. Then when we check the lock, the lock is set to acquired state in the mean time.
We could be loading some memory entry *before*
thenode->lockedhas been set true. I think asmp_rmb(if not asmp_mb) should be set after the while statement.
Yes, I think a smp_rmb() make sense here to correspond to the smp_wmb() in the unlock path.
BTW, you need to move the node->locked = 0; statement before xchg() if you haven't done so.
-Longman
Re: [PATCH v6 5/6] MCS Lock: Restructure the MCS lock defines and locking code into its own file
In reply to Jason LowQuoted 32 lines · Jason Low
Quoted 26 lines · Waiman Long
Quoted 18 lines · Jason Low
Quoted 17 lines · Tim Chen
Okay, that would makes sense for consistency because we always first set
node->lock= 0 at the top of the function.If we prefer to optimize this a bit though, perhaps we can first move the
node->lock= 0 so that it gets executed after the "if (likely(prev == NULL)) {}" code block and then delete "node->lock= 1" inside the code block.static noinline
voidmcs_spin_lock(struct mcs_spin_node **lock, struct mcs_spin_node *node){ struct mcs_spin_node *prev; /* Init node */ node->next = NULL;Quoted 6 lines
Quoted 6 lines
prev = xchg(lock, node); if (likely(prev == NULL)) { /* Lock acquired */ return; } node->locked = 0;Quoted 5 lines
You can remove the locked flag setting statement inside if (prev == NULL), but you can't clear the locked flag after
xchg(). In the interval betweenxchg()and locked=0, the previous lock owner may come in and set the flag. Now if your clear it, the thread will loop forever. You have to clear it beforexchg().
Quoted 6 lines
Yes, in my most recent version, I left locked = 0 in its original place so that the
xchg()can act as a barrier for it.The other option would have been to put another barrier after locked =
0. I went with leaving locked = 0 in its original place so that we don't need that extra barrier.
I don't think putting another barrier after locked=0 will work. Chronologically, the flag must be cleared before the node address is saved in the lock field. There is no way to guarantee that except by putting the locked=0 before xchg().
-Longman
--
To unsubscribe, send a message with 'unsubscribe linux-mm' in the body to [email protected]. For more info on Linux MM,
see: http://www.linux-mm.org/ .
Don't email: <a href=mailto:"[email protected]"> [email protected] </a>