diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-04 00:44:21 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-10-04 00:44:21 -0400 |
commit | 0b5759c654e74c8dc317ea2c6b3a7476160f688a (patch) | |
tree | 50de6ba41dcc19cb76c8a35408b7cc1ba80d2e41 /drivers/char | |
parent | f0a221ef47df3cdde2123fe75ce3b61bb7df656d (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.c | 7 |
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) | |||
518 | static int tty_ldisc_halt(struct tty_struct *tty) | 518 | static 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); |