aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/char/tty_ldisc.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r--drivers/char/tty_ldisc.c51
1 files changed, 44 insertions, 7 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c
index 412f9775d19c..4214d58276f7 100644
--- a/drivers/char/tty_ldisc.c
+++ b/drivers/char/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/**
@@ -452,6 +454,8 @@ static int tty_ldisc_open(struct tty_struct *tty, struct tty_ldisc *ld)
452 /* BTM here locks versus a hangup event */ 454 /* BTM here locks versus a hangup event */
453 WARN_ON(!tty_locked()); 455 WARN_ON(!tty_locked());
454 ret = ld->ops->open(tty); 456 ret = ld->ops->open(tty);
457 if (ret)
458 clear_bit(TTY_LDISC_OPEN, &tty->flags);
455 return ret; 459 return ret;
456 } 460 }
457 return 0; 461 return 0;
@@ -531,6 +535,23 @@ static int tty_ldisc_halt(struct tty_struct *tty)
531} 535}
532 536
533/** 537/**
538 * tty_ldisc_wait_idle - wait for the ldisc to become idle
539 * @tty: tty to wait for
540 *
541 * Wait for the line discipline to become idle. The discipline must
542 * have been halted for this to guarantee it remains idle.
543 */
544static int tty_ldisc_wait_idle(struct tty_struct *tty)
545{
546 int ret;
547 ret = wait_event_interruptible_timeout(tty_ldisc_idle,
548 atomic_read(&tty->ldisc->users) == 1, 5 * HZ);
549 if (ret < 0)
550 return ret;
551 return ret > 0 ? 0 : -EBUSY;
552}
553
554/**
534 * tty_set_ldisc - set line discipline 555 * tty_set_ldisc - set line discipline
535 * @tty: the terminal to set 556 * @tty: the terminal to set
536 * @ldisc: the line discipline 557 * @ldisc: the line discipline
@@ -634,8 +655,17 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
634 655
635 flush_scheduled_work(); 656 flush_scheduled_work();
636 657
658 retval = tty_ldisc_wait_idle(tty);
659
637 tty_lock(); 660 tty_lock();
638 mutex_lock(&tty->ldisc_mutex); 661 mutex_lock(&tty->ldisc_mutex);
662
663 /* handle wait idle failure locked */
664 if (retval) {
665 tty_ldisc_put(new_ldisc);
666 goto enable;
667 }
668
639 if (test_bit(TTY_HUPPED, &tty->flags)) { 669 if (test_bit(TTY_HUPPED, &tty->flags)) {
640 /* We were raced by the hangup method. It will have stomped 670 /* We were raced by the hangup method. It will have stomped
641 the ldisc data and closed the ldisc down */ 671 the ldisc data and closed the ldisc down */
@@ -669,6 +699,7 @@ int tty_set_ldisc(struct tty_struct *tty, int ldisc)
669 699
670 tty_ldisc_put(o_ldisc); 700 tty_ldisc_put(o_ldisc);
671 701
702enable:
672 /* 703 /*
673 * Allow ldisc referencing to occur again 704 * Allow ldisc referencing to occur again
674 */ 705 */
@@ -714,9 +745,12 @@ static void tty_reset_termios(struct tty_struct *tty)
714 * state closed 745 * state closed
715 */ 746 */
716 747
717static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc) 748static int tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
718{ 749{
719 struct tty_ldisc *ld; 750 struct tty_ldisc *ld = tty_ldisc_get(ldisc);
751
752 if (IS_ERR(ld))
753 return -1;
720 754
721 tty_ldisc_close(tty, tty->ldisc); 755 tty_ldisc_close(tty, tty->ldisc);
722 tty_ldisc_put(tty->ldisc); 756 tty_ldisc_put(tty->ldisc);
@@ -724,10 +758,10 @@ static void tty_ldisc_reinit(struct tty_struct *tty, int ldisc)
724 /* 758 /*
725 * Switch the line discipline back 759 * Switch the line discipline back
726 */ 760 */
727 ld = tty_ldisc_get(ldisc);
728 BUG_ON(IS_ERR(ld));
729 tty_ldisc_assign(tty, ld); 761 tty_ldisc_assign(tty, ld);
730 tty_set_termios_ldisc(tty, ldisc); 762 tty_set_termios_ldisc(tty, ldisc);
763
764 return 0;
731} 765}
732 766
733/** 767/**
@@ -802,13 +836,16 @@ void tty_ldisc_hangup(struct tty_struct *tty)
802 a FIXME */ 836 a FIXME */
803 if (tty->ldisc) { /* Not yet closed */ 837 if (tty->ldisc) { /* Not yet closed */
804 if (reset == 0) { 838 if (reset == 0) {
805 tty_ldisc_reinit(tty, tty->termios->c_line); 839
806 err = tty_ldisc_open(tty, tty->ldisc); 840 if (!tty_ldisc_reinit(tty, tty->termios->c_line))
841 err = tty_ldisc_open(tty, tty->ldisc);
842 else
843 err = 1;
807 } 844 }
808 /* If the re-open fails or we reset then go to N_TTY. The 845 /* If the re-open fails or we reset then go to N_TTY. The
809 N_TTY open cannot fail */ 846 N_TTY open cannot fail */
810 if (reset || err) { 847 if (reset || err) {
811 tty_ldisc_reinit(tty, N_TTY); 848 BUG_ON(tty_ldisc_reinit(tty, N_TTY));
812 WARN_ON(tty_ldisc_open(tty, tty->ldisc)); 849 WARN_ON(tty_ldisc_open(tty, tty->ldisc));
813 } 850 }
814 tty_ldisc_enable(tty); 851 tty_ldisc_enable(tty);