Merge branch 'tty-next' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty
[firefly-linux-kernel-4.4.55.git] / drivers / tty / tty_io.c
index 3fdebd306b9436e11c6f65d457464b8723895a10..e41b9bbc107dcb688bc6653e5dc379794657dbf9 100644 (file)
@@ -790,19 +790,24 @@ static void session_clear_tty(struct pid *session)
 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) {
-               tty_pgrp = get_pid(tty->pgrp);
+               struct pid *tty_pgrp = get_pid(tty->pgrp);
                if (on_exit) {
                        if (tty->driver->type != TTY_DRIVER_TYPE_PTY)
                                tty_vhangup(tty);
                }
                tty_kref_put(tty);
+               if (tty_pgrp) {
+                       kill_pgrp(tty_pgrp, SIGHUP, on_exit);
+                       if (!on_exit)
+                               kill_pgrp(tty_pgrp, SIGCONT, on_exit);
+                       put_pid(tty_pgrp);
+               }
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -816,12 +821,6 @@ void disassociate_ctty(int on_exit)
                }
                return;
        }
-       if (tty_pgrp) {
-               kill_pgrp(tty_pgrp, SIGHUP, on_exit);
-               if (!on_exit)
-                       kill_pgrp(tty_pgrp, SIGCONT, on_exit);
-               put_pid(tty_pgrp);
-       }
 
        spin_lock_irq(&current->sighand->siglock);
        put_pid(current->signal->tty_old_pgrp);
@@ -1557,6 +1556,59 @@ static void release_tty(struct tty_struct *tty, int idx)
        tty_kref_put(tty);
 }
 
