Commit d1c815e549ff40f9e9db65654855118e6bdff6a4

Authored by Alan Cox
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

... ... @@ -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