Merge branch 'for-next' into for-linus
[safe/jmp/linux-2.6] / drivers / char / tty_io.c
index 1412a8d..a42c466 100644 (file)
@@ -142,7 +142,6 @@ ssize_t redirected_tty_write(struct file *, const char __user *,
                                                        size_t, loff_t *);
 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 *);
 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,
@@ -295,7 +294,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
        struct tty_driver *p, *res = NULL;
        int tty_line = 0;
        int len;
-       char *str;
+       char *str, *stp;
 
        for (str = name; *str; str++)
                if ((*str >= '0' && *str <= '9') || *str == ',')
@@ -311,13 +310,14 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
        list_for_each_entry(p, &tty_drivers, tty_drivers) {
                if (strncmp(name, p->name, len) != 0)
                        continue;
-               if (*str == ',')
-                       str++;
-               if (*str == '\0')
-                       str = NULL;
+               stp = str;
+               if (*stp == ',')
+                       stp++;
+               if (*stp == '\0')
+                       stp = NULL;
 
                if (tty_line >= 0 && tty_line <= p->num && p->ops &&
-                   p->ops->poll_init && !p->ops->poll_init(p, tty_line, str)) {
+                   p->ops->poll_init && !p->ops->poll_init(p, tty_line, stp)) {
                        res = tty_driver_kref_get(p);
                        *line = tty_line;
                        break;
@@ -464,49 +464,12 @@ void tty_wakeup(struct tty_struct *tty)
                        tty_ldisc_deref(ld);
                }
        }
-       wake_up_interruptible(&tty->write_wait);
+       wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
 }
 
 EXPORT_SYMBOL_GPL(tty_wakeup);
 
 /**
- *     tty_ldisc_flush -       flush line discipline queue
- *     @tty: tty
- *
- *     Flush the line discipline queue (if any) for this tty. If there
- *     is no line discipline active this is a no-op.
- */
-
-void tty_ldisc_flush(struct tty_struct *tty)
-{
-       struct tty_ldisc *ld = tty_ldisc_ref(tty);
-       if (ld) {
-               if (ld->ops->flush_buffer)
-                       ld->ops->flush_buffer(tty);
-               tty_ldisc_deref(ld);
-       }
-       tty_buffer_flush(tty);
-}
-
-EXPORT_SYMBOL_GPL(tty_ldisc_flush);
-
-/**
- *     tty_reset_termios       -       reset terminal state
- *     @tty: tty to reset
- *
- *     Restore a terminal to the driver default state
- */
-
-static void tty_reset_termios(struct tty_struct *tty)
-{
-       mutex_lock(&tty->termios_mutex);
-       *tty->termios = tty->driver->init_termios;
-       tty->termios->c_ispeed = tty_termios_input_baud_rate(tty->termios);
-       tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
-       mutex_unlock(&tty->termios_mutex);
-}
-
-/**
  *     do_tty_hangup           -       actual handler for hangup events
  *     @work: tty device
  *
@@ -535,7 +498,6 @@ static void do_tty_hangup(struct work_struct *work)
        struct file *cons_filp = NULL;
        struct file *filp, *f = NULL;
        struct task_struct *p;
-       struct tty_ldisc *ld;
        int    closecount = 0, n;
        unsigned long flags;
        int refs = 0;
@@ -543,8 +505,6 @@ static void do_tty_hangup(struct work_struct *work)
        if (!tty)
                return;
 
-       /* inuse_filps is protected by the single kernel lock */
-       lock_kernel();
 
        spin_lock(&redirect_lock);
        if (redirect && redirect->private_data == tty) {
@@ -553,7 +513,10 @@ static void do_tty_hangup(struct work_struct *work)
        }
        spin_unlock(&redirect_lock);
 
+       /* inuse_filps is protected by the single kernel lock */
+       lock_kernel();
        check_tty_count(tty, "do_tty_hangup");
+
        file_list_lock();
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
        list_for_each_entry(filp, &tty->tty_files, f_u.fu_list) {
@@ -566,40 +529,8 @@ static void do_tty_hangup(struct work_struct *work)
                filp->f_op = &hung_up_tty_fops;
        }
        file_list_unlock();
-       /*
-        * FIXME! What are the locking issues here? This may me overdoing
-        * things... This question is especially important now that we've
-        * removed the irqlock.
-        */
-       ld = tty_ldisc_ref(tty);
-       if (ld != NULL) {
-               /* We may have no line discipline at this point */
-               if (ld->ops->flush_buffer)
-                       ld->ops->flush_buffer(tty);
-               tty_driver_flush_buffer(tty);
-               if ((test_bit(TTY_DO_WRITE_WAKEUP, &tty->flags)) &&
-                   ld->ops->write_wakeup)
-                       ld->ops->write_wakeup(tty);
-               if (ld->ops->hangup)
-                       ld->ops->hangup(tty);
-       }
-       /*
-        * FIXME: Once we trust the LDISC code better we can wait here for
-        * ldisc completion and fix the driver call race
-        */
-       wake_up_interruptible(&tty->write_wait);
-       wake_up_interruptible(&tty->read_wait);
-       /*
-        * Shutdown the current line discipline, and reset it to
-        * N_TTY.
-        */
-       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS)
-               tty_reset_termios(tty);
-       /* Defer ldisc switch */
-       /* tty_deferred_ldisc_switch(N_TTY);
 
-         This should get done automatically when the port closes and
-         tty_release is called */
+       tty_ldisc_hangup(tty);
 
        read_lock(&tasklist_lock);
        if (tty->session) {
@@ -628,12 +559,15 @@ static void do_tty_hangup(struct work_struct *work)
        read_unlock(&tasklist_lock);
 
        spin_lock_irqsave(&tty->ctrl_lock, flags);
-       tty->flags = 0;
+       clear_bit(TTY_THROTTLED, &tty->flags);
+       clear_bit(TTY_PUSH, &tty->flags);
+       clear_bit(TTY_DO_WRITE_WAKEUP, &tty->flags);
        put_pid(tty->session);
        put_pid(tty->pgrp);
        tty->session = NULL;
        tty->pgrp = NULL;
        tty->ctrl_status = 0;
+       set_bit(TTY_HUPPED, &tty->flags);
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 
        /* Account for the p->signal references we killed */
@@ -659,10 +593,7 @@ static void do_tty_hangup(struct work_struct *work)
         * can't yet guarantee all that.
         */
        set_bit(TTY_HUPPED, &tty->flags);
-       if (ld) {
-               tty_ldisc_enable(tty);
-               tty_ldisc_deref(ld);
-       }
+       tty_ldisc_enable(tty);
        unlock_kernel();
        if (f)
                fput(f);
@@ -777,6 +708,8 @@ void disassociate_ctty(int on_exit)
        struct tty_struct *tty;
        struct pid *tty_pgrp = NULL;
 
+       if (!current->signal->leader)
+               return;
 
        tty = get_current_tty();
        if (tty) {
@@ -842,8 +775,7 @@ void no_tty(void)
 {
        struct task_struct *tsk = current;
        lock_kernel();
-       if (tsk->signal->leader)
-               disassociate_ctty(0);
+       disassociate_ctty(0);
        unlock_kernel();
        proc_clear_tty(tsk);
 }
@@ -879,7 +811,7 @@ void stop_tty(struct tty_struct *tty)
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_START;
                tty->ctrl_status |= TIOCPKT_STOP;
-               wake_up_interruptible(&tty->link->read_wait);
+               wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
        }
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (tty->ops->stop)
@@ -913,7 +845,7 @@ void start_tty(struct tty_struct *tty)
        if (tty->link && tty->link->packet) {
                tty->ctrl_status &= ~TIOCPKT_STOP;
                tty->ctrl_status |= TIOCPKT_START;
-               wake_up_interruptible(&tty->link->read_wait);
+               wake_up_interruptible_poll(&tty->link->read_wait, POLLIN);
        }
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
        if (tty->ops->start)
@@ -970,7 +902,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 void tty_write_unlock(struct tty_struct *tty)
 {
        mutex_unlock(&tty->atomic_write_lock);
-       wake_up_interruptible(&tty->write_wait);
+       wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
 }
 
 int tty_write_lock(struct tty_struct *tty, int ndelay)
@@ -1086,14 +1018,16 @@ out:
 
 void tty_write_message(struct tty_struct *tty, char *msg)
 {
-       lock_kernel();
        if (tty) {
                mutex_lock(&tty->atomic_write_lock);
-               if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+               lock_kernel();
+               if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+                       unlock_kernel();
                        tty->ops->write(tty, msg, strlen(msg));
+               } else
+                       unlock_kernel();
                tty_write_unlock(tty);
        }
-       unlock_kernel();
        return;
 }
 
