aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/tty/tty_ldisc.c85
1 files changed, 16 insertions, 69 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 68947f6de5ad..c3956ca022e4 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -489,41 +489,6 @@ static void tty_ldisc_close(struct tty_struct *tty, struct tty_ldisc *ld)
489} 489}
490 490
491/** 491/**
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 492 * tty_set_ldisc - set line discipline
528 * @tty: the terminal to set 493 * @tty: the terminal to set
529 * @ldisc: the line discipline 494 * @ldisc: the line discipline
@@ -536,12 +501,7 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old)
536 501
537int tty_set_ldisc(struct tty_struct *tty, int disc) 502int tty_set_ldisc(struct tty_struct *tty, int disc)
538{ 503{
539 int retval; 504 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 505
546 tty_lock(tty); 506 tty_lock(tty);
547 retval = tty_ldisc_lock(tty, 5 * HZ); 507 retval = tty_ldisc_lock(tty, 5 * HZ);
@@ -554,7 +514,8 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
554 } 514 }
555 515
556 /* Check the no-op case */ 516 /* Check the no-op case */
557 if (tty->ldisc->ops->num == disc) 517 old_disc = tty->ldisc->ops->num;
518 if (old_disc == disc)
558 goto out; 519 goto out;
559 520
560 if (test_bit(TTY_HUPPED, &tty->flags)) { 521 if (test_bit(TTY_HUPPED, &tty->flags)) {
@@ -563,34 +524,25 @@ int tty_set_ldisc(struct tty_struct *tty, int disc)
563 goto out; 524 goto out;
564 } 525 }
565 526
566 old_ldisc = tty->ldisc; 527 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) { 528 if (retval < 0) {
577 /* Back to the old one or N_TTY if we can't */ 529 /* Back to the old one or N_TTY if we can't */
578 tty_ldisc_put(new_ldisc); 530 if (tty_ldisc_reinit(tty, old_disc) < 0) {
579 tty_ldisc_restore(tty, old_ldisc); 531 pr_err("tty: TIOCSETD failed, reinitializing N_TTY\n");
532 if (tty_ldisc_reinit(tty, N_TTY) < 0) {
533 /* At this point we have tty->ldisc == NULL. */
534 pr_err("tty: reinitializing N_TTY failed\n");
535 }
536 }
580 } 537 }
581 538
582 if (tty->ldisc->ops->num != old_ldisc->ops->num && tty->ops->set_ldisc) { 539 if (tty->ldisc && tty->ldisc->ops->num != old_disc &&
540 tty->ops->set_ldisc) {
583 down_read(&tty->termios_rwsem); 541 down_read(&tty->termios_rwsem);
584 tty->ops->set_ldisc(tty); 542 tty->ops->set_ldisc(tty);
585 up_read(&tty->termios_rwsem); 543 up_read(&tty->termios_rwsem);
586 } 544 }
587 545
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: 546out:
595 tty_ldisc_unlock(tty); 547 tty_ldisc_unlock(tty);
596 548
@@ -598,7 +550,6 @@ out:
598 already running */ 550 already running */
599 tty_buffer_restart_work(tty->port); 551 tty_buffer_restart_work(tty->port);
600err: 552err:
601 tty_ldisc_put(new_ldisc); /* drop the extra reference */
602 tty_unlock(tty); 553 tty_unlock(tty);
603 return retval; 554 return retval;
604} 555}
@@ -659,10 +610,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
659 int retval; 610 int retval;
660 611
661 ld = tty_ldisc_get(tty, disc); 612 ld = tty_ldisc_get(tty, disc);
662 if (IS_ERR(ld)) { 613 if (IS_ERR(ld))
663 BUG_ON(disc == N_TTY);
664 return PTR_ERR(ld); 614 return PTR_ERR(ld);
665 }
666 615
667 if (tty->ldisc) { 616 if (tty->ldisc) {
668 tty_ldisc_close(tty, tty->ldisc); 617 tty_ldisc_close(tty, tty->ldisc);
@@ -674,10 +623,8 @@ int tty_ldisc_reinit(struct tty_struct *tty, int disc)
674 tty_set_termios_ldisc(tty, disc); 623 tty_set_termios_ldisc(tty, disc);
675 retval = tty_ldisc_open(tty, tty->ldisc); 624 retval = tty_ldisc_open(tty, tty->ldisc);
676 if (retval) { 625 if (retval) {
677 if (!WARN_ON(disc == N_TTY)) { 626 tty_ldisc_put(tty->ldisc);
678 tty_ldisc_put(tty->ldisc); 627 tty->ldisc = NULL;
679 tty->ldisc = NULL;
680 }
681 } 628 }
682 return retval; 629 return retval;
683} 630}