Skip to content

Commit 1ee3261

Browse files
Waiman-LongPeter Zijlstra
authored andcommitted
locking/rwsem: Always try to wake waiters in out_nolock path
For writers, the out_nolock path will always attempt to wake up waiters. This may not be really necessary if the waiter to be removed is not the first one. For readers, no attempt to wake up waiter is being made. However, if the HANDOFF bit is set and the reader to be removed is the first waiter, the waiter behind it will inherit the HANDOFF bit and for a write lock waiter waking it up will allow it to spin on the lock to acquire it faster. So it can be beneficial to do a wakeup in this case. Add a new rwsem_del_wake_waiter() helper function to do that consistently for both reader and writer out_nolock paths. Signed-off-by: Waiman Long <[email protected]> Signed-off-by: Peter Zijlstra (Intel) <[email protected]> Link: https://lkml.kernel.org/r/[email protected]
1 parent 54c1ee4 commit 1ee3261

File tree

1 file changed

+34
-10
lines changed

1 file changed

+34
-10
lines changed

kernel/locking/rwsem.c

Lines changed: 34 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -375,16 +375,19 @@ rwsem_add_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
375375
*
376376
* Both rwsem_mark_wake() and rwsem_try_write_lock() contain a full 'copy' of
377377
* this function. Modify with care.
378+
*
379+
* Return: true if wait_list isn't empty and false otherwise
378380
*/
379-
static inline void
381+
static inline bool
380382
rwsem_del_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter)
381383
{
382384
lockdep_assert_held(&sem->wait_lock);
383385
list_del(&waiter->list);
384386
if (likely(!list_empty(&sem->wait_list)))
385-
return;
387+
return true;
386388

387389
atomic_long_andnot(RWSEM_FLAG_HANDOFF | RWSEM_FLAG_WAITERS, &sem->count);
390+
return false;
388391
}
389392

390393
/*
@@ -558,6 +561,33 @@ static void rwsem_mark_wake(struct rw_semaphore *sem,
558561
}
559562
}
560563

564+
/*
565+
* Remove a waiter and try to wake up other waiters in the wait queue
566+
* This function is called from the out_nolock path of both the reader and
567+
* writer slowpaths with wait_lock held. It releases the wait_lock and
568+
* optionally wake up waiters before it returns.
569+
*/
570+
static inline void
571+
rwsem_del_wake_waiter(struct rw_semaphore *sem, struct rwsem_waiter *waiter,
572+
struct wake_q_head *wake_q)
573+
__releases(&sem->wait_lock)
574+
{
575+
bool first = rwsem_first_waiter(sem) == waiter;
576+
577+
wake_q_init(wake_q);
578+
579+
/*
580+
* If the wait_list isn't empty and the waiter to be deleted is
581+
* the first waiter, we wake up the remaining waiters as they may
582+
* be eligible to acquire or spin on the lock.
583+
*/
584+
if (rwsem_del_waiter(sem, waiter) && first)
585+
rwsem_mark_wake(sem, RWSEM_WAKE_ANY, wake_q);
586+
raw_spin_unlock_irq(&sem->wait_lock);
587+
if (!wake_q_empty(wake_q))
588+
wake_up_q(wake_q);
589+
}
590+
561591
/*
562592
* This function must be called with the sem->wait_lock held to prevent
563593
* race conditions between checking the rwsem wait list and setting the
@@ -1050,8 +1080,7 @@ rwsem_down_read_slowpath(struct rw_semaphore *sem, long count, unsigned int stat
10501080
return sem;
10511081

10521082
out_nolock:
1053-
rwsem_del_waiter(sem, &waiter);
1054-
raw_spin_unlock_irq(&sem->wait_lock);
1083+
rwsem_del_wake_waiter(sem, &waiter, &wake_q);
10551084
__set_current_state(TASK_RUNNING);
10561085
lockevent_inc(rwsem_rlock_fail);
10571086
return ERR_PTR(-EINTR);
@@ -1095,7 +1124,6 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
10951124
*/
10961125
raw_spin_unlock_irq(&sem->wait_lock);
10971126
wake_up_q(&wake_q);
1098-
wake_q_init(&wake_q); /* Used again, reinit */
10991127
raw_spin_lock_irq(&sem->wait_lock);
11001128
}
11011129
} else {
@@ -1148,11 +1176,7 @@ rwsem_down_write_slowpath(struct rw_semaphore *sem, int state)
11481176
out_nolock:
11491177
__set_current_state(TASK_RUNNING);
11501178
raw_spin_lock_irq(&sem->wait_lock);
1151-
rwsem_del_waiter(sem, &waiter);
1152-
if (!list_empty(&sem->wait_list))
1153-
rwsem_mark_wake(sem, RWSEM_WAKE_ANY, &wake_q);
1154-
raw_spin_unlock_irq(&sem->wait_lock);
1155-
wake_up_q(&wake_q);
1179+
rwsem_del_wake_waiter(sem, &waiter, &wake_q);
11561180
lockevent_inc(rwsem_wlock_fail);
11571181
return ERR_PTR(-EINTR);
11581182
}

0 commit comments

Comments
 (0)