]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/char/tty_ldisc.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/lrg/voltage-2.6
[linux-2.6-omap-h63xx.git] / drivers / char / tty_ldisc.c
index f307f135cbfb63f23aebb9edc89eb7c559ed2fce..7a84b406a9522d633f6eab84ada8134319b2aeac 100644 (file)
@@ -316,8 +316,7 @@ struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
 {
        /* wait_event is a macro */
        wait_event(tty_ldisc_wait, tty_ldisc_try(tty));
-       if (tty->ldisc.refcount == 0)
-               printk(KERN_ERR "tty_ldisc_ref_wait\n");
+       WARN_ON(tty->ldisc.refcount == 0);
        return &tty->ldisc;
 }
 
@@ -376,15 +375,17 @@ EXPORT_SYMBOL_GPL(tty_ldisc_deref);
  *     @tty: terminal to activate ldisc on
  *
  *     Set the TTY_LDISC flag when the line discipline can be called
- *     again. Do necessary wakeups for existing sleepers.
+ *     again. Do necessary wakeups for existing sleepers. Clear the LDISC
+ *     changing flag to indicate any ldisc change is now over.
  *
- *     Note: nobody should set this bit except via this function. Clearing
- *     directly is allowed.
+ *     Note: nobody should set the TTY_LDISC bit except via this function.
+ *     Clearing directly is allowed.
  */
 
 void tty_ldisc_enable(struct tty_struct *tty)
 {
        set_bit(TTY_LDISC, &tty->flags);
+       clear_bit(TTY_LDISC_CHANGING, &tty->flags);
        wake_up(&tty_ldisc_wait);
 }
 
@@ -496,7 +497,14 @@ restart:
         *      reference to the line discipline. The TTY_LDISC bit
         *      prevents anyone taking a reference once it is clear.
         *      We need the lock to avoid racing reference takers.
+        *
+        *      We must clear the TTY_LDISC bit here to avoid a livelock
+        *      with a userspace app continually trying to use the tty in
+        *      parallel to the change and re-referencing the tty.
         */
+       clear_bit(TTY_LDISC, &tty->flags);
+       if (o_tty)
+               clear_bit(TTY_LDISC, &o_tty->flags);
 
        spin_lock_irqsave(&tty_ldisc_lock, flags);
        if (tty->ldisc.refcount || (o_tty && o_tty->ldisc.refcount)) {
@@ -528,7 +536,7 @@ restart:
         *      If the TTY_LDISC bit is set, then we are racing against
         *      another ldisc change
         */
-       if (!test_bit(TTY_LDISC, &tty->flags)) {
+       if (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
                struct tty_ldisc *ld;
                spin_unlock_irqrestore(&tty_ldisc_lock, flags);
                tty_ldisc_put(new_ldisc.ops);
@@ -536,10 +544,14 @@ restart:
                tty_ldisc_deref(ld);
                goto restart;
        }
-
-       clear_bit(TTY_LDISC, &tty->flags);
+       /*
+        *      This flag is used to avoid two parallel ldisc changes. Once
+        *      open and close are fine grained locked this may work better
+        *      as a mutex shared with the open/close/hup paths
+        */
+       set_bit(TTY_LDISC_CHANGING, &tty->flags);
        if (o_tty)
-               clear_bit(TTY_LDISC, &o_tty->flags);
+               set_bit(TTY_LDISC_CHANGING, &o_tty->flags);
        spin_unlock_irqrestore(&tty_ldisc_lock, flags);
        
        /*