diff options
| author | Philippe Rétornaz <philippe.retornaz@epfl.ch> | 2010-10-27 11:13:21 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2010-11-09 18:05:50 -0500 |
| commit | 1c95ba1e1de7edffc0c4e275e147f1a9eb1f81ae (patch) | |
| tree | 9c0db77569313d05ac0e7298984897f9c003da95 | |
| parent | 100eeae2c5ce23b4db93ff320ee330ef1d740151 (diff) | |
tty_ldisc: Fix BUG() on hangup
A kernel BUG when bluetooth rfcomm connection drop while the associated
serial port is open is sometime triggered.
It seems that the line discipline can disappear between the
tty_ldisc_put and tty_ldisc_get. This patch fall back to the N_TTY line
discipline if the previous discipline is not available anymore.
Signed-off-by: Philippe Retornaz <philippe.retornaz@epfl.ch>
Acked-by: Alan Cox <alan@linux.intel.com>
Cc: stable <stable@kernel.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/tty/tty_ldisc.c | 20 |
1 files changed, 13 insertions, 7 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 5bbf33ad49f1..d8e96b005023 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c | |||
| @@ -743,9 +743,12 @@ static void tty_reset_termios(struct tty_struct *tty) | |||
| 743 | * state closed | 743 | * state closed |
| 744 | */ | 744 | */ |
| 745 | 745 | ||
| 746 | static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) | 746 | static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc) |
| 747 | { | 747 | { |
| 748 | struct tty_ldisc *ld; | 748 | struct tty_ldisc *ld = tty_ldisc_get(ldisc); |
| 749 | |||
| 750 | if (IS_ERR(ld)) | ||
| 751 | return -1; | ||
| 749 | 752 | ||
| 750 | tty_ldisc_close(tty, tty->ldisc); | 753 | tty_ldisc_close(tty, tty->ldisc); |
| 751 | tty_ldisc_put(tty->ldisc); | 754 | tty_ldisc_put(tty->ldisc); |
| @@ -753,10 +756,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) | |||
| 753 | /* | 756 | /* |
| 754 | * Switch the line discipline back | 757 | * Switch the line discipline back |
| 755 | */ | 758 | */ |
| 756 | ld = tty_ldisc_get(ldisc); | ||
| 757 | BUG_ON(IS_ERR(ld)); | ||
| 758 | tty_ldisc_assign(tty, ld); | 759 | tty_ldisc_assign(tty, ld); |
| 759 | tty_set_termios_ldisc(tty, ldisc); | 760 | tty_set_termios_ldisc(tty, ldisc); |
| 761 | |||
| 762 | return 0; | ||
| 760 | } | 763 | } |
| 761 | 764 | ||
| 762 | /** | 765 | /** |
| @@ -831,13 +834,16 @@ void tty_ldisc_hangup(struct tty_struct *tty) | |||
| 831 | a FIXME */ | 834 | a FIXME */ |
| 832 | if (tty->ldisc) { /* Not yet closed */ | 835 | if (tty->ldisc) { /* Not yet closed */ |
| 833 | if (reset == 0) { | 836 | if (reset == 0) { |
| 834 | tty_ldisc_reinit(tty, tty->termios->c_line); | 837 | |
| 835 | err = tty_ldisc_open(tty, tty->ldisc); | 838 | if (!tty_ldisc_reinit(tty, tty->termios->c_line)) |
| 839 | err = tty_ldisc_open(tty, tty->ldisc); | ||
| 840 | else | ||
| 841 | err = 1; | ||
| 836 | } | 842 | } |
| 837 | /* If the re-open fails or we reset then go to N_TTY. The | 843 | /* If the re-open fails or we reset then go to N_TTY. The |
| 838 | N_TTY open cannot fail */ | 844 | N_TTY open cannot fail */ |
| 839 | if (reset || err) { | 845 | if (reset || err) { |
| 840 | tty_ldisc_reinit(tty, N_TTY); | 846 | BUG_ON(tty_ldisc_reinit(tty, N_TTY)); |
| 841 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); | 847 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); |
| 842 | } | 848 | } |
| 843 | tty_ldisc_enable(tty); | 849 | tty_ldisc_enable(tty); |
