Commit 032aaf3f authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty

Pull tty fix from Greg KH:
 "Here is a single tty core revert for a patch that was reported to
  cause problems.

  The original issue is one that we have lived with for decades, so
  trying to scramble to fix the fix in time for 4.11-final does not make
  sense due to the fragility of the tty ldisc layer. Just reverting it
  makes sense for now"

* tag 'tty-4.11-rc7' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  Revert "tty: don't panic on OOM in tty_set_ldisc()"
parents 48538861 a8983d01
...@@ -491,6 +491,41 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) ...@@ -491,6 +491,41 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
tty_ldisc_debug(tty, "%p: closed\n", ld); tty_ldisc_debug(tty, "%p: closed\n", ld);
} }
/**
* tty_ldisc_restore - helper for tty ldisc change
* @tty: tty to recover
* @old: previous ldisc
*
* Restore the previous line discipline or N_TTY when a line discipline
* change fails due to an open error
*/
static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
{
struct tty_ldisc *new_ldisc;
int r;
/* There is an outstanding reference here so this is safe */
old = tty_ldisc_get(tty, old->ops->num);
WARN_ON(IS_ERR(old));
tty->ldisc = old;
tty_set_termios_ldisc(tty, old->ops->num);
if (tty_ldisc_open(tty, old) < 0) {
tty_ldisc_put(old);
/* This driver is always present */
new_ldisc = tty_ldisc_get(tty, N_TTY);
if (IS_ERR(new_ldisc))
panic("n_tty: get");
tty->ldisc = new_ldisc;
tty_set_termios_ldisc(tty, N_TTY);
r = tty_ldisc_open(tty, new_ldisc);
if (r < 0)
panic("Couldn't open N_TTY ldisc for "
"%s --- error %d.",
tty_name(tty), r);
}
}
/** /**
* tty_set_ldisc - set line discipline * tty_set_ldisc - set line discipline
* @tty: the terminal to set * @tty: the terminal to set
...@@ -504,7 +539,12 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) ...@@ -504,7 +539,12 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
int tty_set_ldisc(struct tty_struct *tty, int disc) int tty_set_ldisc(struct tty_struct *tty, int disc)
{ {
int retval, old_disc; int retval;
struct tty_ldisc *old_ldisc, *new_ldisc;
new_ldisc = tty_ldisc_get(tty, disc);
if (IS_ERR(new_ldisc))
return PTR_ERR(new_ldisc);
tty_lock(tty); tty_lock(tty);
retval = tty_ldisc_lock(tty, 5 * HZ); retval = tty_ldisc_lock(tty, 5 * HZ);
...@@ -517,8 +557,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -517,8 +557,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
} }
/* Check the no-op case */ /* Check the no-op case */
old_disc = tty->ldisc->ops->num; if (tty->ldisc->ops->num == disc)
if (old_disc == disc)
goto out; goto out;
if (test_bit(TTY_HUPPED, &tty->flags)) { if (test_bit(TTY_HUPPED, &tty->flags)) {
...@@ -527,25 +566,34 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -527,25 +566,34 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
goto out; goto out;
} }
retval = tty_ldisc_reinit(tty, disc); old_ldisc = tty->ldisc;
/* Shutdown the old discipline. */
tty_ldisc_close(tty, old_ldisc);
/* Now set up the new line discipline. */
tty->ldisc = new_ldisc;
tty_set_termios_ldisc(tty, disc);
retval = tty_ldisc_open(tty, new_ldisc);
if (retval < 0) { if (retval < 0) {
/* Back to the old one or N_TTY if we can't */ /* Back to the old one or N_TTY if we can't */
if (tty_ldisc_reinit(tty, old_disc) < 0) { tty_ldisc_put(new_ldisc);
pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n"); tty_ldisc_restore(tty, old_ldisc);
if (tty_ldisc_reinit(tty, N_TTY) < 0) {
/* At this point we have tty->ldisc == NULL. */
pr_err("tty: reinitializing N_TTY failed\n");
}
}
} }
if (tty->ldisc && tty->ldisc->ops->num != old_disc && if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
tty->ops->set_ldisc) {
down_read(&tty->termios_rwsem); down_read(&tty->termios_rwsem);
tty->ops->set_ldisc(tty); tty->ops->set_ldisc(tty);
up_read(&tty->termios_rwsem); up_read(&tty->termios_rwsem);
} }
/* At this point we hold a reference to the new ldisc and a
reference to the old ldisc, or we hold two references to
the old ldisc (if it was restored as part of error cleanup
above). In either case, releasing a single reference from
the old ldisc is correct. */
new_ldisc = old_ldisc;
out: out:
tty_ldisc_unlock(tty); tty_ldisc_unlock(tty);
...@@ -553,6 +601,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -553,6 +601,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
already running */ already running */
tty_buffer_restart_work(tty->port); tty_buffer_restart_work(tty->port);
err: err:
tty_ldisc_put(new_ldisc); /* drop the extra reference */
tty_unlock(tty); tty_unlock(tty);
return retval; return retval;
} }
...@@ -613,8 +662,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc) ...@@ -613,8 +662,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
int retval; int retval;
ld = tty_ldisc_get(tty, disc); ld = tty_ldisc_get(tty, disc);
if (IS_ERR(ld)) if (IS_ERR(ld)) {
BUG_ON(disc == N_TTY);
return PTR_ERR(ld); return PTR_ERR(ld);
}
if (tty->ldisc) { if (tty->ldisc) {
tty_ldisc_close(tty, tty->ldisc); tty_ldisc_close(tty, tty->ldisc);
...@@ -626,8 +677,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc) ...@@ -626,8 +677,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
tty_set_termios_ldisc(tty, disc); tty_set_termios_ldisc(tty, disc);
retval = tty_ldisc_open(tty, tty->ldisc); retval = tty_ldisc_open(tty, tty->ldisc);
if (retval) { if (retval) {
tty_ldisc_put(tty->ldisc); if (!WARN_ON(disc == N_TTY)) {
tty->ldisc = NULL; tty_ldisc_put(tty->ldisc);
tty->ldisc = NULL;
}
} }
return retval; return retval;
} }
......
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