| .. | .. |
|---|
| 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); |
|---|