+/**
+ *     tty_release_checks - check a tty before real release
+ *     @tty: tty to check
+ *     @o_tty: link of @tty (if any)
+ *     @idx: index of the tty
+ *
+ *     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)
+{
+#ifdef TTY_PARANOIA_CHECK
+       if (idx < 0 || idx >= tty->driver->num) {
+               printk(KERN_DEBUG "%s: bad idx when trying to free (%s)\n",
+                               __func__, tty->name);
+               return -1;
+       }
+
+       /* not much to check for devpts */
+       if (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)
+               return 0;
+
+       if (tty != tty->driver->ttys[idx]) {
+               printk(KERN_DEBUG "%s: driver.table[%d] not tty for (%s)\n",
+                               __func__, idx, tty->name);
+               return -1;
+       }
+       if (tty->termios != tty->driver->termios[idx]) {
+               printk(KERN_DEBUG "%s: driver.termios[%d] not termios for (%s)\n",
+                               __func__, idx, tty->name);
+               return -1;
+       }
+       if (tty->driver->other) {
+               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);
+                       return -1;
+               }
+               if (o_tty->termios != tty->driver->other->termios[idx]) {
+                       printk(KERN_DEBUG "%s: other->termios[%d] not o_termios for (%s)\n",
+                                       __func__, idx, tty->name);
+                       return -1;
+               }
+               if (o_tty->link != tty) {
+                       printk(KERN_DEBUG "%s: bad pty pointers\n", __func__);
+                       return -1;
+               }
+       }
+#endif
+       return 0;
+}
+
 /**
  *     tty_release             -       vfs callback for close
  *     @inode: inode of tty
@@ -1585,11 +1637,11 @@ int tty_release(struct inode *inode, struct file *filp)
        int     idx;
        char    buf[64];
 
-       if (tty_paranoia_check(tty, inode, "tty_release_dev"))
+       if (tty_paranoia_check(tty, inode, __func__))
                return 0;
 
        tty_lock();
-       check_tty_count(tty, "tty_release_dev");
+       check_tty_count(tty, __func__);
 
        __tty_fasync(-1, filp, 0);
 
@@ -1599,59 +1651,16 @@ int tty_release(struct inode *inode, struct file *filp)
        devpts = (tty->driver->flags & TTY_DRIVER_DEVPTS_MEM) != 0;
        o_tty = tty->link;
 
-#ifdef TTY_PARANOIA_CHECK
-       if (idx < 0 || idx >= tty->driver->num) {
-               printk(KERN_DEBUG "tty_release_dev: bad idx when trying to "
-                                 "free (%s)\n", tty->name);
+       if (tty_release_checks(tty, o_tty, idx)) {
                tty_unlock();
                return 0;
        }
-       if (!devpts) {
-               if (tty != tty->driver->ttys[idx]) {
-                       tty_unlock();
-                       printk(KERN_DEBUG "tty_release_dev: driver.table[%d] not tty "
-                              "for (%s)\n", idx, tty->name);
-                       return 0;
-               }
-               if (tty->termios != tty->driver->termios[idx]) {
-                       tty_unlock();
-                       printk(KERN_DEBUG "tty_release_dev: driver.termios[%d] not termios "
-                              "for (%s)\n",
-                              idx, tty->name);
-                       return 0;
-               }
-       }
-#endif
 
 #ifdef TTY_DEBUG_HANGUP
-       printk(KERN_DEBUG "tty_release_dev of %s (tty count=%d)...",
-              tty_name(tty, buf), tty->count);
+       printk(KERN_DEBUG "%s: %s (tty count=%d)...\n", __func__,
+                       tty_name(tty, buf), tty->count);
 #endif
 
-#ifdef TTY_PARANOIA_CHECK
-       if (tty->driver->other &&
-            !(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
-               if (o_tty != tty->driver->other->ttys[idx]) {
-                       tty_unlock();
-                       printk(KERN_DEBUG "tty_release_dev: other->table[%d] "
-                                         "not o_tty for (%s)\n",
-                              idx, tty->name);
-                       return 0 ;
-               }
-               if (o_tty->termios != tty->driver->other->termios[idx]) {
-                       tty_unlock();
-                       printk(KERN_DEBUG "tty_release_dev: other->termios[%d] "
-                                         "not o_termios for (%s)\n",
-                              idx, tty->name);
-                       return 0;
-               }
-               if (o_tty->link != tty) {
-                       tty_unlock();
-                       printk(KERN_DEBUG "tty_release_dev: bad pty pointers\n");
-                       return 0;
-               }
-       }
-#endif
        if (tty->ops->close)
                tty->ops->close(tty, filp);
 
@@ -1707,8 +1716,8 @@ int tty_release(struct inode *inode, struct file *filp)
                if (!do_sleep)
                        break;
 
-               printk(KERN_WARNING "tty_release_dev: %s: read/write wait queue "
-                                   "active!\n", tty_name(tty, buf));
+               printk(KERN_WARNING "%s: %s: read/write wait queue active!\n",
+                               __func__, tty_name(tty, buf));
                tty_unlock();
                mutex_unlock(&tty_mutex);
                schedule();
@@ -1721,15 +1730,14 @@ int tty_release(struct inode *inode, struct file *filp)
         */
        if (pty_master) {
                if (--o_tty->count < 0) {
-                       printk(KERN_WARNING "tty_release_dev: bad pty slave count "
-                                           "(%d) for %s\n",
-                              o_tty->count, tty_name(o_tty, buf));
+                       printk(KERN_WARNING "%s: bad pty slave count (%d) for %s\n",
+                               __func__, o_tty->count, tty_name(o_tty, buf));
                        o_tty->count = 0;
                }
        }
        if (--tty->count < 0) {
-               printk(KERN_WARNING "tty_release_dev: bad tty->count (%d) for %s\n",
-                      tty->count, tty_name(tty, buf));
+               printk(KERN_WARNING "%s: bad tty->count (%d) for %s\n",
+                               __func__, tty->count, tty_name(tty, buf));
                tty->count = 0;
        }
 
@@ -1778,7 +1786,7 @@ int tty_release(struct inode *inode, struct file *filp)
        }
 
 #ifdef TTY_DEBUG_HANGUP
