Commit 04f378b1 authored by Alan Cox's avatar Alan Cox Committed by Linus Torvalds

tty: BKL pushdown

- 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: default avatarAlan Cox <alan@redhat.com>
Signed-off-by: default avatarJiri Slaby <jirislaby@gmail.com>
Signed-off-by: default avatarAndrew Morton <akpm@linux-foundation.org>
Signed-off-by: default avatarLinus Torvalds <torvalds@linux-foundation.org>
parent e5238442
......@@ -578,27 +578,37 @@ 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)
/* frame too large for caller's buffer (discard frame) */
......@@ -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() */
......@@ -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);
......@@ -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() */
......
......@@ -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: */
......@@ -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;
......@@ -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;
}
......@@ -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;
......@@ -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;
}
......
......@@ -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);
}
/**
......@@ -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)
......@@ -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':
......@@ -323,6 +326,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
}
}
tty->driver->put_char(tty, c);
unlock_kernel();
return 0;
}
......@@ -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,
......@@ -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':
......@@ -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;
}
......@@ -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)
......@@ -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:
......@@ -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--;
......@@ -1333,6 +1349,7 @@ 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;
......@@ -1340,7 +1357,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
__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--;
......@@ -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) {
......@@ -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;
}
......
......@@ -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;
......@@ -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);
}
}
......
This diff is collapsed.
......@@ -395,6 +395,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
int canon_change;
struct ktermios old_termios = *tty->termios;
struct tty_ldisc *ld;
unsigned long flags;
/*
* Perform the actual termios internal changes under lock.
......@@ -429,11 +430,13 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021');
if (old_flow != new_flow) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow)
tty->ctrl_status |= TIOCPKT_DOSTOP;
else
tty->ctrl_status |= TIOCPKT_NOSTOP;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
wake_up_interruptible(&tty->link->read_wait);
}
}
......@@ -905,6 +908,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg)
{
struct tty_struct *real_tty;
unsigned long flags;
int retval;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
......@@ -963,6 +967,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
return -ENOTTY;
if (get_user(pktmode, (int __user *) arg))
return -EFAULT;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (pktmode) {
if (!tty->packet) {
tty->packet = 1;
......@@ -970,6 +975,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
}
} else
tty->packet = 0;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
return 0;
}
default:
......
......@@ -2541,6 +2541,9 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
if (get_user(type, p))
return -EFAULT;
ret = 0;
lock_kernel();
switch (type)
{
case TIOCL_SETSEL:
......@@ -2615,6 +2618,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
ret = -EINVAL;
break;
}
unlock_kernel();
return ret;
}
......
......@@ -1046,7 +1046,7 @@ static int vt_check(struct file *file)
struct inode *inode = file->f_path.dentry->d_inode;
struct vc_data *vc;
if (file->f_op->ioctl != tty_ioctl)
if (file->f_op->unlocked_ioctl != tty_ioctl)
return -EINVAL;
tty = (struct tty_struct *)file->private_data;
......
......@@ -183,6 +183,7 @@ struct tty_struct {
int index;
struct tty_ldisc ldisc;
struct mutex termios_mutex;
spinlock_t ctrl_lock;
struct ktermios *termios, *termios_locked;
char name[64];
struct pid *pgrp;
......@@ -323,8 +324,7 @@ extern void tty_ldisc_put(int);
extern void tty_wakeup(struct tty_struct *tty);
extern void tty_ldisc_flush(struct tty_struct *tty);
extern int tty_ioctl(struct inode *inode, struct file *file, unsigned int cmd,
unsigned long arg);
extern long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg);
extern int tty_perform_flush(struct tty_struct *tty, unsigned long arg);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment