diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-26 14:03:42 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2017-03-26 14:03:42 -0400 |
commit | f1638fc65ee23875cf4021e342e8a564debe33e4 (patch) | |
tree | af091401ed9d05c93a9b9545f963fcc566e6d4d9 | |
parent | 53b4d5911d6adef3f543e1ad23643068c5c343ac (diff) | |
parent | a4a3e061149f09c075f108b6f1cf04d9739a6bc2 (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.c | 2 | ||||
-rw-r--r-- | drivers/tty/serial/8250/8250_dw.c | 9 | ||||
-rw-r--r-- | drivers/tty/serial/amba-pl011.c | 2 | ||||
-rw-r--r-- | drivers/tty/tty_ldisc.c | 92 |
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 | ||
272 | struct tty_ldisc *tty_ldisc_ref_wait(struct tty_struct *tty) | 272 | struct 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 | } |
279 | EXPORT_SYMBOL_GPL(tty_ldisc_ref_wait); | 282 | EXPORT_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 | |||
500 | static 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 | ||
537 | int tty_set_ldisc(struct tty_struct *tty, int disc) | 505 | int 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; | ||
594 | out: | 549 | out: |
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); |
600 | err: | 555 | err: |
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 | } |