@@ -1111,9 +1045,7 @@ void tty_write_message(struct tty_struct *tty, char *msg)
  *             Locks the line discipline as required
  *             Writes to the tty driver are serialized by the atomic_write_lock
  *     and are then processed in chunks to the device. The line discipline
- *     write method will not be involked in parallel for each device
- *             The line discipline write method is called under the big
- *     kernel lock for historical reasons. New code should not rely on this.
+ *     write method will not be invoked in parallel for each device.
  */
 
 static ssize_t tty_write(struct file *file, const char __user *buf,
@@ -1213,7 +1145,7 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
  *     be held until the 'fast-open' is also done. Will change once we
  *     have refcounting in the driver and per driver locking
  */
-struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
+static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
                struct inode *inode, int idx)
 {
        struct tty_struct *tty;
@@ -1255,6 +1187,7 @@ int tty_init_termios(struct tty_struct *tty)
        tty->termios->c_ospeed = tty_termios_baud_rate(tty->termios);
        return 0;
 }
+EXPORT_SYMBOL_GPL(tty_init_termios);
 
 /**
  *     tty_driver_install_tty() - install a tty entry in the driver
@@ -1272,14 +1205,21 @@ static int tty_driver_install_tty(struct tty_driver *driver,
                                                struct tty_struct *tty)
 {
        int idx = tty->index;
+       int ret;
 
-       if (driver->ops->install)
-               return driver->ops->install(driver, tty);
+       if (driver->ops->install) {
+               lock_kernel();
+               ret = driver->ops->install(driver, tty);
+               unlock_kernel();
+               return ret;
+       }
 
        if (tty_init_termios(tty) == 0) {
+               lock_kernel();
                tty_driver_kref_get(driver);
                tty->count++;
                driver->ttys[idx] = tty;
+               unlock_kernel();
                return 0;
        }
        return -ENOMEM;
@@ -1334,7 +1274,9 @@ static int tty_reopen(struct tty_struct *tty)
        tty->count++;
        tty->driver = driver; /* N.B. why do this every time?? */
 
+       mutex_lock(&tty->ldisc_mutex);
        WARN_ON(!test_bit(TTY_LDISC, &tty->flags));
+       mutex_unlock(&tty->ldisc_mutex);
 
        return 0;
 }
@@ -1370,10 +1312,14 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
        struct tty_struct *tty;
        int retval;
 
+       lock_kernel();
        /* Check if pty master is being opened multiple times */
        if (driver->subtype == PTY_TYPE_MASTER &&
-               (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok)
+               (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+               unlock_kernel();
                return ERR_PTR(-EIO);
+       }
+       unlock_kernel();
 
        /*
         * First time open is complex, especially for PTY devices.
@@ -1403,7 +1349,6 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
         * If we fail here just call release_tty to clean up.  No need
         * to decrement the use counts, as release_tty doesn't care.
         */
-
        retval = tty_ldisc_setup(tty, tty->link);
        if (retval)
                goto release_mem_out;
@@ -1418,7 +1363,9 @@ release_mem_out:
        if (printk_ratelimit())
                printk(KERN_INFO "tty_init_dev: ldisc open failed, "
                                 "clearing slot %d\n", idx);
+       lock_kernel();
        release_tty(tty, idx);
+       unlock_kernel();
        return ERR_PTR(retval);
 }
 
