Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

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

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
Loading
Loading
Loading
Loading
+13 −7
Original line number Diff line number Diff line
@@ -743,9 +743,12 @@ static void tty_reset_termios(struct tty_struct *tty)
 *	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_put(tty->ldisc);
@@ -753,10 +756,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
	/*
	 *	Switch the line discipline back
	 */
	ld = tty_ldisc_get(ldisc);
	BUG_ON(IS_ERR(ld));
	tty_ldisc_assign(tty, ld);
	tty_set_termios_ldisc(tty, ldisc);

	return 0;
}

/**
@@ -831,13 +834,16 @@ void tty_ldisc_hangup(struct tty_struct *tty)
	   a FIXME */
	if (tty->ldisc) {	/* Not yet closed */
		if (reset == 0) {
			tty_ldisc_reinit(tty, tty->termios->c_line);

			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
		   N_TTY open cannot fail */
		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));
		}
		tty_ldisc_enable(tty);