aboutsummaryrefslogtreecommitdiffstats
path: root/kernel/locking/rwsem-xadd.c
diff options
context:
space:
mode:
Diffstat (limited to 'kernel/locking/rwsem-xadd.c')
-rw-r--r--kernel/locking/rwsem-xadd.c44
1 files changed, 44 insertions, 0 deletions
diff --git a/kernel/locking/rwsem-xadd.c b/kernel/locking/rwsem-xadd.c
index 3417d0172a5d..0f189714e457 100644
--- a/kernel/locking/rwsem-xadd.c
+++ b/kernel/locking/rwsem-xadd.c
@@ -409,11 +409,24 @@ done:
409 return taken; 409 return taken;
410} 410}
411 411
412/*
413 * Return true if the rwsem has active spinner
414 */
415static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
416{
417 return osq_is_locked(&sem->osq);
418}
419
412#else 420#else
413static bool rwsem_optimistic_spin(struct rw_semaphore *sem) 421static bool rwsem_optimistic_spin(struct rw_semaphore *sem)
414{ 422{
415 return false; 423 return false;
416} 424}
425
426static inline bool rwsem_has_spinner(struct rw_semaphore *sem)
427{
428 return false;
429}
417#endif 430#endif
418 431
419/* 432/*
@@ -496,7 +509,38 @@ struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
496{ 509{
497 unsigned long flags; 510 unsigned long flags;
498 511
512 /*
513 * If a spinner is present, it is not necessary to do the wakeup.
514 * Try to do wakeup only if the trylock succeeds to minimize
515 * spinlock contention which may introduce too much delay in the
516 * unlock operation.
517 *
518 * spinning writer up_write/up_read caller
519 * --------------- -----------------------
520 * [S] osq_unlock() [L] osq
521 * MB RMB
522 * [RmW] rwsem_try_write_lock() [RmW] spin_trylock(wait_lock)
523 *
524 * Here, it is important to make sure that there won't be a missed
525 * wakeup while the rwsem is free and the only spinning writer goes
526 * to sleep without taking the rwsem. Even when the spinning writer
527 * is just going to break out of the waiting loop, it will still do
528 * a trylock in rwsem_down_write_failed() before sleeping. IOW, if
529 * rwsem_has_spinner() is true, it will guarantee at least one
530 * trylock attempt on the rwsem later on.
531 */
532 if (rwsem_has_spinner(sem)) {
533 /*
534 * The smp_rmb() here is to make sure that the spinner
535 * state is consulted before reading the wait_lock.
536 */
537 smp_rmb();
538 if (!raw_spin_trylock_irqsave(&sem->wait_lock, flags))
539 return sem;
540 goto locked;
541 }
499 raw_spin_lock_irqsave(&sem->wait_lock, flags); 542 raw_spin_lock_irqsave(&sem->wait_lock, flags);
543locked:
500 544
501 /* do nothing if list empty */ 545 /* do nothing if list empty */
502 if (!list_empty(&sem->wait_list)) 546 if (!list_empty(&sem->wait_list))