@@ -1455,16 +1402,19 @@ EXPORT_SYMBOL(tty_shutdown);
  *             tty_mutex - sometimes only
  *             takes the file list lock internally when working on the list
  *     of ttys that the driver keeps.
+ *
+ *     This method gets called from a work queue so that the driver private
+ *     cleanup ops can sleep (needed for USB at least)
  */
-static void release_one_tty(struct kref *kref)
+static void release_one_tty(struct work_struct *work)
 {
-       struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+       struct tty_struct *tty =
+               container_of(work, struct tty_struct, hangup_work);
        struct tty_driver *driver = tty->driver;
 
-       if (tty->ops->shutdown)
-               tty->ops->shutdown(tty);
-       else
-               tty_shutdown(tty);
+       if (tty->ops->cleanup)
+               tty->ops->cleanup(tty);
+
        tty->magic = 0;
        tty_driver_kref_put(driver);
        module_put(driver->owner);
@@ -1476,6 +1426,21 @@ static void release_one_tty(struct kref *kref)
        free_tty_struct(tty);
 }
 
+static void queue_release_one_tty(struct kref *kref)
+{
+       struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+
+       if (tty->ops->shutdown)
+               tty->ops->shutdown(tty);
+       else
+               tty_shutdown(tty);
+
+       /* The hangup queue is now free so we can reuse it rather than
+          waste a chunk of memory for each port */
+       INIT_WORK(&tty->hangup_work, release_one_tty);
+       schedule_work(&tty->hangup_work);
+}
+
 /**
  *     tty_kref_put            -       release a tty kref
  *     @tty: tty device
@@ -1487,7 +1452,7 @@ static void release_one_tty(struct kref *kref)
 void tty_kref_put(struct tty_struct *tty)
 {
        if (tty)
-               kref_put(&tty->kref, release_one_tty);
+               kref_put(&tty->kref, queue_release_one_tty);
 }
 EXPORT_SYMBOL(tty_kref_put);
 
@@ -1514,7 +1479,17 @@ static void release_tty(struct tty_struct *tty, int idx)
        tty_kref_put(tty);
 }
 
-/*
+/**
+ *     tty_release             -       vfs callback for close
+ *     @inode: inode of tty
+ *     @filp: file pointer for handle to tty
+ *
+ *     Called the last time each file handle is closed that references
+ *     this tty. There may however be several such references.
+ *
+ *     Locking:
+ *             Takes bkl. See tty_release_dev
+ *
  * Even releasing the tty structures is a tricky business.. We have
  * to be very careful that the structures are all released at the
  * same time, as interrupts might otherwise get the wrong pointers.
@@ -1522,20 +1497,20 @@ static void release_tty(struct tty_struct *tty, int idx)
  * WSH 09/09/97: rewritten to avoid some nasty race conditions that could
  * lead to double frees or releasing memory still in use.
  */
-void tty_release_dev(struct file *filp)
+
+int tty_release(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty, *o_tty;
        int     pty_master, tty_closing, o_tty_closing, do_sleep;
        int     devpts;
        int     idx;
        char    buf[64];
-       struct  inode *inode;
 
-       inode = filp->f_path.dentry->d_inode;
        tty = (struct tty_struct *)filp->private_data;
        if (tty_paranoia_check(tty, inode, "tty_release_dev"))
-               return;
+               return 0;
 
+       lock_kernel();
        check_tty_count(tty, "tty_release_dev");
 
        tty_fasync(-1, filp, 0);
@@ -1550,19 +1525,22 @@ void tty_release_dev(struct file *filp)
        if (idx < 0 || idx >= tty->driver->num) {
                printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
                                  "free (%s)\n", tty->name);
-               return;
+               unlock_kernel();
+               return 0;
        }
        if (!devpts) {
                if (tty != tty->driver->ttys[idx]) {
+                       unlock_kernel();
                        printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
                               "for (%s)\n", idx, tty->name);
-                       return;
+                       return 0;
                }
                if (tty->termios != tty->driver->termios[idx]) {
+                       unlock_kernel();
                        printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
                               "for (%s)\n",
                               idx, tty->name);
