提交 f1638fc6 编写于 作者: L Linus Torvalds

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

Pull tty/serial driver fixes from Greg KH:
 "Here are some tty and serial driver fixes for 4.11-rc4.

  One of these fix a long-standing issue in the ldisc code that was
  found by Dmitry Vyukov with his great fuzzing work. The other fixes
  resolve other reported issues, and there is one revert of a patch in
  4.11-rc1 that wasn't correct.

  All of these have been in linux-next for a while with no reported
  issues"

* tag 'tty-4.11-rc4' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/tty:
  tty: fix data race in tty_ldisc_ref_wait()
  tty: don't panic on OOM in tty_set_ldisc()
  Revert "tty: serial: pl011: add ttyAMA for matching pl011 console"
  tty: acpi/spcr: QDF2400 E44 checks for wrong OEM revision
  serial: 8250_dw: Fix breakage when HAVE_CLK=n
  serial: 8250_dw: Honor clk_round_rate errors in dw8250_set_termios
...@@ -30,7 +30,7 @@ static bool qdf2400_erratum_44_present(struct acpi_table_header *h) ...@@ -30,7 +30,7 @@ static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
return true; return true;
if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) && if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
h->oem_revision == 0) h->oem_revision == 1)
return true; return true;
return false; return false;
......
...@@ -257,7 +257,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, ...@@ -257,7 +257,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
{ {
unsigned int baud = tty_termios_baud_rate(termios); unsigned int baud = tty_termios_baud_rate(termios);
struct dw8250_data *d = p->private_data; struct dw8250_data *d = p->private_data;
unsigned int rate; long rate;
int ret; int ret;
if (IS_ERR(d->clk) || !old) if (IS_ERR(d->clk) || !old)
...@@ -265,7 +265,12 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios, ...@@ -265,7 +265,12 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
clk_disable_unprepare(d->clk); clk_disable_unprepare(d->clk);
rate = clk_round_rate(d->clk, baud * 16); rate = clk_round_rate(d->clk, baud * 16);
ret = clk_set_rate(d->clk, rate); if (rate < 0)
ret = rate;
else if (rate == 0)
ret = -ENOENT;
else
ret = clk_set_rate(d->clk, rate);
clk_prepare_enable(d->clk); clk_prepare_enable(d->clk);
if (!ret) if (!ret)
......
...@@ -2373,7 +2373,7 @@ static int __init pl011_console_match(struct console *co, char *name, int idx, ...@@ -2373,7 +2373,7 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
if (strcmp(name, "qdf2400_e44") == 0) { if (strcmp(name, "qdf2400_e44") == 0) {
pr_info_once("UART: Working around QDF2400 SoC erratum 44"); pr_info_once("UART: Working around QDF2400 SoC erratum 44");
qdf2400_e44_present = true; qdf2400_e44_present = true;
} else if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0) { } else if (strcmp(name, "pl011") != 0) {
return -ENODEV; return -ENODEV;
} }
......
...@@ -271,10 +271,13 @@ const struct file_operations tty_ldiscs_proc_fops = { ...@@ -271,10 +271,13 @@ const struct file_operations tty_ldiscs_proc_fops = {
struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
{ {
struct tty_ldisc *ld;
ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
if (!tty->ldisc) ld = tty->ldisc;
if (!ld)
ldsem_up_read(&tty->ldisc_sem); ldsem_up_read(&tty->ldisc_sem);
return tty->ldisc; return ld;
} }
EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
...@@ -488,41 +491,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld) ...@@ -488,41 +491,6 @@ 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
...@@ -536,12 +504,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) ...@@ -536,12 +504,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
int tty_set_ldisc(struct tty_struct *tty, int disc) int tty_set_ldisc(struct tty_struct *tty, int disc)
{ {
int retval; int retval, old_disc;
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);
...@@ -554,7 +517,8 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -554,7 +517,8 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
} }
/* Check the no-op case */ /* Check the no-op case */
if (tty->ldisc->ops->num == disc) old_disc = tty->ldisc->ops->num;
if (old_disc == disc)
goto out; goto out;
if (test_bit(TTY_HUPPED, &tty->flags)) { if (test_bit(TTY_HUPPED, &tty->flags)) {
...@@ -563,34 +527,25 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -563,34 +527,25 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
goto out; goto out;
} }
old_ldisc = tty->ldisc; retval = tty_ldisc_reinit(tty, disc);
/* 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 */
tty_ldisc_put(new_ldisc); if (tty_ldisc_reinit(tty, old_disc) < 0) {
tty_ldisc_restore(tty, old_ldisc); pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
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->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
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);
...@@ -598,7 +553,6 @@ int tty_set_ldisc(struct tty_struct *tty, int disc) ...@@ -598,7 +553,6 @@ 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;
} }
...@@ -659,10 +613,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc) ...@@ -659,10 +613,8 @@ 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);
...@@ -674,10 +626,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc) ...@@ -674,10 +626,8 @@ 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) {
if (!WARN_ON(disc == N_TTY)) { tty_ldisc_put(tty->ldisc);
tty_ldisc_put(tty->ldisc); tty->ldisc = NULL;
tty->ldisc = NULL;
}
} }
return retval; return retval;
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册