diff options
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r-- | drivers/char/tty_ldisc.c | 73 |
1 files changed, 51 insertions, 22 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index feb55075819b..500e740ec5e4 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c | |||
@@ -34,6 +34,8 @@ | |||
34 | #include <linux/vt_kern.h> | 34 | #include <linux/vt_kern.h> |
35 | #include <linux/selection.h> | 35 | #include <linux/selection.h> |
36 | 36 | ||
37 | #include <linux/smp_lock.h> /* For the moment */ | ||
38 | |||
37 | #include <linux/kmod.h> | 39 | #include <linux/kmod.h> |
38 | #include <linux/nsproxy.h> | 40 | #include <linux/nsproxy.h> |
39 | 41 | ||
@@ -443,8 +445,14 @@ static void tty_set_termios_ldisc(struct tty_struct *tty, int num) | |||
443 | static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) | 445 | static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld) |
444 | { | 446 | { |
445 | WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); | 447 | WARN_ON(test_and_set_bit(TTY_LDISC_OPEN, &tty->flags)); |
446 | if (ld->ops->open) | 448 | if (ld->ops->open) { |
447 | return ld->ops->open(tty); | 449 | int ret; |
450 | /* BKL here locks verus a hangup event */ | ||
451 | lock_kernel(); | ||
452 | ret = ld->ops->open(tty); | ||
453 | unlock_kernel(); | ||
454 | return ret; | ||
455 | } | ||
448 | return 0; | 456 | return 0; |
449 | } | 457 | } |
450 | 458 | ||
@@ -545,6 +553,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
545 | if (IS_ERR(new_ldisc)) | 553 | if (IS_ERR(new_ldisc)) |
546 | return PTR_ERR(new_ldisc); | 554 | return PTR_ERR(new_ldisc); |
547 | 555 | ||
556 | lock_kernel(); | ||
548 | /* | 557 | /* |
549 | * We need to look at the tty locking here for pty/tty pairs | 558 | * We need to look at the tty locking here for pty/tty pairs |
550 | * when both sides try to change in parallel. | 559 | * when both sides try to change in parallel. |
@@ -558,10 +567,12 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
558 | */ | 567 | */ |
559 | 568 | ||
560 | if (tty->ldisc->ops->num == ldisc) { | 569 | if (tty->ldisc->ops->num == ldisc) { |
570 | unlock_kernel(); | ||
561 | tty_ldisc_put(new_ldisc); | 571 | tty_ldisc_put(new_ldisc); |
562 | return 0; | 572 | return 0; |
563 | } | 573 | } |
564 | 574 | ||
575 | unlock_kernel(); | ||
565 | /* | 576 | /* |
566 | * Problem: What do we do if this blocks ? | 577 | * Problem: What do we do if this blocks ? |
567 | * We could deadlock here | 578 | * We could deadlock here |
@@ -582,6 +593,9 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
582 | test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); | 593 | test_bit(TTY_LDISC_CHANGING, &tty->flags) == 0); |
583 | mutex_lock(&tty->ldisc_mutex); | 594 | mutex_lock(&tty->ldisc_mutex); |
584 | } | 595 | } |
596 | |||
597 | lock_kernel(); | ||
598 | |||
585 | set_bit(TTY_LDISC_CHANGING, &tty->flags); | 599 | set_bit(TTY_LDISC_CHANGING, &tty->flags); |
586 | 600 | ||
587 | /* | 601 | /* |
@@ -592,6 +606,8 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
592 | tty->receive_room = 0; | 606 | tty->receive_room = 0; |
593 | 607 | ||
594 | o_ldisc = tty->ldisc; | 608 | o_ldisc = tty->ldisc; |
609 | |||
610 | unlock_kernel(); | ||
595 | /* | 611 | /* |
596 | * Make sure we don't change while someone holds a | 612 | * Make sure we don't change while someone holds a |
597 | * reference to the line discipline. The TTY_LDISC bit | 613 | * reference to the line discipline. The TTY_LDISC bit |
@@ -617,12 +633,14 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
617 | flush_scheduled_work(); | 633 | flush_scheduled_work(); |
618 | 634 | ||
619 | mutex_lock(&tty->ldisc_mutex); | 635 | mutex_lock(&tty->ldisc_mutex); |
636 | lock_kernel(); | ||
620 | if (test_bit(TTY_HUPPED, &tty->flags)) { | 637 | if (test_bit(TTY_HUPPED, &tty->flags)) { |
621 | /* We were raced by the hangup method. It will have stomped | 638 | /* We were raced by the hangup method. It will have stomped |
622 | the ldisc data and closed the ldisc down */ | 639 | the ldisc data and closed the ldisc down */ |
623 | clear_bit(TTY_LDISC_CHANGING, &tty->flags); | 640 | clear_bit(TTY_LDISC_CHANGING, &tty->flags); |
624 | mutex_unlock(&tty->ldisc_mutex); | 641 | mutex_unlock(&tty->ldisc_mutex); |
625 | tty_ldisc_put(new_ldisc); | 642 | tty_ldisc_put(new_ldisc); |
643 | unlock_kernel(); | ||
626 | return -EIO; | 644 | return -EIO; |
627 | } | 645 | } |
628 | 646 | ||
@@ -664,6 +682,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc) | |||
664 | if (o_work) | 682 | if (o_work) |
665 | schedule_delayed_work(&o_tty->buf.work, 1); | 683 | schedule_delayed_work(&o_tty->buf.work, 1); |
666 | mutex_unlock(&tty->ldisc_mutex); | 684 | mutex_unlock(&tty->ldisc_mutex); |
685 | unlock_kernel(); | ||
667 | return retval; | 686 | return retval; |
668 | } | 687 | } |
669 | 688 | ||
@@ -687,12 +706,13 @@ static void tty_reset_termios(struct tty_struct *tty) | |||
687 | /** | 706 | /** |
688 | * tty_ldisc_reinit - reinitialise the tty ldisc | 707 | * tty_ldisc_reinit - reinitialise the tty ldisc |
689 | * @tty: tty to reinit | 708 | * @tty: tty to reinit |
709 | * @ldisc: line discipline to reinitialize | ||
690 | * | 710 | * |
691 | * Switch the tty back to N_TTY line discipline and leave the | 711 | * Switch the tty to a line discipline and leave the ldisc |
692 | * ldisc state closed | 712 | * state closed |
693 | */ | 713 | */ |
694 | 714 | ||
695 | static void tty_ldisc_reinit(struct tty_struct *tty) | 715 | static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) |
696 | { | 716 | { |
697 | struct tty_ldisc *ld; | 717 | struct tty_ldisc *ld; |
698 | 718 | ||
@@ -702,10 +722,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty) | |||
702 | /* | 722 | /* |
703 | * Switch the line discipline back | 723 | * Switch the line discipline back |
704 | */ | 724 | */ |
705 | ld = tty_ldisc_get(N_TTY); | 725 | ld = tty_ldisc_get(ldisc); |
706 | BUG_ON(IS_ERR(ld)); | 726 | BUG_ON(IS_ERR(ld)); |
707 | tty_ldisc_assign(tty, ld); | 727 | tty_ldisc_assign(tty, ld); |
708 | tty_set_termios_ldisc(tty, N_TTY); | 728 | tty_set_termios_ldisc(tty, ldisc); |
709 | } | 729 | } |
710 | 730 | ||
711 | /** | 731 | /** |
@@ -726,6 +746,8 @@ static void tty_ldisc_reinit(struct tty_struct *tty) | |||
726 | void tty_ldisc_hangup(struct tty_struct *tty) | 746 | void tty_ldisc_hangup(struct tty_struct *tty) |
727 | { | 747 | { |
728 | struct tty_ldisc *ld; | 748 | struct tty_ldisc *ld; |
749 | int reset = tty->driver->flags & TTY_DRIVER_RESET_TERMIOS; | ||
750 | int err = 0; | ||
729 | 751 | ||
730 | /* | 752 | /* |
731 | * FIXME! What are the locking issues here? This may me overdoing | 753 | * FIXME! What are the locking issues here? This may me overdoing |
@@ -753,25 +775,32 @@ void tty_ldisc_hangup(struct tty_struct *tty) | |||
753 | wake_up_interruptible_poll(&tty->read_wait, POLLIN); | 775 | wake_up_interruptible_poll(&tty->read_wait, POLLIN); |
754 | /* | 776 | /* |
755 | * Shutdown the current line discipline, and reset it to | 777 | * Shutdown the current line discipline, and reset it to |
756 | * N_TTY. | 778 | * N_TTY if need be. |
779 | * | ||
780 | * Avoid racing set_ldisc or tty_ldisc_release | ||
757 | */ | 781 | */ |
758 | if (tty->driver->flags & TTY_DRIVER_RESET_TERMIOS) { | 782 | mutex_lock(&tty->ldisc_mutex); |
759 | /* Avoid racing set_ldisc or tty_ldisc_release */ | 783 | tty_ldisc_halt(tty); |
760 | mutex_lock(&tty->ldisc_mutex); | 784 | /* At this point we have a closed ldisc and we want to |
761 | tty_ldisc_halt(tty); | 785 | reopen it. We could defer this to the next open but |
762 | if (tty->ldisc) { /* Not yet closed */ | 786 | it means auditing a lot of other paths so this is |
763 | /* Switch back to N_TTY */ | 787 | a FIXME */ |
764 | tty_ldisc_reinit(tty); | 788 | if (tty->ldisc) { /* Not yet closed */ |
765 | /* At this point we have a closed ldisc and we want to | 789 | if (reset == 0) { |
766 | reopen it. We could defer this to the next open but | 790 | tty_ldisc_reinit(tty, tty->termios->c_line); |
767 | it means auditing a lot of other paths so this is | 791 | err = tty_ldisc_open(tty, tty->ldisc); |
768 | a FIXME */ | 792 | } |
793 | /* If the re-open fails or we reset then go to N_TTY. The | ||
794 | N_TTY open cannot fail */ | ||
795 | if (reset || err) { | ||
796 | tty_ldisc_reinit(tty, N_TTY); | ||
769 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); | 797 | WARN_ON(tty_ldisc_open(tty, tty->ldisc)); |
770 | tty_ldisc_enable(tty); | ||
771 | } | 798 | } |
772 | mutex_unlock(&tty->ldisc_mutex); | 799 | tty_ldisc_enable(tty); |
773 | tty_reset_termios(tty); | ||
774 | } | 800 | } |
801 | mutex_unlock(&tty->ldisc_mutex); | ||
802 | if (reset) | ||
803 | tty_reset_termios(tty); | ||
775 | } | 804 | } |
776 | 805 | ||
777 | /** | 806 | /** |