aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2009-10-04 00:44:21 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2009-10-04 00:44:21 -0400
commit0b5759c654e74c8dc317ea2c6b3a7476160f688a (patch)
tree50de6ba41dcc19cb76c8a35408b7cc1ba80d2e41 /drivers/char
parentf0a221ef47df3cdde2123fe75ce3b61bb7df656d (diff)
tty: Avoid dropping ldisc_mutex over hangup tty re-initialization
A couple of people have hit the WARN_ON() in drivers/char/tty_io.c, tty_open() that is unhappy about seeing the tty line discipline go away during the tty hangup. See for example http://bugzilla.kernel.org/show_bug.cgi?id=14255 and the reason is that we do the tty_ldisc_halt() outside the ldisc_mutex in order to be able to flush the scheduled work without a deadlock with vhangup_work. However, it turns out that we can solve this particular case by - using "cancel_delayed_work_sync()" in tty_ldisc_halt(), which waits for just the particular work, rather than synchronizing with any random outstanding pending work. This won't deadlock, since the buf.work we synchronize with doesn't care about the ldisc_mutex, it just flushes the tty ldisc buffers. - realize that for this particular case, we don't need to wait for any hangup work, because we are inside the hangup codepaths ourselves. so as a result we can just drop the flush_scheduled_work() entirely, and then move the tty_ldisc_halt() call to inside the mutex. That way we never expose the partially torn down ldisc state to tty_open(), and hold the ldisc_mutex over the whole sequence. Reported-by: Ingo Molnar <mingo@elte.hu> Reported-by: Heinz Diehl <htd@fancy-poultry.org> Cc: stable@kernel.org Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char')
-rw-r--r--drivers/char/tty_ldisc.c7
1 files changed, 2 insertions, 5 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index aafdbaebc16a..feb55075819b 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/tty_ldisc.c
@@ -518,7 +518,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
518static int tty_ldisc_halt(struct tty_struct *tty) 518static int tty_ldisc_halt(struct tty_struct *tty)
519{ 519{
520 clear_bit(TTY_LDISC, &tty->flags); 520 clear_bit(TTY_LDISC, &tty->flags);
521 return cancel_delayed_work(&tty->buf.work); 521 return cancel_delayed_work_sync(&tty->buf.work);
522} 522}
523 523
524/** 524/**
@@ -756,12 +756,9 @@ void tty_ldisc_hangup(struct tty_struct *tty)
756 * N_TTY. 756 * N_TTY.
757 */ 757 */
758 if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { 758 if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) {
759 /* Make sure the old ldisc is quiescent */
760 tty_ldisc_halt(tty);
761 flush_scheduled_work();
762
763 /* Avoid racing set_ldisc or tty_ldisc_release */ 759 /* Avoid racing set_ldisc or tty_ldisc_release */
764 mutex_lock(&tty->ldisc_mutex); 760 mutex_lock(&tty->ldisc_mutex);
761 tty_ldisc_halt(tty);
765 if (tty->ldisc) { /* Not yet closed */ 762 if (tty->ldisc) { /* Not yet closed */
766 /* Switch back to N_TTY */ 763 /* Switch back to N_TTY */
767 tty_ldisc_reinit(tty); 764 tty_ldisc_reinit(tty);