Commit 1c95ba1e authored by Philippe Rétornaz's avatar Philippe Rétornaz Committed by Greg Kroah-Hartman

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: default avatarPhilippe Retornaz <philippe.retornaz@epfl.ch>
Acked-by: default avatarAlan Cox <alan@linux.intel.com>
Cc: stable <stable@kernel.org>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 100eeae2
...@@ -743,9 +743,12 @@ static void tty_reset_termios(struct tty_struct *tty) ...@@ -743,9 +743,12 @@ static void tty_reset_termios(struct tty_struct *tty)
* state closed * state closed
*/ */
static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
{ {
struct tty_ldisc *ld; struct tty_ldisc *ld = tty_ldisc_get(ldisc);
if (IS_ERR(ld))
return -1;
tty_ldisc_close(tty, tty->ldisc); tty_ldisc_close(tty, tty->ldisc);
tty_ldisc_put(tty->ldisc); tty_ldisc_put(tty->ldisc);
...@@ -753,10 +756,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) ...@@ -753,10 +756,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
/* /*
* Switch the line discipline back * Switch the line discipline back
*/ */
ld = tty_ldisc_get(ldisc);
BUG_ON(IS_ERR(ld));
tty_ldisc_assign(tty, ld); tty_ldisc_assign(tty, ld);
tty_set_termios_ldisc(tty, ldisc); tty_set_termios_ldisc(tty, ldisc);
return 0;
} }
/** /**
...@@ -831,13 +834,16 @@ void tty_ldisc_hangup(struct tty_struct *tty) ...@@ -831,13 +834,16 @@ void tty_ldisc_hangup(struct tty_struct *tty)
a FIXME */ a FIXME */
if (tty->ldisc) { /* Not yet closed */ if (tty->ldisc) { /* Not yet closed */
if (reset == 0) { if (reset == 0) {
tty_ldisc_reinit(tty, tty->termios->c_line);
err = tty_ldisc_open(tty, tty->ldisc); if (!tty_ldisc_reinit(tty, tty->termios->c_line))
err = tty_ldisc_open(tty, tty->ldisc);
else
err = 1;
} }
/* If the re-open fails or we reset then go to N_TTY. The /* If the re-open fails or we reset then go to N_TTY. The
N_TTY open cannot fail */ N_TTY open cannot fail */
if (reset || err) { if (reset || err) {
tty_ldisc_reinit(tty, N_TTY); BUG_ON(tty_ldisc_reinit(tty, N_TTY));
WARN_ON(tty_ldisc_open(tty, tty->ldisc)); WARN_ON(tty_ldisc_open(tty, tty->ldisc));
} }
tty_ldisc_enable(tty); tty_ldisc_enable(tty);
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment