diff options
author | Alan Cox <alan@redhat.com> | 2009-01-02 08:47:58 -0500 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2009-01-02 13:19:41 -0500 |
commit | d1c815e549ff40f9e9db65654855118e6bdff6a4 (patch) | |
tree | 7e20548f68c053be312f080e7c1fd7678df45bd7 /drivers/char | |
parent | 542f54823614915780c3459b0e6062f06c0c0f99 (diff) |
tty: relock epca
Bring epca into line with the port locking.
Signed-off-by: Alan Cox <alan@redhat.com>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
Diffstat (limited to 'drivers/char')
-rw-r--r-- | drivers/char/epca.c | 195 |
1 files changed, 104 insertions, 91 deletions
diff --git a/drivers/char/epca.c b/drivers/char/epca.c index da2d2cf16f5..e07d7925c30 100644 --- a/drivers/char/epca.c +++ b/drivers/char/epca.c | |||
@@ -69,7 +69,9 @@ static int invalid_lilo_config; | |||
69 | 69 | ||
70 | /* | 70 | /* |
71 | * The ISA boards do window flipping into the same spaces so its only sane with | 71 | * The ISA boards do window flipping into the same spaces so its only sane with |
72 | * a single lock. It's still pretty efficient. | 72 | * a single lock. It's still pretty efficient. This lock guards the hardware |
73 | * and the tty_port lock guards the kernel side stuff like use counts. Take | ||
74 | * this lock inside the port lock if you must take both. | ||
73 | */ | 75 | */ |
74 | static DEFINE_SPINLOCK(epca_lock); | 76 | static DEFINE_SPINLOCK(epca_lock); |
75 | 77 | ||
@@ -156,7 +158,7 @@ static struct channel *verifyChannel(struct tty_struct *); | |||
156 | static void pc_sched_event(struct channel *, int); | 158 | static void pc_sched_event(struct channel *, int); |
157 | static void epca_error(int, char *); | 159 | static void epca_error(int, char *); |
158 | static void pc_close(struct tty_struct *, struct file *); | 160 | static void pc_close(struct tty_struct *, struct file *); |
159 | static void shutdown(struct channel *); | 161 | static void shutdown(struct channel *, struct tty_struct *tty); |
160 | static void pc_hangup(struct tty_struct *); | 162 | static void pc_hangup(struct tty_struct *); |
161 | static int pc_write_room(struct tty_struct *); | 163 | static int pc_write_room(struct tty_struct *); |
162 | static int pc_chars_in_buffer(struct tty_struct *); | 164 | static int pc_chars_in_buffer(struct tty_struct *); |
@@ -419,76 +421,78 @@ static void epca_error(int line, char *msg) | |||
419 | static void pc_close(struct tty_struct *tty, struct file *filp) | 421 | static void pc_close(struct tty_struct *tty, struct file *filp) |
420 | { | 422 | { |
421 | struct channel *ch; | 423 | struct channel *ch; |
424 | struct tty_port *port; | ||
422 | unsigned long flags; | 425 | unsigned long flags; |
423 | /* | 426 | /* |
424 | * verifyChannel returns the channel from the tty struct if it is | 427 | * verifyChannel returns the channel from the tty struct if it is |
425 | * valid. This serves as a sanity check. | 428 | * valid. This serves as a sanity check. |
426 | */ | 429 | */ |
427 | ch = verifyChannel(tty); | 430 | ch = verifyChannel(tty); |
428 | if (ch != NULL) { | 431 | if (ch == NULL) |
429 | spin_lock_irqsave(&epca_lock, flags); | 432 | return; |
430 | if (tty_hung_up_p(filp)) { | 433 | port = &ch->port; |
431 | spin_unlock_irqrestore(&epca_lock, flags); | ||
432 | return; | ||
433 | } | ||
434 | if (ch->port.count-- > 1) { | ||
435 | /* Begin channel is open more than once */ | ||
436 | /* | ||
437 | * Return without doing anything. Someone might still | ||
438 | * be using the channel. | ||
439 | */ | ||
440 | spin_unlock_irqrestore(&epca_lock, flags); | ||
441 | return; | ||
442 | } | ||
443 | /* Port open only once go ahead with shutdown & reset */ | ||
444 | BUG_ON(ch->port.count < 0); | ||
445 | 434 | ||
435 | spin_lock_irqsave(&port->lock, flags); | ||
436 | if (tty_hung_up_p(filp)) { | ||
437 | spin_unlock_irqrestore(&port->lock, flags); | ||
438 | return; | ||
439 | } | ||
440 | if (port->count-- > 1) { | ||
441 | /* Begin channel is open more than once */ | ||
446 | /* | 442 | /* |
447 | * Let the rest of the driver know the channel is being closed. | 443 | * Return without doing anything. Someone might still |
448 | * This becomes important if an open is attempted before close | 444 | * be using the channel. |
449 | * is finished. | ||
450 | */ | 445 | */ |
451 | ch->port.flags |= ASYNC_CLOSING; | 446 | spin_unlock_irqrestore(&port->lock, flags); |
452 | tty->closing = 1; | 447 | return; |
453 | 448 | } | |
454 | spin_unlock_irqrestore(&epca_lock, flags); | 449 | /* Port open only once go ahead with shutdown & reset */ |
455 | 450 | WARN_ON(port->count < 0); | |
456 | if (ch->port.flags & ASYNC_INITIALIZED) { | ||
457 | /* Setup an event to indicate when the | ||
458 | transmit buffer empties */ | ||
459 | setup_empty_event(tty, ch); | ||
460 | /* 30 seconds timeout */ | ||
461 | tty_wait_until_sent(tty, 3000); | ||
462 | } | ||
463 | pc_flush_buffer(tty); | ||
464 | 451 | ||
465 | tty_ldisc_flush(tty); | 452 | /* |
466 | shutdown(ch); | 453 | * Let the rest of the driver know the channel is being closed. |
454 | * This becomes important if an open is attempted before close | ||
455 | * is finished. | ||
456 | */ | ||
457 | port->flags |= ASYNC_CLOSING; | ||
458 | tty->closing = 1; | ||
467 | 459 | ||
468 | spin_lock_irqsave(&epca_lock, flags); | 460 | spin_unlock_irqrestore(&port->lock, flags); |
469 | tty->closing = 0; | ||
470 | ch->event = 0; | ||
471 | ch->port.tty = NULL; | ||
472 | spin_unlock_irqrestore(&epca_lock, flags); | ||
473 | 461 | ||
474 | if (ch->port.blocked_open) { | 462 | if (port->flags & ASYNC_INITIALIZED) { |
475 | if (ch->close_delay) | 463 | /* Setup an event to indicate when the |
476 | msleep_interruptible(jiffies_to_msecs(ch->close_delay)); | 464 | transmit buffer empties */ |
477 | wake_up_interruptible(&ch->port.open_wait); | 465 | setup_empty_event(tty, ch); |
478 | } | 466 | /* 30 seconds timeout */ |
479 | ch->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | | 467 | tty_wait_until_sent(tty, 3000); |
480 | ASYNC_CLOSING); | 468 | } |
481 | wake_up_interruptible(&ch->port.close_wait); | 469 | pc_flush_buffer(tty); |
470 | tty_ldisc_flush(tty); | ||
471 | shutdown(ch, tty); | ||
472 | |||
473 | spin_lock_irqsave(&port->lock, flags); | ||
474 | tty->closing = 0; | ||
475 | ch->event = 0; | ||
476 | port->tty = NULL; | ||
477 | spin_unlock_irqrestore(&port->lock, flags); | ||
478 | |||
479 | if (port->blocked_open) { | ||
480 | if (ch->close_delay) | ||
481 | msleep_interruptible(jiffies_to_msecs(ch->close_delay)); | ||
482 | wake_up_interruptible(&port->open_wait); | ||
482 | } | 483 | } |
484 | port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED | | ||
485 | ASYNC_CLOSING); | ||
486 | wake_up_interruptible(&port->close_wait); | ||
483 | } | 487 | } |
484 | 488 | ||
485 | static void shutdown(struct channel *ch) | 489 | static void shutdown(struct channel *ch, struct tty_struct *tty) |
486 | { | 490 | { |
487 | unsigned long flags; | 491 | unsigned long flags; |
488 | struct tty_struct *tty; | ||
489 | struct board_chan __iomem *bc; | 492 | struct board_chan __iomem *bc; |
493 | struct tty_port *port = &ch->port; | ||
490 | 494 | ||
491 | if (!(ch->port.flags & ASYNC_INITIALIZED)) | 495 | if (!(port->flags & ASYNC_INITIALIZED)) |
492 | return; | 496 | return; |
493 | 497 | ||
494 | spin_lock_irqsave(&epca_lock, flags); | 498 | spin_lock_irqsave(&epca_lock, flags); |
@@ -503,7 +507,6 @@ static void shutdown(struct channel *ch) | |||
503 | */ | 507 | */ |
504 | if (bc) | 508 | if (bc) |
505 | writeb(0, &bc->idata); | 509 | writeb(0, &bc->idata); |
506 | tty = ch->port.tty; | ||
507 | 510 | ||
508 | /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */ | 511 | /* If we're a modem control device and HUPCL is on, drop RTS & DTR. */ |
509 | if (tty->termios->c_cflag & HUPCL) { | 512 | if (tty->termios->c_cflag & HUPCL) { |
@@ -517,13 +520,15 @@ static void shutdown(struct channel *ch) | |||
517 | * will have to reinitialized. Set a flag to indicate this. | 520 | * will have to reinitialized. Set a flag to indicate this. |
518 | */ | 521 | */ |
519 | /* Prevent future Digi programmed interrupts from coming active */ | 522 | /* Prevent future Digi programmed interrupts from coming active */ |
520 | ch->port.flags &= ~ASYNC_INITIALIZED; | 523 | port->flags &= ~ASYNC_INITIALIZED; |
521 | spin_unlock_irqrestore(&epca_lock, flags); | 524 | spin_unlock_irqrestore(&epca_lock, flags); |
522 | } | 525 | } |
523 | 526 | ||
524 | static void pc_hangup(struct tty_struct *tty) | 527 | static void pc_hangup(struct tty_struct *tty) |
525 | { | 528 | { |
526 | struct channel *ch; | 529 | struct channel *ch; |
530 | struct tty_port *port; | ||
531 | |||
527 | /* | 532 | /* |
528 | * verifyChannel returns the channel from the tty struct if it is | 533 | * verifyChannel returns the channel from the tty struct if it is |
529 | * valid. This serves as a sanity check. | 534 | * valid. This serves as a sanity check. |
@@ -531,18 +536,19 @@ static void pc_hangup(struct tty_struct *tty) | |||
531 | ch = verifyChannel(tty); | 536 | ch = verifyChannel(tty); |
532 | if (ch != NULL) { | 537 | if (ch != NULL) { |
533 | unsigned long flags; | 538 | unsigned long flags; |
539 | port = &ch->port; | ||
534 | 540 | ||
535 | pc_flush_buffer(tty); | 541 | pc_flush_buffer(tty); |
536 | tty_ldisc_flush(tty); | 542 | tty_ldisc_flush(tty); |
537 | shutdown(ch); | 543 | shutdown(ch, tty); |
538 | 544 | ||
539 | spin_lock_irqsave(&epca_lock, flags); | 545 | spin_lock_irqsave(&port->lock, flags); |
540 | ch->port.tty = NULL; | 546 | port->tty = NULL; |
541 | ch->event = 0; | 547 | ch->event = 0; /* FIXME: review locking of ch->event */ |
542 | ch->port.count = 0; | 548 | port->count = 0; |
543 | ch->port.flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); | 549 | port->flags &= ~(ASYNC_NORMAL_ACTIVE | ASYNC_INITIALIZED); |
544 | spin_unlock_irqrestore(&epca_lock, flags); | 550 | spin_unlock_irqrestore(&port->lock, flags); |
545 | wake_up_interruptible(&ch->port.open_wait); | 551 | wake_up_interruptible(&port->open_wait); |
546 | } | 552 | } |
547 | } | 553 | } |
548 | 554 | ||
@@ -792,9 +798,10 @@ static int block_til_ready(struct tty_struct *tty, | |||
792 | DECLARE_WAITQUEUE(wait, current); | 798 | DECLARE_WAITQUEUE(wait, current); |
793 | int retval, do_clocal = 0; | 799 | int retval, do_clocal = 0; |
794 | unsigned long flags; | 800 | unsigned long flags; |
801 | struct tty_port *port = &ch->port; | ||
795 | 802 | ||
796 | if (tty_hung_up_p(filp)) { | 803 | if (tty_hung_up_p(filp)) { |
797 | if (ch->port.flags & ASYNC_HUP_NOTIFY) | 804 | if (port->flags & ASYNC_HUP_NOTIFY) |
798 | retval = -EAGAIN; | 805 | retval = -EAGAIN; |
799 | else | 806 | else |
800 | retval = -ERESTARTSYS; | 807 | retval = -ERESTARTSYS; |
@@ -805,10 +812,10 @@ static int block_til_ready(struct tty_struct *tty, | |||
805 | * If the device is in the middle of being closed, then block until | 812 | * If the device is in the middle of being closed, then block until |
806 | * it's done, and then try again. | 813 | * it's done, and then try again. |
807 | */ | 814 | */ |
808 | if (ch->port.flags & ASYNC_CLOSING) { | 815 | if (port->flags & ASYNC_CLOSING) { |
809 | interruptible_sleep_on(&ch->port.close_wait); | 816 | interruptible_sleep_on(&port->close_wait); |
810 | 817 | ||
811 | if (ch->port.flags & ASYNC_HUP_NOTIFY) | 818 | if (port->flags & ASYNC_HUP_NOTIFY) |
812 | return -EAGAIN; | 819 | return -EAGAIN; |
813 | else | 820 | else |
814 | return -ERESTARTSYS; | 821 | return -ERESTARTSYS; |
@@ -819,7 +826,7 @@ static int block_til_ready(struct tty_struct *tty, | |||
819 | * If non-blocking mode is set, then make the check up front | 826 | * If non-blocking mode is set, then make the check up front |
820 | * and then exit. | 827 | * and then exit. |
821 | */ | 828 | */ |
822 | ch->port.flags |= ASYNC_NORMAL_ACTIVE; | 829 | port->flags |= ASYNC_NORMAL_ACTIVE; |
823 | return 0; | 830 | return 0; |
824 | } | 831 | } |
825 | if (tty->termios->c_cflag & CLOCAL) | 832 | if (tty->termios->c_cflag & CLOCAL) |
@@ -827,31 +834,31 @@ static int block_til_ready(struct tty_struct *tty, | |||
827 | /* Block waiting for the carrier detect and the line to become free */ | 834 | /* Block waiting for the carrier detect and the line to become free */ |
828 | 835 | ||
829 | retval = 0; | 836 | retval = 0; |
830 | add_wait_queue(&ch->port.open_wait, &wait); | 837 | add_wait_queue(&port->open_wait, &wait); |
831 | 838 | ||
832 | spin_lock_irqsave(&epca_lock, flags); | 839 | spin_lock_irqsave(&port->lock, flags); |
833 | /* We dec count so that pc_close will know when to free things */ | 840 | /* We dec count so that pc_close will know when to free things */ |
834 | if (!tty_hung_up_p(filp)) | 841 | if (!tty_hung_up_p(filp)) |
835 | ch->port.count--; | 842 | port->count--; |
836 | ch->port.blocked_open++; | 843 | port->blocked_open++; |
837 | while (1) { | 844 | while (1) { |
838 | set_current_state(TASK_INTERRUPTIBLE); | 845 | set_current_state(TASK_INTERRUPTIBLE); |
839 | if (tty_hung_up_p(filp) || | 846 | if (tty_hung_up_p(filp) || |
840 | !(ch->port.flags & ASYNC_INITIALIZED)) { | 847 | !(port->flags & ASYNC_INITIALIZED)) { |
841 | if (ch->port.flags & ASYNC_HUP_NOTIFY) | 848 | if (port->flags & ASYNC_HUP_NOTIFY) |
842 | retval = -EAGAIN; | 849 | retval = -EAGAIN; |
843 | else | 850 | else |
844 | retval = -ERESTARTSYS; | 851 | retval = -ERESTARTSYS; |
845 | break; | 852 | break; |
846 | } | 853 | } |
847 | if (!(ch->port.flags & ASYNC_CLOSING) && | 854 | if (!(port->flags & ASYNC_CLOSING) && |
848 | (do_clocal || (ch->imodem & ch->dcd))) | 855 | (do_clocal || (ch->imodem & ch->dcd))) |
849 | break; | 856 | break; |
850 | if (signal_pending(current)) { | 857 | if (signal_pending(current)) { |
851 | retval = -ERESTARTSYS; | 858 | retval = -ERESTARTSYS; |
852 | break; | 859 | break; |
853 | } | 860 | } |
854 | spin_unlock_irqrestore(&epca_lock, flags); | 861 | spin_unlock_irqrestore(&port->lock, flags); |
855 | /* | 862 | /* |
856 | * Allow someone else to be scheduled. We will occasionally go | 863 | * Allow someone else to be scheduled. We will occasionally go |
857 | * through this loop until one of the above conditions change. | 864 | * through this loop until one of the above conditions change. |
@@ -859,27 +866,28 @@ static int block_til_ready(struct tty_struct *tty, | |||
859 | * and prevent this loop from hogging the cpu. | 866 | * and prevent this loop from hogging the cpu. |
860 | */ | 867 | */ |
861 | schedule(); | 868 | schedule(); |
862 | spin_lock_irqsave(&epca_lock, flags); | 869 | spin_lock_irqsave(&port->lock, flags); |
863 | } | 870 | } |
864 | 871 | ||
865 | __set_current_state(TASK_RUNNING); | 872 | __set_current_state(TASK_RUNNING); |
866 | remove_wait_queue(&ch->port.open_wait, &wait); | 873 | remove_wait_queue(&port->open_wait, &wait); |
867 | if (!tty_hung_up_p(filp)) | 874 | if (!tty_hung_up_p(filp)) |
868 | ch->port.count++; | 875 | port->count++; |
869 | ch->port.blocked_open--; | 876 | port->blocked_open--; |
870 | 877 | ||
871 | spin_unlock_irqrestore(&epca_lock, flags); | 878 | spin_unlock_irqrestore(&port->lock, flags); |
872 | 879 | ||
873 | if (retval) | 880 | if (retval) |
874 | return retval; | 881 | return retval; |
875 | 882 | ||
876 | ch->port.flags |= ASYNC_NORMAL_ACTIVE; | 883 | port->flags |= ASYNC_NORMAL_ACTIVE; |
877 | return 0; | 884 | return 0; |
878 | } | 885 | } |
879 | 886 | ||
880 | static int pc_open(struct tty_struct *tty, struct file *filp) | 887 | static int pc_open(struct tty_struct *tty, struct file *filp) |
881 | { | 888 | { |
882 | struct channel *ch; | 889 | struct channel *ch; |
890 | struct tty_port *port; | ||
883 | unsigned long flags; | 891 | unsigned long flags; |
884 | int line, retval, boardnum; | 892 | int line, retval, boardnum; |
885 | struct board_chan __iomem *bc; | 893 | struct board_chan __iomem *bc; |
@@ -890,6 +898,7 @@ static int pc_open(struct tty_struct *tty, struct file *filp) | |||
890 | return -ENODEV; | 898 | return -ENODEV; |
891 | 899 | ||
892 | ch = &digi_channels[line]; | 900 | ch = &digi_channels[line]; |
901 | port = &ch->port; | ||
893 | boardnum = ch->boardnum; | 902 | boardnum = ch->boardnum; |
894 | 903 | ||
895 | /* Check status of board configured in system. */ | 904 | /* Check status of board configured in system. */ |
@@ -926,22 +935,24 @@ static int pc_open(struct tty_struct *tty, struct file *filp) | |||
926 | return -ENODEV; | 935 | return -ENODEV; |
927 | } | 936 | } |
928 | 937 | ||
929 | spin_lock_irqsave(&epca_lock, flags); | 938 | spin_lock_irqsave(&port->lock, flags); |
930 | /* | 939 | /* |
931 | * Every time a channel is opened, increment a counter. This is | 940 | * Every time a channel is opened, increment a counter. This is |
932 | * necessary because we do not wish to flush and shutdown the channel | 941 | * necessary because we do not wish to flush and shutdown the channel |
933 | * until the last app holding the channel open, closes it. | 942 | * until the last app holding the channel open, closes it. |
934 | */ | 943 | */ |
935 | ch->port.count++; | 944 | port->count++; |
936 | /* | 945 | /* |
937 | * Set a kernel structures pointer to our local channel structure. This | 946 | * Set a kernel structures pointer to our local channel structure. This |
938 | * way we can get to it when passed only a tty struct. | 947 | * way we can get to it when passed only a tty struct. |
939 | */ | 948 | */ |
940 | tty->driver_data = ch; | 949 | tty->driver_data = ch; |
950 | port->tty = tty; | ||
941 | /* | 951 | /* |
942 | * If this is the first time the channel has been opened, initialize | 952 | * If this is the first time the channel has been opened, initialize |
943 | * the tty->termios struct otherwise let pc_close handle it. | 953 | * the tty->termios struct otherwise let pc_close handle it. |
944 | */ | 954 | */ |
955 | spin_lock(&epca_lock); | ||
945 | globalwinon(ch); | 956 | globalwinon(ch); |
946 | ch->statusflags = 0; | 957 | ch->statusflags = 0; |
947 | 958 | ||
@@ -956,16 +967,16 @@ static int pc_open(struct tty_struct *tty, struct file *filp) | |||
956 | writew(head, &bc->rout); | 967 | writew(head, &bc->rout); |
957 | 968 | ||
958 | /* Set the channels associated tty structure */ | 969 | /* Set the channels associated tty structure */ |
959 | ch->port.tty = tty; | ||
960 | 970 | ||
961 | /* | 971 | /* |
962 | * The below routine generally sets up parity, baud, flow control | 972 | * The below routine generally sets up parity, baud, flow control |
963 | * issues, etc.... It effect both control flags and input flags. | 973 | * issues, etc.... It effect both control flags and input flags. |
964 | */ | 974 | */ |
965 | epcaparam(tty, ch); | 975 | epcaparam(tty, ch); |
966 | ch->port.flags |= ASYNC_INITIALIZED; | ||
967 | memoff(ch); | 976 | memoff(ch); |
968 | spin_unlock_irqrestore(&epca_lock, flags); | 977 | spin_unlock(&epca_lock); |
978 | port->flags |= ASYNC_INITIALIZED; | ||
979 | spin_unlock_irqrestore(&port->lock, flags); | ||
969 | 980 | ||
970 | retval = block_til_ready(tty, filp, ch); | 981 | retval = block_til_ready(tty, filp, ch); |
971 | if (retval) | 982 | if (retval) |
@@ -974,13 +985,15 @@ static int pc_open(struct tty_struct *tty, struct file *filp) | |||
974 | * Set this again in case a hangup set it to zero while this open() was | 985 | * Set this again in case a hangup set it to zero while this open() was |
975 | * waiting for the line... | 986 | * waiting for the line... |
976 | */ | 987 | */ |
977 | spin_lock_irqsave(&epca_lock, flags); | 988 | spin_lock_irqsave(&port->lock, flags); |
978 | ch->port.tty = tty; | 989 | port->tty = tty; |
990 | spin_lock(&epca_lock); | ||
979 | globalwinon(ch); | 991 | globalwinon(ch); |
980 | /* Enable Digi Data events */ | 992 | /* Enable Digi Data events */ |
981 | writeb(1, &bc->idata); | 993 | writeb(1, &bc->idata); |
982 | memoff(ch); | 994 | memoff(ch); |
983 | spin_unlock_irqrestore(&epca_lock, flags); | 995 | spin_unlock(&epca_lock); |
996 | spin_unlock_irqrestore(&port->lock, flags); | ||
984 | return 0; | 997 | return 0; |
985 | } | 998 | } |
986 | 999 | ||