-                       return;
+                       return 0;
                }
        }
 #endif
@@ -1576,26 +1554,30 @@ void tty_release_dev(struct file *filp)
        if (tty->driver->other &&
             !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
                if (o_tty != tty->driver->other->ttys[idx]) {
+                       unlock_kernel();
                        printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
                                          "not o_tty for (%s)\n",
                               idx, tty->name);
-                       return;
+                       return 0 ;
                }
                if (o_tty->termios != tty->driver->other->termios[idx]) {
+                       unlock_kernel();
                        printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
                                          "not o_termios for (%s)\n",
                               idx, tty->name);
-                       return;
+                       return 0;
                }
                if (o_tty->link != tty) {
+                       unlock_kernel();
                        printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
-                       return;
+                       return 0;
                }
        }
 #endif
        if (tty->ops->close)
                tty->ops->close(tty, filp);
 
+       unlock_kernel();
        /*
         * Sanity check: if tty->count is going to zero, there shouldn't be
         * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -1618,6 +1600,7 @@ void tty_release_dev(struct file *filp)
                   opens on /dev/tty */
 
                mutex_lock(&tty_mutex);
+               lock_kernel();
                tty_closing = tty->count <= 1;
                o_tty_closing = o_tty &&
                        (o_tty->count <= (pty_master ? 1 : 0));
@@ -1625,21 +1608,21 @@ void tty_release_dev(struct file *filp)
 
                if (tty_closing) {
                        if (waitqueue_active(&tty->read_wait)) {
-                               wake_up(&tty->read_wait);
+                               wake_up_poll(&tty->read_wait, POLLIN);
                                do_sleep++;
                        }
                        if (waitqueue_active(&tty->write_wait)) {
-                               wake_up(&tty->write_wait);
+                               wake_up_poll(&tty->write_wait, POLLOUT);
                                do_sleep++;
                        }
                }
                if (o_tty_closing) {
                        if (waitqueue_active(&o_tty->read_wait)) {
-                               wake_up(&o_tty->read_wait);
+                               wake_up_poll(&o_tty->read_wait, POLLIN);
                                do_sleep++;
                        }
                        if (waitqueue_active(&o_tty->write_wait)) {
-                               wake_up(&o_tty->write_wait);
+                               wake_up_poll(&o_tty->write_wait, POLLOUT);
                                do_sleep++;
                        }
                }
@@ -1648,6 +1631,7 @@ void tty_release_dev(struct file *filp)
 
                printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
                                    "active!\n", tty_name(tty, buf));
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                schedule();
        }
@@ -1711,8 +1695,10 @@ void tty_release_dev(struct file *filp)
        mutex_unlock(&tty_mutex);
 
        /* check whether both sides are closing ... */
-       if (!tty_closing || (o_tty && !o_tty_closing))
-               return;
+       if (!tty_closing || (o_tty && !o_tty_closing)) {
+               unlock_kernel();
+               return 0;
+       }
 
 #ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "freeing tty structure...");
@@ -1730,10 +1716,12 @@ void tty_release_dev(struct file *filp)
        /* Make this pty number available for reallocation */
        if (devpts)
                devpts_kill_index(inode, idx);
+       unlock_kernel();
+       return 0;
 }
 
 /**
- *     __tty_open              -       open a tty device
+ *     tty_open                -       open a tty device
  *     @inode: inode of device file
  *     @filp: file pointer to tty
  *
@@ -1753,14 +1741,14 @@ void tty_release_dev(struct file *filp)
  *              ->siglock protects ->signal/->sighand
  */
 
