]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/tty_io.c
tty: shutdown method
[linux-2.6-omap-h63xx.git] / drivers / char / tty_io.c
index 3a726936aa5bd3198bba9f8055e070b88d6778a7..f91704d57a4e3c288d70826c0bd717e523db86f3 100644 (file)
@@ -559,6 +559,7 @@ static void do_tty_hangup(struct work_struct *work)
        struct tty_ldisc *ld;
        int    closecount = 0, n;
        unsigned long flags;
+       int refs = 0;
 
        if (!tty)
                return;
@@ -625,8 +626,12 @@ static void do_tty_hangup(struct work_struct *work)
        if (tty->session) {
                do_each_pid_task(tty->session, PIDTYPE_SID, p) {
                        spin_lock_irq(&p->sighand->siglock);
-                       if (p->signal->tty == tty)
+                       if (p->signal->tty == tty) {
                                p->signal->tty = NULL;
+                               /* We defer the dereferences outside fo
+                                  the tasklist lock */
+                               refs++;
+                       }
                        if (!p->signal->leader) {
                                spin_unlock_irq(&p->sighand->siglock);
                                continue;
@@ -652,6 +657,10 @@ static void do_tty_hangup(struct work_struct *work)
        tty->ctrl_status = 0;
        spin_unlock_irqrestore(&tty->ctrl_lock, flags);
 
+       /* Account for the p->signal references we killed */
+       while (refs--)
+               tty_kref_put(tty);
+
        /*
         * If one of the devices matches a console pointer, we
         * cannot just call hangup() because that will cause
@@ -720,6 +729,23 @@ void tty_vhangup(struct tty_struct *tty)
 
 EXPORT_SYMBOL(tty_vhangup);
 
+/**
+ *     tty_vhangup_self        -       process vhangup for own ctty
+ *
+ *     Perform a vhangup on the current controlling tty
+ */
+
+void tty_vhangup_self(void)
+{
+       struct tty_struct *tty;
+
+       tty = get_current_tty();
+       if (tty) {
+               tty_vhangup(tty);
+               tty_kref_put(tty);
+       }
+}
+
 /**
  *     tty_hung_up_p           -       was tty hung up
  *     @filp: file pointer of tty
@@ -773,16 +799,14 @@ void disassociate_ctty(int on_exit)
        struct pid *tty_pgrp = NULL;
 
 
-       mutex_lock(&tty_mutex);
        tty = get_current_tty();
        if (tty) {
                tty_pgrp = get_pid(tty->pgrp);
                lock_kernel();
-               mutex_unlock(&tty_mutex);
-               /* XXX: here we race, there is nothing protecting tty */
                if (on_exit && tty->driver->type != TTY_DRIVER_TYPE_PTY)
                        tty_vhangup(tty);
                unlock_kernel();
+               tty_kref_put(tty);
        } else if (on_exit) {
                struct pid *old_pgrp;
                spin_lock_irq(&current->sighand->siglock);
@@ -794,7 +818,6 @@ void disassociate_ctty(int on_exit)
                        kill_pgrp(old_pgrp, SIGCONT, on_exit);
                        put_pid(old_pgrp);
                }
-               mutex_unlock(&tty_mutex);
                return;
        }
        if (tty_pgrp) {
@@ -809,8 +832,6 @@ void disassociate_ctty(int on_exit)
        current->signal->tty_old_pgrp = NULL;
        spin_unlock_irq(&current->sighand->siglock);
 
-       mutex_lock(&tty_mutex);
-       /* It is possible that do_tty_hangup has free'd this tty */
        tty = get_current_tty();
        if (tty) {
                unsigned long flags;
@@ -820,13 +841,13 @@ void disassociate_ctty(int on_exit)
                tty->session = NULL;
                tty->pgrp = NULL;
                spin_unlock_irqrestore(&tty->ctrl_lock, flags);
+               tty_kref_put(tty);
        } else {
 #ifdef TTY_DEBUG_HANGUP
                printk(KERN_DEBUG "error attempted to write to tty [0x%p]"
                       " = NULL", tty);
 #endif
        }
-       mutex_unlock(&tty_mutex);
 
        /* Now clear signal->tty under the lock */
        read_lock(&tasklist_lock);
@@ -1072,6 +1093,31 @@ out:
        return ret;
 }
 
+/**
+ * tty_write_message - write a message to a certain tty, not just the console.
+ * @tty: the destination tty_struct
+ * @msg: the message to write
+ *
+ * This is used for messages that need to be redirected to a specific tty.
+ * We don't put it into the syslog queue right now maybe in the future if
+ * really needed.
+ *
+ * We must still hold the BKL and test the CLOSING flag for the moment.
+ */
+
+void tty_write_message(struct tty_struct *tty, char *msg)
+{
+       lock_kernel();
+       if (tty) {
+               mutex_lock(&tty->atomic_write_lock);
+               if (tty->ops->write && !test_bit(TTY_CLOSING, &tty->flags))
+                       tty->ops->write(tty, msg, strlen(msg));
+               tty_write_unlock(tty);
+       }
+       unlock_kernel();
+       return;
+}
+
 
 /**
  *     tty_write               -       write method for tty device file
@@ -1139,42 +1185,6 @@ ssize_t redirected_tty_write(struct file *file, const char __user *buf,
        return tty_write(file, buf, count, ppos);
 }
 
-void tty_port_init(struct tty_port *port)
-{
-       memset(port, 0, sizeof(*port));
-       init_waitqueue_head(&port->open_wait);
-       init_waitqueue_head(&port->close_wait);
-       mutex_init(&port->mutex);
-       port->close_delay = (50 * HZ) / 100;
-       port->closing_wait = (3000 * HZ) / 100;
-}
-EXPORT_SYMBOL(tty_port_init);
-
-int tty_port_alloc_xmit_buf(struct tty_port *port)
-{
-       /* We may sleep in get_zeroed_page() */
-       mutex_lock(&port->mutex);
-       if (port->xmit_buf == NULL)
-               port->xmit_buf = (unsigned char *)get_zeroed_page(GFP_KERNEL);
-       mutex_unlock(&port->mutex);
-       if (port->xmit_buf == NULL)
-               return -ENOMEM;
-       return 0;
-}
-EXPORT_SYMBOL(tty_port_alloc_xmit_buf);
-
-void tty_port_free_xmit_buf(struct tty_port *port)
-{
-       mutex_lock(&port->mutex);
-       if (port->xmit_buf != NULL) {
-               free_page((unsigned long)port->xmit_buf);
-               port->xmit_buf = NULL;
-       }
-       mutex_unlock(&port->mutex);
-}
-EXPORT_SYMBOL(tty_port_free_xmit_buf);
-
-
 static char ptychar[] = "pqrstuvwxyzabcde";
 
 /**
@@ -1217,7 +1227,8 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
  *     init_dev                -       initialise a tty device
  *     @driver: tty driver we are opening a device on
  *     @idx: device index
- *     @tty: returned tty structure
+ *     @ret_tty: returned tty structure
+ *     @first_ok: ok to open a new device (used by ptmx)
  *
  *     Prepare a tty device. This may not be a "new" clean device but
  *     could also be an active device. The pty drivers require special
@@ -1238,7 +1249,7 @@ static void tty_line_name(struct tty_driver *driver, int index, char *p)
  */
 
 static int init_dev(struct tty_driver *driver, int idx,
-       struct tty_struct **ret_tty)
+       struct tty_struct **ret_tty, int first_ok)
 {
        struct tty_struct *tty, *o_tty;
        struct ktermios *tp, **tp_loc, *o_tp, **o_tp_loc;
@@ -1269,6 +1280,11 @@ static int init_dev(struct tty_driver *driver, int idx,
        }
        if (tty) goto fast_track;
 
+       if (driver->subtype == PTY_TYPE_MASTER &&
+               (driver->flags & TTY_DRIVER_DEVPTS_MEM) && !first_ok) {
+               retval = -EIO;
+               goto end_init;
+       }
        /*
         * First time open is complex, especially for PTY devices.
         * This code guarantees that either everything succeeds and the
@@ -1320,6 +1336,12 @@ static int init_dev(struct tty_driver *driver, int idx,
                o_tty = alloc_tty_struct();
                if (!o_tty)
                        goto free_mem_out;
+               if (!try_module_get(driver->other->owner)) {
+                       /* This cannot in fact currently happen */
+                       free_tty_struct(o_tty);
+                       o_tty = NULL;
+                       goto free_mem_out;
+               }
                initialize_tty_struct(o_tty);
                o_tty->driver = driver->other;
                o_tty->ops = driver->ops;
