Merge branch 'perf-fixes-for-linus' of git://git.kernel.org/pub/scm/linux/kernel...
[cascardo/linux.git] / drivers / char / tty_ldisc.c
index 500e740..412f977 100644 (file)
@@ -440,6 +440,8 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num)
  *
  *     A helper opening method. Also a convenient debugging and check
  *     point.
+ *
+ *     Locking: always called with BTM already held.
  */
 
 static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
@@ -447,10 +449,9 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
        WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags));
        if (ld->ops->open) {
                int ret;
-                /* BKL here locks verus a hangup event */
-               lock_kernel();
+                /* BTM here locks versus a hangup event */
+               WARN_ON(!tty_locked());
                ret = ld->ops->open(tty);
-               unlock_kernel();
                return ret;
        }
        return 0;
@@ -553,7 +554,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
        if (IS_ERR(new_ldisc))
                return PTR_ERR(new_ldisc);
 
-       lock_kernel();
+       tty_lock();
        /*
         *      We need to look at the tty locking here for pty/tty pairs
         *      when both sides try to change in parallel.
@@ -567,12 +568,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
         */
 
        if (tty->ldisc->ops->num == ldisc) {
-               unlock_kernel();
+               tty_unlock();
                tty_ldisc_put(new_ldisc);
                return 0;
        }
 
-       unlock_kernel();
+       tty_unlock();
        /*
         *      Problem: What do we do if this blocks ?
         *      We could deadlock here
@@ -580,6 +581,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
        tty_wait_until_sent(tty, 0);
 
+       tty_lock();
        mutex_lock(&tty->ldisc_mutex);
 
        /*
@@ -589,13 +591,13 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
        while (test_bit(TTY_LDISC_CHANGING, &tty->flags)) {
                mutex_unlock(&tty->ldisc_mutex);
+               tty_unlock();
                wait_event(tty_ldisc_wait,
                        test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0);
+               tty_lock();
                mutex_lock(&tty->ldisc_mutex);
        }
 
-       lock_kernel();
-
        set_bit(TTY_LDISC_CHANGING, &tty->flags);
 
        /*
@@ -607,7 +609,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
        o_ldisc = tty->ldisc;
 
-       unlock_kernel();
+       tty_unlock();
        /*
         *      Make sure we don't change while someone holds a
         *      reference to the line discipline. The TTY_LDISC bit
@@ -632,15 +634,15 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
 
        flush_scheduled_work();
 
+       tty_lock();
        mutex_lock(&tty->ldisc_mutex);
-       lock_kernel();
        if (test_bit(TTY_HUPPED, &tty->flags)) {
                /* We were raced by the hangup method. It will have stomped
                   the ldisc data and closed the ldisc down */
                clear_bit(TTY_LDISC_CHANGING, &tty->flags);
                mutex_unlock(&tty->ldisc_mutex);
                tty_ldisc_put(new_ldisc);
-               unlock_kernel();
+               tty_unlock();
                return -EIO;
        }
 
@@ -682,7 +684,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
        if (o_work)
                schedule_delayed_work(&o_tty->buf.work, 1);
        mutex_unlock(&tty->ldisc_mutex);
-       unlock_kernel();
+       tty_unlock();
        return retval;
 }
 
@@ -780,7 +782,20 @@ void tty_ldisc_hangup(struct tty_struct *tty)
         * Avoid racing set_ldisc or tty_ldisc_release
         */
        mutex_lock(&tty->ldisc_mutex);
-       tty_ldisc_halt(tty);
+
+       /*
+        * this is like tty_ldisc_halt, but we need to give up
+        * the BTM before calling cancel_delayed_work_sync,
+        * which may need to wait for another function taking the BTM
+        */
+       clear_bit(TTY_LDISC, &tty->flags);
+       tty_unlock();
+       cancel_delayed_work_sync(&tty->buf.work);
+       mutex_unlock(&tty->ldisc_mutex);
+
+       tty_lock();
+       mutex_lock(&tty->ldisc_mutex);
+
        /* At this point we have a closed ldisc and we want to
           reopen it. We could defer this to the next open but
           it means auditing a lot of other paths so this is
@@ -851,8 +866,10 @@ void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty)
         * race with the set_ldisc code path.
         */
 
+       tty_unlock();
        tty_ldisc_halt(tty);
        flush_scheduled_work();
+       tty_lock();
 
        mutex_lock(&tty->ldisc_mutex);
        /*