tty: BKL pushdown
authorAlan Cox <alan@lxorguk.ukuu.org.uk>
Wed, 30 Apr 2008 07:53:29 +0000 (00:53 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 30 Apr 2008 15:29:40 +0000 (08:29 -0700)
- 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>
drivers/char/n_hdlc.c
drivers/char/n_r3964.c
drivers/char/n_tty.c
drivers/char/pty.c
drivers/char/tty_io.c
drivers/char/tty_ioctl.c
drivers/char/vt.c
fs/compat_ioctl.c
include/linux/tty.h

index 06803ed..a07c0af 100644 (file)
@@ -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)
@@ -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() */
index 6b918b8..3f6486e 100644 (file)
@@ -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;
 }
 
index 0c09409..001d9d8 100644 (file)
@@ -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 @@ break_out:
        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 @@ do_it_again:
                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 @@ do_it_again:
                                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 @@ do_it_again:
                __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 @@ do_it_again:
                                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 @@ do_it_again:
                 goto do_it_again;
 
        n_tty_set_room(tty);
-
        return retval;
 }
 
index 706ff34..6288356 100644 (file)
@@ -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);
        }
 }
 
index 2fa6856..0b0354b 100644 (file)
@@ -152,8 +152,7 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
 static unsigned int tty_poll(struct file *, poll_table *);
 static int tty_open(struct inode *, struct file *);
 static int tty_release(struct inode *, struct file *);
-int tty_ioctl(struct inode *inode, struct file *file,
-             unsigned int cmd, unsigned long arg);
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg);
 #ifdef CONFIG_COMPAT
 static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                                unsigned long arg);
@@ -1205,7 +1204,7 @@ EXPORT_SYMBOL_GPL(tty_find_polling_driver);
  *     not in the foreground, send a SIGTTOU.  If the signal is blocked or
  *     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)
@@ -1247,8 +1246,8 @@ static unsigned int hung_up_tty_poll(struct file *filp, poll_table *wait)
        return POLLIN | POLLOUT | POLLERR | POLLHUP | POLLRDNORM | POLLWRNORM;
 }
 
-static int hung_up_tty_ioctl(struct inode *inode, struct file *file,
-                            unsigned int cmd, unsigned long arg)
+static long hung_up_tty_ioctl(struct file *file, unsigned int cmd,
+               unsigned long arg)
 {
        return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
 }
@@ -1264,7 +1263,7 @@ static const struct file_operations tty_fops = {
        .read           = tty_read,
        .write          = tty_write,
        .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
+       .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
@@ -1277,7 +1276,7 @@ static const struct file_operations ptmx_fops = {
        .read           = tty_read,
        .write          = tty_write,
        .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
+       .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = ptmx_open,
        .release        = tty_release,
@@ -1290,7 +1289,7 @@ static const struct file_operations console_fops = {
        .read           = tty_read,
        .write          = redirected_tty_write,
        .poll           = tty_poll,
-       .ioctl          = tty_ioctl,
+       .unlocked_ioctl = tty_ioctl,
        .compat_ioctl   = tty_compat_ioctl,
        .open           = tty_open,
        .release        = tty_release,
@@ -1302,7 +1301,7 @@ static const struct file_operations hung_up_tty_fops = {
        .read           = hung_up_tty_read,
        .write          = hung_up_tty_write,
        .poll           = hung_up_tty_poll,
-       .ioctl          = hung_up_tty_ioctl,
+       .unlocked_ioctl = hung_up_tty_ioctl,
        .compat_ioctl   = hung_up_tty_compat_ioctl,
        .release        = tty_release,
 };
@@ -1626,16 +1625,17 @@ void disassociate_ctty(int on_exit)
        struct tty_struct *tty;
        struct pid *tty_pgrp = NULL;
 
-       lock_kernel();
 
        mutex_lock(&tty_mutex);
        tty = get_current_tty();
        if (tty) {
                tty_pgrp = get_pid(tty->pgrp);
                mutex_unlock(&tty_mutex);
+               lock_kernel();
                /* XXX: here we race, there is nothing protecting tty */
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
+               unlock_kernel();
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -1648,7 +1648,6 @@ void disassociate_ctty(int on_exit)
                        put_pid(old_pgrp);
                }
                mutex_unlock(&tty_mutex);
-               unlock_kernel();
                return;
        }
        if (tty_pgrp) {
@@ -1683,7 +1682,6 @@ void disassociate_ctty(int on_exit)
        read_lock(&tasklist_lock);
        session_clear_tty(task_session(current));
        read_unlock(&tasklist_lock);
-       unlock_kernel();
 }
 
 /**
@@ -1693,8 +1691,10 @@ void disassociate_ctty(int on_exit)
 void no_tty(void)
 {
        struct task_struct *tsk = current;
+       lock_kernel();
        if (tsk->signal->leader)
                disassociate_ctty(0);
+       unlock_kernel();
        proc_clear_tty(tsk);
 }
 
@@ -1714,19 +1714,24 @@ void no_tty(void)
  *     but not always.
  *
  *     Locking:
- *             Broken. Relies on BKL which is unsafe here.
+ *             Uses the tty control lock internally
  */
 
 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;
+       }
        tty->stopped = 1;
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_START;
                tty->ctrl_status |= TIOCPKT_STOP;
                wake_up_interruptible(&tty->link->read_wait);
        }
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (tty->driver->stop)
                (tty->driver->stop)(tty);
 }
