Skip to content

Commit cf69482

Browse files
Waiman-LongIngo Molnar
authored andcommitted
locking/rwsem: Enable readers spinning on writer
This patch enables readers to optimistically spin on a rwsem when it is owned by a writer instead of going to sleep directly. The rwsem_can_spin_on_owner() function is extracted out of rwsem_optimistic_spin() and is called directly by rwsem_down_read_slowpath() and rwsem_down_write_slowpath(). With a locking microbenchmark running on 5.1 based kernel, the total locking rates (in kops/s) on a 8-socket IvyBrige-EX system with equal numbers of readers and writers before and after the patch were as follows: # of Threads Pre-patch Post-patch ------------ --------- ---------- 4 1,674 1,684 8 1,062 1,074 16 924 900 32 300 458 64 195 208 128 164 168 240 149 143 The performance change wasn't significant in this case, but this change is required by a follow-on patch. Signed-off-by: Waiman Long <longman@redhat.com> Signed-off-by: Peter Zijlstra (Intel) <peterz@infradead.org> Cc: Borislav Petkov <bp@alien8.de> Cc: Davidlohr Bueso <dave@stgolabs.net> Cc: H. Peter Anvin <hpa@zytor.com> Cc: Linus Torvalds <torvalds@linux-foundation.org> Cc: Peter Zijlstra <peterz@infradead.org> Cc: Thomas Gleixner <tglx@linutronix.de> Cc: Tim Chen <tim.c.chen@linux.intel.com> Cc: Will Deacon <will.deacon@arm.com> Cc: huang ying <huang.ying.caritas@gmail.com> Link: https://lkml.kernel.org/r/20190520205918.22251-13-longman@redhat.com Signed-off-by: Ingo Molnar <mingo@kernel.org>
1 parent 02f1082 commit cf69482

File tree

2 files changed

+75
-12
lines changed

2 files changed

+75
-12
lines changed

kernel/locking/lock_events_list.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ LOCK_EVENT(rwsem_sleep_reader) /* # of reader sleeps */
5656
LOCK_EVENT(rwsem_sleep_writer) /* # of writer sleeps */
5757
LOCK_EVENT(rwsem_wake_reader) /* # of reader wakeups */
5858
LOCK_EVENT(rwsem_wake_writer) /* # of writer wakeups */
59+
LOCK_EVENT(rwsem_opt_rlock) /* # of read locks opt-spin acquired */
5960
LOCK_EVENT(rwsem_opt_wlock) /* # of write locks opt-spin acquired */
6061
LOCK_EVENT(rwsem_opt_fail) /* # of failed opt-spinnings */
6162
LOCK_EVENT(rwsem_rlock) /* # of read locks acquired */

kernel/locking/rwsem.c

Lines changed: 74 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -457,6 +457,30 @@ static inline bool rwsem_try_write_lock(struct rw_semaphore *sem,
457457
}
458458

459459
#ifdef CONFIG_RWSEM_SPIN_ON_OWNER
460+
/*
461+
* Try to acquire read lock before the reader is put on wait queue.
462+
* Lock acquisition isn't allowed if the rwsem is locked or a writer handoff
463+
* is ongoing.
464+
*/
465+
static inline bool rwsem_try_read_lock_unqueued(struct rw_semaphore *sem)
466+
{
467+
long count = atomic_long_read(&sem->count);
468+
469+
if (count & (RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))
470+
return false;
471+
472+
count = atomic_long_fetch_add_acquire(RWSEM_READER_BIAS, &sem->count);
473+
if (!(count & (RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))) {
474+
rwsem_set_reader_owned(sem);
475+
lockevent_inc(rwsem_opt_rlock);
476+
return true;
477+
}
478+
479+
/* Back out the change */
480+
atomic_long_add(-RWSEM_READER_BIAS, &sem->count);
481+
return false;
482+
}
483+
460484
/*
461485
* Try to acquire write lock before the writer has been put on wait queue.
462486
*/
@@ -491,16 +515,22 @@ static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
491515

492516
BUILD_BUG_ON(is_rwsem_owner_spinnable(RWSEM_OWNER_UNKNOWN));
493517

494-
if (need_resched())
518+
if (need_resched()) {
519+
lockevent_inc(rwsem_opt_fail);
495520
return false;
521+
}
496522

523+
preempt_disable();
497524
rcu_read_lock();
498525
owner = READ_ONCE(sem->owner);
499526
if (owner) {
500527
ret = is_rwsem_owner_spinnable(owner) &&
501528
owner_on_cpu(owner);
502529
}
503530
rcu_read_unlock();
531+
preempt_enable();
532+
533+
lockevent_cond_inc(rwsem_opt_fail, !ret);
504534
return ret;
505535
}
506536

