2005-04-17 06:20:36 +08:00
|
|
|
/* rwsem.c: R/W semaphores: contention handling functions
|
|
|
|
*
|
|
|
|
* Written by David Howells (dhowells@redhat.com).
|
|
|
|
* Derived from arch/i386/kernel/semaphore.c
|
2013-02-05 21:11:55 +08:00
|
|
|
*
|
|
|
|
* Writer lock-stealing by Alex Shi <alex.shi@intel.com>
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
* and Michel Lespinasse <walken@google.com>
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
#include <linux/rwsem.h>
|
|
|
|
#include <linux/sched.h>
|
|
|
|
#include <linux/init.h>
|
2011-11-17 10:29:17 +08:00
|
|
|
#include <linux/export.h>
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2014-05-03 03:53:57 +08:00
|
|
|
/*
|
|
|
|
* Guide to the rw_semaphore's count field for common values.
|
|
|
|
* (32-bit case illustrated, similar for 64-bit)
|
|
|
|
*
|
|
|
|
* 0x0000000X (1) X readers active or attempting lock, no writer waiting
|
|
|
|
* X = #active_readers + #readers attempting to lock
|
|
|
|
* (X*ACTIVE_BIAS)
|
|
|
|
*
|
|
|
|
* 0x00000000 rwsem is unlocked, and no one is waiting for the lock or
|
|
|
|
* attempting to read lock or write lock.
|
|
|
|
*
|
|
|
|
* 0xffff000X (1) X readers active or attempting lock, with waiters for lock
|
|
|
|
* X = #active readers + # readers attempting lock
|
|
|
|
* (X*ACTIVE_BIAS + WAITING_BIAS)
|
|
|
|
* (2) 1 writer attempting lock, no waiters for lock
|
|
|
|
* X-1 = #active readers + #readers attempting lock
|
|
|
|
* ((X-1)*ACTIVE_BIAS + ACTIVE_WRITE_BIAS)
|
|
|
|
* (3) 1 writer active, no waiters for lock
|
|
|
|
* X-1 = #active readers + #readers attempting lock
|
|
|
|
* ((X-1)*ACTIVE_BIAS + ACTIVE_WRITE_BIAS)
|
|
|
|
*
|
|
|
|
* 0xffff0001 (1) 1 reader active or attempting lock, waiters for lock
|
|
|
|
* (WAITING_BIAS + ACTIVE_BIAS)
|
|
|
|
* (2) 1 writer active or attempting lock, no waiters for lock
|
|
|
|
* (ACTIVE_WRITE_BIAS)
|
|
|
|
*
|
|
|
|
* 0xffff0000 (1) There are writers or readers queued but none active
|
|
|
|
* or in the process of attempting lock.
|
|
|
|
* (WAITING_BIAS)
|
|
|
|
* Note: writer can attempt to steal lock for this count by adding
|
|
|
|
* ACTIVE_WRITE_BIAS in cmpxchg and checking the old count
|
|
|
|
*
|
|
|
|
* 0xfffe0001 (1) 1 writer active, or attempting lock. Waiters on queue.
|
|
|
|
* (ACTIVE_WRITE_BIAS + WAITING_BIAS)
|
|
|
|
*
|
|
|
|
* Note: Readers attempt to lock by adding ACTIVE_BIAS in down_read and checking
|
|
|
|
* the count becomes more than 0 for successful lock acquisition,
|
|
|
|
* i.e. the case where there are only readers or nobody has lock.
|
|
|
|
* (1st and 2nd case above).
|
|
|
|
*
|
|
|
|
* Writers attempt to lock by adding ACTIVE_WRITE_BIAS in down_write and
|
|
|
|
* checking the count becomes ACTIVE_WRITE_BIAS for successful lock
|
|
|
|
* acquisition (i.e. nobody else has lock or attempts lock). If
|
|
|
|
* unsuccessful, in rwsem_down_write_failed, we'll check to see if there
|
|
|
|
* are only waiters but none active (5th case above), and attempt to
|
|
|
|
* steal the lock.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2006-07-03 15:24:53 +08:00
|
|
|
/*
|
|
|
|
* Initialize an rwsem:
|
|
|
|
*/
|
|
|
|
void __init_rwsem(struct rw_semaphore *sem, const char *name,
|
|
|
|
struct lock_class_key *key)
|
|
|
|
{
|
|
|
|
#ifdef CONFIG_DEBUG_LOCK_ALLOC
|
|
|
|
/*
|
|
|
|
* Make sure we are not reinitializing a held semaphore:
|
|
|
|
*/
|
|
|
|
debug_check_no_locks_freed((void *)sem, sizeof(*sem));
|
2006-10-11 13:45:14 +08:00
|
|
|
lockdep_init_map(&sem->dep_map, name, key, 0);
|
2006-07-03 15:24:53 +08:00
|
|
|
#endif
|
|
|
|
sem->count = RWSEM_UNLOCKED_VALUE;
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_lock_init(&sem->wait_lock);
|
2006-07-03 15:24:53 +08:00
|
|
|
INIT_LIST_HEAD(&sem->wait_list);
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(__init_rwsem);
|
|
|
|
|
2013-05-07 21:45:49 +08:00
|
|
|
enum rwsem_waiter_type {
|
|
|
|
RWSEM_WAITING_FOR_WRITE,
|
|
|
|
RWSEM_WAITING_FOR_READ
|
|
|
|
};
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
struct rwsem_waiter {
|
|
|
|
struct list_head list;
|
|
|
|
struct task_struct *task;
|
2013-05-07 21:45:49 +08:00
|
|
|
enum rwsem_waiter_type type;
|
2005-04-17 06:20:36 +08:00
|
|
|
};
|
|
|
|
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
enum rwsem_wake_type {
|
|
|
|
RWSEM_WAKE_ANY, /* Wake whatever's at head of wait list */
|
|
|
|
RWSEM_WAKE_READERS, /* Wake readers only */
|
|
|
|
RWSEM_WAKE_READ_OWNED /* Waker thread holds the read lock */
|
|
|
|
};
|
2010-08-10 08:21:17 +08:00
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
|
|
|
* handle the lock release when processes blocked on it that can now run
|
|
|
|
* - if we come here from up_xxxx(), then:
|
|
|
|
* - the 'active part' of count (&0x0000ffff) reached 0 (but may have changed)
|
|
|
|
* - the 'waiting part' of count (&0xffff0000) is -ve (and will still be so)
|
2010-08-10 08:21:15 +08:00
|
|
|
* - there must be someone on the queue
|
2005-04-17 06:20:36 +08:00
|
|
|
* - the spinlock must be held by the caller
|
|
|
|
* - woken process blocks are discarded from the list after having task zeroed
|
|
|
|
* - writers are only woken if downgrading is false
|
|
|
|
*/
|
2010-08-10 08:21:17 +08:00
|
|
|
static struct rw_semaphore *
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
__rwsem_do_wake(struct rw_semaphore *sem, enum rwsem_wake_type wake_type)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
struct rwsem_waiter *waiter;
|
|
|
|
struct task_struct *tsk;
|
|
|
|
struct list_head *next;
|
2013-05-07 21:46:02 +08:00
|
|
|
long oldcount, woken, loop, adjustment;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-10 08:21:15 +08:00
|
|
|
waiter = list_entry(sem->wait_list.next, struct rwsem_waiter, list);
|
2013-05-07 21:45:58 +08:00
|
|
|
if (waiter->type == RWSEM_WAITING_FOR_WRITE) {
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
if (wake_type == RWSEM_WAKE_ANY)
|
2013-05-07 21:45:58 +08:00
|
|
|
/* Wake writer at the front of the queue, but do not
|
|
|
|
* grant it the lock yet as we want other writers
|
|
|
|
* to be able to steal it. Readers, on the other hand,
|
|
|
|
* will block as they will notice the queued writer.
|
|
|
|
*/
|
|
|
|
wake_up_process(waiter->task);
|
2010-08-10 08:21:15 +08:00
|
|
|
goto out;
|
2013-05-07 21:45:58 +08:00
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
/* Writers might steal the lock before we grant it to the next reader.
|
|
|
|
* We prefer to do the first reader grant before counting readers
|
|
|
|
* so we can bail out early if a writer stole the lock.
|
2010-08-10 08:21:17 +08:00
|
|
|
*/
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
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. */
|
|
|
|
if (rwsem_atomic_update(-adjustment, sem) &
|
|
|
|
RWSEM_ACTIVE_MASK)
|
|
|
|
goto out;
|
|
|
|
/* Last active locker left. Retry waking readers. */
|
|
|
|
goto try_reader_grant;
|
|
|
|
}
|
|
|
|
}
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-10 08:21:15 +08:00
|
|
|
/* Grant an infinite number of read locks to the readers at the front
|
|
|
|
* of the queue. Note we increment the 'active part' of the count by
|
|
|
|
* the number of readers before waking any processes up.
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
|
|
|
woken = 0;
|
|
|
|
do {
|
|
|
|
woken++;
|
|
|
|
|
|
|
|
if (waiter->list.next == &sem->wait_list)
|
|
|
|
break;
|
|
|
|
|
|
|
|
waiter = list_entry(waiter->list.next,
|
|
|
|
struct rwsem_waiter, list);
|
|
|
|
|
2013-05-07 21:45:49 +08:00
|
|
|
} while (waiter->type != RWSEM_WAITING_FOR_WRITE);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
adjustment = woken * RWSEM_ACTIVE_READ_BIAS - adjustment;
|
2013-05-07 21:45:49 +08:00
|
|
|
if (waiter->type != RWSEM_WAITING_FOR_WRITE)
|
2010-08-10 08:21:18 +08:00
|
|
|
/* hit end of list above */
|
|
|
|
adjustment -= RWSEM_WAITING_BIAS;
|
2005-04-17 06:20:36 +08:00
|
|
|
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
if (adjustment)
|
|
|
|
rwsem_atomic_add(adjustment, sem);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
next = sem->wait_list.next;
|
2013-05-07 21:45:58 +08:00
|
|
|
loop = woken;
|
|
|
|
do {
|
2005-04-17 06:20:36 +08:00
|
|
|
waiter = list_entry(next, struct rwsem_waiter, list);
|
|
|
|
next = waiter->list.next;
|
|
|
|
tsk = waiter->task;
|
2005-05-01 23:58:47 +08:00
|
|
|
smp_mb();
|
2005-04-17 06:20:36 +08:00
|
|
|
waiter->task = NULL;
|
|
|
|
wake_up_process(tsk);
|
|
|
|
put_task_struct(tsk);
|
2013-05-07 21:45:58 +08:00
|
|
|
} while (--loop);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
sem->wait_list.next = next;
|
|
|
|
next->prev = &sem->wait_list;
|
|
|
|
|
|
|
|
out:
|
|
|
|
return sem;
|
2013-02-05 21:11:55 +08:00
|
|
|
}
|
|
|
|
|
2005-04-17 06:20:36 +08:00
|
|
|
/*
|
2013-05-07 21:45:51 +08:00
|
|
|
* wait for the read lock to be granted
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2014-02-08 15:52:05 +08:00
|
|
|
__visible
|
2013-05-07 21:45:51 +08:00
|
|
|
struct rw_semaphore __sched *rwsem_down_read_failed(struct rw_semaphore *sem)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-05-07 21:46:02 +08:00
|
|
|
long count, adjustment = -RWSEM_ACTIVE_READ_BIAS;
|
2010-08-10 08:21:20 +08:00
|
|
|
struct rwsem_waiter waiter;
|
2005-04-17 06:20:36 +08:00
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
|
|
|
/* set up my own style of waitqueue */
|
2010-08-10 08:21:20 +08:00
|
|
|
waiter.task = tsk;
|
2013-05-07 21:45:52 +08:00
|
|
|
waiter.type = RWSEM_WAITING_FOR_READ;
|
2005-04-17 06:20:36 +08:00
|
|
|
get_task_struct(tsk);
|
|
|
|
|
2013-05-07 21:45:50 +08:00
|
|
|
raw_spin_lock_irq(&sem->wait_lock);
|
2010-08-10 08:21:18 +08:00
|
|
|
if (list_empty(&sem->wait_list))
|
|
|
|
adjustment += RWSEM_WAITING_BIAS;
|
2010-08-10 08:21:20 +08:00
|
|
|
list_add_tail(&waiter.list, &sem->wait_list);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-08-10 08:21:17 +08:00
|
|
|
/* we're now waiting on the lock, but no longer actively locking */
|
2005-04-17 06:20:36 +08:00
|
|
|
count = rwsem_atomic_update(adjustment, sem);
|
|
|
|
|
2013-05-07 21:46:00 +08:00
|
|
|
/* If there are no active locks, wake the front queued process(es).
|
|
|
|
*
|
|
|
|
* If there are no writers and we are first in the queue,
|
|
|
|
* wake our own waiter to join the existing active readers !
|
|
|
|
*/
|
|
|
|
if (count == RWSEM_WAITING_BIAS ||
|
|
|
|
(count > RWSEM_WAITING_BIAS &&
|
|
|
|
adjustment != -RWSEM_ACTIVE_READ_BIAS))
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_unlock_irq(&sem->wait_lock);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* wait to be given the lock */
|
2013-05-07 21:45:50 +08:00
|
|
|
while (true) {
|
|
|
|
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
|
2010-08-10 08:21:20 +08:00
|
|
|
if (!waiter.task)
|
2005-04-17 06:20:36 +08:00
|
|
|
break;
|
|
|
|
schedule();
|
|
|
|
}
|
|
|
|
|
|
|
|
tsk->state = TASK_RUNNING;
|
|
|
|
|
|
|
|
return sem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2013-05-07 21:45:53 +08:00
|
|
|
* wait until we successfully acquire the write lock
|
2005-04-17 06:20:36 +08:00
|
|
|
*/
|
2014-02-08 15:52:05 +08:00
|
|
|
__visible
|
2011-01-27 04:32:01 +08:00
|
|
|
struct rw_semaphore __sched *rwsem_down_write_failed(struct rw_semaphore *sem)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
2013-05-07 21:46:02 +08:00
|
|
|
long count, adjustment = -RWSEM_ACTIVE_WRITE_BIAS;
|
2013-05-07 21:45:51 +08:00
|
|
|
struct rwsem_waiter waiter;
|
|
|
|
struct task_struct *tsk = current;
|
|
|
|
|
|
|
|
/* set up my own style of waitqueue */
|
|
|
|
waiter.task = tsk;
|
2013-05-07 21:45:53 +08:00
|
|
|
waiter.type = RWSEM_WAITING_FOR_WRITE;
|
2013-05-07 21:45:51 +08:00
|
|
|
|
|
|
|
raw_spin_lock_irq(&sem->wait_lock);
|
|
|
|
if (list_empty(&sem->wait_list))
|
|
|
|
adjustment += RWSEM_WAITING_BIAS;
|
|
|
|
list_add_tail(&waiter.list, &sem->wait_list);
|
|
|
|
|
|
|
|
/* we're now waiting on the lock, but no longer actively locking */
|
|
|
|
count = rwsem_atomic_update(adjustment, sem);
|
|
|
|
|
2013-05-07 21:45:54 +08:00
|
|
|
/* If there were already threads queued before us and there are no
|
|
|
|
* active writers, the lock must be read owned; so we try to wake
|
|
|
|
* any read locks that were queued ahead of us. */
|
|
|
|
if (count > RWSEM_WAITING_BIAS &&
|
|
|
|
adjustment == -RWSEM_ACTIVE_WRITE_BIAS)
|
rwsem: implement support for write lock stealing on the fastpath
When we decide to wake up readers, we must first grant them as many read
locks as necessary, and then actually wake up all these readers. But in
order to know how many read shares to grant, we must first count the
readers at the head of the queue. This might take a while if there are
many readers, and we want to be protected against a writer stealing the
lock while we're counting. To that end, we grant the first reader lock
before counting how many more readers are queued.
We also require some adjustments to the wake_type semantics.
RWSEM_WAKE_NO_ACTIVE used to mean that we had found the count to be
RWSEM_WAITING_BIAS, in which case the rwsem was known to be free as
nobody could steal it while we hold the wait_lock. This doesn't make
sense once we implement fastpath write lock stealing, so we now use
RWSEM_WAKE_ANY in that case.
Similarly, when rwsem_down_write_failed found that a read lock was
active, it would use RWSEM_WAKE_READ_OWNED which signalled that new
readers could be woken without checking first that the rwsem was
available. We can't do that anymore since the existing readers might
release their read locks, and a writer could steal the lock before we
wake up additional readers. So, we have to use a new RWSEM_WAKE_READERS
value to indicate we only want to wake readers, but we don't currently
hold any read lock.
Signed-off-by: Michel Lespinasse <walken@google.com>
Reviewed-by: Peter Hurley <peter@hurleysoftware.com>
Acked-by: Davidlohr Bueso <davidlohr.bueso@hp.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2013-05-07 21:45:59 +08:00
|
|
|
sem = __rwsem_do_wake(sem, RWSEM_WAKE_READERS);
|
2013-05-07 21:45:51 +08:00
|
|
|
|
2013-05-07 21:45:53 +08:00
|
|
|
/* wait until we successfully acquire the lock */
|
2013-05-07 21:45:56 +08:00
|
|
|
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
|
2013-05-07 21:45:51 +08:00
|
|
|
while (true) {
|
2013-05-07 21:45:57 +08:00
|
|
|
if (!(count & RWSEM_ACTIVE_MASK)) {
|
|
|
|
/* Try acquiring the write lock. */
|
|
|
|
count = RWSEM_ACTIVE_WRITE_BIAS;
|
|
|
|
if (!list_is_singular(&sem->wait_list))
|
|
|
|
count += RWSEM_WAITING_BIAS;
|
2013-05-08 06:39:03 +08:00
|
|
|
|
|
|
|
if (sem->count == RWSEM_WAITING_BIAS &&
|
|
|
|
cmpxchg(&sem->count, RWSEM_WAITING_BIAS, count) ==
|
2013-05-07 21:45:55 +08:00
|
|
|
RWSEM_WAITING_BIAS)
|
2013-05-07 21:45:57 +08:00
|
|
|
break;
|
|
|
|
}
|
2013-05-07 21:45:51 +08:00
|
|
|
|
|
|
|
raw_spin_unlock_irq(&sem->wait_lock);
|
2013-05-07 21:45:56 +08:00
|
|
|
|
|
|
|
/* Block until there are no active lockers. */
|
|
|
|
do {
|
|
|
|
schedule();
|
|
|
|
set_task_state(tsk, TASK_UNINTERRUPTIBLE);
|
2013-05-07 21:45:57 +08:00
|
|
|
} while ((count = sem->count) & RWSEM_ACTIVE_MASK);
|
2013-05-07 21:45:56 +08:00
|
|
|
|
2013-05-07 21:45:53 +08:00
|
|
|
raw_spin_lock_irq(&sem->wait_lock);
|
2013-05-07 21:45:51 +08:00
|
|
|
}
|
|
|
|
|
2013-05-07 21:45:53 +08:00
|
|
|
list_del(&waiter.list);
|
|
|
|
raw_spin_unlock_irq(&sem->wait_lock);
|
2013-05-07 21:45:51 +08:00
|
|
|
tsk->state = TASK_RUNNING;
|
|
|
|
|
|
|
|
return sem;
|
2005-04-17 06:20:36 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* handle waking up a waiter on the semaphore
|
|
|
|
* - up_read/up_write has decremented the active part of count if we come here
|
|
|
|
*/
|
2014-02-08 15:52:05 +08:00
|
|
|
__visible
|
2011-01-27 04:32:01 +08:00
|
|
|
struct rw_semaphore *rwsem_wake(struct rw_semaphore *sem)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_lock_irqsave(&sem->wait_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* do nothing if list empty */
|
|
|
|
if (!list_empty(&sem->wait_list))
|
2010-08-10 08:21:17 +08:00
|
|
|
sem = __rwsem_do_wake(sem, RWSEM_WAKE_ANY);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return sem;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* downgrade a write lock into a read lock
|
|
|
|
* - caller incremented waiting part of count and discovered it still negative
|
|
|
|
* - just wake up any readers at the front of the queue
|
|
|
|
*/
|
2014-02-08 15:52:05 +08:00
|
|
|
__visible
|
2011-01-27 04:32:01 +08:00
|
|
|
struct rw_semaphore *rwsem_downgrade_wake(struct rw_semaphore *sem)
|
2005-04-17 06:20:36 +08:00
|
|
|
{
|
|
|
|
unsigned long flags;
|
|
|
|
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_lock_irqsave(&sem->wait_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
/* do nothing if list empty */
|
|
|
|
if (!list_empty(&sem->wait_list))
|
2010-08-10 08:21:17 +08:00
|
|
|
sem = __rwsem_do_wake(sem, RWSEM_WAKE_READ_OWNED);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
2010-02-24 16:54:54 +08:00
|
|
|
raw_spin_unlock_irqrestore(&sem->wait_lock, flags);
|
2005-04-17 06:20:36 +08:00
|
|
|
|
|
|
|
return sem;
|
|
|
|
}
|
|
|
|
|
|
|
|
EXPORT_SYMBOL(rwsem_down_read_failed);
|
|
|
|
EXPORT_SYMBOL(rwsem_down_write_failed);
|
|
|
|
EXPORT_SYMBOL(rwsem_wake);
|
|
|
|
EXPORT_SYMBOL(rwsem_downgrade_wake);
|