]> git.karo-electronics.de Git - karo-tx-linux.git/blobdiff - drivers/tty/tty_io.c
Merge remote-tracking branch 'tty/tty-next'
[karo-tx-linux.git] / drivers / tty / tty_io.c
index a7eacef1bd2216ef697c12d046cb8975f9fefb20..c14c45fefa70d8fa5f522c5715e9ae6931235cad 100644 (file)
@@ -123,7 +123,8 @@ struct ktermios tty_std_termios = { /* for the benefit of tty drivers  */
                   ECHOCTL | ECHOKE | IEXTEN,
        .c_cc = INIT_C_CC,
        .c_ispeed = 38400,
-       .c_ospeed = 38400
+       .c_ospeed = 38400,
+       /* .c_line = N_TTY, */
 };
 
 EXPORT_SYMBOL(tty_std_termios);
@@ -134,13 +135,8 @@ EXPORT_SYMBOL(tty_std_termios);
 
 LIST_HEAD(tty_drivers);                        /* linked list of tty drivers */
 
-/* Mutex to protect creating and releasing a tty. This is shared with
-   vt.c for deeply disgusting hack reasons */
+/* Mutex to protect creating and releasing a tty */
 DEFINE_MUTEX(tty_mutex);
-EXPORT_SYMBOL(tty_mutex);
-
-/* Spinlock to protect the tty->tty_files list */
-DEFINE_SPINLOCK(tty_files_lock);
 
 static ssize_t tty_read(struct file *, char __user *, size_t, loff_t *);
 static ssize_t tty_write(struct file *, const char __user *, size_t, loff_t *);
@@ -168,10 +164,9 @@ static void release_tty(struct tty_struct *tty, int idx);
  *     Locking: none. Must be called after tty is definitely unused
  */
 
-void free_tty_struct(struct tty_struct *tty)
+static void free_tty_struct(struct tty_struct *tty)
 {
-       if (!tty)
-               return;
+       tty_ldisc_deinit(tty);
        put_device(tty->dev);
        kfree(tty->write_buf);
        tty->magic = 0xDEADDEAD;
@@ -204,9 +199,9 @@ void tty_add_file(struct tty_struct *tty, struct file *file)
        priv->tty = tty;
        priv->file = file;
 
-       spin_lock(&tty_files_lock);
+       spin_lock(&tty->files_lock);
        list_add(&priv->list, &tty->tty_files);
-       spin_unlock(&tty_files_lock);
+       spin_unlock(&tty->files_lock);
 }
 
 /**
@@ -227,10 +222,11 @@ void tty_free_file(struct file *file)
 static void tty_del_file(struct file *file)
 {
        struct tty_file_private *priv = file->private_data;
+       struct tty_struct *tty = priv->tty;
 
-       spin_lock(&tty_files_lock);
+       spin_lock(&tty->files_lock);
        list_del(&priv->list);
-       spin_unlock(&tty_files_lock);
+       spin_unlock(&tty->files_lock);
        tty_free_file(file);
 }
 
@@ -288,11 +284,11 @@ static int check_tty_count(struct tty_struct *tty, const char *routine)
        struct list_head *p;
        int count = 0;
 
-       spin_lock(&tty_files_lock);
+       spin_lock(&tty->files_lock);
        list_for_each(p, &tty->tty_files) {
                count++;
        }
-       spin_unlock(&tty_files_lock);
+       spin_unlock(&tty->files_lock);
        if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
            tty->driver->subtype == PTY_TYPE_SLAVE &&
            tty->link && tty->link->count)
@@ -383,6 +379,12 @@ struct tty_driver *tty_find_polling_driver(char *name, int *line)
 EXPORT_SYMBOL_GPL(tty_find_polling_driver);
 #endif
 
+static int is_ignored(int sig)
+{
+       return (sigismember(&current->blocked, sig) ||
+               current->sighand->action[sig-1].sa.sa_handler == SIG_IGN);
+}
+
 /**
  *     tty_check_change        -       check for POSIX terminal changes
  *     @tty: tty to check
@@ -466,6 +468,11 @@ static long hung_up_tty_compat_ioctl(struct file *file,
        return cmd == TIOCSPGRP ? -ENOTTY : -EIO;
 }
 
+static int hung_up_tty_fasync(int fd, struct file *file, int on)
+{
+       return -ENOTTY;
+}
+
 static const struct file_operations tty_fops = {
        .llseek         = no_llseek,
        .read           = tty_read,
@@ -498,6 +505,7 @@ static const struct file_operations hung_up_tty_fops = {
        .unlocked_ioctl = hung_up_tty_ioctl,
        .compat_ioctl   = hung_up_tty_compat_ioctl,
        .release        = tty_release,
+       .fasync         = hung_up_tty_fasync,
 };
 
 static DEFINE_SPINLOCK(redirect_lock);
@@ -709,7 +717,7 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
           workqueue with the lock held */
        check_tty_count(tty, "tty_hangup");
 
