aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/tty/tty_ldisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/tty_ldisc.c')
-rw-r--r--drivers/tty/tty_ldisc.c49
1 files changed, 42 insertions, 7 deletions
diff --git a/drivers/tty/tty_ldisc.c b/drivers/tty/tty_ldisc.c
index 412f9775d19c..d8e96b005023 100644
--- a/drivers/tty/tty_ldisc.c
+++ b/drivers/tty/tty_ldisc.c
@@ -47,6 +47,7 @@
47 47
48static DEFINE_SPINLOCK(tty_ldisc_lock); 48static DEFINE_SPINLOCK(tty_ldisc_lock);
49static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait); 49static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_wait);
50static DECLARE_WAIT_QUEUE_HEAD(tty_ldisc_idle);
50/* Line disc dispatch table */ 51/* Line disc dispatch table */
51static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS]; 52static struct tty_ldisc_ops *tty_ldiscs[NR_LDISCS];
52 53
@@ -83,6 +84,7 @@ static void put_ldisc(struct tty_ldisc *ld)
83 return; 84 return;
84 } 85 }
85 local_irq_restore(flags); 86 local_irq_restore(flags);
87 wake_up(&tty_ldisc_idle);
86} 88}
87 89
88/** 90/**
@@ -531,6 +533,23 @@ static int tty_ldisc_halt(struct tty_struct *tty)
531} 533}
532 534
533/** 535/**
536 * tty_ldisc_wait_idle - wait for the ldisc to become idle
537 * @tty: tty to wait for
538 *
539 * Wait for the line discipline to become idle. The discipline must
540 * have been halted for this to guarantee it remains idle.
541 */
542static int tty_ldisc_wait_idle(struct tty_struct *tty)
543{
544 int ret;
545 ret = wait_event_interruptible_timeout(tty_ldisc_idle,
546 atomic_read(&tty->ldisc->users) == 1, 5 * HZ);
547 if (ret < 0)
548 return ret;
549 return ret > 0 ? 0 : -EBUSY;
550}
551
552/**
534 * tty_set_ldisc - set line discipline 553 * tty_set_ldisc - set line discipline
535 * @tty: the terminal to set 554 * @tty: the terminal to set
536 * @ldisc: the line discipline 555 * @ldisc: the line discipline
@@ -634,8 +653,17 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
634 653
635 flush_scheduled_work(); 654 flush_scheduled_work();
636 655
656 retval = tty_ldisc_wait_idle(tty);
657
637 tty_lock(); 658 tty_lock();
638 mutex_lock(&tty->ldisc_mutex); 659 mutex_lock(&tty->ldisc_mutex);
660
661 /* handle wait idle failure locked */
662 if (retval) {
663 tty_ldisc_put(new_ldisc);
664 goto enable;
665 }
666
639 if (test_bit(TTY_HUPPED, &tty->flags)) { 667 if (test_bit(TTY_HUPPED, &tty->flags)) {
640 /* We were raced by the hangup method. It will have stomped 668 /* We were raced by the hangup method. It will have stomped
641 the ldisc data and closed the ldisc down */ 669 the ldisc data and closed the ldisc down */
@@ -669,6 +697,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
669 697
670 tty_ldisc_put(o_ldisc); 698 tty_ldisc_put(o_ldisc);
671 699
700enable:
672 /* 701 /*
673 * Allow ldisc referencing to occur again 702 * Allow ldisc referencing to occur again
674 */ 703 */
@@ -714,9 +743,12 @@ static void tty_reset_termios(struct tty_struct *tty)
714 * state closed 743 * state closed
715 */ 744 */
716 745
717static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) 746static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
718{ 747{
719 struct tty_ldisc *ld; 748 struct tty_ldisc *ld = tty_ldisc_get(ldisc);
749
750 if (IS_ERR(ld))
751 return -1;
720 752
721 tty_ldisc_close(tty, tty->ldisc); 753 tty_ldisc_close(tty, tty->ldisc);
722 tty_ldisc_put(tty->ldisc); 754 tty_ldisc_put(tty->ldisc);
@@ -724,10 +756,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
724 /* 756 /*
725 * Switch the line discipline back 757 * Switch the line discipline back
726 */ 758 */
727 ld = tty_ldisc_get(ldisc);
728 BUG_ON(IS_ERR(ld));
729 tty_ldisc_assign(tty, ld); 759 tty_ldisc_assign(tty, ld);
730 tty_set_termios_ldisc(tty, ldisc); 760 tty_set_termios_ldisc(tty, ldisc);
761
762 return 0;
731} 763}
732 764
733/** 765/**
@@ -802,13 +834,16 @@ void tty_ldisc_hangup(struct tty_struct *tty)
802 a FIXME */ 834 a FIXME */
803 if (tty->ldisc) { /* Not yet closed */ 835 if (tty->ldisc) { /* Not yet closed */
804 if (reset == 0) { 836 if (reset == 0) {
805 tty_ldisc_reinit(tty, tty->termios->c_line); 837
806 err = tty_ldisc_open(tty, tty->ldisc); 838 if (!tty_ldisc_reinit(tty, tty->termios->c_line))
839 err = tty_ldisc_open(tty, tty->ldisc);
840 else
841 err = 1;
807 } 842 }
808 /* If the re-open fails or we reset then go to N_TTY. The 843 /* If the re-open fails or we reset then go to N_TTY. The
809 N_TTY open cannot fail */ 844 N_TTY open cannot fail */
810 if (reset || err) { 845 if (reset || err) {
811 tty_ldisc_reinit(tty, N_TTY); 846 BUG_ON(tty_ldisc_reinit(tty, N_TTY));
812 WARN_ON(tty_ldisc_open(tty, tty->ldisc)); 847 WARN_ON(tty_ldisc_open(tty, tty->ldisc));
813 } 848 }
814 tty_ldisc_enable(tty); 849 tty_ldisc_enable(tty);