-static int __tty_open(struct inode *inode, struct file *filp)
+static int tty_open(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty = NULL;
        int noctty, retval;
        struct tty_driver *driver;
        int index;
        dev_t device = inode->i_rdev;
-       unsigned short saved_flags = filp->f_flags;
+       unsigned saved_flags = filp->f_flags;
 
        nonseekable_open(inode, filp);
 
@@ -1770,10 +1758,12 @@ retry_open:
        retval = 0;
 
        mutex_lock(&tty_mutex);
+       lock_kernel();
 
        if (device == MKDEV(TTYAUX_MAJOR, 0)) {
                tty = get_current_tty();
                if (!tty) {
+                       unlock_kernel();
                        mutex_unlock(&tty_mutex);
                        return -ENXIO;
                }
@@ -1805,12 +1795,14 @@ retry_open:
                                goto got_driver;
                        }
                }
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
 
        driver = get_tty_driver(device, &index);
        if (!driver) {
+               unlock_kernel();
                mutex_unlock(&tty_mutex);
                return -ENODEV;
        }
@@ -1819,8 +1811,11 @@ got_driver:
                /* check whether we're reopening an existing tty */
                tty = tty_driver_lookup_tty(driver, inode, index);
 
-               if (IS_ERR(tty))
+               if (IS_ERR(tty)) {
+                       unlock_kernel();
+                       mutex_unlock(&tty_mutex);
                        return PTR_ERR(tty);
+               }
        }
 
        if (tty) {
@@ -1832,8 +1827,10 @@ got_driver:
 
        mutex_unlock(&tty_mutex);
        tty_driver_kref_put(driver);
-       if (IS_ERR(tty))
+       if (IS_ERR(tty)) {
+               unlock_kernel();
                return PTR_ERR(tty);
+       }
 
        filp->private_data = tty;
        file_move(filp, &tty->tty_files);
@@ -1861,11 +1858,15 @@ got_driver:
                printk(KERN_DEBUG "error %d in opening %s...", retval,
                       tty->name);
 #endif
-               tty_release_dev(filp);
-               if (retval != -ERESTARTSYS)
+               tty_release(inode, filp);
+               if (retval != -ERESTARTSYS) {
+                       unlock_kernel();
                        return retval;
-               if (signal_pending(current))
+               }
+               if (signal_pending(current)) {
+                       unlock_kernel();
                        return retval;
+               }
                schedule();
                /*
                 * Need to reset f_op in case a hangup happened.
@@ -1874,8 +1875,11 @@ got_driver:
                        filp->f_op = &tty_fops;
                goto retry_open;
        }
+       unlock_kernel();
+
 
        mutex_lock(&tty_mutex);
+       lock_kernel();
        spin_lock_irq(&current->sighand->siglock);
        if (!noctty &&
            current->signal->leader &&
@@ -1883,43 +1887,12 @@ got_driver:
            tty->session == NULL)
                __proc_set_tty(current, tty);
        spin_unlock_irq(&current->sighand->siglock);
+       unlock_kernel();
        mutex_unlock(&tty_mutex);
        return 0;
 }
 
-/* BKL pushdown: scary code avoidance wrapper */
-static int tty_open(struct inode *inode, struct file *filp)
-{
-       int ret;
-
-       lock_kernel();
-       ret = __tty_open(inode, filp);
-       unlock_kernel();
-       return ret;
-}
-
-
-
-
-/**
- *     tty_release             -       vfs callback for close
- *     @inode: inode of tty
- *     @filp: file pointer for handle to tty
- *
- *     Called the last time each file handle is closed that references
- *     this tty. There may however be several such references.
- *
- *     Locking:
- *             Takes bkl. See tty_release_dev
- */
 
-static int tty_release(struct inode *inode, struct file *filp)
-{
-       lock_kernel();
-       tty_release_dev(filp);
-       unlock_kernel();
-       return 0;
-}
 
 /**
  *     tty_poll        -       check tty status
@@ -1978,8 +1951,10 @@ static int tty_fasync(int fd, struct file *filp, int on)
                        pid = task_pid(current);
                        type = PIDTYPE_PID;
                }
+               get_pid(pid);
                spin_unlock_irqrestore(&tty->ctrl_lock, flags);
                retval = __f_setown(filp, pid, type, 0);
+               put_pid(pid);
                if (retval)
                        goto out;
        } else {
@@ -2018,6 +1993,7 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
                return -EPERM;
        if (get_user(ch, p))
                return -EFAULT;
+       tty_audit_tiocsti(tty, ch);
        ld = tty_ldisc_ref_wait(tty);
        ld->ops->receive_buf(tty, &ch, &mbz, 1);
        tty_ldisc_deref(ld);
@@ -2049,49 +2025,41 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
 /**
  *     tty_do_resize           -       resize event
  *     @tty: tty being resized
- *     @real_tty: real tty (not the same as tty if using a pty/tty pair)
  *     @rows: rows (character)
  *     @cols: cols (character)
  *
- *     Update the termios variables and send the neccessary signals to
+ *     Update the termios variables and send the necessary signals to
  *     peform a terminal resize correctly
  */
 
-int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
-                                       struct winsize *ws)
+int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
 {
-       struct pid *pgrp, *rpgrp;
+       struct pid *pgrp;
        unsigned long flags;
 
-       /* For a PTY we need to lock the tty side */
-       mutex_lock(&real_tty->termios_mutex);
-       if (!memcmp(ws, &real_tty->winsize, sizeof(*ws)))
+       /* Lock the tty */
+       mutex_lock(&tty->termios_mutex);
+       if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
                goto done;
        /* Get the PID values and reference them so we can
           avoid holding the tty ctrl lock while sending signals */
        spin_lock_irqsave(&tty->ctrl_lock, flags);
        pgrp = get_pid(tty->pgrp);
-       rpgrp = get_pid(real_tty->pgrp);
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 
        if (pgrp)
                kill_pgrp(pgrp, SIGWINCH, 1);
-       if (rpgrp != pgrp && rpgrp)
-               kill_pgrp(rpgrp, SIGWINCH, 1);
-
        put_pid(pgrp);
-       put_pid(rpgrp);
 
        tty->winsize = *ws;
-       real_tty->winsize = *ws;
 done:
-       mutex_unlock(&real_tty->termios_mutex);
+       mutex_unlock(&tty->termios_mutex);
        return 0;
 }
 
 /**
  *     tiocswinsz              -       implement window size set ioctl
- *     @tty; tty
+ *     @tty; tty side of tty
  *     @arg: user buffer for result
  *
  *     Copies the user idea of the window size to the kernel. Traditionally
@@ -2104,17 +2072,16 @@ done:
  *     then calls into the default method.
  */
 
