tty: Clarify re-open behavior of master ptys
[firefly-linux-kernel-4.4.55.git] / drivers / tty / tty_io.c
index b645519300c265291b4d3add3316c90815b10478..168382baf3702539630dce319d80d45e997087d4 100644 (file)
@@ -502,14 +502,25 @@ void proc_clear_tty(struct task_struct *p)
        tty_kref_put(tty);
 }
 
-/* Called under the sighand lock */
-
+/**
+ * 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;
 
-       /* We should not have a session or pgrp to put here but.... */
        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));
@@ -679,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 */
@@ -706,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);
@@ -741,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)
@@ -1442,29 +1444,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 (test_bit(TTY_CLOSING, &tty->flags))
                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);
@@ -2145,11 +2139,9 @@ retry_open:
                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 &&
@@ -2157,8 +2149,8 @@ retry_open:
            tty->session == NULL)
                __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);
@@ -2439,7 +2431,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
  */
@@ -2447,10 +2439,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.
@@ -2469,9 +2464,7 @@ 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;
@@ -2479,7 +2472,8 @@ static int tiocsctty(struct tty_struct *tty, int arg)
        }
        proc_set_tty(tty);
 unlock:
-       mutex_unlock(&tty_mutex);
+       read_unlock(&tasklist_lock);
+       tty_unlock(tty);
        return ret;
 }
 
@@ -2504,6 +2498,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