xen-netback: support frontends without feature-rx-notify again
[cascardo/linux.git] / drivers / tty / tty_io.c
index 0508a1d..4f35b43 100644 (file)
@@ -153,8 +153,6 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
 static int __tty_fasync(int fd, struct file *filp, int on);
 static int tty_fasync(int fd, struct file *filp, int on);
 static void release_tty(struct tty_struct *tty, int idx);
-static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
-static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty);
 
 /**
  *     free_tty_struct         -       free a disused tty
@@ -169,8 +167,7 @@ void free_tty_struct(struct tty_struct *tty)
 {
        if (!tty)
                return;
-       if (tty->dev)
-               put_device(tty->dev);
+       put_device(tty->dev);
        kfree(tty->write_buf);
        tty->magic = 0xDEADDEAD;
        kfree(tty);
@@ -277,6 +274,7 @@ int tty_paranoia_check(struct tty_struct *tty, struct inode *inode,
        return 0;
 }
 
+/* Caller must hold tty_lock */
 static int check_tty_count(struct tty_struct *tty, const char *routine)
 {
 #ifdef CHECK_TTY_COUNT
@@ -492,6 +490,78 @@ static const struct file_operations hung_up_tty_fops = {
 static DEFINE_SPINLOCK(redirect_lock);
 static struct file *redirect;
 
+
+void proc_clear_tty(struct task_struct *p)
+{
+       unsigned long flags;
+       struct tty_struct *tty;
+       spin_lock_irqsave(&p->sighand->siglock, flags);
+       tty = p->signal->tty;
+       p->signal->tty = NULL;
+       spin_unlock_irqrestore(&p->sighand->siglock, flags);
+       tty_kref_put(tty);
+}
+
+/**
+ * proc_set_tty -  set the controlling terminal
+ *
+ * Only callable by the session leader and only if it does not already have
+ * a controlling terminal.
+ *
+ * Caller must hold:  tty_lock()
+ *                   a readlock on tasklist_lock
+ *                   sighand lock
+ */
+static void __proc_set_tty(struct tty_struct *tty)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&tty->ctrl_lock, flags);
+       /*
+        * The session and fg pgrp references will be non-NULL if
+        * tiocsctty() is stealing the controlling tty
+        */
+       put_pid(tty->session);
+       put_pid(tty->pgrp);
+       tty->pgrp = get_pid(task_pgrp(current));
+       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+       tty->session = get_pid(task_session(current));
+       if (current->signal->tty) {
+               printk(KERN_DEBUG "tty not NULL!!\n");
+               tty_kref_put(current->signal->tty);
+       }
+       put_pid(current->signal->tty_old_pgrp);
+       current->signal->tty = tty_kref_get(tty);
+       current->signal->tty_old_pgrp = NULL;
+}
+
+static void proc_set_tty(struct tty_struct *tty)
+{
+       spin_lock_irq(&current->sighand->siglock);
+       __proc_set_tty(tty);
+       spin_unlock_irq(&current->sighand->siglock);
+}
+
+struct tty_struct *get_current_tty(void)
+{
+       struct tty_struct *tty;
+       unsigned long flags;
+
+       spin_lock_irqsave(&current->sighand->siglock, flags);
+       tty = tty_kref_get(current->signal->tty);
+       spin_unlock_irqrestore(&current->sighand->siglock, flags);
+       return tty;
+}
+EXPORT_SYMBOL_GPL(get_current_tty);
+
+static void session_clear_tty(struct pid *session)
+{
+       struct task_struct *p;
+       do_each_pid_task(session, PIDTYPE_SID, p) {
+               proc_clear_tty(p);
+       } while_each_pid_task(session, PIDTYPE_SID, p);
+}
+
 /**
  *     tty_wakeup      -       request more data
  *     @tty: terminal
@@ -620,9 +690,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
                return;
        }
 
-       /* some functions below drop BTM, so we need this bit */
-       set_bit(TTY_HUPPING, &tty->flags);
-
        /* inuse_filps is protected by the single tty lock,
           this really needs to change if we want to flush the
           workqueue with the lock held */
@@ -647,10 +714,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
        while (refs--)
                tty_kref_put(tty);
 