-       spin_lock(&tty_files_lock);
+       spin_lock(&tty->files_lock);
        /* This breaks for file handles being sent over AF_UNIX sockets ? */
        list_for_each_entry(priv, &tty->tty_files, list) {
                filp = priv->file;
@@ -721,14 +729,14 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
                __tty_fasync(-1, filp, 0);      /* can't block */
                filp->f_op = &hung_up_tty_fops;
        }
-       spin_unlock(&tty_files_lock);
+       spin_unlock(&tty->files_lock);
 
        refs = tty_signal_session_leader(tty, exit_session);
        /* Account for the p->signal references we killed */
        while (refs--)
                tty_kref_put(tty);
 
-       tty_ldisc_hangup(tty);
+       tty_ldisc_hangup(tty, cons_filp != NULL);
 
        spin_lock_irq(&tty->ctrl_lock);
        clear_bit(TTY_THROTTLED, &tty->flags);
@@ -753,10 +761,9 @@ static void __tty_hangup(struct tty_struct *tty, int exit_session)
        } else if (tty->ops->hangup)
                tty->ops->hangup(tty);
        /*
-        * We don't want to have driver/ldisc interactions beyond
-        * the ones we did here. The driver layer expects no
-        * calls after ->hangup() from the ldisc side. However we
-        * can't yet guarantee all that.
+        * We don't want to have driver/ldisc interactions beyond the ones
+        * we did here. The driver layer expects no calls after ->hangup()
+        * from the ldisc side, which is now guaranteed.
         */
        set_bit(TTY_HUPPED, &tty->flags);
        tty_unlock(tty);