@@ -1743,19 +1748,24 @@ EXPORT_SYMBOL(stop_tty);
  *     driver start method is invoked and the line discipline woken.
  *
  *     Locking:
- *             Broken. Relies on BKL which is unsafe here.
+ *             ctrl_lock
  */
 
 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;
+       }
        tty->stopped = 0;
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_STOP;
                tty->ctrl_status |= TIOCPKT_START;
                wake_up_interruptible(&tty->link->read_wait);
        }
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (tty->driver->start)
                (tty->driver->start)(tty);
        /* 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,
        /* We want to wait for the line discipline to sort out in this
           situation */
        ld = tty_ldisc_ref_wait(tty);
-       lock_kernel();
        if (ld->read)
                i = (ld->read)(tty, file, buf, count);
        else
                i = -EIO;
        tty_ldisc_deref(ld);
-       unlock_kernel();
        if (i > 0)
                inode->i_atime = current_fs_time(inode->i_sb);
        return i;
@@ -1893,9 +1901,7 @@ static inline ssize_t do_tty_write(
                ret = -EFAULT;
                if (copy_from_user(tty->write_buf, buf, size))
                        break;
-               lock_kernel();
                ret = write(tty, file, tty->write_buf, size);
-               unlock_kernel();
                if (ret <= 0)
                        break;
                written += ret;
@@ -3070,10 +3076,13 @@ static int fionbio(struct file *file, int __user *p)
        if (get_user(nonblock, p))
                return -EFAULT;
 
+       /* file->f_flags is still BKL protected in the fs layer - vomit */
+       lock_kernel();
        if (nonblock)
                file->f_flags |= O_NONBLOCK;
        else
                file->f_flags &= ~O_NONBLOCK;
+       unlock_kernel();
        return 0;
 }
 
@@ -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
  *     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)
@@ -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)
 {
        int ldisc;
+       int ret;
 
        if (get_user(ldisc, p))
                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)
 
 static int send_break(struct tty_struct *tty, unsigned int duration)
 {
+       int retval = -EINTR;
+
+       lock_kernel();
        if (tty_write_lock(tty, 0) < 0)
-               return -EINTR;
+               goto out;
        tty->driver->break_ctl(tty, -1);
        if (!signal_pending(current))
                msleep_interruptible(duration);
        tty->driver->break_ctl(tty, 0);
        tty_write_unlock(tty);
-       if (signal_pending(current))
-               return -EINTR;
-       return 0;
+       if (!signal_pending(current))
+               retval = 0;
+out:
+       unlock_kernel();
+       return retval;
 }
 
 /**
@@ -3287,7 +3307,9 @@ static int tty_tiocmget(struct tty_struct *tty, struct file *file, int __user *p
        int retval = -EINVAL;
 
        if (tty->driver->tiocmget) {
+               lock_kernel();
                retval = tty->driver->tiocmget(tty, file);
+               unlock_kernel();
 
                if (retval >= 0)
                        retval = put_user(retval, p);
@@ -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;
                clear &= TIOCM_DTR|TIOCM_RTS|TIOCM_OUT1|TIOCM_OUT2|TIOCM_LOOP;
 
+               lock_kernel();
                retval = tty->driver->tiocmset(tty, file, set, clear);
+               unlock_kernel();
        }
        return retval;
 }
@@ -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..
  */