-       /*
-        * it drops BTM and thus races with reopen
-        * we protect the race by TTY_HUPPING
-        */
        tty_ldisc_hangup(tty);
 
        spin_lock_irq(&tty->ctrl_lock);
@@ -682,8 +745,6 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
         * can't yet guarantee all that.
         */
        set_bit(TTY_HUPPED, &tty->flags);
-       clear_bit(TTY_HUPPING, &tty->flags);
-
        tty_unlock(tty);
 
        if (f)
@@ -792,14 +853,6 @@ int tty_hung_up_p(struct file *filp)
 
 EXPORT_SYMBOL(tty_hung_up_p);
 
-static void session_clear_tty(struct pid *session)
-{
-       struct task_struct *p;
-       do_each_pid_task(session, PIDTYPE_SID, p) {
-               proc_clear_tty(p);
-       } while_each_pid_task(session, PIDTYPE_SID, p);
-}
-
 /**
  *     disassociate_ctty       -       disconnect controlling tty
  *     @on_exit: true if exiting so need to "hang up" the session
@@ -927,7 +980,7 @@ void __stop_tty(struct tty_struct *tty)
                return;
        tty->stopped = 1;
        if (tty->ops->stop)
-               (tty->ops->stop)(tty);
+               tty->ops->stop(tty);
 }
 
 void stop_tty(struct tty_struct *tty)
@@ -958,7 +1011,7 @@ void __start_tty(struct tty_struct *tty)
                return;
        tty->stopped = 0;
        if (tty->ops->start)
-               (tty->ops->start)(tty);
+               tty->ops->start(tty);
        tty_wakeup(tty);
 }
 
@@ -1012,7 +1065,7 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
           situation */
        ld = tty_ldisc_ref_wait(tty);
        if (ld->ops->read)
-               i = (ld->ops->read)(tty, file, buf, count);
+               i = ld->ops->read(tty, file, buf, count);
        else
                i = -EIO;
        tty_ldisc_deref(ld);
@@ -1024,14 +1077,12 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
 }
 
 static void tty_write_unlock(struct tty_struct *tty)
-       __releases(&tty->atomic_write_lock)
 {
        mutex_unlock(&tty->atomic_write_lock);
        wake_up_interruptible_poll(&tty->write_wait, POLLOUT);
 }
 
 static int tty_write_lock(struct tty_struct *tty, int ndelay)