-static int tiocswinsz(struct tty_struct *tty, struct tty_struct *real_tty,
-       struct winsize __user *arg)
+static int tiocswinsz(struct tty_struct *tty, struct winsize __user *arg)
 {
        struct winsize tmp_ws;
        if (copy_from_user(&tmp_ws, arg, sizeof(*arg)))
                return -EFAULT;
 
        if (tty->ops->resize)
-               return tty->ops->resize(tty, real_tty, &tmp_ws);
+               return tty->ops->resize(tty, &tmp_ws);
        else
-               return tty_do_resize(tty, real_tty, &tmp_ws);
+               return tty_do_resize(tty, &tmp_ws);
 }
 
 /**
@@ -2160,7 +2127,7 @@ static int tioccons(struct file *file)
  *     the generic functionality existed. This piece of history is preserved
  *     in the expected tty API of posix OS's.
  *
- *     Locking: none, the open fle handle ensures it won't go away.
+ *     Locking: none, the open file handle ensures it won't go away.
  */
 
 static int fionbio(struct file *file, int __user *p)
@@ -2170,13 +2137,12 @@ 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();
+       spin_lock(&file->f_lock);
        if (nonblock)
                file->f_flags |= O_NONBLOCK;
        else
                file->f_flags &= ~O_NONBLOCK;
-       unlock_kernel();
+       spin_unlock(&file->f_lock);
        return 0;
 }
 
@@ -2374,9 +2340,7 @@ static int tiocsetd(struct tty_struct *tty, int __user *p)
        if (get_user(ldisc, p))
                return -EFAULT;
 