@@ -1397,7 +1419,7 @@ static int init_dev(struct tty_driver *driver, int idx,
 
        if (retval)
                goto release_mem_out;
-        goto success;
+       goto success;
 
        /*
         * This fast open can be used if the tty is already open.
@@ -1438,8 +1460,10 @@ end_init:
        /* Release locally allocated memory ... nothing placed in slots */
 free_mem_out:
        kfree(o_tp);
-       if (o_tty)
+       if (o_tty) {
+               module_put(o_tty->driver->owner);
                free_tty_struct(o_tty);
+       }
        kfree(ltp);
        kfree(tp);
        free_tty_struct(tty);
@@ -1458,8 +1482,34 @@ release_mem_out:
        goto end_init;
 }
 
+void tty_free_termios(struct tty_struct *tty)
+{
+       struct ktermios *tp;
+       int idx = tty->index;
+       /* Kill this flag and push into drivers for locking etc */
+       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
+               /* FIXME: Locking on ->termios array */
+               tp = tty->termios;
+               tty->driver->termios[idx] = NULL;
+               kfree(tp);
+
+               tp = tty->termios_locked;
+               tty->driver->termios_locked[idx] = NULL;
+               kfree(tp);
+       }
+}
+EXPORT_SYMBOL(tty_free_termios);
+
+void tty_shutdown(struct tty_struct *tty)
+{
+       tty->driver->ttys[tty->index] = NULL;
+       tty_free_termios(tty);
+}
+EXPORT_SYMBOL(tty_shutdown);
+
 /**
  *     release_one_tty         -       release tty structure memory
+ *     @kref: kref of tty we are obliterating
  *
  *     Releases memory associated with a tty structure, and clears out the
  *     driver table slots. This function is called when a device is no longer
@@ -1469,31 +1519,20 @@ release_mem_out:
  *             tty_mutex - sometimes only
  *             takes the file list lock internally when working on the list
  *     of ttys that the driver keeps.
- *             FIXME: should we require tty_mutex is held here ??
  */