-       __acquires(&tty->atomic_write_lock)
 {
        if (!mutex_trylock(&tty->atomic_write_lock)) {
                if (ndelay)
@@ -1146,7 +1197,7 @@ void tty_write_message(struct tty_struct *tty, char *msg)
        if (tty) {
                mutex_lock(&tty->atomic_write_lock);
                tty_lock(tty);
-               if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags)) {
+               if (tty->ops->write && tty->count > 0) {
                        tty_unlock(tty);
                        tty->ops->write(tty, msg, strlen(msg));
                } else
@@ -1293,19 +1344,24 @@ static ssize_t tty_line_name(struct tty_driver *driver, int index, char *p)
  *     @driver: the driver for the tty
  *     @idx:    the minor number
  *
- *     Return the tty, if found or ERR_PTR() otherwise.
+ *     Return the tty, if found. If not found, return NULL or ERR_PTR() if the
+ *     driver lookup() method returns an error.
  *
- *     Locking: tty_mutex must be held. If tty is found, the mutex must
- *     be held until the 'fast-open' is also done. Will change once we
- *     have refcounting in the driver and per driver locking
+ *     Locking: tty_mutex must be held. If the tty is found, bump the tty kref.
  */
 static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
                struct inode *inode, int idx)
 {
+       struct tty_struct *tty;
+
        if (driver->ops->lookup)
-               return driver->ops->lookup(driver, inode, idx);
+               tty = driver->ops->lookup(driver, inode, idx);
+       else
+               tty = driver->ttys[idx];
 
-       return driver->ttys[idx];
+       if (!IS_ERR(tty))
+               tty_kref_get(tty);
+       return tty;
 }
 
 /**
@@ -1393,29 +1449,21 @@ void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
  *     @tty    - the tty to open
  *
  *     Return 0 on success, -errno on error.
+ *     Re-opens on master ptys are not allowed and return -EIO.
  *
- *     Locking: tty_mutex must be held from the time the tty was found
- *              till this open completes.
+ *     Locking: Caller must hold tty_lock
  */
 static int tty_reopen(struct tty_struct *tty)
 {
        struct tty_driver *driver = tty->driver;
 
-       if (test_bit(TTY_CLOSING, &tty->flags) ||
-                       test_bit(TTY_HUPPING, &tty->flags))
+       if (!tty->count)
                return -EIO;
 
        if (driver->type == TTY_DRIVER_TYPE_PTY &&
-           driver->subtype == PTY_TYPE_MASTER) {
-               /*
-                * special case for PTY masters: only one open permitted,
-                * and the slave side open count is incremented as well.
-                */
-               if (tty->count)
-                       return -EIO;
+           driver->subtype == PTY_TYPE_MASTER)
+               return -EIO;
 
-               tty->link->count++;
-       }
        tty->count++;
 
        WARN_ON(!tty->ldisc);
@@ -1535,15 +1583,19 @@ void tty_free_termios(struct tty_struct *tty)
 EXPORT_SYMBOL(tty_free_termios);
 
 /**
- *     tty_flush_works         -       flush all works of a tty
- *     @tty: tty device to flush works for
+ *     tty_flush_works         -       flush all works of a tty/pty pair
+ *     @tty: tty device to flush works for (or either end of a pty pair)
  *
- *     Sync flush all works belonging to @tty.
+ *     Sync flush all works belonging to @tty (and the 'other' tty).
  */
 static void tty_flush_works(struct tty_struct *tty)
 {
        flush_work(&tty->SAK_work);
        flush_work(&tty->hangup_work);
+       if (tty->link) {
+               flush_work(&tty->link->SAK_work);
+               flush_work(&tty->link->hangup_work);
+       }
 }
 
 /**
@@ -1635,8 +1687,7 @@ static void release_tty(struct tty_struct *tty, int idx)
                tty->link->port->itty = NULL;
        cancel_work_sync(&tty->port->buf.work);
 
-       if (tty->link)
-               tty_kref_put(tty->link);
+       tty_kref_put(tty->link);
        tty_kref_put(tty);
 }
 
@@ -1649,8 +1700,7 @@ static void release_tty(struct tty_struct *tty, int idx)
  *     Performs some paranoid checking before true release of the @tty.
  *     This is a no-op unless TTY_PARANOIA_CHECK is defined.
  */
-static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty,
-               int idx)
+static int tty_release_checks(struct tty_struct *tty, int idx)
 {
 #ifdef TTY_PARANOIA_CHECK
        if (idx < 0 || idx >= tty->driver->num) {
@@ -1669,6 +1719,8 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty,
                return -1;
        }
        if (tty->driver->other) {
+               struct tty_struct *o_tty = tty->link;
+
                if (o_tty != tty->driver->other->ttys[idx]) {
                        printk(KERN_DEBUG "%s: other->table[%d] not o_tty for (%s)\n",
                                        __func__, idx, tty->name);
@@ -1705,8 +1757,8 @@ static int tty_release_checks(struct tty_struct *tty, struct tty_struct *o_tty,
 int tty_release(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty = file_tty(filp);
-       struct tty_struct *o_tty;
-       int     pty_master, tty_closing, o_tty_closing, do_sleep;
+       struct tty_struct *o_tty = NULL;
+       int     do_sleep, final;
        int     idx;
        char    buf[64];
        long    timeout = 0;
@@ -1721,12 +1773,11 @@ int tty_release(struct inode *inode, struct file *filp)
        __tty_fasync(-1, filp, 0);
 
        idx = tty->index;
-       pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-                     tty->driver->subtype == PTY_TYPE_MASTER);
-       /* Review: parallel close */
-       o_tty = tty->link;
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+               o_tty = tty->link;
 
-       if (tty_release_checks(tty, o_tty, idx)) {
+       if (tty_release_checks(tty, idx)) {
                tty_unlock(tty);
                return 0;
        }
@@ -1739,7 +1790,9 @@ int tty_release(struct inode *inode, struct file *filp)
        if (tty->ops->close)
                tty->ops->close(tty, filp);
 
-       tty_unlock(tty);
+       /* If tty is pty master, lock the slave pty (stable lock order) */
+       tty_lock_slave(o_tty);
+
        /*
         * 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
@@ -1750,25 +1803,13 @@ int tty_release(struct inode *inode, struct file *filp)
         * The test for the o_tty closing is necessary, since the master and
         * slave sides may close in any order.  If the slave side closes out
         * first, its count will be one, since the master side holds an open.
-        * Thus this test wouldn't be triggered at the time the slave closes,
+        * Thus this test wouldn't be triggered at the time the slave closed,
         * so we do it now.
-        *
-        * Note that it's possible for the tty to be opened again while we're
-        * flushing out waiters.  By recalculating the closing flags before
-        * each iteration we avoid any problems.
         */
        while (1) {
-               /* Guard against races with tty->count changes elsewhere and
-                  opens on /dev/tty */
-
-               mutex_lock(&tty_mutex);
-               tty_lock_pair(tty, o_tty);
-               tty_closing = tty->count <= 1;
-               o_tty_closing = o_tty &&
-                       (o_tty->count <= (pty_master ? 1 : 0));
                do_sleep = 0;
 
-               if (tty_closing) {
+               if (tty->count <= 1) {
                        if (waitqueue_active(&tty->read_wait)) {
                                wake_up_poll(&tty->read_wait, POLLIN);
                                do_sleep++;
@@ -1778,7 +1819,7 @@ int tty_release(struct inode *inode, struct file *filp)
                                do_sleep++;
                        }
                }
-               if (o_tty_closing) {
+               if (o_tty && o_tty->count <= 1) {
                        if (waitqueue_active(&o_tty->read_wait)) {
                                wake_up_poll(&o_tty->read_wait, POLLIN);
                                do_sleep++;
@@ -1796,8 +1837,6 @@ int tty_release(struct inode *inode, struct file *filp)
                        printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
                               __func__, tty_name(tty, buf));
                }
-               tty_unlock_pair(tty, o_tty);
-               mutex_unlock(&tty_mutex);
                schedule_timeout_killable(timeout);
                if (timeout < 120 * HZ)
                        timeout = 2 * timeout + 1;
@@ -1805,15 +1844,7 @@ int tty_release(struct inode *inode, struct file *filp)
                        timeout = MAX_SCHEDULE_TIMEOUT;
        }
 
-       /*
-        * The closing flags are now consistent with the open counts on
-        * both sides, and we've completed the last operation that could
-        * block, so it's safe to proceed with closing.
-        *
-        * We must *not* drop the tty_mutex until we ensure that a further
-        * entry into tty_open can not pick up this tty.
-        */
-       if (pty_master) {
+       if (o_tty) {
                if (--o_tty->count < 0) {
                        printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
                                __func__, o_tty->count, tty_name(o_tty, buf));
@@ -1840,21 +1871,11 @@ int tty_release(struct inode *inode, struct file *filp)
        /*
         * Perform some housekeeping before deciding whether to return.
         *
-        * Set the TTY_CLOSING flag if this was the last open.  In the
-        * case of a pty we may have to wait around for the other side
-        * to close, and TTY_CLOSING makes sure we can't be reopened.
-        */
-       if (tty_closing)
-               set_bit(TTY_CLOSING, &tty->flags);
-       if (o_tty_closing)
-               set_bit(TTY_CLOSING, &o_tty->flags);
-
-       /*
         * If _either_ side is closing, make sure there aren't any
         * processes that still think tty or o_tty is their controlling
         * tty.
         */
-       if (tty_closing || o_tty_closing) {
+       if (!tty->count) {
                read_lock(&tasklist_lock);
                session_clear_tty(tty->session);
                if (o_tty)
@@ -1862,13 +1883,16 @@ int tty_release(struct inode *inode, struct file *filp)
                read_unlock(&tasklist_lock);
        }
 
-       mutex_unlock(&tty_mutex);
-       tty_unlock_pair(tty, o_tty);
-       /* At this point the TTY_CLOSING flag should ensure a dead tty
+       /* check whether both sides are closing ... */
+       final = !tty->count && !(o_tty && o_tty->count);
+
+       tty_unlock_slave(o_tty);
+       tty_unlock(tty);
+
+       /* At this point, the tty->count == 0 should ensure a dead tty
           cannot be re-opened by a racing opener */
 
-       /* check whether both sides are closing ... */
-       if (!tty_closing || (o_tty && !o_tty_closing))
+       if (!final)
                return 0;
 
 #ifdef TTY_DEBUG_HANGUP
@@ -1877,12 +1901,10 @@ int tty_release(struct inode *inode, struct file *filp)
        /*
         * Ask the line discipline code to release its structures
         */
-       tty_ldisc_release(tty, o_tty);
+       tty_ldisc_release(tty);
 
        /* Wait for pending work before tty destruction commmences */
        tty_flush_works(tty);
-       if (o_tty)
-               tty_flush_works(o_tty);
 
 #ifdef TTY_DEBUG_HANGUP
        printk(KERN_DEBUG "%s: %s: freeing structure...\n", __func__, tty_name(tty, buf));
@@ -1901,20 +1923,20 @@ int tty_release(struct inode *inode, struct file *filp)
 }
 
 /**
- *     tty_open_current_tty - get tty of current task for open
+ *     tty_open_current_tty - get locked tty of current task
  *     @device: device number
  *     @filp: file pointer to tty
- *     @return: tty of the current task iff @device is /dev/tty
+ *     @return: locked tty of the current task iff @device is /dev/tty
+ *
+ *     Performs a re-open of the current task's controlling tty.
  *
  *     We cannot return driver and index like for the other nodes because
  *     devpts will not work then. It expects inodes to be from devpts FS.
- *
- *     We need to move to returning a refcounted object from all the lookup
- *     paths including this one.
  */
 static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
 {
        struct tty_struct *tty;
+       int retval;
 
        if (device != MKDEV(TTYAUX_MAJOR, 0))
                return NULL;
@@ -1925,9 +1947,14 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
 
        filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
        /* noctty = 1; */
-       tty_kref_put(tty);
-       /* FIXME: we put a reference and return a TTY! */
-       /* This is only safe because the caller holds tty_mutex */
+       tty_lock(tty);
+       tty_kref_put(tty);      /* safe to drop the kref now */
+
+       retval = tty_reopen(tty);
+       if (retval < 0) {
+               tty_unlock(tty);
+               tty = ERR_PTR(retval);
+       }
        return tty;
 }
 
@@ -2025,13 +2052,9 @@ retry_open:
        index  = -1;
        retval = 0;
 
-       mutex_lock(&tty_mutex);
-       /* This is protected by the tty_mutex */
        tty = tty_open_current_tty(device, filp);
-       if (IS_ERR(tty)) {
-               retval = PTR_ERR(tty);
-               goto err_unlock;
-       } else if (!tty) {
+       if (!tty) {
+               mutex_lock(&tty_mutex);
                driver = tty_lookup_driver(device, filp, &noctty, &index);
                if (IS_ERR(driver)) {
                        retval = PTR_ERR(driver);
@@ -2044,21 +2067,25 @@ retry_open:
                        retval = PTR_ERR(tty);
                        goto err_unlock;
                }
-       }
 
-       if (tty) {
-               tty_lock(tty);
-               retval = tty_reopen(tty);
-               if (retval < 0) {
-                       tty_unlock(tty);
-                       tty = ERR_PTR(retval);
+               if (tty) {
+                       mutex_unlock(&tty_mutex);
+                       tty_lock(tty);
+                       /* safe to drop the kref from tty_driver_lookup_tty() */
+                       tty_kref_put(tty);
+                       retval = tty_reopen(tty);
+                       if (retval < 0) {
+                               tty_unlock(tty);
+                               tty = ERR_PTR(retval);
+                       }
+               } else { /* Returns with the tty_lock held for now */
+                       tty = tty_init_dev(driver, index);
+                       mutex_unlock(&tty_mutex);
                }
-       } else  /* Returns with the tty_lock held for now */
-               tty = tty_init_dev(driver, index);
 
-       mutex_unlock(&tty_mutex);
-       if (driver)
                tty_driver_kref_put(driver);
+       }
+
        if (IS_ERR(tty)) {
                retval = PTR_ERR(tty);
                goto err_file;
@@ -2100,25 +2127,23 @@ retry_open:
                /*
                 * Need to reset f_op in case a hangup happened.
                 */
-               if (filp->f_op == &hung_up_tty_fops)
+               if (tty_hung_up_p(filp))
                        filp->f_op = &tty_fops;
                goto retry_open;
        }
        clear_bit(TTY_HUPPED, &tty->flags);
-       tty_unlock(tty);
 
 
-       mutex_lock(&tty_mutex);
-       tty_lock(tty);
+       read_lock(&tasklist_lock);
        spin_lock_irq(&current->sighand->siglock);
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
            tty->session == NULL)
-               __proc_set_tty(current, tty);
+               __proc_set_tty(tty);
        spin_unlock_irq(&current->sighand->siglock);
+       read_unlock(&tasklist_lock);
        tty_unlock(tty);
-       mutex_unlock(&tty_mutex);
        return 0;
 err_unlock:
        mutex_unlock(&tty_mutex);
@@ -2155,7 +2180,7 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
 
        ld = tty_ldisc_ref_wait(tty);
        if (ld->ops->poll)
-               ret = (ld->ops->poll)(tty, filp, wait);
+               ret = ld->ops->poll(tty, filp, wait);
        tty_ldisc_deref(ld);
        return ret;
 }
@@ -2283,18 +2308,14 @@ static int tiocgwinsz(struct tty_struct *tty, struct winsize __user *arg)
 int tty_do_resize(struct tty_struct *tty, struct winsize *ws)
 {
        struct pid *pgrp;
-       unsigned long flags;
 
        /* Lock the tty */
        mutex_lock(&tty->winsize_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);
-       spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 
+       /* Signal the foreground process group */
+       pgrp = tty_get_pgrp(tty);
        if (pgrp)
                kill_pgrp(pgrp, SIGWINCH, 1);
        put_pid(pgrp);
@@ -2403,7 +2424,7 @@ static int fionbio(struct file *file, int __user *p)
  *     leader to set this tty as the controlling tty for the session.
  *
  *     Locking:
- *             Takes tty_mutex() to protect tty instance
+ *             Takes tty_lock() to serialize proc_set_tty() for this tty
  *             Takes tasklist_lock internally to walk sessions
  *             Takes ->siglock() when updating signal->tty
  */
@@ -2411,10 +2432,13 @@ static int fionbio(struct file *file, int __user *p)
 static int tiocsctty(struct tty_struct *tty, int arg)
 {
        int ret = 0;
+
+       tty_lock(tty);
+       read_lock(&tasklist_lock);
+
        if (current->signal->leader && (task_session(current) == tty->session))
-               return ret;
+               goto unlock;
 
-       mutex_lock(&tty_mutex);
        /*
         * The process must be a session leader and
         * not have a controlling tty already.
@@ -2433,17 +2457,16 @@ static int tiocsctty(struct tty_struct *tty, int arg)
                        /*
                         * Steal it away
                         */
-                       read_lock(&tasklist_lock);
                        session_clear_tty(tty->session);
-                       read_unlock(&tasklist_lock);
                } else {
                        ret = -EPERM;
                        goto unlock;
                }
        }
-       proc_set_tty(current, tty);
+       proc_set_tty(tty);
 unlock:
-       mutex_unlock(&tty_mutex);
+       read_unlock(&tasklist_lock);
+       tty_unlock(tty);
        return ret;
 }
 
@@ -2468,6 +2491,27 @@ struct pid *tty_get_pgrp(struct tty_struct *tty)
 }
 EXPORT_SYMBOL_GPL(tty_get_pgrp);
 
+/*
+ * This checks not only the pgrp, but falls back on the pid if no
+ * satisfactory pgrp is found. I dunno - gdb doesn't work correctly
+ * without this...
+ *
+ * The caller must hold rcu lock or the tasklist lock.
+ */
+static struct pid *session_of_pgrp(struct pid *pgrp)
+{
+       struct task_struct *p;
+       struct pid *sid = NULL;
+
+       p = pid_task(pgrp, PIDTYPE_PGID);
+       if (p == NULL)
+               p = pid_task(pgrp, PIDTYPE_PID);
+       if (p != NULL)
+               sid = task_session(p);
+
+       return sid;
+}
+
 /**
  *     tiocgpgrp               -       get process group
  *     @tty: tty passed by user
@@ -2714,23 +2758,35 @@ static int tty_tiocgicount(struct tty_struct *tty, void __user *arg)
        return 0;
 }
 
-struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+static void tty_warn_deprecated_flags(struct serial_struct __user *ss)
 {
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_MASTER)
-               tty = tty->link;
-       return tty;
+       static DEFINE_RATELIMIT_STATE(depr_flags,
+                       DEFAULT_RATELIMIT_INTERVAL,
+                       DEFAULT_RATELIMIT_BURST);
+       char comm[TASK_COMM_LEN];
+       int flags;
+
+       if (get_user(flags, &ss->flags))
+               return;
+
+       flags &= ASYNC_DEPRECATED;
+
+       if (flags && __ratelimit(&depr_flags))
+               pr_warning("%s: '%s' is using deprecated serial flags (with no effect): %.8x\n",
+                               __func__, get_task_comm(comm, current), flags);
 }
-EXPORT_SYMBOL(tty_pair_get_tty);
 
-struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+/*
+ * if pty, return the slave side (real_tty)
+ * otherwise, return self
+ */
+static 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)
-           return tty;
-       return tty->link;
+               tty = tty->link;
+       return tty;
 }
-EXPORT_SYMBOL(tty_pair_get_pty);
 
 /*
  * Split this up, as gcc can choke on it otherwise..
@@ -2859,13 +2915,16 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                case TCIFLUSH:
                case TCIOFLUSH:
                /* flush tty buffer and allow ldisc to process ioctl */
-                       tty_buffer_flush(tty);
+                       tty_buffer_flush(tty, NULL);
                        break;
                }
                break;
+       case TIOCSSERIAL:
+               tty_warn_deprecated_flags(p);
+               break;
        }
        if (tty->ops->ioctl) {
-               retval = (tty->ops->ioctl)(tty, cmd, arg);
+               retval = tty->ops->ioctl(tty, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
@@ -2892,7 +2951,7 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
                return -EINVAL;
 
        if (tty->ops->compat_ioctl) {
-               retval = (tty->ops->compat_ioctl)(tty, cmd, arg);
+               retval = tty->ops->compat_ioctl(tty, cmd, arg);
                if (retval != -ENOIOCTLCMD)
                        return retval;
        }
@@ -3443,59 +3502,6 @@ dev_t tty_devnum(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(tty_devnum);
 
-void proc_clear_tty(struct task_struct *p)
-{
-       unsigned long flags;
-       struct tty_struct *tty;
-       spin_lock_irqsave(&p->sighand->siglock, flags);
-       tty = p->signal->tty;
-       p->signal->tty = NULL;
-       spin_unlock_irqrestore(&p->sighand->siglock, flags);
-       tty_kref_put(tty);
-}
-
-/* Called under the sighand lock */
-
-static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
-{
-       if (tty) {
-               unsigned long flags;
-               /* We should not have a session or pgrp to put here but.... */
-               spin_lock_irqsave(&tty->ctrl_lock, flags);
-               put_pid(tty->session);
-               put_pid(tty->pgrp);
-               tty->pgrp = get_pid(task_pgrp(tsk));
-               spin_unlock_irqrestore(&tty->ctrl_lock, flags);
-               tty->session = get_pid(task_session(tsk));
-               if (tsk->signal->tty) {
-                       printk(KERN_DEBUG "tty not NULL!!\n");
-                       tty_kref_put(tsk->signal->tty);
-               }
-       }
-       put_pid(tsk->signal->tty_old_pgrp);
-       tsk->signal->tty = tty_kref_get(tty);
-       tsk->signal->tty_old_pgrp = NULL;
-}
-
-static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
-{
-       spin_lock_irq(&tsk->sighand->siglock);
-       __proc_set_tty(tsk, tty);
-       spin_unlock_irq(&tsk->sighand->siglock);
-}
-
-struct tty_struct *get_current_tty(void)
-{
-       struct tty_struct *tty;
-       unsigned long flags;
-
-       spin_lock_irqsave(&current->sighand->siglock, flags);
-       tty = tty_kref_get(current->signal->tty);
-       spin_unlock_irqrestore(&current->sighand->siglock, flags);
-       return tty;
-}
-EXPORT_SYMBOL_GPL(get_current_tty);
-
 void tty_default_fops(struct file_operations *fops)
 {
        *fops = tty_fops;