]> git.proxmox.com Git - mirror_ubuntu-bionic-kernel.git/blobdiff - drivers/tty/tty_io.c
tty: serial: owl: Fix the link time qualifier of 'owl_uart_exit()'
[mirror_ubuntu-bionic-kernel.git] / drivers / tty / tty_io.c
index dc60aeea87d8145671bfc664d23121b47e98bb69..078c1e46f14cb88738023d8a4db3e60266c4d529 100644 (file)
@@ -408,7 +408,7 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
        mutex_lock(&tty_mutex);
        /* Search through the tty devices to look for a match */
        list_for_each_entry(p, &tty_drivers, tty_drivers) {
-               if (strncmp(name, p->name, len) != 0)
+               if (!len || strncmp(name, p->name, len) != 0)
                        continue;
                stp = str;
                if (*stp == ',')
@@ -512,6 +512,8 @@ static const struct file_operations hung_up_tty_fops = {
 static DEFINE_SPINLOCK(redirect_lock);
 static struct file *redirect;
 
+extern void tty_sysctl_init(void);
+
 /**
  *     tty_wakeup      -       request more data
  *     @tty: terminal
@@ -586,6 +588,14 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
                return;
        }
 
+       /*
+        * Some console devices aren't actually hung up for technical and
+        * historical reasons, which can lead to indefinite interruptible
+        * sleep in n_tty_read().  The following explicitly tells
+        * n_tty_read() to abort readers.
+        */
+       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 */
@@ -640,6 +650,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
         * from the ldisc side, which is now guaranteed.
         */
        set_bit(TTY_HUPPED, &tty->flags);
+       clear_bit(TTY_HUPPING, &tty->flags);
        tty_unlock(tty);
 
        if (f)
@@ -1246,6 +1257,8 @@ static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *
 static int tty_reopen(struct tty_struct *tty)
 {
        struct tty_driver *driver = tty->driver;
+       struct tty_ldisc *ld;
+       int retval = 0;
 
        if (driver->type == TTY_DRIVER_TYPE_PTY &&
            driver->subtype == PTY_TYPE_MASTER)
@@ -1257,12 +1270,23 @@ static int tty_reopen(struct tty_struct *tty)
        if (test_bit(TTY_EXCLUSIVE, &tty->flags) && !capable(CAP_SYS_ADMIN))
                return -EBUSY;
 
-       tty->count++;
+       ld = tty_ldisc_ref_wait(tty);
+       if (ld) {
+               tty_ldisc_deref(ld);
+       } else {
+               retval = tty_ldisc_lock(tty, 5 * HZ);
+               if (retval)
+                       return retval;
+
+               if (!tty->ldisc)
+                       retval = tty_ldisc_reinit(tty, tty->termios.c_line);
+               tty_ldisc_unlock(tty);
+       }
 
-       if (!tty->ldisc)
-               return tty_ldisc_reinit(tty, tty->termios.c_line);
+       if (retval == 0)
+               tty->count++;
 
-       return 0;
+       return retval;
 }
 
 /**
@@ -1323,6 +1347,9 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
                        "%s: %s driver does not set tty->port. This will crash the kernel later. Fix the driver!\n",
                        __func__, tty->driver->name);
 
+       retval = tty_ldisc_lock(tty, 5 * HZ);
+       if (retval)
+               goto err_release_lock;
        tty->port->itty = tty;
 
        /*
@@ -1333,6 +1360,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
        retval = tty_ldisc_setup(tty, tty->link);
        if (retval)
                goto err_release_tty;
+       tty_ldisc_unlock(tty);
        /* Return the tty locked so that it cannot vanish under the caller */
        return tty;
 
@@ -1345,14 +1373,22 @@ err_module_put:
 
        /* call the tty release_tty routine to clean out this slot */
 err_release_tty:
-       tty_unlock(tty);
+       tty_ldisc_unlock(tty);
        tty_info_ratelimited(tty, "ldisc open failed (%d), clearing slot %d\n",
                             retval, idx);
+err_release_lock:
+       tty_unlock(tty);
        release_tty(tty, idx);
        return ERR_PTR(retval);
 }
 
-static void tty_free_termios(struct tty_struct *tty)
+/**
+ * tty_save_termios() - save tty termios data in driver table
+ * @tty: tty whose termios data to save
+ *
+ * Locking: Caller guarantees serialisation with tty_init_termios().
+ */
+void tty_save_termios(struct tty_struct *tty)
 {
        struct ktermios *tp;
        int idx = tty->index;
@@ -1371,6 +1407,7 @@ static void tty_free_termios(struct tty_struct *tty)
        }
        *tp = tty->termios;
 }
+EXPORT_SYMBOL_GPL(tty_save_termios);
 
 /**
  *     tty_flush_works         -       flush all works of a tty/pty pair
@@ -1470,12 +1507,14 @@ static void release_tty(struct tty_struct *tty, int idx)
        WARN_ON(!mutex_is_locked(&tty_mutex));
        if (tty->ops->shutdown)
                tty->ops->shutdown(tty);
-       tty_free_termios(tty);
+       tty_save_termios(tty);
        tty_driver_remove_tty(tty->driver, tty);
        tty->port->itty = NULL;
        if (tty->link)
                tty->link->port->itty = NULL;
        tty_buffer_cancel_work(tty->port);
+       if (tty->link)
+               tty_buffer_cancel_work(tty->link->port);
 
        tty_kref_put(tty->link);
        tty_kref_put(tty);
@@ -2151,7 +2190,8 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
        ld = tty_ldisc_ref_wait(tty);
        if (!ld)
                return -EIO;
-       ld->ops->receive_buf(tty, &ch, &mbz, 1);
+       if (ld->ops->receive_buf)
+               ld->ops->receive_buf(tty, &ch, &mbz, 1);
        tty_ldisc_deref(ld);
        return 0;
 }
@@ -2799,7 +2839,10 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
 
        kref_init(&tty->kref);
        tty->magic = TTY_MAGIC;
-       tty_ldisc_init(tty);
+       if (tty_ldisc_init(tty)) {
+               kfree(tty);
+               return NULL;
+       }
        tty->session = NULL;
        tty->pgrp = NULL;
        mutex_init(&tty->legacy_mutex);
@@ -3299,6 +3342,7 @@ void console_sysfs_notify(void)
  */
 int __init tty_init(void)
 {
+       tty_sysctl_init();
        cdev_init(&tty_cdev, &tty_fops);
        if (cdev_add(&tty_cdev, MKDEV(TTYAUX_MAJOR, 0), 1) ||
            register_chrdev_region(MKDEV(TTYAUX_MAJOR, 0), 1, "/dev/tty") < 0)