tty: Extract various bits of ldisc code
authorAlan Cox <alan@linux.intel.com>
Thu, 11 Jun 2009 11:48:02 +0000 (12:48 +0100)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 11 Jun 2009 15:51:01 +0000 (08:51 -0700)
Before trying to tackle the ldisc bugs the code needs to be a good deal
more readable, so do the simple extractions of routines first.

Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
drivers/char/tty_io.c
drivers/char/tty_ldisc.c
include/linux/tty.h

index 6c817398232e45020a0e52d66c7b399efa94f028..be49d0730bb99b41b5e400ddb1c1454b800a2100 100644 (file)
@@ -2481,6 +2481,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int
        return tty->ops->tiocmset(tty, file, set, clear);
 }
 
+struct tty_struct *tty_pair_get_tty(struct tty_struct *tty)
+{
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+               tty = tty->link;
+       return tty;
+}
+EXPORT_SYMBOL(tty_pair_get_tty);
+
+struct tty_struct *tty_pair_get_pty(struct tty_struct *tty)
+{
+       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
+           tty->driver->subtype == PTY_TYPE_MASTER)
+           return tty;
+       return tty->link;
+}
+EXPORT_SYMBOL(tty_pair_get_pty);
+
 /*
  * Split this up, as gcc can choke on it otherwise..
  */
@@ -2496,11 +2514,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
        if (tty_paranoia_check(tty, inode, "tty_ioctl"))
                return -EINVAL;
 
-       real_tty = tty;
-       if (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
-           tty->driver->subtype == PTY_TYPE_MASTER)
-               real_tty = tty->link;
-
+       real_tty = tty_pair_get_tty(tty);
 
        /*
         * Factor out some common prep work
index f78f5b0127a88501ec0b21cf9d42c517209ad933..e3c6416aa86db4ba6b89860b2617b3f4f5e92f01 100644 (file)
@@ -443,6 +443,50 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
        }
 }
 
+/**
+ *     tty_ldisc_halt          -       shutdown the line discipline
+ *     @tty: tty device
+ *
+ *     Shut down the line discipline and work queue for this tty device.
+ *     The TTY_LDISC flag being cleared ensures no further references can
+ *     be obtained while the delayed work queue halt ensures that no more
+ *     data is fed to the ldisc.
+ *
+ *     In order to wait for any existing references to complete see 
+ *     tty_ldisc_wait_idle.
+ */
+
+static void tty_ldisc_halt(struct tty_struct *tty)
+{
+       clear_bit(TTY_LDISC, &tty->flags);
+       cancel_delayed_work(&tty->buf.work);
+       /*
+        * Wait for ->hangup_work and ->buf.work handlers to terminate
+        */
+       flush_scheduled_work();
+}
+
+/**
+ *     tty_ldisc_wait_idle     -       wait for the ldisc to become idle
+ *     @tty: tty to wait for
+ *
+ *     Wait for the line discipline to become idle. The discipline must
+ *     have been halted for this to guarantee it remains idle.
+ *
+ */
+
+static void tty_ldisc_wait_idle(struct tty_struct *tty)
+{
+       unsigned long flags;
+       spin_lock_irqsave(&tty_ldisc_lock, flags);
+       while (tty->ldisc.refcount) {
+               spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+               wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
+               spin_lock_irqsave(&tty_ldisc_lock, flags);
+       }
+       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+}
+
 /**
  *     tty_set_ldisc           -       set line discipline
  *     @tty: the terminal to set
@@ -636,6 +680,21 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
        return 0;
 }
 
+static void tty_ldisc_reinit(struct tty_struct *tty)
+{
+       struct tty_ldisc ld;
+
+       if (tty->ldisc.ops->close)
+               (tty->ldisc.ops->close)(tty);
+       tty_ldisc_put(tty->ldisc.ops);
+       /*
+        *      Switch the line discipline back
+        */
+       WARN_ON(tty_ldisc_get(N_TTY, &ld));
+       tty_ldisc_assign(tty, &ld);
+       tty_set_termios_ldisc(tty, N_TTY);
+}
+
 /**
  *     tty_ldisc_release               -       release line discipline
  *     @tty: tty being shut down
@@ -647,58 +706,34 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty)
 
 void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
 {
-       unsigned long flags;
-       struct tty_ldisc ld;
+
        /*
         * Prevent flush_to_ldisc() from rescheduling the work for later.  Then
         * kill any delayed work. As this is the final close it does not
         * race with the set_ldisc code path.
         */
-       clear_bit(TTY_LDISC, &tty->flags);
-       cancel_delayed_work(&tty->buf.work);
 
-       /*
-        * Wait for ->hangup_work and ->buf.work handlers to terminate
-        */
-
-       flush_scheduled_work();
+       tty_ldisc_halt(tty);
 
        /*
         * Wait for any short term users (we know they are just driver
         * side waiters as the file is closing so user count on the file
         * side is zero.
         */
-       spin_lock_irqsave(&tty_ldisc_lock, flags);
-       while (tty->ldisc.refcount) {
-               spin_unlock_irqrestore(&tty_ldisc_lock, flags);
-               wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0);
-               spin_lock_irqsave(&tty_ldisc_lock, flags);
-       }
-       spin_unlock_irqrestore(&tty_ldisc_lock, flags);
+
+       tty_ldisc_wait_idle(tty);
+
        /*
         * Shutdown the current line discipline, and reset it to N_TTY.
         *
         * FIXME: this MUST get fixed for the new reflocking
         */
-       if (tty->ldisc.ops->close)
-               (tty->ldisc.ops->close)(tty);
-       tty_ldisc_put(tty->ldisc.ops);
 
-       /*
-        *      Switch the line discipline back
-        */
-       WARN_ON(tty_ldisc_get(N_TTY, &ld));
-       tty_ldisc_assign(tty, &ld);
-       tty_set_termios_ldisc(tty, N_TTY);
+       tty_ldisc_reinit(tty);
        if (o_tty) {
                /* FIXME: could o_tty be in setldisc here ? */
                clear_bit(TTY_LDISC, &o_tty->flags);
-               if (o_tty->ldisc.ops->close)
-                       (o_tty->ldisc.ops->close)(o_tty);
-               tty_ldisc_put(o_tty->ldisc.ops);
-               WARN_ON(tty_ldisc_get(N_TTY, &ld));
-               tty_ldisc_assign(o_tty, &ld);
-               tty_set_termios_ldisc(o_tty, N_TTY);
+               tty_ldisc_reinit(o_tty);
        }
 }
 
index bed5a3d4030743d10fb4d28e4e93314e87a09722..f9c13c83790c03ddf0703dec5c505ee7811e9def 100644 (file)
@@ -428,6 +428,9 @@ extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx,
 extern void tty_release_dev(struct file *filp);
 extern int tty_init_termios(struct tty_struct *tty);
 
+extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty);
+extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty);
+
 extern struct mutex tty_mutex;
 
 extern void tty_write_unlock(struct tty_struct *tty);