diff options
-rw-r--r-- | drivers/tty/tty_ldisc.c | 85 |
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 | |||
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 | 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 | ||
537 | int tty_set_ldisc(struct tty_struct *tty, int disc) | 502 | int 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; | ||
594 | out: | 546 | out: |
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); |
600 | err: | 552 | err: |
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 | } |