aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-03-26 14:03:42 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-03-26 14:03:42 -0400
commitf1638fc65ee23875cf4021e342e8a564debe33e4 (patch)
treeaf091401ed9d05c93a9b9545f963fcc566e6d4d9
parent53b4d5911d6adef3f543e1ad23643068c5c343ac (diff)
parenta4a3e061149f09c075f108b6f1cf04d9739a6bc2 (diff)
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
-rw-r--r--drivers/acpi/spcr.c2
-rw-r--r--drivers/tty/serial/8250/8250_dw.c9
-rw-r--r--drivers/tty/serial/amba-pl011.c2
-rw-r--r--drivers/tty/tty_ldisc.c92
4 files changed, 30 insertions, 75 deletions
diff --git a/drivers/acpi/spcr.c b/drivers/acpi/spcr.c
index 01c94669a2b0..3afa8c1fa127 100644
--- a/drivers/acpi/spcr.c
+++ b/drivers/acpi/spcr.c
@@ -30,7 +30,7 @@ static bool qdf2400_erratum_44_present(struct acpi_table_header *h)
30 return true; 30 return true;
31 31
32 if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) && 32 if (!memcmp(h->oem_table_id, "QDF2400 ", ACPI_OEM_TABLE_ID_SIZE) &&
33 h->oem_revision == 0) 33 h->oem_revision == 1)
34 return true; 34 return true;
35 35
36 return false; 36 return false;
diff --git a/drivers/tty/serial/8250/8250_dw.c b/drivers/tty/serial/8250/8250_dw.c
index 6ee55a2d47bb..e65808c482f1 100644
--- a/drivers/tty/serial/8250/8250_dw.c
+++ b/drivers/tty/serial/8250/8250_dw.c
@@ -257,7 +257,7 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
257{ 257{
258 unsigned int baud = tty_termios_baud_rate(termios); 258 unsigned int baud = tty_termios_baud_rate(termios);
259 struct dw8250_data *d = p->private_data; 259 struct dw8250_data *d = p->private_data;
260 unsigned int rate; 260 long rate;
261 int ret; 261 int ret;
262 262
263 if (IS_ERR(d->clk) || !old) 263 if (IS_ERR(d->clk) || !old)
@@ -265,7 +265,12 @@ static void dw8250_set_termios(struct uart_port *p, struct ktermios *termios,
265 265
266 clk_disable_unprepare(d->clk); 266 clk_disable_unprepare(d->clk);
267 rate = clk_round_rate(d->clk, baud * 16); 267 rate = clk_round_rate(d->clk, baud * 16);
268 ret = clk_set_rate(d->clk, rate); 268 if (rate < 0)
269 ret = rate;
270 else if (rate == 0)
271 ret = -ENOENT;
272 else
273 ret = clk_set_rate(d->clk, rate);
269 clk_prepare_enable(d->clk); 274 clk_prepare_enable(d->clk);
270 275
271 if (!ret) 276 if (!ret)
diff --git a/drivers/tty/serial/amba-pl011.c b/drivers/tty/serial/amba-pl011.c
index 8789ea423ccf..56f92d7348bf 100644
--- a/drivers/tty/serial/amba-pl011.c
+++ b/drivers/tty/serial/amba-pl011.c
@@ -2373,7 +2373,7 @@ static int __init pl011_console_match(struct console *co, char *name, int idx,
2373 if (strcmp(name, "qdf2400_e44") == 0) { 2373 if (strcmp(name, "qdf2400_e44") == 0) {
2374 pr_info_once("UART: Working around QDF2400 SoC erratum 44"); 2374 pr_info_once("UART: Working around QDF2400 SoC erratum 44");
2375 qdf2400_e44_present = true; 2375 qdf2400_e44_present = true;
2376 } else if (strcmp(name, "pl011") != 0 || strcmp(name, "ttyAMA") != 0) { 2376 } else if (strcmp(name, "pl011") != 0) {
2377 return -ENODEV; 2377 return -ENODEV;
2378 } 2378 }
2379 2379
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 68947f6de5ad..b0500a0a87b8 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -271,10 +271,13 @@ const struct file_operations tty_ldiscs_proc_fops = {
271 271
272struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) 272struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty)
273{ 273{
274 struct tty_ldisc *ld;
275
274 ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT); 276 ldsem_down_read(&tty->ldisc_sem, MAX_SCHEDULE_TIMEOUT);
275 if (!tty->ldisc) 277 ld = tty->ldisc;
278 if (!ld)
276 ldsem_up_read(&tty->ldisc_sem); 279 ldsem_up_read(&tty->ldisc_sem);
277 return tty->ldisc; 280 return ld;
278} 281}
279EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); 282EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait);
280 283
@@ -489,41 +492,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
489} 492}
490 493
491/** 494/**
492 * tty_ldisc_restore - helper for tty ldisc change
493 * @tty: tty to recover
494 * @old: previous ldisc
495 *
496 * Restore the previous line discipline or N_TTY when a line discipline
497 * change fails due to an open error
498 */
499
500static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
501{
502 struct tty_ldisc *new_ldisc;
503 int r;
504
505 /* There is an outstanding reference here so this is safe */
506 old = tty_ldisc_get(tty, old->ops->num);
507 WARN_ON(IS_ERR(old));
508 tty->ldisc = old;
509 tty_set_termios_ldisc(tty, old->ops->num);
510 if (tty_ldisc_open(tty, old) < 0) {
511 tty_ldisc_put(old);
512 /* This driver is always present */
513 new_ldisc = tty_ldisc_get(tty, N_TTY);
514 if (IS_ERR(new_ldisc))
515 panic("n_tty: get");
516 tty->ldisc = new_ldisc;
517 tty_set_termios_ldisc(tty, N_TTY);
518 r = tty_ldisc_open(tty, new_ldisc);
519 if (r < 0)
520 panic("Couldn't open N_TTY ldisc for "
521 "%s --- error %d.",
522 tty_name(tty), r);
523 }
524}
525
526/**
527 * tty_set_ldisc - set line discipline 495 * tty_set_ldisc - set line discipline
528 * @tty: the terminal to set 496 * @tty: the terminal to set
529 * @ldisc: the line discipline 497 * @ldisc: the line discipline
@@ -536,12 +504,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
536 504
537int tty_set_ldisc(struct tty_struct *tty, int disc) 505int tty_set_ldisc(struct tty_struct *tty, int disc)
538{ 506{
539 int retval; 507 int retval, old_disc;
540 struct tty_ldisc *old_ldisc, *new_ldisc;
541
542 new_ldisc = tty_ldisc_get(tty, disc);
543 if (IS_ERR(new_ldisc))
544 return PTR_ERR(new_ldisc);
545 508
546 tty_lock(tty); 509 tty_lock(tty);
547 retval = tty_ldisc_lock(tty, 5 * HZ); 510 retval = tty_ldisc_lock(tty, 5 * HZ);
@@ -554,7 +517,8 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
554 } 517 }
555 518
556 /* Check the no-op case */ 519 /* Check the no-op case */
557 if (tty->ldisc->ops->num == disc) 520 old_disc = tty->ldisc->ops->num;
521 if (old_disc == disc)
558 goto out; 522 goto out;
559 523
560 if (test_bit(TTY_HUPPED, &tty->flags)) { 524 if (test_bit(TTY_HUPPED, &tty->flags)) {
@@ -563,34 +527,25 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
563 goto out; 527 goto out;
564 } 528 }
565 529
566 old_ldisc = tty->ldisc; 530 retval = tty_ldisc_reinit(tty, disc);
567
568 /* Shutdown the old discipline. */
569 tty_ldisc_close(tty, old_ldisc);
570
571 /* Now set up the new line discipline. */
572 tty->ldisc = new_ldisc;
573 tty_set_termios_ldisc(tty, disc);
574
575 retval = tty_ldisc_open(tty, new_ldisc);
576 if (retval < 0) { 531 if (retval < 0) {
577 /* Back to the old one or N_TTY if we can't */ 532 /* Back to the old one or N_TTY if we can't */
578 tty_ldisc_put(new_ldisc); 533 if (tty_ldisc_reinit(tty, old_disc) < 0) {
579 tty_ldisc_restore(tty, old_ldisc); 534 pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
535 if (tty_ldisc_reinit(tty, N_TTY) < 0) {
536 /* At this point we have tty->ldisc == NULL. */
537 pr_err("tty: reinitializing N_TTY failed\n");
538 }
539 }
580 } 540 }
581 541
582 if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { 542 if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
543 tty->ops->set_ldisc) {
583 down_read(&tty->termios_rwsem); 544 down_read(&tty->termios_rwsem);
584 tty->ops->set_ldisc(tty); 545 tty->ops->set_ldisc(tty);
585 up_read(&tty->termios_rwsem); 546 up_read(&tty->termios_rwsem);
586 } 547 }
587 548
588 /* At this point we hold a reference to the new ldisc and a
589 reference to the old ldisc, or we hold two references to
590 the old ldisc (if it was restored as part of error cleanup
591 above). In either case, releasing a single reference from
592 the old ldisc is correct. */
593 new_ldisc = old_ldisc;
594out: 549out:
595 tty_ldisc_unlock(tty); 550 tty_ldisc_unlock(tty);
596 551
@@ -598,7 +553,6 @@ out:
598 already running */ 553 already running */
599 tty_buffer_restart_work(tty->port); 554 tty_buffer_restart_work(tty->port);
600err: 555err:
601 tty_ldisc_put(new_ldisc); /* drop the extra reference */
602 tty_unlock(tty); 556 tty_unlock(tty);
603 return retval; 557 return retval;
604} 558}
@@ -659,10 +613,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
659 int retval; 613 int retval;
660 614
661 ld = tty_ldisc_get(tty, disc); 615 ld = tty_ldisc_get(tty, disc);
662 if (IS_ERR(ld)) { 616 if (IS_ERR(ld))
663 BUG_ON(disc == N_TTY);
664 return PTR_ERR(ld); 617 return PTR_ERR(ld);
665 }
666 618
667 if (tty->ldisc) { 619 if (tty->ldisc) {
668 tty_ldisc_close(tty, tty->ldisc); 620 tty_ldisc_close(tty, tty->ldisc);
@@ -674,10 +626,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
674 tty_set_termios_ldisc(tty, disc); 626 tty_set_termios_ldisc(tty, disc);
675 retval = tty_ldisc_open(tty, tty->ldisc); 627 retval = tty_ldisc_open(tty, tty->ldisc);
676 if (retval) { 628 if (retval) {
677 if (!WARN_ON(disc == N_TTY)) { 629 tty_ldisc_put(tty->ldisc);
678 tty_ldisc_put(tty->ldisc); 630 tty->ldisc = NULL;
679 tty->ldisc = NULL;
680 }
681 } 631 }
682 return retval; 632 return retval;
683} 633}