-int tty_ioctl(struct inode *inode, struct file *file,
-             unsigned int cmd, unsigned long arg)
+long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
 {
        struct tty_struct *tty, *real_tty;
        void __user *p = (void __user *)arg;
        int retval;
        struct tty_ldisc *ld;
+       struct inode *inode = file->f_dentry->d_inode;
 
        tty = (struct tty_struct *)file->private_data;
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       /* CHECKME: is this safe as one end closes ? */
-
        real_tty = tty;
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_MASTER)
@@ -3367,13 +3389,19 @@ int tty_ioctl(struct inode *inode, struct file *file,
        /*
         * Break handling by driver
         */
+
+       retval = -EINVAL;
+
        if (!tty->driver->break_ctl) {
                switch (cmd) {
                case TIOCSBRK:
                case TIOCCBRK:
-                       if (tty->driver->ioctl)
-                               return tty->driver->ioctl(tty, file, cmd, arg);
-                       return -EINVAL;
+                       if (tty->driver->ioctl) {
+                               lock_kernel();
+                               retval = tty->driver->ioctl(tty, file, cmd, arg);
+                               unlock_kernel();
+                       }
+                       return retval;
 
                /* These two ioctl's always return success; even if */
                /* the driver doesn't support them. */
@@ -3381,7 +3409,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
                case TCSBRKP:
                        if (!tty->driver->ioctl)
                                return 0;
+                       lock_kernel();
                        retval = tty->driver->ioctl(tty, file, cmd, arg);
+                       unlock_kernel();
                        if (retval == -ENOIOCTLCMD)
                                retval = 0;
                        return retval;
@@ -3401,7 +3431,9 @@ int tty_ioctl(struct inode *inode, struct file *file,
                if (retval)
                        return retval;
                if (cmd != TIOCCBRK) {
+                       lock_kernel();
                        tty_wait_until_sent(tty, 0);
+                       unlock_kernel();
                        if (signal_pending(current))
                                return -EINTR;
                }
@@ -3451,11 +3483,15 @@ int tty_ioctl(struct inode *inode, struct file *file,
         * Break handling
         */
        case TIOCSBRK:  /* Turn break on, unconditionally */
+               lock_kernel();
                tty->driver->break_ctl(tty, -1);
+               unlock_kernel();
                return 0;
 
        case TIOCCBRK:  /* Turn break off, unconditionally */
+               lock_kernel();
                tty->driver->break_ctl(tty, 0);
+               unlock_kernel();
                return 0;
        case TCSBRK:   /* SVID version: non-zero arg --> no break */
                /* non-zero arg means wait for all output data
@@ -3485,14 +3521,18 @@ int tty_ioctl(struct inode *inode, struct file *file,
                break;
        }
        if (tty->driver->ioctl) {
+               lock_kernel();
                retval = (tty->driver->ioctl)(tty, file, cmd, arg);
+               unlock_kernel();
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
        ld = tty_ldisc_ref_wait(tty);
        retval = -EINVAL;
        if (ld->ioctl) {
+               lock_kernel();
                retval = ld->ioctl(tty, file, cmd, arg);
+               unlock_kernel();
                if (retval == -ENOIOCTLCMD)
                        retval = -EINVAL;
        }
@@ -3770,6 +3810,7 @@ static void initialize_tty_struct(struct tty_struct *tty)
        mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->read_lock);
+       spin_lock_init(&tty->ctrl_lock);
        INIT_LIST_HEAD(&tty->tty_files);
        INIT_WORK(&tty->SAK_work, do_SAK_work);
 }
index f95a80b..d6353d8 100644 (file)
@@ -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:
index 1c26604..e64f0bf 100644 (file)
@@ -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:
@@ -2560,7 +2563,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = sel_loadlut(p);
                        break;
                case TIOCL_GETSHIFTSTATE:
-                       
+
        /*
         * Make it possible to react to Shift+Mousebutton.
         * Note that 'shift_state' is an undocumented
@@ -2615,6 +2618,7 @@ int tioclinux(struct tty_struct *tty, unsigned long arg)
                        ret = -EINVAL;
                        break;
        }
+       unlock_kernel();
        return ret;
 }
 
@@ -3829,7 +3833,7 @@ static int con_font_get(struct vc_data *vc, struct console_font_op *op)
                goto out;
 
        c = (font.width+7)/8 * 32 * font.charcount;
-       
+
        if (op->data && font.charcount > op->charcount)
                rc = -ENOSPC;
        if (!(op->flags & KD_FONT_FLAG_OLD)) {
index c6e72ae..9663e87 100644 (file)
@@ -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;
index 265831c..4d3702b 100644 (file)
@@ -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);