.. | .. |
---|
102 | 102 | |
---|
103 | 103 | struct gserial *port_usb; |
---|
104 | 104 | |
---|
105 | | - bool openclose; /* open/close in progress */ |
---|
106 | 105 | u8 port_num; |
---|
107 | 106 | |
---|
108 | 107 | struct list_head read_pool; |
---|
.. | .. |
---|
595 | 594 | { |
---|
596 | 595 | int port_num = tty->index; |
---|
597 | 596 | struct gs_port *port; |
---|
598 | | - int status; |
---|
| 597 | + int status = 0; |
---|
599 | 598 | |
---|
600 | | - do { |
---|
601 | | - mutex_lock(&ports[port_num].lock); |
---|
602 | | - port = ports[port_num].port; |
---|
603 | | - if (!port) |
---|
604 | | - status = -ENODEV; |
---|
605 | | - else { |
---|
606 | | - spin_lock_irq(&port->port_lock); |
---|
| 599 | + mutex_lock(&ports[port_num].lock); |
---|
| 600 | + port = ports[port_num].port; |
---|
| 601 | + if (!port) { |
---|
| 602 | + status = -ENODEV; |
---|
| 603 | + goto out; |
---|
| 604 | + } |
---|
607 | 605 | |
---|
608 | | - /* already open? Great. */ |
---|
609 | | - if (port->port.count) { |
---|
610 | | - status = 0; |
---|
611 | | - port->port.count++; |
---|
612 | | - |
---|
613 | | - /* currently opening/closing? wait ... */ |
---|
614 | | - } else if (port->openclose) { |
---|
615 | | - status = -EBUSY; |
---|
616 | | - |
---|
617 | | - /* ... else we do the work */ |
---|
618 | | - } else { |
---|
619 | | - status = -EAGAIN; |
---|
620 | | - port->openclose = true; |
---|
621 | | - } |
---|
622 | | - spin_unlock_irq(&port->port_lock); |
---|
623 | | - } |
---|
624 | | - mutex_unlock(&ports[port_num].lock); |
---|
625 | | - |
---|
626 | | - switch (status) { |
---|
627 | | - default: |
---|
628 | | - /* fully handled */ |
---|
629 | | - return status; |
---|
630 | | - case -EAGAIN: |
---|
631 | | - /* must do the work */ |
---|
632 | | - break; |
---|
633 | | - case -EBUSY: |
---|
634 | | - /* wait for EAGAIN task to finish */ |
---|
635 | | - msleep(1); |
---|
636 | | - /* REVISIT could have a waitchannel here, if |
---|
637 | | - * concurrent open performance is important |
---|
638 | | - */ |
---|
639 | | - break; |
---|
640 | | - } |
---|
641 | | - } while (status != -EAGAIN); |
---|
642 | | - |
---|
643 | | - /* Do the "real open" */ |
---|
644 | 606 | spin_lock_irq(&port->port_lock); |
---|
645 | 607 | |
---|
646 | 608 | /* allocate circular buffer on first open */ |
---|
647 | 609 | if (!kfifo_initialized(&port->port_write_buf)) { |
---|
648 | 610 | |
---|
649 | 611 | spin_unlock_irq(&port->port_lock); |
---|
| 612 | + |
---|
| 613 | + /* |
---|
| 614 | + * portmaster's mutex still protects from simultaneous open(), |
---|
| 615 | + * and close() can't happen, yet. |
---|
| 616 | + */ |
---|
| 617 | + |
---|
650 | 618 | status = kfifo_alloc(&port->port_write_buf, |
---|
651 | 619 | WRITE_BUF_SIZE, GFP_KERNEL); |
---|
652 | | - spin_lock_irq(&port->port_lock); |
---|
653 | | - |
---|
654 | 620 | if (status) { |
---|
655 | 621 | pr_debug("gs_open: ttyGS%d (%p,%p) no buffer\n", |
---|
656 | | - port->port_num, tty, file); |
---|
657 | | - port->openclose = false; |
---|
658 | | - goto exit_unlock_port; |
---|
| 622 | + port_num, tty, file); |
---|
| 623 | + goto out; |
---|
659 | 624 | } |
---|
| 625 | + |
---|
| 626 | + spin_lock_irq(&port->port_lock); |
---|
660 | 627 | } |
---|
661 | 628 | |
---|
662 | | - /* REVISIT if REMOVED (ports[].port NULL), abort the open |
---|
663 | | - * to let rmmod work faster (but this way isn't wrong). |
---|
664 | | - */ |
---|
665 | | - |
---|
666 | | - /* REVISIT maybe wait for "carrier detect" */ |
---|
| 629 | + /* already open? Great. */ |
---|
| 630 | + if (port->port.count++) |
---|
| 631 | + goto exit_unlock_port; |
---|
667 | 632 | |
---|
668 | 633 | tty->driver_data = port; |
---|
669 | 634 | port->port.tty = tty; |
---|
670 | | - |
---|
671 | | - port->port.count = 1; |
---|
672 | | - port->openclose = false; |
---|
673 | 635 | |
---|
674 | 636 | /* if connected, start the I/O stream */ |
---|
675 | 637 | if (port->port_usb) { |
---|
.. | .. |
---|
684 | 646 | |
---|
685 | 647 | pr_debug("gs_open: ttyGS%d (%p,%p)\n", port->port_num, tty, file); |
---|
686 | 648 | |
---|
687 | | - status = 0; |
---|
688 | | - |
---|
689 | 649 | exit_unlock_port: |
---|
690 | 650 | spin_unlock_irq(&port->port_lock); |
---|
| 651 | +out: |
---|
| 652 | + mutex_unlock(&ports[port_num].lock); |
---|
691 | 653 | return status; |
---|
692 | 654 | } |
---|
693 | 655 | |
---|
694 | | -static int gs_writes_finished(struct gs_port *p) |
---|
| 656 | +static int gs_close_flush_done(struct gs_port *p) |
---|
695 | 657 | { |
---|
696 | 658 | int cond; |
---|
697 | 659 | |
---|
698 | | - /* return true on disconnect or empty buffer */ |
---|
| 660 | + /* return true on disconnect or empty buffer or if raced with open() */ |
---|
699 | 661 | spin_lock_irq(&p->port_lock); |
---|
700 | | - cond = (p->port_usb == NULL) || !kfifo_len(&p->port_write_buf); |
---|
| 662 | + cond = p->port_usb == NULL || !kfifo_len(&p->port_write_buf) || |
---|
| 663 | + p->port.count > 1; |
---|
701 | 664 | spin_unlock_irq(&p->port_lock); |
---|
702 | 665 | |
---|
703 | 666 | return cond; |
---|
.. | .. |
---|
711 | 674 | spin_lock_irq(&port->port_lock); |
---|
712 | 675 | |
---|
713 | 676 | if (port->port.count != 1) { |
---|
| 677 | +raced_with_open: |
---|
714 | 678 | if (port->port.count == 0) |
---|
715 | 679 | WARN_ON(1); |
---|
716 | 680 | else |
---|
.. | .. |
---|
719 | 683 | } |
---|
720 | 684 | |
---|
721 | 685 | pr_debug("gs_close: ttyGS%d (%p,%p) ...\n", port->port_num, tty, file); |
---|
722 | | - |
---|
723 | | - /* mark port as closing but in use; we can drop port lock |
---|
724 | | - * and sleep if necessary |
---|
725 | | - */ |
---|
726 | | - port->openclose = true; |
---|
727 | | - port->port.count = 0; |
---|
728 | 686 | |
---|
729 | 687 | gser = port->port_usb; |
---|
730 | 688 | if (gser && gser->disconnect) |
---|
.. | .. |
---|
736 | 694 | if (kfifo_len(&port->port_write_buf) > 0 && gser) { |
---|
737 | 695 | spin_unlock_irq(&port->port_lock); |
---|
738 | 696 | wait_event_interruptible_timeout(port->drain_wait, |
---|
739 | | - gs_writes_finished(port), |
---|
| 697 | + gs_close_flush_done(port), |
---|
740 | 698 | GS_CLOSE_TIMEOUT * HZ); |
---|
741 | 699 | spin_lock_irq(&port->port_lock); |
---|
| 700 | + |
---|
| 701 | + if (port->port.count != 1) |
---|
| 702 | + goto raced_with_open; |
---|
| 703 | + |
---|
742 | 704 | gser = port->port_usb; |
---|
743 | 705 | } |
---|
744 | 706 | |
---|
.. | .. |
---|
751 | 713 | else |
---|
752 | 714 | kfifo_reset(&port->port_write_buf); |
---|
753 | 715 | |
---|
| 716 | + port->port.count = 0; |
---|
754 | 717 | port->port.tty = NULL; |
---|
755 | | - |
---|
756 | | - port->openclose = false; |
---|
757 | 718 | |
---|
758 | 719 | pr_debug("gs_close: ttyGS%d (%p,%p) done!\n", |
---|
759 | 720 | port->port_num, tty, file); |
---|
.. | .. |
---|
1181 | 1142 | int cond; |
---|
1182 | 1143 | |
---|
1183 | 1144 | spin_lock_irq(&port->port_lock); |
---|
1184 | | - cond = (port->port.count == 0) && !port->openclose; |
---|
| 1145 | + cond = port->port.count == 0; |
---|
1185 | 1146 | spin_unlock_irq(&port->port_lock); |
---|
| 1147 | + |
---|
1186 | 1148 | return cond; |
---|
1187 | 1149 | } |
---|
1188 | 1150 | |
---|
.. | .. |
---|
1375 | 1337 | |
---|
1376 | 1338 | port->port_usb = NULL; |
---|
1377 | 1339 | gser->ioport = NULL; |
---|
1378 | | - if (port->port.count > 0 || port->openclose) { |
---|
| 1340 | + if (port->port.count > 0) { |
---|
1379 | 1341 | wake_up_interruptible(&port->drain_wait); |
---|
1380 | 1342 | if (port->port.tty) |
---|
1381 | 1343 | tty_hangup(port->port.tty); |
---|
.. | .. |
---|
1388 | 1350 | |
---|
1389 | 1351 | /* finally, free any unused/unusable I/O buffers */ |
---|
1390 | 1352 | spin_lock_irqsave(&port->port_lock, flags); |
---|
1391 | | - if (port->port.count == 0 && !port->openclose) |
---|
| 1353 | + if (port->port.count == 0) |
---|
1392 | 1354 | kfifo_free(&port->port_write_buf); |
---|
1393 | 1355 | gs_free_requests(gser->out, &port->read_pool, NULL); |
---|
1394 | 1356 | gs_free_requests(gser->out, &port->read_queue, NULL); |
---|