rwsem: check the lock before cpmxchg in down_write_trylock
PATCH v2 1/5 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 v2 2/5 rwsem: remove 'out' label in do_wake
In reply to a message not in the archiveMake 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 v2 3/5 rwsem: remove try_reader_grant label do_wake
In reply to a message not in the archiveMake 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 v2 4/5 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 · 10 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 v2 5/5 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 we've noticed that 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, in a nutshell, 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 wait queue overhead.
For rwsems on the other hand, upon entering the writer slowpath in rwsem_down_write_failed(), we just acquire the ->wait_lock, add ourselves to the wait_queue and blocking until we get the lock.
--- include/linux/rwsem.h | 3 + init/Kconfig | 9 +++ kernel/rwsem.c | 29 +++++++++- lib/rwsem.c | 150 +++++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 179 insertions(+), 12 deletions(-)
include/linux/rwsem.h +3 -0
@@ -29,6 +29,9 @@ struct rw_semaphore {#ifdef CONFIG_DEBUG_LOCK_ALLOC struct lockdep_map dep_map;#endif +#ifdef CONFIG_RWSEM_SPIN_ON_WRITE_OWNER +struct task_struct *owner; +#endif };extern struct rw_semaphore *rwsem_down_read_failed (struct rw_semaphore *sem);
init/Kconfig +9 -0
@@ -1595,6 +1595,15 @@ config TRACEPOINTS source "arch/Kconfig" +config CONFIG_RWSEM_SPIN_ON_WRTIE_OWNER + bool "Optimistic spin write acquisition for writer owned rw-sem" + default n + depends on SMP + help + Allows a writer to perform optimistic spinning if another writer own + the read write semaphore. This gives a greater chance for writer to + acquire a semaphore before blocking it and putting it to sleep. + endmenu # General setup config HAVE_GENERIC_DMA_COHERENT
kernel/rwsem.c +28 -1
@@ -12,6 +12,26 @@#include <linux/atomic.h> +#ifdef CONFIG_RWSEM_SPIN_ON_WRITE_OWNER +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 ; +} +#else +static inline void rwsem_set_owner (struct rw_semaphore *sem) +{ +} + +static inline void rwsem_clear_owner (struct rw_semaphore *sem) +{ +} +#endif +/* * lock for reading */
@@ -48,6 +68,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 +80,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 +109,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 +124,7 @@ void downgrade_write(struct rw_semaphore *sem)* dependency. */ __downgrade_write (sem); +rwsem_clear_owner (sem); }EXPORT_SYMBOL (downgrade_write);
@@ -122,6 +147,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 +167,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 +139 -11
183 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>
Peter Zijlstra
maintainerRe: [PATCH v2 5/5] rwsem: do optimistic spinning for writer lock acquisition
lib/rwsem.c struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem) · line 226
Quoted 8 lines · Tim Chen
lib/rwsem.c +8
-0+#ifdef CONFIG_RWSEM_SPIN_ON_WRITE_OWNER +static inline bool rwsem_can_spin_on_owner (struct rw_semaphore *sem) +{ +int retval; +task_struct *owner; + +rcu_read_lock (); + owner = sem->owner;
That wants to be: owner = ACCESS_ONCE(sem->owner);
Quoted 5 lines
lib/rwsem.c +5
-0+ +/* Spin only if active writer running */ +if (owner) + retval = owner->on_cpu; +else
lib/rwsem.c struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem) · line 241
Quoted 10 lines
lib/rwsem.c +10
-0+ retval = false; + +rcu_read_unlock (); +/* +* if lock->owner is not set, the sem owner may have just acquired +* it and not set the owner yet, or the sem has been released, or +* reader active. +*/ +return retval; +}
--
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>