tty: Change tty lock order to master->slave
authorPeter Hurley <peter@hurleysoftware.com>
Wed, 5 Nov 2014 17:13:01 +0000 (12:13 -0500)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 6 Nov 2014 02:50:42 +0000 (18:50 -0800)
When releasing the master pty, the slave pty also needs to be locked
to prevent concurrent tty count changes for the slave pty and to
ensure that only one parallel master and slave release observe the
final close, and proceed to destruct the pty pair. Conversely, when
releasing the slave pty, locking the master pty is not necessary
(since the master's state can be inferred by the slave tty count).

Introduce tty_lock_slave()/tty_unlock_slave() which acquires/releases
the tty lock of the slave pty. Remove tty_lock_pair()/tty_unlock_pair().

Dropping the tty_lock is no longer required to re-establish a stable
lock order.

Reviewed-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
drivers/tty/tty_io.c
drivers/tty/tty_mutex.c
include/linux/tty.h

index bd7cde3c56efa30557c623bd926c2b6b757a4d5d..4ecee2856eceb6a24ca1f4c6afeaaa6f6b14db76 100644 (file)
@@ -1790,7 +1790,9 @@ int tty_release(struct inode *inode, struct file *filp)
        if (tty->ops->close)
                tty->ops->close(tty, filp);
 
-       tty_unlock(tty);
+       /* If tty is pty master, lock the slave pty (stable lock order) */
+       tty_lock_slave(o_tty);
+
        /*
         * Sanity check: if tty->count is going to zero, there shouldn't be
         * any waiters on tty->read_wait or tty->write_wait.  We test the
@@ -1804,8 +1806,6 @@ int tty_release(struct inode *inode, struct file *filp)
         * Thus this test wouldn't be triggered at the time the slave closed,
         * so we do it now.
         */
-       tty_lock_pair(tty, o_tty);
-
        while (1) {
                do_sleep = 0;
 
@@ -1879,7 +1879,9 @@ int tty_release(struct inode *inode, struct file *filp)
        /* check whether both sides are closing ... */
        final = !tty->count && !(o_tty && o_tty->count);
 
-       tty_unlock_pair(tty, o_tty);
+       tty_unlock_slave(o_tty);
+       tty_unlock(tty);
+
        /* At this point, the tty->count == 0 should ensure a dead tty
           cannot be re-opened by a racing opener */
 
index 2e41abebbcbaef923c38a4ea6c3cee56dd6fe9fd..f43e995c7a0fc8a64ae1ee41eb2c7afa418df5b4 100644 (file)
@@ -4,6 +4,11 @@
 #include <linux/semaphore.h>
 #include <linux/sched.h>
 
+/*
+ * Nested tty locks are necessary for releasing pty pairs.
+ * The stable lock order is master pty first, then slave pty.
+ */
+
 /* Legacy tty mutex glue */
 
 enum {
@@ -45,29 +50,18 @@ void __lockfunc tty_unlock(struct tty_struct *tty)
 }
 EXPORT_SYMBOL(tty_unlock);
 
-/*
- * Getting the big tty mutex for a pair of ttys with lock ordering
- * On a non pty/tty pair tty2 can be NULL which is just fine.
- */
-void __lockfunc tty_lock_pair(struct tty_struct *tty,
-                                       struct tty_struct *tty2)
+void __lockfunc tty_lock_slave(struct tty_struct *tty)
 {
-       if (tty < tty2) {
-               tty_lock(tty);
-               tty_lock_nested(tty2, TTY_MUTEX_NESTED);
-       } else {
-               if (tty2 && tty2 != tty)
-                       tty_lock(tty2);
+       if (tty && tty != tty->link) {
+               WARN_ON(!mutex_is_locked(&tty->link->legacy_mutex) ||
+                       !tty->driver->type == TTY_DRIVER_TYPE_PTY ||
+                       !tty->driver->type == PTY_TYPE_SLAVE);
                tty_lock_nested(tty, TTY_MUTEX_NESTED);
        }
 }
-EXPORT_SYMBOL(tty_lock_pair);
 
-void __lockfunc tty_unlock_pair(struct tty_struct *tty,
-                                               struct tty_struct *tty2)
+void __lockfunc tty_unlock_slave(struct tty_struct *tty)
 {
-       tty_unlock(tty);
-       if (tty2 && tty2 != tty)
-               tty_unlock(tty2);
+       if (tty && tty != tty->link)
+               tty_unlock(tty);
 }
-EXPORT_SYMBOL(tty_unlock_pair);
index af1a7f3e4e4acc5e6e025a268bbaa7343d34cc8f..a07b4b415db89958b2df8a4930a25b15cc852aeb 100644 (file)
@@ -638,11 +638,8 @@ extern long vt_compat_ioctl(struct tty_struct *tty,
 /* functions for preparation of BKL removal */
 extern void __lockfunc tty_lock(struct tty_struct *tty);
 extern void __lockfunc tty_unlock(struct tty_struct *tty);
-extern void __lockfunc tty_lock_pair(struct tty_struct *tty,
-                               struct tty_struct *tty2);
-extern void __lockfunc tty_unlock_pair(struct tty_struct *tty,
-                               struct tty_struct *tty2);
-
+extern void __lockfunc tty_lock_slave(struct tty_struct *tty);
+extern void __lockfunc tty_unlock_slave(struct tty_struct *tty);
 /*
  * this shall be called only from where BTM is held (like close)
  *
This page took 0.028669 seconds and 5 git commands to generate.