Skip to content

Commit

Permalink
tty: Fix ldisc halt sequence on hangup
Browse files Browse the repository at this point in the history
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: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
  • Loading branch information
Peter Hurley authored and Greg Kroah-Hartman committed Mar 18, 2013
1 parent 2276ad9 commit 76bc35e
Showing 1 changed file with 19 additions and 20 deletions.
39 changes: 19 additions & 20 deletions drivers/tty/tty_ldisc.c
Original file line number Diff line number Diff line change
Expand Up @@ -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: tty to wait for
*
* Wait for the line discipline to become idle. The discipline must
* have been halted for this to guarantee it remains idle.
* tty_ldisc_hangup_halt - halt the line discipline for hangup
* @tty: tty being hung up
*
* 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.
*
* NB: tty_set_ldisc() is prevented from changing the ldisc concurrently
* 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];
long timeout = 3 * HZ;

clear_bit(TTY_LDISC, &tty->flags);

if (tty->ldisc) { /* Not yet closed */
tty_unlock(tty);

Expand All @@ -577,6 +585,10 @@ static bool tty_ldisc_hangup_wait_idle(struct tty_struct *tty)
__func__, get_task_comm(cur_n, current),
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 */
mutex_unlock(&tty->ldisc_mutex);
tty_lock(tty);
Expand Down Expand Up @@ -851,24 +863,11 @@ void tty_ldisc_hangup(struct tty_struct *tty)
*/
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
reopen it. We could defer this to the next open but
it means auditing a lot of other paths so this is
a FIXME */
if (tty_ldisc_hangup_wait_idle(tty)) {
if (tty_ldisc_hangup_halt(tty)) {
if (reset == 0) {

if (!tty_ldisc_reinit(tty, tty->termios.c_line))
Expand Down

0 comments on commit 76bc35e

Please sign in to comment.