Skip to content

Commit

Permalink
tty: BKL pushdown
Browse files Browse the repository at this point in the history
- Push the BKL down into the line disciplines
- Switch the tty layer to unlocked_ioctl
- Introduce a new ctrl_lock spin lock for the control bits
- Eliminate much of the lock_kernel use in n_tty
- Prepare to (but don't yet) call the drivers with the lock dropped
  on the paths that historically held the lock

BKL now primarily protects open/close/ldisc change in the tty layer

[jirislaby@gmail.com: a couple of fixes]
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Jiri Slaby <jirislaby@gmail.com>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
  • Loading branch information
Alan Cox authored and Linus Torvalds committed Apr 30, 2008
1 parent e523844 commit 04f378b
Show file tree
Hide file tree
Showing 9 changed files with 149 additions and 53 deletions.
24 changes: 18 additions & 6 deletions drivers/char/n_hdlc.c
Original file line number Diff line number Diff line change
Expand Up @@ -578,26 +578,36 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
return -EFAULT;
}

lock_kernel();

for (;;) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags))
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
unlock_kernel();
return -EIO;
}

n_hdlc = tty2n_hdlc (tty);
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
tty != n_hdlc->tty)
tty != n_hdlc->tty) {
unlock_kernel();
return 0;
}

rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
if (rbuf)
break;

/* no data */
if (file->f_flags & O_NONBLOCK)
if (file->f_flags & O_NONBLOCK) {
unlock_kernel();
return -EAGAIN;
}

interruptible_sleep_on (&tty->read_wait);
if (signal_pending(current))
if (signal_pending(current)) {
unlock_kernel();
return -EINTR;
}
}

if (rbuf->count > nr)
Expand All @@ -618,7 +628,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
kfree(rbuf);
else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);

unlock_kernel();
return ret;

} /* end of n_hdlc_tty_read() */
Expand Down Expand Up @@ -661,6 +671,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
count = maxframe;
}

lock_kernel();

add_wait_queue(&tty->write_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE);

Expand Down Expand Up @@ -695,7 +707,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
n_hdlc_buf_put(&n_hdlc->tx_buf_list,tbuf);
n_hdlc_send_frames(n_hdlc,tty);
}

unlock_kernel();
return error;

} /* end of n_hdlc_tty_write() */
Expand Down
16 changes: 14 additions & 2 deletions drivers/char/n_r3964.c
Original file line number Diff line number Diff line change
Expand Up @@ -1075,12 +1075,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,

TRACE_L("read()");

lock_kernel();

pClient = findClient(pInfo, task_pid(current));
if (pClient) {
pMsg = remove_msg(pInfo, pClient);
if (pMsg == NULL) {
/* no messages available. */
if (file->f_flags & O_NONBLOCK) {
unlock_kernel();
return -EAGAIN;
}
/* block until there is a message: */
Expand All @@ -1090,8 +1093,10 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,

/* If we still haven't got a message, we must have been signalled */

if (!pMsg)
if (!pMsg) {
unlock_kernel();
return -EINTR;
}

/* deliver msg to client process: */
theMsg.msg_id = pMsg->msg_id;
Expand All @@ -1102,12 +1107,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
kfree(pMsg);
TRACE_M("r3964_read - msg kfree %p", pMsg);

if (copy_to_user(buf, &theMsg, count))
if (copy_to_user(buf, &theMsg, count)) {
unlock_kernel();
return -EFAULT;
}

TRACE_PS("read - return %d", count);
return count;
}
unlock_kernel();
return -EPERM;
}

Expand Down Expand Up @@ -1156,6 +1164,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
pHeader->locks = 0;
pHeader->owner = NULL;

lock_kernel();

pClient = findClient(pInfo, task_pid(current));
if (pClient) {
pHeader->owner = pClient;
Expand All @@ -1173,6 +1183,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
add_tx_queue(pInfo, pHeader);
trigger_transmit(pInfo);

unlock_kernel();

return 0;
}

Expand Down
32 changes: 25 additions & 7 deletions drivers/char/n_tty.c
Original file line number Diff line number Diff line change
Expand Up @@ -183,22 +183,24 @@ static void reset_buffer_flags(struct tty_struct *tty)
* at hangup) or when the N_TTY line discipline internally has to
* clean the pending queue (for example some signals).
*
* FIXME: tty->ctrl_status is not spinlocked and relies on
* lock_kernel() still.
* Locking: ctrl_lock
*/

static void n_tty_flush_buffer(struct tty_struct *tty)
{
unsigned long flags;
/* clear everything and unthrottle the driver */
reset_buffer_flags(tty);

if (!tty->link)
return;

spin_lock_irqsave(&tty->ctrl_lock, flags);
if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->read_wait);
}
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}

/**
Expand Down Expand Up @@ -264,7 +266,7 @@ static inline int is_continuation(unsigned char c, struct tty_struct *tty)
* relevant in the world today. If you ever need them, add them here.
*
* Called from both the receive and transmit sides and can be called
* re-entrantly. Relies on lock_kernel() still.
* re-entrantly. Relies on lock_kernel() for tty->column state.
*/

static int opost(unsigned char c, struct tty_struct *tty)
Expand All @@ -275,6 +277,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
if (!space)
return -1;

lock_kernel();
if (O_OPOST(tty)) {
switch (c) {
case '\n':
Expand Down Expand Up @@ -323,6 +326,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
}
}
tty->driver->put_char(tty, c);
unlock_kernel();
return 0;
}

Expand All @@ -337,7 +341,8 @@ static int opost(unsigned char c, struct tty_struct *tty)
* the simple cases normally found and helps to generate blocks of
* symbols for the console driver and thus improve performance.
*
* Called from write_chan under the tty layer write lock.
* Called from write_chan under the tty layer write lock. Relies
* on lock_kernel for the tty->column state.
*/

static ssize_t opost_block(struct tty_struct *tty,
Expand All @@ -353,6 +358,7 @@ static ssize_t opost_block(struct tty_struct *tty,
if (nr > space)
nr = space;

lock_kernel();
for (i = 0, cp = buf; i < nr; i++, cp++) {
switch (*cp) {
case '\n':
Expand Down Expand Up @@ -387,6 +393,7 @@ static ssize_t opost_block(struct tty_struct *tty,
if (tty->driver->flush_chars)
tty->driver->flush_chars(tty);
i = tty->driver->write(tty, buf, i);
unlock_kernel();
return i;
}

Expand Down Expand Up @@ -1194,6 +1201,11 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *,
* Perform job control management checks on this file/tty descriptor
* and if appropriate send any needed signals and return a negative
* error code if action should be taken.
*
* FIXME:
* Locking: None - redirected write test is safe, testing
* current->signal should possibly lock current->sighand
* pgrp locking ?
*/

static int job_control(struct tty_struct *tty, struct file *file)
Expand Down Expand Up @@ -1246,6 +1258,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
ssize_t size;
long timeout;
unsigned long flags;
int packet;

do_it_again:

Expand Down Expand Up @@ -1289,16 +1302,19 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
if (mutex_lock_interruptible(&tty->atomic_read_lock))
return -ERESTARTSYS;
}
packet = tty->packet;

add_wait_queue(&tty->read_wait, &wait);
while (nr) {
/* First test for status change. */
if (tty->packet && tty->link->ctrl_status) {
if (packet && tty->link->ctrl_status) {
unsigned char cs;
if (b != buf)
break;
spin_lock_irqsave(&tty->link->ctrl_lock, flags);
cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0;
spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
if (tty_put_user(tty, cs, b++)) {
retval = -EFAULT;
b--;
Expand Down Expand Up @@ -1333,14 +1349,15 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
retval = -ERESTARTSYS;
break;
}
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty);
timeout = schedule_timeout(timeout);
continue;
}
__set_current_state(TASK_RUNNING);

/* Deal with packet mode. */
if (tty->packet && b == buf) {
if (packet && b == buf) {
if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
retval = -EFAULT;
b--;
Expand Down Expand Up @@ -1388,6 +1405,8 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
break;
} else {
int uncopied;
/* The copy function takes the read lock and handles
locking internally for this case */
uncopied = copy_from_read_buf(tty, &b, &nr);
uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) {
Expand Down Expand Up @@ -1429,7 +1448,6 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
goto do_it_again;

n_tty_set_room(tty);

return retval;
}

Expand Down
3 changes: 3 additions & 0 deletions drivers/char/pty.c
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ static int pty_set_lock(struct tty_struct *tty, int __user * arg)
static void pty_flush_buffer(struct tty_struct *tty)
{
struct tty_struct *to = tty->link;
unsigned long flags;

if (!to)
return;
Expand All @@ -189,8 +190,10 @@ static void pty_flush_buffer(struct tty_struct *tty)
to->ldisc.flush_buffer(to);

if (to->packet) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait);
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
}
}

Expand Down
Loading

0 comments on commit 04f378b

Please sign in to comment.