@@ -578,17 +608,14 @@ static noinline enum owner_state rwsem_spin_on_owner(struct rw_semaphore *sem)
578608
return state;
579609
}
580610

581-
static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
611+
static bool rwsem_optimistic_spin(struct rw_semaphore *sem, bool wlock)
582612
{
583613
bool taken = false;
584614
int prev_owner_state = OWNER_NULL;
585615

586616
preempt_disable();
587617

588618
/* sem->wait_lock should not be held when doing optimistic spinning */
589-
if (!rwsem_can_spin_on_owner(sem))
590-
goto done;
591-
592619
if (!osq_lock(&sem->osq))
593620
goto done;
594621

@@ -608,10 +635,11 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
608635
/*
609636
* Try to acquire the lock
610637
*/
611-
if (rwsem_try_write_lock_unqueued(sem)) {
612-
taken = true;
638+
taken = wlock ? rwsem_try_write_lock_unqueued(sem)
639+
: rwsem_try_read_lock_unqueued(sem);
640+
641+
if (taken)
613642
break;
614-
}
615643

616644
/*
617645
* An RT task cannot do optimistic spinning if it cannot
@@ -668,7 +696,12 @@ static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
668696
return taken;
669697
}
670698
#else
671-
static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
699+
static inline bool rwsem_can_spin_on_owner(struct rw_semaphore *sem)
700+
{
701+
return false;
702+
}
703+
704+
static inline bool rwsem_optimistic_spin(struct rw_semaphore *sem, bool wlock)
672705
{
673706
return false;
674707
}
@@ -684,6 +717,31 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, int state)
684717
struct rwsem_waiter waiter;
685718
DEFINE_WAKE_Q(wake_q);
686719

720+
if (!rwsem_can_spin_on_owner(sem))
721+
goto queue;
722+
723+
/*
724+
* Undo read bias from down_read() and do optimistic spinning.
725+
*/
726+
atomic_long_add(-RWSEM_READER_BIAS, &sem->count);
727+
adjustment = 0;
728+
if (rwsem_optimistic_spin(sem, false)) {
729+
/*
730+
* Wake up other readers in the wait list if the front
731+
* waiter is a reader.
732+
*/
733+
if ((atomic_long_read(&sem->count) & RWSEM_FLAG_WAITERS)) {
734+
raw_spin_lock_irq(&sem->wait_lock);
735+
if (!list_empty(&sem->wait_list))
736+
rwsem_mark_wake(sem, RWSEM_WAKE_READ_OWNED,
737+
&wake_q);
738+
raw_spin_unlock_irq(&sem->wait_lock);
739+
wake_up_q(&wake_q);
740+
}
741+
return sem;
742+
}
743+
744+
queue:
687745
waiter.task = current;
688746
waiter.type = RWSEM_WAITING_FOR_READ;
689747
waiter.timeout = jiffies + RWSEM_WAIT_TIMEOUT;
@@ -696,7 +754,7 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, int state)
696754
* exit the slowpath and return immediately as its
697755
* RWSEM_READER_BIAS has already been set in the count.
698756
*/
699-
if (!(atomic_long_read(&sem->count) &
757+
if (adjustment && !(atomic_long_read(&sem->count) &
700758
(RWSEM_WRITER_MASK | RWSEM_FLAG_HANDOFF))) {
701759
raw_spin_unlock_irq(&sem->wait_lock);
702760
rwsem_set_reader_owned(sem);
@@ -708,7 +766,10 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, int state)
708766
list_add_tail(&waiter.list, &sem->wait_list);
709767

710768
/* we're now waiting on the lock, but no longer actively locking */
711-
count = atomic_long_add_return(adjustment, &sem->count);
769+
if (adjustment)
770+
count = atomic_long_add_return(adjustment, &sem->count);
771+
else
772+
count = atomic_long_read(&sem->count);
712773

713774
/*
714775
* If there are no active locks, wake the front queued process(es).
@@ -767,7 +828,8 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
767828
DEFINE_WAKE_Q(wake_q);
768829

769830
/* do optimistic spinning and steal lock if possible */
770-
if (rwsem_optimistic_spin(sem))
831+
if (rwsem_can_spin_on_owner(sem) &&
832+
rwsem_optimistic_spin(sem, true))
771833
return sem;
772834

773835
/*

0 commit comments

Comments
 (0)