diff options
author | Alan Cox <alan@linux.intel.com> | 2009-06-11 07:48:02 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-06-11 11:51:01 -0400 |
commit | e8b70e7d3e86319a8b2aaabde3866833d92cd80f (patch) | |
tree | dac8e7b42f553ee3a9a0920e20b5a39da197b13a /drivers/char/tty_ldisc.c | |
parent | 5f0878acba7db24323f5ba4055ec9a96895bb150 (diff) |
tty: Extract various bits of ldisc code
Before trying to tackle the ldisc bugs the code needs to be a good deal
more readable, so do the simple extractions of routines first.
Signed-off-by: Alan Cox <alan@linux.intel.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char/tty_ldisc.c')
-rw-r--r-- | drivers/char/tty_ldisc.c | 97 |
1 files changed, 66 insertions, 31 deletions
diff --git a/drivers/char/tty_ldisc.c b/drivers/char/tty_ldisc.c index f78f5b0127a8..e3c6416aa86d 100644 --- a/drivers/char/tty_ldisc.c +++ b/drivers/char/tty_ldisc.c | |||
@@ -444,6 +444,50 @@ static void tty_ldisc_restore(struct tty_struct *tty, struct tty_ldisc *old) | |||
444 | } | 444 | } |
445 | 445 | ||
446 | /** | 446 | /** |
447 | * tty_ldisc_halt - shutdown the line discipline | ||
448 | * @tty: tty device | ||
449 | * | ||
450 | * Shut down the line discipline and work queue for this tty device. | ||
451 | * The TTY_LDISC flag being cleared ensures no further references can | ||
452 | * be obtained while the delayed work queue halt ensures that no more | ||
453 | * data is fed to the ldisc. | ||
454 | * | ||
455 | * In order to wait for any existing references to complete see | ||
456 | * tty_ldisc_wait_idle. | ||
457 | */ | ||
458 | |||
459 | static void tty_ldisc_halt(struct tty_struct *tty) | ||
460 | { | ||
461 | clear_bit(TTY_LDISC, &tty->flags); | ||
462 | cancel_delayed_work(&tty->buf.work); | ||
463 | /* | ||
464 | * Wait for ->hangup_work and ->buf.work handlers to terminate | ||
465 | */ | ||
466 | flush_scheduled_work(); | ||
467 | } | ||
468 | |||
469 | /** | ||
470 | * tty_ldisc_wait_idle - wait for the ldisc to become idle | ||
471 | * @tty: tty to wait for | ||
472 | * | ||
473 | * Wait for the line discipline to become idle. The discipline must | ||
474 | * have been halted for this to guarantee it remains idle. | ||
475 | * | ||
476 | */ | ||
477 | |||
478 | static void tty_ldisc_wait_idle(struct tty_struct *tty) | ||
479 | { | ||
480 | unsigned long flags; | ||
481 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
482 | while (tty->ldisc.refcount) { | ||
483 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
484 | wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); | ||
485 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
486 | } | ||
487 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
488 | } | ||
489 | |||
490 | /** | ||
447 | * tty_set_ldisc - set line discipline | 491 | * tty_set_ldisc - set line discipline |
448 | * @tty: the terminal to set | 492 | * @tty: the terminal to set |
449 | * @ldisc: the line discipline | 493 | * @ldisc: the line discipline |
@@ -636,6 +680,21 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) | |||
636 | return 0; | 680 | return 0; |
637 | } | 681 | } |
638 | 682 | ||
683 | static void tty_ldisc_reinit(struct tty_struct *tty) | ||
684 | { | ||
685 | struct tty_ldisc ld; | ||
686 | |||
687 | if (tty->ldisc.ops->close) | ||
688 | (tty->ldisc.ops->close)(tty); | ||
689 | tty_ldisc_put(tty->ldisc.ops); | ||
690 | /* | ||
691 | * Switch the line discipline back | ||
692 | */ | ||
693 | WARN_ON(tty_ldisc_get(N_TTY, &ld)); | ||
694 | tty_ldisc_assign(tty, &ld); | ||
695 | tty_set_termios_ldisc(tty, N_TTY); | ||
696 | } | ||
697 | |||
639 | /** | 698 | /** |
640 | * tty_ldisc_release - release line discipline | 699 | * tty_ldisc_release - release line discipline |
641 | * @tty: tty being shut down | 700 | * @tty: tty being shut down |
@@ -647,58 +706,34 @@ int tty_ldisc_setup(struct tty_struct *tty, struct tty_struct *o_tty) | |||
647 | 706 | ||
648 | void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) | 707 | void tty_ldisc_release(struct tty_struct *tty, struct tty_struct *o_tty) |
649 | { | 708 | { |
650 | unsigned long flags; | 709 | |
651 | struct tty_ldisc ld; | ||
652 | /* | 710 | /* |
653 | * Prevent flush_to_ldisc() from rescheduling the work for later. Then | 711 | * Prevent flush_to_ldisc() from rescheduling the work for later. Then |
654 | * kill any delayed work. As this is the final close it does not | 712 | * kill any delayed work. As this is the final close it does not |
655 | * race with the set_ldisc code path. | 713 | * race with the set_ldisc code path. |
656 | */ | 714 | */ |
657 | clear_bit(TTY_LDISC, &tty->flags); | ||
658 | cancel_delayed_work(&tty->buf.work); | ||
659 | 715 | ||
660 | /* | 716 | tty_ldisc_halt(tty); |
661 | * Wait for ->hangup_work and ->buf.work handlers to terminate | ||
662 | */ | ||
663 | |||
664 | flush_scheduled_work(); | ||
665 | 717 | ||
666 | /* | 718 | /* |
667 | * Wait for any short term users (we know they are just driver | 719 | * Wait for any short term users (we know they are just driver |
668 | * side waiters as the file is closing so user count on the file | 720 | * side waiters as the file is closing so user count on the file |
669 | * side is zero. | 721 | * side is zero. |
670 | */ | 722 | */ |
671 | spin_lock_irqsave(&tty_ldisc_lock, flags); | 723 | |
672 | while (tty->ldisc.refcount) { | 724 | tty_ldisc_wait_idle(tty); |
673 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | 725 | |
674 | wait_event(tty_ldisc_wait, tty->ldisc.refcount == 0); | ||
675 | spin_lock_irqsave(&tty_ldisc_lock, flags); | ||
676 | } | ||
677 | spin_unlock_irqrestore(&tty_ldisc_lock, flags); | ||
678 | /* | 726 | /* |
679 | * Shutdown the current line discipline, and reset it to N_TTY. | 727 | * Shutdown the current line discipline, and reset it to N_TTY. |
680 | * | 728 | * |
681 | * FIXME: this MUST get fixed for the new reflocking | 729 | * FIXME: this MUST get fixed for the new reflocking |
682 | */ | 730 | */ |
683 | if (tty->ldisc.ops->close) | ||
684 | (tty->ldisc.ops->close)(tty); | ||
685 | tty_ldisc_put(tty->ldisc.ops); | ||
686 | 731 | ||
687 | /* | 732 | tty_ldisc_reinit(tty); |
688 | * Switch the line discipline back | ||
689 | */ | ||
690 | WARN_ON(tty_ldisc_get(N_TTY, &ld)); | ||
691 | tty_ldisc_assign(tty, &ld); | ||
692 | tty_set_termios_ldisc(tty, N_TTY); | ||
693 | if (o_tty) { | 733 | if (o_tty) { |
694 | /* FIXME: could o_tty be in setldisc here ? */ | 734 | /* FIXME: could o_tty be in setldisc here ? */ |
695 | clear_bit(TTY_LDISC, &o_tty->flags); | 735 | clear_bit(TTY_LDISC, &o_tty->flags); |
696 | if (o_tty->ldisc.ops->close) | 736 | tty_ldisc_reinit(o_tty); |
697 | (o_tty->ldisc.ops->close)(o_tty); | ||
698 | tty_ldisc_put(o_tty->ldisc.ops); | ||
699 | WARN_ON(tty_ldisc_get(N_TTY, &ld)); | ||
700 | tty_ldisc_assign(o_tty, &ld); | ||
701 | tty_set_termios_ldisc(o_tty, N_TTY); | ||
702 | } | 737 | } |
703 | } | 738 | } |
704 | 739 | ||