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