-static void release_one_tty(struct tty_struct *tty, int idx)
+static void release_one_tty(struct kref *kref)
 {
-       int devpts = tty->driver->flags & TTY_DRIVER_DEVPTS_MEM;
-       struct ktermios *tp;
-
-       if (!devpts)
-               tty->driver->ttys[idx] = NULL;
-
-       if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
-               tp = tty->termios;
-               if (!devpts)
-                       tty->driver->termios[idx] = NULL;
-               kfree(tp);
-
-               tp = tty->termios_locked;
-               if (!devpts)
-                       tty->driver->termios_locked[idx] = NULL;
-               kfree(tp);
-       }
-
+       struct tty_struct *tty = container_of(kref, struct tty_struct, kref);
+       struct tty_driver *driver = tty->driver;
 
+       if (tty->ops->shutdown)
+               tty->ops->shutdown(tty);
+       else
+               tty_shutdown(tty);
        tty->magic = 0;
+       /* FIXME: locking on tty->driver->refcount */
        tty->driver->refcount--;
+       module_put(driver->owner);
 
        file_list_lock();
        list_del_init(&tty->tty_files);
@@ -1502,6 +1541,21 @@ static void release_one_tty(struct tty_struct *tty, int idx)
        free_tty_struct(tty);
 }
 