-       lock_kernel();
        ret = tty_set_ldisc(tty, ldisc);
-       unlock_kernel();
 
        return ret;
 }
@@ -2489,6 +2453,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
        return tty->ops->tiocmset(tty, file, set, clear);
 }
 
+struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+{
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+               tty = tty->link;
+       return tty;
+}
+EXPORT_SYMBOL(tty_pair_get_tty);
+
+struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+{
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+           return tty;
+       return tty->link;
+}
+EXPORT_SYMBOL(tty_pair_get_pty);
+
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
@@ -2504,11 +2486,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       real_tty = tty;
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_MASTER)
-               real_tty = tty->link;
-
+       real_tty = tty_pair_get_tty(tty);
 
        /*
         * Factor out some common prep work
@@ -2539,7 +2517,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCGWINSZ:
                return tiocgwinsz(real_tty, p);
        case TIOCSWINSZ:
-               return tiocswinsz(tty, real_tty, p);
+               return tiocswinsz(real_tty, p);
        case TIOCCONS:
                return real_tty != tty ? -EINVAL : tioccons(file);
        case FIONBIO:
@@ -2564,7 +2542,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCGSID:
                return tiocgsid(tty, real_tty, p);
        case TIOCGETD:
-               return put_user(tty->ldisc.ops->num, (int __user *)p);
+               return put_user(tty->ldisc->ops->num, (int __user *)p);
        case TIOCSETD:
                return tiocsetd(tty, p);
        /*
@@ -2690,7 +2668,7 @@ void __do_SAK(struct tty_struct *tty)
        /* Kill the entire session */
        do_each_pid_task(session, PIDTYPE_SID, p) {
                printk(KERN_NOTICE "SAK: killed process %d"
-                       " (%s): task_session_nr(p)==tty->session\n",
+                       " (%s): task_session(p)==tty->session\n",
                        task_pid_nr(p), p->comm);
                send_sig(SIGKILL, p, 1);
        } while_each_pid_task(session, PIDTYPE_SID, p);
@@ -2700,7 +2678,7 @@ void __do_SAK(struct tty_struct *tty)
        do_each_thread(g, p) {
                if (p->signal->tty == tty) {
                        printk(KERN_NOTICE "SAK: killed process %d"
-                           " (%s): task_session_nr(p)==tty->session\n",
+                           " (%s): task_session(p)==tty->session\n",
                            task_pid_nr(p), p->comm);
                        send_sig(SIGKILL, p, 1);
                        continue;
@@ -2779,11 +2757,14 @@ void initialize_tty_struct(struct tty_struct *tty,
        tty->buf.head = tty->buf.tail = NULL;
        tty_buffer_init(tty);
        mutex_init(&tty->termios_mutex);
+       mutex_init(&tty->ldisc_mutex);
        init_waitqueue_head(&tty->write_wait);
        init_waitqueue_head(&tty->read_wait);
        INIT_WORK(&tty->hangup_work, do_tty_hangup);
        mutex_init(&tty->atomic_read_lock);
        mutex_init(&tty->atomic_write_lock);
+       mutex_init(&tty->output_lock);
+       mutex_init(&tty->echo_lock);
        spin_lock_init(&tty->read_lock);
        spin_lock_init(&tty->ctrl_lock);
        INIT_LIST_HEAD(&tty->tty_files);
@@ -3115,11 +3096,22 @@ void __init console_init(void)
        }
 }
 
+static char *tty_devnode(struct device *dev, mode_t *mode)
+{
+       if (!mode)
+               return NULL;
+       if (dev->devt == MKDEV(TTYAUX_MAJOR, 0) ||
+           dev->devt == MKDEV(TTYAUX_MAJOR, 2))
+               *mode = 0666;
+       return NULL;
+}
+
 static int __init tty_class_init(void)
 {
        tty_class = class_create(THIS_MODULE, "tty");
        if (IS_ERR(tty_class))
                return PTR_ERR(tty_class);
+       tty_class->devnode = tty_devnode;
        return 0;
 }