diff options
author | Peter Hurley <peter@hurleysoftware.com> | 2016-01-11 09:41:05 +0300 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2016-01-28 02:01:44 +0300 |
commit | 7896f30d6fc602f02198999acca4840620288990 (patch) | |
tree | 96f385ae7c987248ac90a01cc323c1c72e2f47c1 /drivers/tty/tty_ldisc.c | |
parent | c12da96f801a3f45b0634c966b9e7cda307daa72 (diff) | |
download | linux-7896f30d6fc602f02198999acca4840620288990.tar.xz |
tty: Refactor tty_ldisc_reinit() for reuse
At tty hangup, the line discipline instance is reinitialized by
closing the current ldisc instance and opening a new instance.
This operation is complicated by error recovery: if the attempt
to reinit the current line discipline fails, the line discipline
is reset to N_TTY (which should not but can fail).
Re-purpose tty_ldisc_reinit() to return a valid, open line discipline
instance, or otherwise, an error.
Signed-off-by: Peter Hurley <peter@hurleysoftware.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/tty/tty_ldisc.c')
-rw-r--r-- | drivers/tty/tty_ldisc.c | 53 |
1 files changed, 31 insertions, 22 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c index 45aa31b923e5..e18c8e864110 100644 --- a/drivers/tty/tty_ldisc.c +++ b/drivers/tty/tty_ldisc.c @@ -641,26 +641,41 @@ static void tty_reset_termios(struct tty_struct *tty) * @tty: tty to reinit * @disc: line discipline to reinitialize * - * Switch the tty to a line discipline and leave the ldisc - * state closed + * Completely reinitialize the line discipline state, by closing the + * current instance and opening a new instance. If an error occurs opening + * the new non-N_TTY instance, the instance is dropped and tty->ldisc reset + * to NULL. The caller can then retry with N_TTY instead. + * + * Returns 0 if successful, otherwise error code < 0 */ static int tty_ldisc_reinit(struct tty_struct *tty, int disc) { - struct tty_ldisc *ld = tty_ldisc_get(tty, disc); + struct tty_ldisc *ld; + int retval; - if (IS_ERR(ld)) - return -1; + ld = tty_ldisc_get(tty, disc); + if (IS_ERR(ld)) { + BUG_ON(disc == N_TTY); + return PTR_ERR(ld); + } - tty_ldisc_close(tty, tty->ldisc); - tty_ldisc_put(tty->ldisc); - /* - * Switch the line discipline back - */ + if (tty->ldisc) { + tty_ldisc_close(tty, tty->ldisc); + tty_ldisc_put(tty->ldisc); + } + + /* switch the line discipline */ tty->ldisc = ld; tty_set_termios_ldisc(tty, disc); - - return 0; + retval = tty_ldisc_open(tty, tty->ldisc); + if (retval) { + if (!WARN_ON(disc == N_TTY)) { + tty_ldisc_put(tty->ldisc); + tty->ldisc = NULL; + } + } + return retval; } /** @@ -716,19 +731,13 @@ void tty_ldisc_hangup(struct tty_struct *tty) reopen a new ldisc. We could defer the reopen to the next open but it means auditing a lot of other paths so this is a FIXME */ - if (reset == 0) { + if (reset == 0) + err = 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) { - BUG_ON(tty_ldisc_reinit(tty, N_TTY)); - WARN_ON(tty_ldisc_open(tty, tty->ldisc)); - } + if (reset || err < 0) + tty_ldisc_reinit(tty, N_TTY); } tty_ldisc_unlock(tty); if (reset) |