lkml.org 
[lkml]   [2013]   [Mar]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
Patch in this message
/
From
Subject[PATCH v5 44/44] tty: Early-out tardy ldsem readers
Date
As long as there are no other waiters, read locks should early out.
Otherwise, a reader can end up sleeping while readers are already
running and there are no waiting writers.
This can happen in the following scenario:

CPU 0 | CPU 1
|
| down_write()

... CPU 1 has the write lock for the semaphore.
Meanwhile, 1 or more down_read(s) are attempted and fail;
these are put on the wait list. Then ...

down_read() | up_write()
local = atomic_update(+read_bias) |
local <= 0? | local = atomic_update(-write_bias)
if (true) | local < 0?
down_read_failed() | if (true)
| wake()
| grab wait_lock
wait for wait_lock | wake all readers
| release wait_lock

... At this point, sem->count > 0 and the wait list is empty,
but down_read_failed() will sleep the reader.

Instead, try to reverse the down_read() attempt, but if the count has
changed so that reversing fails, check if there are no other waiters
and early-out if not.

Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
---
drivers/tty/tty_ldsem.c | 28 +++++++++++++++++++++-------
1 file changed, 21 insertions(+), 7 deletions(-)

diff --git a/drivers/tty/tty_ldsem.c b/drivers/tty/tty_ldsem.c
index 84ea8a7..2f35661 100644
--- a/drivers/tty/tty_ldsem.c
+++ b/drivers/tty/tty_ldsem.c
@@ -191,23 +191,34 @@ static void ldsem_wake(struct ld_semaphore *sem)
* wait for the read lock to be granted
*/
static struct ld_semaphore __sched *
-down_read_failed(struct ld_semaphore *sem, long timeout)
+down_read_failed(struct ld_semaphore *sem, long count, long timeout)
{
struct ldsem_waiter waiter;
long adjust = -LDSEM_ACTIVE_BIAS + LDSEM_WAIT_BIAS;

/* set up my own style of waitqueue */
raw_spin_lock_irq(&sem->wait_lock);
+
+ /* Try to reverse the lock attempt but if the count has changed
+ * so that reversing fails, check if there are are no waiters,
+ * and early-out if not */
+ do {
+ if (ldsem_cmpxchg(&count, count + adjust, sem))
+ break;
+ if (count > 0) {
+ raw_spin_unlock_irq(&sem->wait_lock);
+ return sem;
+ }
+ } while (1);
+
list_add_tail(&waiter.list, &sem->read_wait);
sem->wait_readers++;

waiter.task = current;
get_task_struct(current);

- /* change the lock attempt to a wait --
- * if there are no active locks, wake the new lock owner(s)
- */
- if ((ldsem_atomic_update(adjust, sem) & LDSEM_ACTIVE_MASK) == 0)
+ /* if there are no active locks, wake the new lock owner(s) */
+ if ((count & LDSEM_ACTIVE_MASK) == 0)
__ldsem_wake(sem);

raw_spin_unlock_irq(&sem->wait_lock);
@@ -304,11 +315,14 @@ down_write_failed(struct ld_semaphore *sem, long count, long timeout)
static inline int __ldsem_down_read_nested(struct ld_semaphore *sem,
int subclass, long timeout)
{
+ long count;
+
lockdep_acquire_read(sem, subclass, 0, _RET_IP_);

- if (ldsem_atomic_update(LDSEM_READ_BIAS, sem) <= 0) {
+ count = ldsem_atomic_update(LDSEM_READ_BIAS, sem);
+ if (count <= 0) {
lock_stat(sem, contended);
- if (!down_read_failed(sem, timeout)) {
+ if (!down_read_failed(sem, count, timeout)) {
lockdep_release(sem, 1, _RET_IP_);
return 0;
}
--
1.8.1.2


\
 
 \ /
  Last update: 2013-03-12 05:02    [W:1.750 / U:0.012 seconds]
©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site