@@ -1069,6 +1076,8 @@ static ssize_t tty_read(struct file *file, char __user *buf, size_t count,
        /* We want to wait for the line discipline to sort out in this
           situation */
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return hung_up_tty_read(file, buf, count, ppos);
        if (ld->ops->read)
                i = ld->ops->read(tty, file, buf, count);
        else
@@ -1243,6 +1252,8 @@ static ssize_t tty_write(struct file *file, const char __user *buf,
        if (tty->ops->write_room == NULL)
                tty_err(tty, "missing write_room method\n");
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return hung_up_tty_write(file, buf, count, ppos);
        if (!ld->ops->write)
                ret = -EIO;
        else
@@ -1378,7 +1389,7 @@ static struct tty_struct *tty_driver_lookup_tty(struct tty_driver *driver,
  *     the tty_mutex currently so we can be relaxed about ordering.
  */
 
-int tty_init_termios(struct tty_struct *tty)
+void tty_init_termios(struct tty_struct *tty)
 {
        struct ktermios *tp;
        int idx = tty->index;
@@ -1388,24 +1399,21 @@ int tty_init_termios(struct tty_struct *tty)
        else {
                /* Check for lazy saved data */
                tp = tty->driver->termios[idx];
-               if (tp != NULL)
+               if (tp != NULL) {
                        tty->termios = *tp;
-               else
+                       tty->termios.c_line  = tty->driver->init_termios.c_line;
+               } else
                        tty->termios = tty->driver->init_termios;
        }
        /* Compatibility until drivers always set this */
        tty->termios.c_ispeed = tty_termios_input_baud_rate(&tty->termios);
        tty->termios.c_ospeed = tty_termios_baud_rate(&tty->termios);
-       return 0;
 }
 EXPORT_SYMBOL_GPL(tty_init_termios);
 
 int tty_standard_install(struct tty_driver *driver, struct tty_struct *tty)
 {
-       int ret = tty_init_termios(tty);
-       if (ret)
-               return ret;
-
+       tty_init_termios(tty);
        tty_driver_kref_get(driver);
        tty->count++;
        driver->ttys[tty->index] = tty;
@@ -1442,7 +1450,7 @@ static int tty_driver_install_tty(struct tty_driver *driver,
  *
  *     Locking: tty_mutex for now
  */
-void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
+static void tty_driver_remove_tty(struct tty_driver *driver, struct tty_struct *tty)
 {
        if (driver->ops->remove)
                driver->ops->remove(driver, tty);
@@ -1475,7 +1483,8 @@ static int tty_reopen(struct tty_struct *tty)
 
        tty->count++;
 
-       WARN_ON(!tty->ldisc);
+       if (!tty->ldisc)
+               return tty_ldisc_reinit(tty, tty->termios.c_line);
 
        return 0;
 }
@@ -1529,7 +1538,7 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
        tty_lock(tty);
        retval = tty_driver_install_tty(driver, tty);
        if (retval < 0)
-               goto err_deinit_tty;
+               goto err_free_tty;
 
        if (!tty->port)
                tty->port = driver->ports[idx];
@@ -1551,9 +1560,8 @@ struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx)
        /* Return the tty locked so that it cannot vanish under the caller */
        return tty;
 
-err_deinit_tty:
+err_free_tty:
        tty_unlock(tty);
-       deinitialize_tty_struct(tty);
        free_tty_struct(tty);
 err_module_put:
        module_put(driver->owner);
@@ -1568,7 +1576,7 @@ err_release_tty:
        return ERR_PTR(retval);
 }
 
-void tty_free_termios(struct tty_struct *tty)
+static void tty_free_termios(struct tty_struct *tty)
 {
        struct ktermios *tp;
        int idx = tty->index;
@@ -1587,7 +1595,6 @@ void tty_free_termios(struct tty_struct *tty)
        }
        *tp = tty->termios;
 }
-EXPORT_SYMBOL(tty_free_termios);
 
 /**
  *     tty_flush_works         -       flush all works of a tty/pty pair
@@ -1634,9 +1641,9 @@ static void release_one_tty(struct work_struct *work)
        tty_driver_kref_put(driver);
        module_put(owner);
 
-       spin_lock(&tty_files_lock);
+       spin_lock(&tty->files_lock);
        list_del_init(&tty->tty_files);
-       spin_unlock(&tty_files_lock);
+       spin_unlock(&tty->files_lock);
 
        put_pid(tty->pgrp);
        put_pid(tty->session);
@@ -1967,7 +1974,7 @@ static struct tty_struct *tty_open_current_tty(dev_t device, struct file *filp)
  *     Locking: tty_mutex protects get_tty_driver
  */
 static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
-               int *noctty, int *index)
+               int *index)
 {
        struct tty_driver *driver;
 
@@ -1977,7 +1984,6 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
                extern struct tty_driver *console_driver;
                driver = tty_driver_kref_get(console_driver);
                *index = fg_console;
-               *noctty = 1;
                break;
        }
 #endif
@@ -1988,7 +1994,6 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
                        if (driver) {
                                /* Don't let /dev/console block */
                                filp->f_flags |= O_NONBLOCK;
-                               *noctty = 1;
                                break;
                        }
                }
@@ -2003,6 +2008,68 @@ static struct tty_driver *tty_lookup_driver(dev_t device, struct file *filp,
        return driver;
 }
 
+/**
+ *     tty_open_by_driver      -       open a tty device
+ *     @device: dev_t of device to open
+ *     @inode: inode of device file
+ *     @filp: file pointer to tty
+ *
+ *     Performs the driver lookup, checks for a reopen, or otherwise
+ *     performs the first-time tty initialization.
+ *
+ *     Returns the locked initialized or re-opened &tty_struct
+ *
+ *     Claims the global tty_mutex to serialize:
+ *       - concurrent first-time tty initialization
+ *       - concurrent tty driver removal w/ lookup
+ *       - concurrent tty removal from driver table
+ */
+static struct tty_struct *tty_open_by_driver(dev_t device, struct inode *inode,
+                                            struct file *filp)
+{
+       struct tty_struct *tty;
+       struct tty_driver *driver = NULL;
+       int index = -1;
+       int retval;
+
+       mutex_lock(&tty_mutex);
+       driver = tty_lookup_driver(device, filp, &index);
+       if (IS_ERR(driver)) {
+               mutex_unlock(&tty_mutex);
+               return ERR_CAST(driver);
+       }
+
+       /* check whether we're reopening an existing tty */
+       tty = tty_driver_lookup_tty(driver, inode, index);
+       if (IS_ERR(tty)) {
+               mutex_unlock(&tty_mutex);
+               goto out;
+       }
+
+       if (tty) {
+               mutex_unlock(&tty_mutex);
+               retval = tty_lock_interruptible(tty);
+               tty_kref_put(tty);  /* drop kref from tty_driver_lookup_tty() */
+               if (retval) {
+                       if (retval == -EINTR)
+                               retval = -ERESTARTSYS;
+                       tty = ERR_PTR(retval);
+                       goto out;
+               }
+               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);
+       }
+out:
+       tty_driver_kref_put(driver);
+       return tty;
+}
+
 /**
  *     tty_open                -       open a tty device
  *     @inode: inode of device file
@@ -2031,8 +2098,6 @@ static int tty_open(struct inode *inode, struct file *filp)
 {
        struct tty_struct *tty;
        int noctty, retval;
-       struct tty_driver *driver = NULL;
-       int index;
        dev_t device = inode->i_rdev;
        unsigned saved_flags = filp->f_flags;
 
@@ -2043,53 +2108,15 @@ retry_open:
        if (retval)
                return -ENOMEM;
 
-       noctty = filp->f_flags & O_NOCTTY;
-       index  = -1;
-       retval = 0;
-
        tty = tty_open_current_tty(device, filp);
-       if (!tty) {
-               mutex_lock(&tty_mutex);
-               driver = tty_lookup_driver(device, filp, &noctty, &index);
-               if (IS_ERR(driver)) {
-                       retval = PTR_ERR(driver);
-                       goto err_unlock;
-               }
-
-               /* check whether we're reopening an existing tty */
-               tty = tty_driver_lookup_tty(driver, inode, index);
-               if (IS_ERR(tty)) {
-                       retval = PTR_ERR(tty);
-                       goto err_unlock;
-               }
-
-               if (tty) {
-                       mutex_unlock(&tty_mutex);
-                       retval = tty_lock_interruptible(tty);
-                       tty_kref_put(tty);  /* drop kref from tty_driver_lookup_tty() */
-                       if (retval) {
-                               if (retval == -EINTR)
-                                       retval = -ERESTARTSYS;
-                               goto err_unref;
-                       }
-                       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);
-               }
-
-               tty_driver_kref_put(driver);
-       }
+       if (!tty)
+               tty = tty_open_by_driver(device, inode, filp);
 
        if (IS_ERR(tty)) {
+               tty_free_file(filp);
                retval = PTR_ERR(tty);
                if (retval != -EAGAIN || signal_pending(current))
-                       goto err_file;
-               tty_free_file(filp);
+                       return retval;
                schedule();
                goto retry_open;
        }
@@ -2097,10 +2124,6 @@ retry_open:
        tty_add_file(tty, filp);
 
        check_tty_count(tty, __func__);
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_MASTER)
-               noctty = 1;
-
        tty_debug_hangup(tty, "opening (count=%d)\n", tty->count);
 
        if (tty->ops->open)
@@ -2133,6 +2156,12 @@ retry_open:
 
        read_lock(&tasklist_lock);
        spin_lock_irq(&current->sighand->siglock);
+       noctty = (filp->f_flags & O_NOCTTY) ||
+                       device == MKDEV(TTY_MAJOR, 0) ||
+                       device == MKDEV(TTYAUX_MAJOR, 1) ||
+                       (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+                        tty->driver->subtype == PTY_TYPE_MASTER);
+
        if (!noctty &&
            current->signal->leader &&
            !current->signal->tty &&
@@ -2158,15 +2187,6 @@ retry_open:
        read_unlock(&tasklist_lock);
        tty_unlock(tty);
        return 0;
-err_unlock:
-       mutex_unlock(&tty_mutex);
-err_unref:
-       /* after locks to avoid deadlock */
-       if (!IS_ERR_OR_NULL(driver))
-               tty_driver_kref_put(driver);
-err_file:
-       tty_free_file(filp);
-       return retval;
 }
 
 
@@ -2193,6 +2213,8 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
                return 0;
 
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return hung_up_tty_poll(filp, wait);
        if (ld->ops->poll)
                ret = ld->ops->poll(tty, filp, wait);
        tty_ldisc_deref(ld);