-       printk(KERN_DEBUG "freeing tty structure...");
+       printk(KERN_DEBUG "%s: freeing tty structure...\n", __func__);
 #endif
        /*
         * Ask the line discipline code to release its structures
@@ -1797,6 +1805,83 @@ int tty_release(struct inode *inode, struct file *filp)
        return 0;
 }
 
+/**
+ *     tty_open_current_tty - get tty of current task for open
+ *     @device: device number
+ *     @filp: file pointer to tty
+ *     @return: tty of the current task iff @device is /dev/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.
+ */
+static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
+{
+       struct tty_struct *tty;
+
+       if (device != MKDEV(TTYAUX_MAJOR, 0))
+               return NULL;
+
+       tty = get_current_tty();
+       if (!tty)
+               return ERR_PTR(-ENXIO);
+
+       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! */
+       return tty;
+}
+
+/**
+ *     tty_lookup_driver - lookup a tty driver for a given device file
+ *     @device: device number
+ *     @filp: file pointer to tty
+ *     @noctty: set if the device should not become a controlling tty
+ *     @index: index for the device in the @return driver
+ *     @return: driver for this inode (with increased refcount)
+ *
+ *     If @return is not erroneous, the caller is responsible to decrement the
+ *     refcount by tty_driver_kref_put.
+ *
+ *     Locking: tty_mutex protects get_tty_driver
+ */
+static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
+               int *noctty, int *index)
+{
+       struct tty_driver *driver;
+
+       switch (device) {
+#ifdef CONFIG_VT
+       case MKDEV(TTY_MAJOR, 0): {
+               extern struct tty_driver *console_driver;
+               driver = tty_driver_kref_get(console_driver);
+               *index = fg_console;
+               *noctty = 1;
+               break;
+       }
+#endif
+       case MKDEV(TTYAUX_MAJOR, 1): {
+               struct tty_driver *console_driver = console_device(index);
+               if (console_driver) {
+                       driver = tty_driver_kref_get(console_driver);
+                       if (driver) {
+                               /* Don't let /dev/console block */
+                               filp->f_flags |= O_NONBLOCK;
+                               *noctty = 1;
+                               break;
+                       }
+               }
+               return ERR_PTR(-ENODEV);
+       }
+       default:
+               driver = get_tty_driver(device, index);
+               if (!driver)
+                       return ERR_PTR(-ENODEV);
+               break;
+       }
+       return driver;
+}
+
 /**
  *     tty_open                -       open a tty device
  *     @inode: inode of device file
@@ -1813,16 +1898,16 @@ int tty_release(struct inode *inode, struct file *filp)
  *     The termios state of a pty is reset on first open so that
  *     settings don't persist across reuse.
  *
- *     Locking: tty_mutex protects tty, get_tty_driver and tty_init_dev work.
+ *     Locking: tty_mutex protects tty, tty_lookup_driver and tty_init_dev.
  *              tty->count should protect the rest.
  *              ->siglock protects ->signal/->sighand
  */
 
 static int tty_open(struct inode *inode, struct file *filp)
 {
-       struct tty_struct *tty = NULL;
+       struct tty_struct *tty;
        int noctty, retval;
-       struct tty_driver *driver;
+       struct tty_driver *driver = NULL;
        int index;
        dev_t device = inode->i_rdev;
        unsigned saved_flags = filp->f_flags;
@@ -1841,66 +1926,22 @@ retry_open:
        mutex_lock(&tty_mutex);
        tty_lock();
 
-       if (device == MKDEV(TTYAUX_MAJOR, 0)) {
-               tty = get_current_tty();
-               if (!tty) {
-                       tty_unlock();
-                       mutex_unlock(&tty_mutex);
-                       tty_free_file(filp);
-                       return -ENXIO;
-               }
-               driver = tty_driver_kref_get(tty->driver);
-               index = tty->index;
-               filp->f_flags |= O_NONBLOCK; /* Don't let /dev/tty block */
-               /* noctty = 1; */
-               /* FIXME: Should we take a driver reference ? */
-               tty_kref_put(tty);
-               goto got_driver;
-       }
-#ifdef CONFIG_VT
-       if (device == MKDEV(TTY_MAJOR, 0)) {
-               extern struct tty_driver *console_driver;
-               driver = tty_driver_kref_get(console_driver);
-               index = fg_console;
-               noctty = 1;
-               goto got_driver;
-       }
-#endif
-       if (device == MKDEV(TTYAUX_MAJOR, 1)) {
-               struct tty_driver *console_driver = console_device(&index);
-               if (console_driver) {
-                       driver = tty_driver_kref_get(console_driver);
-                       if (driver) {
-                               /* Don't let /dev/console block */
-                               filp->f_flags |= O_NONBLOCK;
-                               noctty = 1;
-                               goto got_driver;
-                       }
+       tty = tty_open_current_tty(device, filp);
+       if (IS_ERR(tty)) {
+               retval = PTR_ERR(tty);
+               goto err_unlock;
+       } else if (!tty) {
+               driver = tty_lookup_driver(device, filp, &noctty, &index);
+               if (IS_ERR(driver)) {
+                       retval = PTR_ERR(driver);
+                       goto err_unlock;
                }
-               tty_unlock();
-               mutex_unlock(&tty_mutex);
-               tty_free_file(filp);
-               return -ENODEV;
-       }
 
-       driver = get_tty_driver(device, &index);
-       if (!driver) {
-               tty_unlock();
-               mutex_unlock(&tty_mutex);
-               tty_free_file(filp);
-               return -ENODEV;
-       }
-got_driver:
-       if (!tty) {
                /* check whether we're reopening an existing tty */
                tty = tty_driver_lookup_tty(driver, inode, index);
-
                if (IS_ERR(tty)) {
-                       tty_unlock();
-                       mutex_unlock(&tty_mutex);
-                       tty_driver_kref_put(driver);
-                       tty_free_file(filp);
-                       return PTR_ERR(tty);
+                       retval = PTR_ERR(tty);
+                       goto err_unlock;
                }
        }
 
@@ -1912,21 +1953,22 @@ got_driver:
                tty = tty_init_dev(driver, index, 0);
 
        mutex_unlock(&tty_mutex);
-       tty_driver_kref_put(driver);
+       if (driver)
+               tty_driver_kref_put(driver);
        if (IS_ERR(tty)) {
                tty_unlock();
-               tty_free_file(filp);
-               return PTR_ERR(tty);
+               retval = PTR_ERR(tty);
+               goto err_file;
        }
 
        tty_add_file(tty, filp);
 
-       check_tty_count(tty, "tty_open");
+       check_tty_count(tty, __func__);
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_MASTER)
                noctty = 1;
 #ifdef TTY_DEBUG_HANGUP
-       printk(KERN_DEBUG "opening %s...", tty->name);
+       printk(KERN_DEBUG "%s: opening %s...\n", __func__, tty->name);
 #endif
        if (tty->ops->open)
                retval = tty->ops->open(tty, filp);
@@ -1940,8 +1982,8 @@ got_driver:
 
        if (retval) {
 #ifdef TTY_DEBUG_HANGUP
-               printk(KERN_DEBUG "error %d in opening %s...", retval,
-                      tty->name);
+               printk(KERN_DEBUG "%s: error %d in opening %s...\n", __func__,
+                               retval, tty->name);
 #endif
                tty_unlock(); /* need to call tty_release without BTM */
                tty_release(inode, filp);
@@ -1976,6 +2018,15 @@ got_driver:
        tty_unlock();
        mutex_unlock(&tty_mutex);
        return 0;
+err_unlock:
+       tty_unlock();
+       mutex_unlock(&tty_mutex);
+       /* after locks to avoid deadlock */
+       if (!IS_ERR_OR_NULL(driver))
+               tty_driver_kref_put(driver);
+err_file:
+       tty_free_file(filp);
+       return retval;
 }