aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@linux-foundation.org>2017-04-16 14:35:34 -0400
committerLinus Torvalds <torvalds@linux-foundation.org>2017-04-16 14:35:34 -0400
commit032aaf3f9ff9faa039673d95ea95e02ddff3a26b (patch)
tree05675db863d0f686a1d6f45e19affb1ef62d7e93
parent48538861b993255ec6e1012b5af1cfbb8787a4d6 (diff)
parenta8983d01f9b7d600fbdb3916899edf395954cc83 (diff)
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()"
-rw-r--r--drivers/tty/tty_ldisc.c85
1 files changed, 69 insertions, 16 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index b0500a0a87b8..e4603b09863a 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -492,6 +492,41 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
492} 492}
493 493
494/** 494/**
495 * tty_ldisc_restore - helper for tty ldisc change
496 * @tty: tty to recover
497 * @old: previous ldisc
498 *
499 * Restore the previous line discipline or N_TTY when a line discipline
500 * change fails due to an open error
501 */
502
503static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
504{
505 struct tty_ldisc *new_ldisc;
506 int r;
507
508 /* There is an outstanding reference here so this is safe */
509 old = tty_ldisc_get(tty, old->ops->num);
510 WARN_ON(IS_ERR(old));
511 tty->ldisc = old;
512 tty_set_termios_ldisc(tty, old->ops->num);
513 if (tty_ldisc_open(tty, old) < 0) {
514 tty_ldisc_put(old);
515 /* This driver is always present */
516 new_ldisc = tty_ldisc_get(tty, N_TTY);
517 if (IS_ERR(new_ldisc))
518 panic("n_tty: get");
519 tty->ldisc = new_ldisc;
520 tty_set_termios_ldisc(tty, N_TTY);
521 r = tty_ldisc_open(tty, new_ldisc);
522 if (r < 0)
523 panic("Couldn't open N_TTY ldisc for "
524 "%s --- error %d.",
525 tty_name(tty), r);
526 }
527}
528
529/**
495 * tty_set_ldisc - set line discipline 530 * tty_set_ldisc - set line discipline
496 * @tty: the terminal to set 531 * @tty: the terminal to set
497 * @ldisc: the line discipline 532 * @ldisc: the line discipline
@@ -504,7 +539,12 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
504 539
505int tty_set_ldisc(struct tty_struct *tty, int disc) 540int tty_set_ldisc(struct tty_struct *tty, int disc)
506{ 541{
507 int retval, old_disc; 542 int retval;
543 struct tty_ldisc *old_ldisc, *new_ldisc;
544
545 new_ldisc = tty_ldisc_get(tty, disc);
546 if (IS_ERR(new_ldisc))
547 return PTR_ERR(new_ldisc);
508 548
509 tty_lock(tty); 549 tty_lock(tty);
510 retval = tty_ldisc_lock(tty, 5 * HZ); 550 retval = tty_ldisc_lock(tty, 5 * HZ);
@@ -517,8 +557,7 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
517 } 557 }
518 558
519 /* Check the no-op case */ 559 /* Check the no-op case */
520 old_disc = tty->ldisc->ops->num; 560 if (tty->ldisc->ops->num == disc)
521 if (old_disc == disc)
522 goto out; 561 goto out;
523 562
524 if (test_bit(TTY_HUPPED, &tty->flags)) { 563 if (test_bit(TTY_HUPPED, &tty->flags)) {
@@ -527,25 +566,34 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
527 goto out; 566 goto out;
528 } 567 }
529 568
530 retval = tty_ldisc_reinit(tty, disc); 569 old_ldisc = tty->ldisc;
570
571 /* Shutdown the old discipline. */
572 tty_ldisc_close(tty, old_ldisc);
573
574 /* Now set up the new line discipline. */
575 tty->ldisc = new_ldisc;
576 tty_set_termios_ldisc(tty, disc);
577
578 retval = tty_ldisc_open(tty, new_ldisc);
531 if (retval < 0) { 579 if (retval < 0) {
532 /* Back to the old one or N_TTY if we can't */ 580 /* Back to the old one or N_TTY if we can't */
533 if (tty_ldisc_reinit(tty, old_disc) < 0) { 581 tty_ldisc_put(new_ldisc);
534 pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n"); 582 tty_ldisc_restore(tty, old_ldisc);
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 }
540 } 583 }
541 584
542 if (tty->ldisc && tty->ldisc->ops->num != old_disc && 585 if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) {
543 tty->ops->set_ldisc) {
544 down_read(&tty->termios_rwsem); 586 down_read(&tty->termios_rwsem);
545 tty->ops->set_ldisc(tty); 587 tty->ops->set_ldisc(tty);
546 up_read(&tty->termios_rwsem); 588 up_read(&tty->termios_rwsem);
547 } 589 }
548 590
591 /* At this point we hold a reference to the new ldisc and a
592 reference to the old ldisc, or we hold two references to
593 the old ldisc (if it was restored as part of error cleanup
594 above). In either case, releasing a single reference from
595 the old ldisc is correct. */
596 new_ldisc = old_ldisc;
549out: 597out:
550 tty_ldisc_unlock(tty); 598 tty_ldisc_unlock(tty);
551 599
@@ -553,6 +601,7 @@ out:
553 already running */ 601 already running */
554 tty_buffer_restart_work(tty->port); 602 tty_buffer_restart_work(tty->port);
555err: 603err:
604 tty_ldisc_put(new_ldisc); /* drop the extra reference */
556 tty_unlock(tty); 605 tty_unlock(tty);
557 return retval; 606 return retval;
558} 607}
@@ -613,8 +662,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
613 int retval; 662 int retval;
614 663
615 ld = tty_ldisc_get(tty, disc); 664 ld = tty_ldisc_get(tty, disc);
616 if (IS_ERR(ld)) 665 if (IS_ERR(ld)) {
666 BUG_ON(disc == N_TTY);
617 return PTR_ERR(ld); 667 return PTR_ERR(ld);
668 }
618 669
619 if (tty->ldisc) { 670 if (tty->ldisc) {
620 tty_ldisc_close(tty, tty->ldisc); 671 tty_ldisc_close(tty, tty->ldisc);
@@ -626,8 +677,10 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
626 tty_set_termios_ldisc(tty, disc); 677 tty_set_termios_ldisc(tty, disc);
627 retval = tty_ldisc_open(tty, tty->ldisc); 678 retval = tty_ldisc_open(tty, tty->ldisc);
628 if (retval) { 679 if (retval) {
629 tty_ldisc_put(tty->ldisc); 680 if (!WARN_ON(disc == N_TTY)) {
630 tty->ldisc = NULL; 681 tty_ldisc_put(tty->ldisc);
682 tty->ldisc = NULL;
683 }
631 } 684 }
632 return retval; 685 return retval;
633} 686}