@@ -2202,7 +2224,6 @@ static unsigned int tty_poll(struct file *filp, poll_table *wait)
 static int __tty_fasync(int fd, struct file *filp, int on)
 {
        struct tty_struct *tty = file_tty(filp);
-       struct tty_ldisc *ldisc;
        unsigned long flags;
        int retval = 0;
 
@@ -2213,13 +2234,6 @@ static int __tty_fasync(int fd, struct file *filp, int on)
        if (retval <= 0)
                goto out;
 
-       ldisc = tty_ldisc_ref(tty);
-       if (ldisc) {
-               if (ldisc->ops->fasync)
-                       ldisc->ops->fasync(tty, on);
-               tty_ldisc_deref(ldisc);
-       }
-
        if (on) {
                enum pid_type type;
                struct pid *pid;
@@ -2245,10 +2259,11 @@ out:
 static int tty_fasync(int fd, struct file *filp, int on)
 {
        struct tty_struct *tty = file_tty(filp);
-       int retval;
+       int retval = -ENOTTY;
 
        tty_lock(tty);
-       retval = __tty_fasync(fd, filp, on);
+       if (!tty_hung_up_p(filp))
+               retval = __tty_fasync(fd, filp, on);
        tty_unlock(tty);
 
        return retval;
@@ -2282,6 +2297,8 @@ static int tiocsti(struct tty_struct *tty, char __user *p)
                return -EFAULT;
        tty_audit_tiocsti(tty, ch);
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return -EIO;
        ld->ops->receive_buf(tty, &ch, &mbz, 1);
        tty_ldisc_deref(ld);
        return 0;
@@ -2646,13 +2663,13 @@ static int tiocgsid(struct tty_struct *tty, struct tty_struct *real_tty, pid_t _
 
 static int tiocsetd(struct tty_struct *tty, int __user *p)
 {
-       int ldisc;
+       int disc;
        int ret;
 
-       if (get_user(ldisc, p))
+       if (get_user(disc, p))
                return -EFAULT;
 
-       ret = tty_set_ldisc(tty, ldisc);
+       ret = tty_set_ldisc(tty, disc);
 
        return ret;
 }
@@ -2674,6 +2691,8 @@ static int tiocgetd(struct tty_struct *tty, int __user *p)
        int ret;
 
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return -EIO;
        ret = put_user(ld->ops->num, p);
        tty_ldisc_deref(ld);
        return ret;
@@ -2971,6 +2990,8 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
                        return retval;
        }
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return hung_up_tty_ioctl(file, cmd, arg);
        retval = -EINVAL;
        if (ld->ops->ioctl) {
                retval = ld->ops->ioctl(tty, file, cmd, arg);
@@ -2999,6 +3020,8 @@ static long tty_compat_ioctl(struct file *file, unsigned int cmd,
        }
 
        ld = tty_ldisc_ref_wait(tty);
+       if (!ld)
+               return hung_up_tty_compat_ioctl(file, cmd, arg);
        if (ld->ops->compat_ioctl)
                retval = ld->ops->compat_ioctl(tty, file, cmd, arg);
        else
@@ -3149,6 +3172,7 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
        mutex_init(&tty->atomic_write_lock);
        spin_lock_init(&tty->ctrl_lock);
        spin_lock_init(&tty->flow_lock);
+       spin_lock_init(&tty->files_lock);
        INIT_LIST_HEAD(&tty->tty_files);
        INIT_WORK(&tty->SAK_work, do_SAK_work);
 
@@ -3161,20 +3185,6 @@ struct tty_struct *alloc_tty_struct(struct tty_driver *driver, int idx)
        return tty;
 }
 
-/**
- *     deinitialize_tty_struct
- *     @tty: tty to deinitialize
- *
- *     This subroutine deinitializes a tty structure that has been newly
- *     allocated but tty_release cannot be called on that yet.
- *
- *     Locking: none - tty in question must not be exposed at this point
- */
-void deinitialize_tty_struct(struct tty_struct *tty)
-{
-       tty_ldisc_deinit(tty);
-}
-
 /**
  *     tty_put_char    -       write one character to a tty
  *     @tty: tty
@@ -3569,7 +3579,7 @@ void __init console_init(void)
        initcall_t *call;
 
        /* Setup the default TTY line discipline. */
-       tty_ldisc_begin();
+       n_tty_init();
 
        /*
         * set up the console device so that later boot sequences can