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 | |
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>
-rw-r--r-- | drivers/char/tty_io.c | 24 | ||||
-rw-r--r-- | drivers/char/tty_ldisc.c | 97 | ||||
-rw-r--r-- | include/linux/tty.h | 3 |
3 files changed, 88 insertions, 36 deletions
diff --git a/drivers/char/tty_io.c b/drivers/char/tty_io.c index 6c817398232e..be49d0730bb9 100644 --- a/drivers/char/tty_io.c +++ b/drivers/char/tty_io.c | |||
@@ -2481,6 +2481,24 @@ static int tty_tiocmset(struct tty_struct *tty, struct file *file, unsigned int | |||
2481 | return tty->ops->tiocmset(tty, file, set, clear); | 2481 | return tty->ops->tiocmset(tty, file, set, clear); |
2482 | } | 2482 | } |
2483 | 2483 | ||
2484 | struct tty_struct *tty_pair_get_tty(struct tty_struct *tty) | ||
2485 | { | ||
2486 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | ||
2487 | tty->driver->subtype == PTY_TYPE_MASTER) | ||
2488 | tty = tty->link; | ||
2489 | return tty; | ||
2490 | } | ||
2491 | EXPORT_SYMBOL(tty_pair_get_tty); | ||
2492 | |||
2493 | struct tty_struct *tty_pair_get_pty(struct tty_struct *tty) | ||
2494 | { | ||
2495 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | ||
2496 | tty->driver->subtype == PTY_TYPE_MASTER) | ||
2497 | return tty; | ||
2498 | return tty->link; | ||
2499 | } | ||
2500 | EXPORT_SYMBOL(tty_pair_get_pty); | ||
2501 | |||
2484 | /* | 2502 | /* |
2485 | * Split this up, as gcc can choke on it otherwise.. | 2503 | * Split this up, as gcc can choke on it otherwise.. |
2486 | */ | 2504 | */ |
@@ -2496,11 +2514,7 @@ long tty_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |||
2496 | if (tty_paranoia_check(tty, inode, "tty_ioctl")) | 2514 | if (tty_paranoia_check(tty, inode, "tty_ioctl")) |
2497 | return -EINVAL; | 2515 | return -EINVAL; |
2498 | 2516 | ||
2499 | real_tty = tty; | 2517 | real_tty = tty_pair_get_tty(tty); |
2500 | if (tty->driver->type == TTY_DRIVER_TYPE_PTY && | ||
2501 | tty->driver->subtype == PTY_TYPE_MASTER) | ||
2502 | real_tty = tty->link; | ||
2503 | |||
2504 | 2518 | ||
2505 | /* | 2519 | /* |
2506 | * Factor out some common prep work | 2520 | * Factor out some common prep work |
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 | ||
diff --git a/include/linux/tty.h b/include/linux/tty.h index bed5a3d40307..f9c13c83790c 100644 --- a/include/linux/tty.h +++ b/include/linux/tty.h | |||
@@ -428,6 +428,9 @@ extern struct tty_struct *tty_init_dev(struct tty_driver *driver, int idx, | |||
428 | extern void tty_release_dev(struct file *filp); | 428 | extern void tty_release_dev(struct file *filp); |
429 | extern int tty_init_termios(struct tty_struct *tty); | 429 | extern int tty_init_termios(struct tty_struct *tty); |
430 | 430 | ||
431 | extern struct tty_struct *tty_pair_get_tty(struct tty_struct *tty); | ||
432 | extern struct tty_struct *tty_pair_get_pty(struct tty_struct *tty); | ||
433 | |||
431 | extern struct mutex tty_mutex; | 434 | extern struct mutex tty_mutex; |
432 | 435 | ||
433 | extern void tty_write_unlock(struct tty_struct *tty); | 436 | extern void tty_write_unlock(struct tty_struct *tty); |