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,26 +578,36 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, ...@@ -578,26 +578,36 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
return -EFAULT; return -EFAULT;
} }
lock_kernel();
for (;;) { for (;;) {
if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) if (test_bit(TTY_OTHER_CLOSED, &tty->flags)) {
unlock_kernel();
return -EIO; return -EIO;
}
n_hdlc = tty2n_hdlc (tty); n_hdlc = tty2n_hdlc (tty);
if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC || if (!n_hdlc || n_hdlc->magic != HDLC_MAGIC ||
tty != n_hdlc->tty) tty != n_hdlc->tty) {
unlock_kernel();
return 0; return 0;
}
rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list); rbuf = n_hdlc_buf_get(&n_hdlc->rx_buf_list);
if (rbuf) if (rbuf)
break; break;
/* no data */ /* no data */
if (file->f_flags & O_NONBLOCK) if (file->f_flags & O_NONBLOCK) {
unlock_kernel();
return -EAGAIN; return -EAGAIN;
}
interruptible_sleep_on (&tty->read_wait); interruptible_sleep_on (&tty->read_wait);
if (signal_pending(current)) if (signal_pending(current)) {
unlock_kernel();
return -EINTR; return -EINTR;
}
} }
if (rbuf->count > nr) if (rbuf->count > nr)
...@@ -618,7 +628,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file, ...@@ -618,7 +628,7 @@ static ssize_t n_hdlc_tty_read(struct tty_struct *tty, struct file *file,
kfree(rbuf); kfree(rbuf);
else else
n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf); n_hdlc_buf_put(&n_hdlc->rx_free_buf_list,rbuf);
unlock_kernel();
return ret; return ret;
} /* end of n_hdlc_tty_read() */ } /* end of n_hdlc_tty_read() */
...@@ -661,6 +671,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, ...@@ -661,6 +671,8 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file,
count = maxframe; count = maxframe;
} }
lock_kernel();
add_wait_queue(&tty->write_wait, &wait); add_wait_queue(&tty->write_wait, &wait);
set_current_state(TASK_INTERRUPTIBLE); set_current_state(TASK_INTERRUPTIBLE);
...@@ -695,7 +707,7 @@ static ssize_t n_hdlc_tty_write(struct tty_struct *tty, struct file *file, ...@@ -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_buf_put(&n_hdlc->tx_buf_list,tbuf);
n_hdlc_send_frames(n_hdlc,tty); n_hdlc_send_frames(n_hdlc,tty);
} }
unlock_kernel();
return error; return error;
} /* end of n_hdlc_tty_write() */ } /* end of n_hdlc_tty_write() */
......
...@@ -1075,12 +1075,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, ...@@ -1075,12 +1075,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
TRACE_L("read()"); TRACE_L("read()");
lock_kernel();
pClient = findClient(pInfo, task_pid(current)); pClient = findClient(pInfo, task_pid(current));
if (pClient) { if (pClient) {
pMsg = remove_msg(pInfo, pClient); pMsg = remove_msg(pInfo, pClient);
if (pMsg == NULL) { if (pMsg == NULL) {
/* no messages available. */ /* no messages available. */
if (file->f_flags & O_NONBLOCK) { if (file->f_flags & O_NONBLOCK) {
unlock_kernel();
return -EAGAIN; return -EAGAIN;
} }
/* block until there is a message: */ /* block until there is a message: */
...@@ -1090,8 +1093,10 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, ...@@ -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 we still haven't got a message, we must have been signalled */
if (!pMsg) if (!pMsg) {
unlock_kernel();
return -EINTR; return -EINTR;
}
/* deliver msg to client process: */ /* deliver msg to client process: */
theMsg.msg_id = pMsg->msg_id; theMsg.msg_id = pMsg->msg_id;
...@@ -1102,12 +1107,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file, ...@@ -1102,12 +1107,15 @@ static ssize_t r3964_read(struct tty_struct *tty, struct file *file,
kfree(pMsg); kfree(pMsg);
TRACE_M("r3964_read - msg kfree %p", 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; return -EFAULT;
}
TRACE_PS("read - return %d", count); TRACE_PS("read - return %d", count);
return count; return count;
} }
unlock_kernel();
return -EPERM; return -EPERM;
} }
...@@ -1156,6 +1164,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, ...@@ -1156,6 +1164,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
pHeader->locks = 0; pHeader->locks = 0;
pHeader->owner = NULL; pHeader->owner = NULL;
lock_kernel();
pClient = findClient(pInfo, task_pid(current)); pClient = findClient(pInfo, task_pid(current));
if (pClient) { if (pClient) {
pHeader->owner = pClient; pHeader->owner = pClient;
...@@ -1173,6 +1183,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file, ...@@ -1173,6 +1183,8 @@ static ssize_t r3964_write(struct tty_struct *tty, struct file *file,
add_tx_queue(pInfo, pHeader); add_tx_queue(pInfo, pHeader);
trigger_transmit(pInfo); trigger_transmit(pInfo);
unlock_kernel();
return 0; return 0;
} }
......
...@@ -183,22 +183,24 @@ static void reset_buffer_flags(struct tty_struct *tty) ...@@ -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 * at hangup) or when the N_TTY line discipline internally has to
* clean the pending queue (for example some signals). * clean the pending queue (for example some signals).
* *
* FIXME: tty->ctrl_status is not spinlocked and relies on * Locking: ctrl_lock
* lock_kernel() still.
*/ */
static void n_tty_flush_buffer(struct tty_struct *tty) static void n_tty_flush_buffer(struct tty_struct *tty)
{ {
unsigned long flags;
/* clear everything and unthrottle the driver */ /* clear everything and unthrottle the driver */
reset_buffer_flags(tty); reset_buffer_flags(tty);
if (!tty->link) if (!tty->link)
return; return;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (tty->link->packet) { if (tty->link->packet) {
tty->ctrl_status |= TIOCPKT_FLUSHREAD; tty->ctrl_status |= TIOCPKT_FLUSHREAD;
wake_up_interruptible(&tty->link->read_wait); 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) ...@@ -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. * 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 * 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) static int opost(unsigned char c, struct tty_struct *tty)
...@@ -275,6 +277,7 @@ 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) if (!space)
return -1; return -1;
lock_kernel();
if (O_OPOST(tty)) { if (O_OPOST(tty)) {
switch (c) { switch (c) {
case '\n': case '\n':
...@@ -323,6 +326,7 @@ static int opost(unsigned char c, struct tty_struct *tty) ...@@ -323,6 +326,7 @@ static int opost(unsigned char c, struct tty_struct *tty)
} }
} }
tty->driver->put_char(tty, c); tty->driver->put_char(tty, c);
unlock_kernel();
return 0; return 0;
} }
...@@ -337,7 +341,8 @@ static int opost(unsigned char c, struct tty_struct *tty) ...@@ -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 * the simple cases normally found and helps to generate blocks of
* symbols for the console driver and thus improve performance. * 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, static ssize_t opost_block(struct tty_struct *tty,
...@@ -353,6 +358,7 @@ 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) if (nr > space)
nr = space; nr = space;
lock_kernel();
for (i = 0, cp = buf; i < nr; i++, cp++) { for (i = 0, cp = buf; i < nr; i++, cp++) {
switch (*cp) { switch (*cp) {
case '\n': case '\n':
...@@ -387,6 +393,7 @@ static ssize_t opost_block(struct tty_struct *tty, ...@@ -387,6 +393,7 @@ static ssize_t opost_block(struct tty_struct *tty,
if (tty->driver->flush_chars) if (tty->driver->flush_chars)
tty->driver->flush_chars(tty); tty->driver->flush_chars(tty);
i = tty->driver->write(tty, buf, i); i = tty->driver->write(tty, buf, i);
unlock_kernel();
return i; return i;
} }
...@@ -1194,6 +1201,11 @@ extern ssize_t redirected_tty_write(struct file *, const char __user *, ...@@ -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 * Perform job control management checks on this file/tty descriptor
* and if appropriate send any needed signals and return a negative * and if appropriate send any needed signals and return a negative
* error code if action should be taken. * 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) 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, ...@@ -1246,6 +1258,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
ssize_t size; ssize_t size;
long timeout; long timeout;
unsigned long flags; unsigned long flags;
int packet;
do_it_again: do_it_again:
...@@ -1289,16 +1302,19 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ...@@ -1289,16 +1302,19 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
if (mutex_lock_interruptible(&tty->atomic_read_lock)) if (mutex_lock_interruptible(&tty->atomic_read_lock))
return -ERESTARTSYS; return -ERESTARTSYS;
} }
packet = tty->packet;
add_wait_queue(&tty->read_wait, &wait); add_wait_queue(&tty->read_wait, &wait);
while (nr) { while (nr) {
/* First test for status change. */ /* First test for status change. */
if (tty->packet && tty->link->ctrl_status) { if (packet && tty->link->ctrl_status) {
unsigned char cs; unsigned char cs;
if (b != buf) if (b != buf)
break; break;
spin_lock_irqsave(&tty->link->ctrl_lock, flags);
cs = tty->link->ctrl_status; cs = tty->link->ctrl_status;
tty->link->ctrl_status = 0; tty->link->ctrl_status = 0;
spin_unlock_irqrestore(&tty->link->ctrl_lock, flags);
if (tty_put_user(tty, cs, b++)) { if (tty_put_user(tty, cs, b++)) {
retval = -EFAULT; retval = -EFAULT;
b--; b--;
...@@ -1333,6 +1349,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ...@@ -1333,6 +1349,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
retval = -ERESTARTSYS; retval = -ERESTARTSYS;
break; break;
} }
/* FIXME: does n_tty_set_room need locking ? */
n_tty_set_room(tty); n_tty_set_room(tty);
timeout = schedule_timeout(timeout); timeout = schedule_timeout(timeout);
continue; continue;
...@@ -1340,7 +1357,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ...@@ -1340,7 +1357,7 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
__set_current_state(TASK_RUNNING); __set_current_state(TASK_RUNNING);
/* Deal with packet mode. */ /* Deal with packet mode. */
if (tty->packet && b == buf) { if (packet && b == buf) {
if (tty_put_user(tty, TIOCPKT_DATA, b++)) { if (tty_put_user(tty, TIOCPKT_DATA, b++)) {
retval = -EFAULT; retval = -EFAULT;
b--; b--;
...@@ -1388,6 +1405,8 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ...@@ -1388,6 +1405,8 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
break; break;
} else { } else {
int uncopied; 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);
uncopied += copy_from_read_buf(tty, &b, &nr); uncopied += copy_from_read_buf(tty, &b, &nr);
if (uncopied) { if (uncopied) {
...@@ -1429,7 +1448,6 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file, ...@@ -1429,7 +1448,6 @@ static ssize_t read_chan(struct tty_struct *tty, struct file *file,
goto do_it_again; goto do_it_again;
n_tty_set_room(tty); n_tty_set_room(tty);
return retval; return retval;
} }
......
...@@ -181,6 +181,7 @@ static int pty_set_lock(struct tty_struct *tty, int __user * arg) ...@@ -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) static void pty_flush_buffer(struct tty_struct *tty)
{ {
struct tty_struct *to = tty->link; struct tty_struct *to = tty->link;
unsigned long flags;
if (!to) if (!to)
return; return;
...@@ -189,8 +190,10 @@ static void pty_flush_buffer(struct tty_struct *tty) ...@@ -189,8 +190,10 @@ static void pty_flush_buffer(struct tty_struct *tty)
to->ldisc.flush_buffer(to); to->ldisc.flush_buffer(to);
if (to->packet) { if (to->packet) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status |= TIOCPKT_FLUSHWRITE; tty->ctrl_status |= TIOCPKT_FLUSHWRITE;
wake_up_interruptible(&to->read_wait); wake_up_interruptible(&to->read_wait);
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
} }
} }
......
...@@ -152,8 +152,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *, ...@@ -152,8 +152,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
static unsigned int tty_poll(struct file *, poll_table *); static unsigned int tty_poll(struct file *, poll_table *);
static int tty_open(struct inode *, struct file *); static int tty_open(struct inode *, struct file *);
static int tty_release(struct inode *, struct file *); static int tty_release(struct inode *, struct file *);
int tty_ioctl(struct inode *inode, struct file *file, long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
unsigned int cmd, unsigned long arg);
#ifdef CONFIG_COMPAT #ifdef CONFIG_COMPAT
static long tty_compat_ioctl(struct file *file, unsigned int cmd, static long tty_compat_ioctl(struct file *file, unsigned int cmd,
unsigned long arg); unsigned long arg);
...@@ -1205,7 +1204,7 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver); ...@@ -1205,7 +1204,7 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver);
* not in the foreground, send a SIGTTOU. If the signal is blocked or * not in the foreground, send a SIGTTOU. If the signal is blocked or
* ignored, go ahead and perform the operation. (POSIX 7.2) * ignored, go ahead and perform the operation. (POSIX 7.2)
* *
* Locking: none * Locking: none - FIXME: review this
*/ */
int tty_check_change(struct tty_struct *tty) int tty_check_change(struct tty_struct *tty)
...@@ -1247,8 +1246,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait) ...@@ -1247,8 +1246,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM; return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
} }
static int hung_up_tty_ioctl(struct inode *inode, struct file *file, static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
unsigned int cmd, unsigned long arg) unsigned long arg)
{ {
return cmd == TIOCSPGRP ? -ENOTTY : -EIO; return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
} }
...@@ -1264,7 +1263,7 @@ static const struct file_operations tty_fops = { ...@@ -1264,7 +1263,7 @@ static const struct file_operations tty_fops = {
.read = tty_read, .read = tty_read,
.write = tty_write, .write = tty_write,
.poll = tty_poll, .poll = tty_poll,
.ioctl = tty_ioctl, .unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl, .compat_ioctl = tty_compat_ioctl,
.open = tty_open, .open = tty_open,
.release = tty_release, .release = tty_release,
...@@ -1277,7 +1276,7 @@ static const struct file_operations ptmx_fops = { ...@@ -1277,7 +1276,7 @@ static const struct file_operations ptmx_fops = {
.read = tty_read, .read = tty_read,
.write = tty_write, .write = tty_write,
.poll = tty_poll, .poll = tty_poll,
.ioctl = tty_ioctl, .unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl, .compat_ioctl = tty_compat_ioctl,
.open = ptmx_open, .open = ptmx_open,
.release = tty_release, .release = tty_release,
...@@ -1290,7 +1289,7 @@ static const struct file_operations console_fops = { ...@@ -1290,7 +1289,7 @@ static const struct file_operations console_fops = {
.read = tty_read, .read = tty_read,
.write = redirected_tty_write, .write = redirected_tty_write,
.poll = tty_poll, .poll = tty_poll,
.ioctl = tty_ioctl, .unlocked_ioctl = tty_ioctl,
.compat_ioctl = tty_compat_ioctl, .compat_ioctl = tty_compat_ioctl,
.open = tty_open, .open = tty_open,
.release = tty_release, .release = tty_release,
...@@ -1302,7 +1301,7 @@ static const struct file_operations hung_up_tty_fops = { ...@@ -1302,7 +1301,7 @@ static const struct file_operations hung_up_tty_fops = {
.read = hung_up_tty_read, .read = hung_up_tty_read,
.write = hung_up_tty_write, .write = hung_up_tty_write,
.poll = hung_up_tty_poll, .poll = hung_up_tty_poll,
.ioctl = hung_up_tty_ioctl, .unlocked_ioctl = hung_up_tty_ioctl,
.compat_ioctl = hung_up_tty_compat_ioctl, .compat_ioctl = hung_up_tty_compat_ioctl,
.release = tty_release, .release = tty_release,
}; };
...@@ -1626,16 +1625,17 @@ void disassociate_ctty(int on_exit) ...@@ -1626,16 +1625,17 @@ void disassociate_ctty(int on_exit)
struct tty_struct *tty; struct tty_struct *tty;
struct pid *tty_pgrp = NULL; struct pid *tty_pgrp = NULL;
lock_kernel();
mutex_lock(&tty_mutex); mutex_lock(&tty_mutex);
tty = get_current_tty(); tty = get_current_tty();
if (tty) { if (tty) {
tty_pgrp = get_pid(tty->pgrp); tty_pgrp = get_pid(tty->pgrp);
mutex_unlock(&tty_mutex); mutex_unlock(&tty_mutex);
lock_kernel();
/* XXX: here we race, there is nothing protecting tty */ /* XXX: here we race, there is nothing protecting tty */
if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY) if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
tty_vhangup(tty); tty_vhangup(tty);
unlock_kernel();
} else if (on_exit) { } else if (on_exit) {
struct pid *old_pgrp; struct pid *old_pgrp;
spin_lock_irq(&current->sighand->siglock); spin_lock_irq(&current->sighand->siglock);
...@@ -1648,7 +1648,6 @@ void disassociate_ctty(int on_exit) ...@@ -1648,7 +1648,6 @@ void disassociate_ctty(int on_exit)
put_pid(old_pgrp); put_pid(old_pgrp);
} }
mutex_unlock(&tty_mutex); mutex_unlock(&tty_mutex);
unlock_kernel();
return; return;
} }
if (tty_pgrp) { if (tty_pgrp) {
...@@ -1683,7 +1682,6 @@ void disassociate_ctty(int on_exit) ...@@ -1683,7 +1682,6 @@ void disassociate_ctty(int on_exit)
read_lock(&tasklist_lock); read_lock(&tasklist_lock);
session_clear_tty(task_session(current)); session_clear_tty(task_session(current));
read_unlock(&tasklist_lock); read_unlock(&tasklist_lock);
unlock_kernel();
} }
/** /**
...@@ -1693,8 +1691,10 @@ void disassociate_ctty(int on_exit) ...@@ -1693,8 +1691,10 @@ void disassociate_ctty(int on_exit)
void no_tty(void) void no_tty(void)
{ {
struct task_struct *tsk = current; struct task_struct *tsk = current;
lock_kernel();
if (tsk->signal->leader) if (tsk->signal->leader)
disassociate_ctty(0); disassociate_ctty(0);
unlock_kernel();
proc_clear_tty(tsk); proc_clear_tty(tsk);
} }
...@@ -1714,19 +1714,24 @@ void no_tty(void) ...@@ -1714,19 +1714,24 @@ void no_tty(void)
* but not always. * but not always.
* *
* Locking: * Locking:
* Broken. Relies on BKL which is unsafe here. * Uses the tty control lock internally
*/ */
void stop_tty(struct tty_struct *tty) void stop_tty(struct tty_struct *tty)
{ {
if (tty->stopped) unsigned long flags;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (tty->stopped) {
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
return; return;
}
tty->stopped = 1; tty->stopped = 1;
if (tty->link && tty->link->packet) { if (tty->link && tty->link->packet) {
tty->ctrl_status &= ~TIOCPKT_START; tty->ctrl_status &= ~TIOCPKT_START;
tty->ctrl_status |= TIOCPKT_STOP; tty->ctrl_status |= TIOCPKT_STOP;
wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->read_wait);
} }
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->driver->stop) if (tty->driver->stop)
(tty->driver->stop)(tty); (tty->driver->stop)(tty);
} }
...@@ -1743,19 +1748,24 @@ EXPORT_SYMBOL(stop_tty); ...@@ -1743,19 +1748,24 @@ EXPORT_SYMBOL(stop_tty);
* driver start method is invoked and the line discipline woken. * driver start method is invoked and the line discipline woken.
* *
* Locking: * Locking:
* Broken. Relies on BKL which is unsafe here. * ctrl_lock
*/ */
void start_tty(struct tty_struct *tty) void start_tty(struct tty_struct *tty)
{ {
if (!tty->stopped || tty->flow_stopped) unsigned long flags;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (!tty->stopped || tty->flow_stopped) {
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
return; return;
}
tty->stopped = 0; tty->stopped = 0;
if (tty->link && tty->link->packet) { if (tty->link && tty->link->packet) {
tty->ctrl_status &= ~TIOCPKT_STOP; tty->ctrl_status &= ~TIOCPKT_STOP;
tty->ctrl_status |= TIOCPKT_START; tty->ctrl_status |= TIOCPKT_START;
wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->read_wait);
} }
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
if (tty->driver->start) if (tty->driver->start)
(tty->driver->start)(tty); (tty->driver->start)(tty);
/* If we have a running line discipline it may need kicking */ /* If we have a running line discipline it may need kicking */
...@@ -1799,13 +1809,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count, ...@@ -1799,13 +1809,11 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
/* We want to wait for the line discipline to sort out in this /* We want to wait for the line discipline to sort out in this
situation */ situation */
ld = tty_ldisc_ref_wait(tty); ld = tty_ldisc_ref_wait(tty);
lock_kernel();
if (ld->read) if (ld->read)
i = (ld->read)(tty, file, buf, count); i = (ld->read)(tty, file, buf, count);
else else
i = -EIO; i = -EIO;
tty_ldisc_deref(ld); tty_ldisc_deref(ld);
unlock_kernel();
if (i > 0) if (i > 0)
inode->i_atime = current_fs_time(inode->i_sb); inode->i_atime = current_fs_time(inode->i_sb);
return i; return i;
...@@ -1893,9 +1901,7 @@ static inline ssize_t do_tty_write( ...@@ -1893,9 +1901,7 @@ static inline ssize_t do_tty_write(
ret = -EFAULT; ret = -EFAULT;
if (copy_from_user(tty->write_buf, buf, size)) if (copy_from_user(tty->write_buf, buf, size))
break; break;
lock_kernel();
ret = write(tty, file, tty->write_buf, size); ret = write(tty, file, tty->write_buf, size);
unlock_kernel();
if (ret <= 0) if (ret <= 0)
break; break;
written += ret; written += ret;
...@@ -3070,10 +3076,13 @@ static int fionbio(struct file *file, int __user *p) ...@@ -3070,10 +3076,13 @@ static int fionbio(struct file *file, int __user *p)
if (get_user(nonblock, p)) if (get_user(nonblock, p))
return -EFAULT; return -EFAULT;
/* file->f_flags is still BKL protected in the fs layer - vomit */
lock_kernel();
if (nonblock) if (nonblock)
file->f_flags |= O_NONBLOCK; file->f_flags |= O_NONBLOCK;
else else
file->f_flags &= ~O_NONBLOCK; file->f_flags &= ~O_NONBLOCK;
unlock_kernel();
return 0; return 0;
} }
...@@ -3162,7 +3171,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t ...@@ -3162,7 +3171,7 @@ static int tiocgpgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t
* Set the process group of the tty to the session passed. Only * Set the process group of the tty to the session passed. Only
* permitted where the tty session is our session. * permitted where the tty session is our session.
* *
* Locking: None * Locking: RCU
*/ */
static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p) static int tiocspgrp(struct tty_struct *tty, struct tty_struct *real_tty, pid_t __user *p)
...@@ -3237,10 +3246,16 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _ ...@@ -3237,10 +3246,16 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
static int tiocsetd(struct tty_struct *tty, int __user *p) static int tiocsetd(struct tty_struct *tty, int __user *p)
{ {
int ldisc; int ldisc;
int ret;
if (get_user(ldisc, p)) if (get_user(ldisc, p))
return -EFAULT; return -EFAULT;
return tty_set_ldisc(tty, ldisc);
lock_kernel();
ret = tty_set_ldisc(tty, ldisc);
unlock_kernel();
return ret;
} }
/** /**
...@@ -3258,16 +3273,21 @@ static int tiocsetd(struct tty_struct *tty, int __user *p) ...@@ -3258,16 +3273,21 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
static int send_break(struct tty_struct *tty, unsigned int duration) static int send_break(struct tty_struct *tty, unsigned int duration)
{ {
int retval = -EINTR;
lock_kernel();
if (tty_write_lock(tty, 0) < 0) if (tty_write_lock(tty, 0) < 0)
return -EINTR; goto out;
tty->driver->break_ctl(tty, -1); tty->driver->break_ctl(tty, -1);
if (!signal_pending(current)) if (!signal_pending(current))
msleep_interruptible(duration); msleep_interruptible(duration);
tty->driver->break_ctl(tty, 0); tty->driver->break_ctl(tty, 0);
tty_write_unlock(tty); tty_write_unlock(tty);
if (signal_pending(current)) if (!signal_pending(current))
return -EINTR; retval = 0;
return 0; out:
unlock_kernel();
return retval;
} }
/** /**
...@@ -3287,7 +3307,9 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p ...@@ -3287,7 +3307,9 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
int retval = -EINVAL; int retval = -EINVAL;
if (tty->driver->tiocmget) { if (tty->driver->tiocmget) {
lock_kernel();
retval = tty->driver->tiocmget(tty, file); retval = tty->driver->tiocmget(tty, file);
unlock_kernel();
if (retval >= 0) if (retval >= 0)
retval = put_user(retval, p); retval = put_user(retval, p);
...@@ -3337,7 +3359,9 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int ...@@ -3337,7 +3359,9 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; set &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP; clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
lock_kernel();
retval = tty->driver->tiocmset(tty, file, set, clear); retval = tty->driver->tiocmset(tty, file, set, clear);
unlock_kernel();
} }
return retval; return retval;
} }
...@@ -3345,20 +3369,18 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int ...@@ -3345,20 +3369,18 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
/* /*
* Split this up, as gcc can choke on it otherwise.. * Split this up, as gcc can choke on it otherwise..
*/ */
int tty_ioctl(struct inode *inode, struct file *file, long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
unsigned int cmd, unsigned long arg)
{ {
struct tty_struct *tty, *real_tty; struct tty_struct *tty, *real_tty;
void __user *p = (void __user *)arg; void __user *p = (void __user *)arg;
int retval; int retval;
struct tty_ldisc *ld; struct tty_ldisc *ld;
struct inode *inode = file->f_dentry->d_inode;
tty = (struct tty_struct *)file->private_data; tty = (struct tty_struct *)file->private_data;
if (tty_paranoia_check(tty, inode, "tty_ioctl")) if (tty_paranoia_check(tty, inode, "tty_ioctl"))
return -EINVAL; return -EINVAL;
/* CHECKME: is this safe as one end closes ? */
real_tty = tty; real_tty = tty;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY && if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
tty->driver->subtype == PTY_TYPE_MASTER) tty->driver->subtype == PTY_TYPE_MASTER)
...@@ -3367,13 +3389,19 @@ int tty_ioctl(struct inode *inode, struct file *file, ...@@ -3367,13 +3389,19 @@ int tty_ioctl(struct inode *inode, struct file *file,
/* /*
* Break handling by driver * Break handling by driver
*/ */
retval = -EINVAL;
if (!tty->driver->break_ctl) { if (!tty->driver->break_ctl) {
switch (cmd) { switch (cmd) {
case TIOCSBRK: case TIOCSBRK:
case TIOCCBRK: case TIOCCBRK:
if (tty->driver->ioctl) if (tty->driver->ioctl) {
return tty->driver->ioctl(tty, file, cmd, arg); lock_kernel();
return -EINVAL; retval = tty->driver->ioctl(tty, file, cmd, arg);
unlock_kernel();
}
return retval;
/* These two ioctl's always return success; even if */ /* These two ioctl's always return success; even if */
/* the driver doesn't support them. */ /* the driver doesn't support them. */
...@@ -3381,7 +3409,9 @@ int tty_ioctl(struct inode *inode, struct file *file, ...@@ -3381,7 +3409,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
case TCSBRKP: case TCSBRKP:
if (!tty->driver->ioctl) if (!tty->driver->ioctl)
return 0; return 0;
lock_kernel();
retval = tty->driver->ioctl(tty, file, cmd, arg); retval = tty->driver->ioctl(tty, file, cmd, arg);
unlock_kernel();
if (retval == -ENOIOCTLCMD) if (retval == -ENOIOCTLCMD)
retval = 0; retval = 0;
return retval; return retval;
...@@ -3401,7 +3431,9 @@ int tty_ioctl(struct inode *inode, struct file *file, ...@@ -3401,7 +3431,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
if (retval) if (retval)
return retval; return retval;
if (cmd != TIOCCBRK) { if (cmd != TIOCCBRK) {
lock_kernel();
tty_wait_until_sent(tty, 0); tty_wait_until_sent(tty, 0);
unlock_kernel();
if (signal_pending(current)) if (signal_pending(current))
return -EINTR; return -EINTR;
} }
...@@ -3451,11 +3483,15 @@ int tty_ioctl(struct inode *inode, struct file *file, ...@@ -3451,11 +3483,15 @@ int tty_ioctl(struct inode *inode, struct file *file,
* Break handling * Break handling
*/ */
case TIOCSBRK: /* Turn break on, unconditionally */ case TIOCSBRK: /* Turn break on, unconditionally */
lock_kernel();
tty->driver->break_ctl(tty, -1); tty->driver->break_ctl(tty, -1);
unlock_kernel();
return 0; return 0;
case TIOCCBRK: /* Turn break off, unconditionally */ case TIOCCBRK: /* Turn break off, unconditionally */
lock_kernel();
tty->driver->break_ctl(tty, 0); tty->driver->break_ctl(tty, 0);
unlock_kernel();
return 0; return 0;
case TCSBRK: /* SVID version: non-zero arg --> no break */ case TCSBRK: /* SVID version: non-zero arg --> no break */
/* non-zero arg means wait for all output data /* non-zero arg means wait for all output data
...@@ -3485,14 +3521,18 @@ int tty_ioctl(struct inode *inode, struct file *file, ...@@ -3485,14 +3521,18 @@ int tty_ioctl(struct inode *inode, struct file *file,
break; break;
} }
if (tty->driver->ioctl) { if (tty->driver->ioctl) {
lock_kernel();
retval = (tty->driver->ioctl)(tty, file, cmd, arg); retval = (tty->driver->ioctl)(tty, file, cmd, arg);
unlock_kernel();
if (retval != -ENOIOCTLCMD) if (retval != -ENOIOCTLCMD)
return retval; return retval;
} }
ld = tty_ldisc_ref_wait(tty); ld = tty_ldisc_ref_wait(tty);
retval = -EINVAL; retval = -EINVAL;
if (ld->ioctl) { if (ld->ioctl) {
lock_kernel();
retval = ld->ioctl(tty, file, cmd, arg); retval = ld->ioctl(tty, file, cmd, arg);
unlock_kernel();
if (retval == -ENOIOCTLCMD) if (retval == -ENOIOCTLCMD)
retval = -EINVAL; retval = -EINVAL;
} }
...@@ -3770,6 +3810,7 @@ static void initialize_tty_struct(struct tty_struct *tty) ...@@ -3770,6 +3810,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
mutex_init(&tty->atomic_read_lock); mutex_init(&tty->atomic_read_lock);
mutex_init(&tty->atomic_write_lock); mutex_init(&tty->atomic_write_lock);
spin_lock_init(&tty->read_lock); spin_lock_init(&tty->read_lock);
spin_lock_init(&tty->ctrl_lock);
INIT_LIST_HEAD(&tty->tty_files); INIT_LIST_HEAD(&tty->tty_files);
INIT_WORK(&tty->SAK_work, do_SAK_work); INIT_WORK(&tty->SAK_work, do_SAK_work);
} }
......
...@@ -395,6 +395,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) ...@@ -395,6 +395,7 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
int canon_change; int canon_change;
struct ktermios old_termios = *tty->termios; struct ktermios old_termios = *tty->termios;
struct tty_ldisc *ld; struct tty_ldisc *ld;
unsigned long flags;
/* /*
* Perform the actual termios internal changes under lock. * Perform the actual termios internal changes under lock.
...@@ -429,11 +430,13 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios) ...@@ -429,11 +430,13 @@ static void change_termios(struct tty_struct *tty, struct ktermios *new_termios)
STOP_CHAR(tty) == '\023' && STOP_CHAR(tty) == '\023' &&
START_CHAR(tty) == '\021'); START_CHAR(tty) == '\021');
if (old_flow != new_flow) { if (old_flow != new_flow) {
spin_lock_irqsave(&tty->ctrl_lock, flags);
tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP); tty->ctrl_status &= ~(TIOCPKT_DOSTOP | TIOCPKT_NOSTOP);
if (new_flow) if (new_flow)
tty->ctrl_status |= TIOCPKT_DOSTOP; tty->ctrl_status |= TIOCPKT_DOSTOP;
else else
tty->ctrl_status |= TIOCPKT_NOSTOP; tty->ctrl_status |= TIOCPKT_NOSTOP;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
wake_up_interruptible(&tty->link->read_wait); wake_up_interruptible(&tty->link->read_wait);
} }
} }
...@@ -905,6 +908,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, ...@@ -905,6 +908,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg) unsigned int cmd, unsigned long arg)
{ {
struct tty_struct *real_tty; struct tty_struct *real_tty;
unsigned long flags;
int retval; int retval;
if (tty->driver->type == TTY_DRIVER_TYPE_PTY && if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
...@@ -963,6 +967,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, ...@@ -963,6 +967,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
return -ENOTTY; return -ENOTTY;
if (get_user(pktmode, (int __user *) arg)) if (get_user(pktmode, (int __user *) arg))
return -EFAULT; return -EFAULT;
spin_lock_irqsave(&tty->ctrl_lock, flags);
if (pktmode) { if (pktmode) {
if (!tty->packet) { if (!tty->packet) {
tty->packet = 1; tty->packet = 1;
...@@ -970,6 +975,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file, ...@@ -970,6 +975,7 @@ int n_tty_ioctl(struct tty_struct *tty, struct file *file,
} }
} else } else
tty->packet = 0; tty->packet = 0;
spin_unlock_irqrestore(&tty->ctrl_lock, flags);
return 0; return 0;
} }
default: default:
......
...@@ -2541,6 +2541,9 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) ...@@ -2541,6 +2541,9 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
if (get_user(type, p)) if (get_user(type, p))
return -EFAULT; return -EFAULT;
ret = 0; ret = 0;
lock_kernel();
switch (type) switch (type)
{ {
case TIOCL_SETSEL: case TIOCL_SETSEL:
...@@ -2560,7 +2563,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) ...@@ -2560,7 +2563,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
ret = sel_loadlut(p); ret = sel_loadlut(p);
break; break;
case TIOCL_GETSHIFTSTATE: case TIOCL_GETSHIFTSTATE:
/* /*
* Make it possible to react to Shift+Mousebutton. * Make it possible to react to Shift+Mousebutton.
* Note that 'shift_state' is an undocumented * Note that 'shift_state' is an undocumented
...@@ -2615,6 +2618,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg) ...@@ -2615,6 +2618,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
ret = -EINVAL; ret = -EINVAL;
break; break;
} }
unlock_kernel();
return ret; return ret;
} }
...@@ -3829,7 +3833,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op) ...@@ -3829,7 +3833,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
goto out; goto out;
c = (font.width+7)/8 * 32 * font.charcount; c = (font.width+7)/8 * 32 * font.charcount;
if (op->data && font.charcount > op->charcount) if (op->data && font.charcount > op->charcount)
rc = -ENOSPC; rc = -ENOSPC;
if (!(op->flags & KD_FONT_FLAG_OLD)) { if (!(op->flags & KD_FONT_FLAG_OLD)) {
......
...@@ -1046,7 +1046,7 @@ static int vt_check(struct file *file) ...@@ -1046,7 +1046,7 @@ static int vt_check(struct file *file)
struct inode *inode = file->f_path.dentry->d_inode; struct inode *inode = file->f_path.dentry->d_inode;
struct vc_data *vc; struct vc_data *vc;
if (file->f_op->ioctl != tty_ioctl) if (file->f_op->unlocked_ioctl != tty_ioctl)
return -EINVAL; return -EINVAL;
tty = (struct tty_struct *)file->private_data; tty = (struct tty_struct *)file->private_data;
......
...@@ -183,6 +183,7 @@ struct tty_struct { ...@@ -183,6 +183,7 @@ struct tty_struct {
int index; int index;
struct tty_ldisc ldisc; struct tty_ldisc ldisc;
struct mutex termios_mutex; struct mutex termios_mutex;
spinlock_t ctrl_lock;
struct ktermios *termios, *termios_locked; struct ktermios *termios, *termios_locked;
char name[64]; char name[64];
struct pid *pgrp; struct pid *pgrp;
...@@ -323,8 +324,7 @@ extern void tty_ldisc_put(int); ...@@ -323,8 +324,7 @@ extern void tty_ldisc_put(int);
extern void tty_wakeup(struct tty_struct *tty); extern void tty_wakeup(struct tty_struct *tty);
extern void tty_ldisc_flush(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, extern long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
unsigned long arg);
extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file, extern int tty_mode_ioctl(struct tty_struct *tty, struct file *file,
unsigned int cmd, unsigned long arg); unsigned int cmd, unsigned long arg);
extern int tty_perform_flush(struct tty_struct *tty, 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