+/**
+ *     tty_kref_put            -       release a tty kref
+ *     @tty: tty device
+ *
+ *     Release a reference to a tty device and if need be let the kref
+ *     layer destruct the object for us
+ */
+
+void tty_kref_put(struct tty_struct *tty)
+{
+       if (tty)
+               kref_put(&tty->kref, release_one_tty);
+}
+EXPORT_SYMBOL(tty_kref_put);
+
 /**
  *     release_tty             -       release tty structure memory
  *
@@ -1513,15 +1567,16 @@ static void release_one_tty(struct tty_struct *tty, int idx)
  *             takes the file list lock internally when working on the list
  *     of ttys that the driver keeps.
  *             FIXME: should we require tty_mutex is held here ??
+ *
  */
 static void release_tty(struct tty_struct *tty, int idx)
 {
-       struct tty_driver *driver = tty->driver;
+       /* This should always be true but check for the moment */
+       WARN_ON(tty->index != idx);
 
        if (tty->link)
-               release_one_tty(tty->link, idx);
-       release_one_tty(tty, idx);
-       module_put(driver->owner);
+               tty_kref_put(tty->link);
+       tty_kref_put(tty);
 }
 
 /*
@@ -1755,7 +1810,7 @@ static void release_dev(struct file *filp)
 }
 
 /**
- *     tty_open                -       open a tty device
+ *     __tty_open              -       open a tty device
  *     @inode: inode of device file
  *     @filp: file pointer to tty
  *
@@ -1803,6 +1858,8 @@ retry_open:
                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
@@ -1832,7 +1889,7 @@ retry_open:
                return -ENODEV;
        }
 got_driver:
-       retval = init_dev(driver, index, &tty);
+       retval = init_dev(driver, index, &tty, 0);
        mutex_unlock(&tty_mutex);
        if (retval)
                return retval;
@@ -1929,7 +1986,7 @@ static int __ptmx_open(struct inode *inode, struct file *filp)
                return index;
 
        mutex_lock(&tty_mutex);
-       retval = init_dev(ptm_driver, index, &tty);
+       retval = init_dev(ptm_driver, index, &tty, 1);
        mutex_unlock(&tty_mutex);
 
        if (retval)
@@ -2130,7 +2187,7 @@ int tty_do_resize(struct tty_struct *tty, struct tty_struct *real_tty,
 
        /* For a PTY we need to lock the tty side */
        mutex_lock(&real_tty->termios_mutex);
-       if (!memcmp(ws, &tty->winsize, sizeof(*ws)))
+       if (!memcmp(ws, &real_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 */
@@ -2602,7 +2659,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        case TIOCSTI:
                return tiocsti(tty, p);
        case TIOCGWINSZ:
-               return tiocgwinsz(tty, p);
+               return tiocgwinsz(real_tty, p);
        case TIOCSWINSZ:
                return tiocswinsz(tty, real_tty, p);
        case TIOCCONS:
@@ -2834,6 +2891,7 @@ EXPORT_SYMBOL(do_SAK);
 static void initialize_tty_struct(struct tty_struct *tty)
 {
        memset(tty, 0, sizeof(struct tty_struct));
+       kref_init(&tty->kref);
        tty->magic = TTY_MAGIC;
        tty_ldisc_init(tty);
        tty->session = NULL;
@@ -3089,9 +3147,12 @@ EXPORT_SYMBOL(tty_devnum);
 
 void proc_clear_tty(struct task_struct *p)
 {
+       struct tty_struct *tty;
        spin_lock_irq(&p->sighand->siglock);
+       tty = p->signal->tty;
        p->signal->tty = NULL;
        spin_unlock_irq(&p->sighand->siglock);
+       tty_kref_put(tty);
 }
 
 /* Called under the sighand lock */
@@ -3107,9 +3168,13 @@ static void __proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
                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;
+       tsk->signal->tty = tty_kref_get(tty);
        tsk->signal->tty_old_pgrp = NULL;
 }
 
@@ -3123,14 +3188,11 @@ static void proc_set_tty(struct task_struct *tsk, struct tty_struct *tty)
 struct tty_struct *get_current_tty(void)
 {
        struct tty_struct *tty;
-       WARN_ON_ONCE(!mutex_is_locked(&tty_mutex));
-       tty = current->signal->tty;
-       /*
-        * session->tty can be changed/cleared from under us, make sure we
-        * issue the load. The obtained pointer, when not NULL, is valid as
-        * long as we hold tty_mutex.
-        */
-       barrier();
+       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);