OK, so my last attempt didn't fix it.  Still, the problem is that con_close()
can sleep, dropping bkl, permitting another task to come in and grab a
tty->count ref while con_close() is in the process of tearing things down.

We've always had this race with CONFIG_DEVFS=y, because
vcs_remove_devfs()->devfs_remvoe() can sleep.

The patch extends tty_sem coverage to release_dev(), as well as init_dev(),
thus preventing the open-vs-close races.  Judging by the comment over the
tty_sem definition this was always intended.  Mabe someone backed it out
again because of some deadlock...


---

 25-akpm/drivers/char/tty_io.c |   22 +++++++++++++---------
 1 files changed, 13 insertions(+), 9 deletions(-)

diff -puN drivers/char/tty_io.c~tty-locking-again drivers/char/tty_io.c
--- 25/drivers/char/tty_io.c~tty-locking-again	2004-03-27 16:25:08.265371360 -0800
+++ 25-akpm/drivers/char/tty_io.c	2004-03-27 16:25:40.840419200 -0800
@@ -1084,6 +1084,7 @@ static void release_dev(struct file * fi
 	tty_fasync(-1, filp, 0);
 
 	idx = tty->index;
+	down(&tty_sem);
 	pty_master = (tty->driver->type == TTY_DRIVER_TYPE_PTY &&
 		      tty->driver->subtype == PTY_TYPE_MASTER);
 	o_tty = tty->link;
@@ -1092,25 +1093,25 @@ static void release_dev(struct file * fi
 	if (idx < 0 || idx >= tty->driver->num) {
 		printk(KERN_DEBUG "release_dev: bad idx when trying to "
 				  "free (%s)\n", tty->name);
-		return;
+		goto out;
 	}
 	if (!(tty->driver->flags & TTY_DRIVER_DEVPTS_MEM)) {
 		if (tty != tty->driver->ttys[idx]) {
 			printk(KERN_DEBUG "release_dev: driver.table[%d] not tty "
 			       "for (%s)\n", idx, tty->name);
-			return;
+			goto out;
 		}
 		if (tty->termios != tty->driver->termios[idx]) {
 			printk(KERN_DEBUG "release_dev: driver.termios[%d] not termios "
 			       "for (%s)\n",
 			       idx, tty->name);
-			return;
+			goto out;
 		}
 		if (tty->termios_locked != tty->driver->termios_locked[idx]) {
 			printk(KERN_DEBUG "release_dev: driver.termios_locked[%d] not "
 			       "termios_locked for (%s)\n",
 			       idx, tty->name);
-			return;
+			goto out;
 		}
 	}
 #endif
@@ -1127,24 +1128,24 @@ static void release_dev(struct file * fi
 			printk(KERN_DEBUG "release_dev: other->table[%d] "
 					  "not o_tty for (%s)\n",
 			       idx, tty->name);
-			return;
+			goto out;
 		}
 		if (o_tty->termios != tty->driver->other->termios[idx]) {
 			printk(KERN_DEBUG "release_dev: other->termios[%d] "
 					  "not o_termios for (%s)\n",
 			       idx, tty->name);
-			return;
+			goto out;
 		}
 		if (o_tty->termios_locked != 
 		      tty->driver->other->termios_locked[idx]) {
 			printk(KERN_DEBUG "release_dev: other->termios_locked["
 					  "%d] not o_termios_locked for (%s)\n",
 			       idx, tty->name);
-			return;
+			goto out;
 		}
 		if (o_tty->link != tty) {
 			printk(KERN_DEBUG "release_dev: bad pty pointers\n");
-			return;
+			goto out;
 		}
 	}
 #endif
@@ -1276,7 +1277,7 @@ static void release_dev(struct file * fi
 
 	/* check whether both sides are closing ... */
 	if (!tty_closing || (o_tty && !o_tty_closing))
-		return;
+		goto out;
 	
 #ifdef TTY_DEBUG_HANGUP
 	printk(KERN_DEBUG "freeing tty structure...");
@@ -1316,6 +1317,9 @@ static void release_dev(struct file * fi
 	 * the slots and preserving the termios structure.
 	 */
 	release_mem(tty, idx);
+out:
+	up(&tty_sem);
+	return;
 }
 
 /*

_