Commit 76bc35e7 authored by Peter Hurley's avatar Peter Hurley Committed by Greg Kroah-Hartman

tty: Fix ldisc halt sequence on hangup

Flip buffer work cannot be cancelled until all outstanding ldisc
references have been released. Convert the ldisc ref wait into
a full ldisc halt with buffer work cancellation.

Note that the legacy mutex is not held while cancelling.
Signed-off-by: default avatarPeter Hurley <peter@hurleysoftware.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent 2276ad97
...@@ -551,22 +551,30 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout) ...@@ -551,22 +551,30 @@ static int tty_ldisc_wait_idle(struct tty_struct *tty, long timeout)
} }
/** /**
* tty_ldisc_hangup_wait_idle - wait for the ldisc to become idle * tty_ldisc_hangup_halt - halt the line discipline for hangup
* @tty: tty to wait for * @tty: tty being hung up
*
* Wait for the line discipline to become idle. The discipline must
* have been halted for this to guarantee it remains idle.
* *
* Shut down the line discipline and work queue for the tty device
* being hungup. Clear the TTY_LDISC flag to ensure no further
* references can be obtained, wait for remaining references to be
* released, and cancel pending buffer work to ensure no more
* data is fed to this ldisc.
* Caller must hold legacy and ->ldisc_mutex. * Caller must hold legacy and ->ldisc_mutex.
* *
* NB: tty_set_ldisc() is prevented from changing the ldisc concurrently * NB: tty_set_ldisc() is prevented from changing the ldisc concurrently
* with this function by checking the TTY_HUPPING flag. * with this function by checking the TTY_HUPPING flag.
*
* NB: if tty->ldisc is NULL then buffer work does not need to be
* cancelled because it must already have done as a precondition
* of closing the ldisc and setting tty->ldisc to NULL
*/ */
static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) static bool tty_ldisc_hangup_halt(struct tty_struct *tty)
{ {
char cur_n[TASK_COMM_LEN], tty_n[64]; char cur_n[TASK_COMM_LEN], tty_n[64];
long timeout = 3 * HZ; long timeout = 3 * HZ;
clear_bit(TTY_LDISC, &tty->flags);
if (tty->ldisc) { /* Not yet closed */ if (tty->ldisc) { /* Not yet closed */
tty_unlock(tty); tty_unlock(tty);
...@@ -577,6 +585,10 @@ static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty) ...@@ -577,6 +585,10 @@ static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty)
__func__, get_task_comm(cur_n, current), __func__, get_task_comm(cur_n, current),
tty_name(tty, tty_n)); tty_name(tty, tty_n));
} }
cancel_work_sync(&tty->port->buf.work);
set_bit(TTY_LDISC_HALTED, &tty->flags);
/* must reacquire both locks and preserve lock order */ /* must reacquire both locks and preserve lock order */
mutex_unlock(&tty->ldisc_mutex); mutex_unlock(&tty->ldisc_mutex);
tty_lock(tty); tty_lock(tty);
...@@ -851,24 +863,11 @@ void tty_ldisc_hangup(struct tty_struct *tty) ...@@ -851,24 +863,11 @@ void tty_ldisc_hangup(struct tty_struct *tty)
*/ */
mutex_lock(&tty->ldisc_mutex); mutex_lock(&tty->ldisc_mutex);
/*
* this is like tty_ldisc_halt, but we need to give up
* the BTM before calling cancel_work_sync, which may
* need to wait for another function taking the BTM
*/
clear_bit(TTY_LDISC, &tty->flags);
tty_unlock(tty);
cancel_work_sync(&tty->port->buf.work);
set_bit(TTY_LDISC_HALTED, &tty->flags);
mutex_unlock(&tty->ldisc_mutex);
tty_lock(tty);
mutex_lock(&tty->ldisc_mutex);
/* At this point we have a closed ldisc and we want to /* At this point we have a closed ldisc and we want to
reopen it. We could defer this to the next open but reopen it. We could defer this to the next open but
it means auditing a lot of other paths so this is it means auditing a lot of other paths so this is
a FIXME */ a FIXME */
if (tty_ldisc_hangup_wait_idle(tty)) { if (tty_ldisc_hangup_halt(tty)) {
if (reset == 0) { if (reset == 0) {
if (!tty_ldisc_reinit(tty, tty->termios.c_line)) if (!tty_ldisc_reinit(tty, tty->termios.c_line))
......
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