diff options
Diffstat (limited to 'drivers/usb/class')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 63 | ||||
-rw-r--r-- | drivers/usb/class/cdc-acm.h | 3 |
2 files changed, 33 insertions, 33 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index ec666eb4b7b4..183b41753c98 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -468,14 +468,13 @@ static void acm_read_bulk_callback(struct urb *urb) | |||
468 | { | 468 | { |
469 | struct acm_rb *rb = urb->context; | 469 | struct acm_rb *rb = urb->context; |
470 | struct acm *acm = rb->instance; | 470 | struct acm *acm = rb->instance; |
471 | unsigned long flags; | ||
472 | int status = urb->status; | 471 | int status = urb->status; |
472 | bool stopped = false; | ||
473 | bool stalled = false; | ||
473 | 474 | ||
474 | dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", | 475 | dev_vdbg(&acm->data->dev, "got urb %d, len %d, status %d\n", |
475 | rb->index, urb->actual_length, status); | 476 | rb->index, urb->actual_length, status); |
476 | 477 | ||
477 | set_bit(rb->index, &acm->read_urbs_free); | ||
478 | |||
479 | if (!acm->dev) { | 478 | if (!acm->dev) { |
480 | dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); | 479 | dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); |
481 | return; | 480 | return; |
@@ -488,15 +487,16 @@ static void acm_read_bulk_callback(struct urb *urb) | |||
488 | break; | 487 | break; |
489 | case -EPIPE: | 488 | case -EPIPE: |
490 | set_bit(EVENT_RX_STALL, &acm->flags); | 489 | set_bit(EVENT_RX_STALL, &acm->flags); |
491 | schedule_work(&acm->work); | 490 | stalled = true; |
492 | return; | 491 | break; |
493 | case -ENOENT: | 492 | case -ENOENT: |
494 | case -ECONNRESET: | 493 | case -ECONNRESET: |
495 | case -ESHUTDOWN: | 494 | case -ESHUTDOWN: |
496 | dev_dbg(&acm->data->dev, | 495 | dev_dbg(&acm->data->dev, |
497 | "%s - urb shutting down with status: %d\n", | 496 | "%s - urb shutting down with status: %d\n", |
498 | __func__, status); | 497 | __func__, status); |
499 | return; | 498 | stopped = true; |
499 | break; | ||
500 | default: | 500 | default: |
501 | dev_dbg(&acm->data->dev, | 501 | dev_dbg(&acm->data->dev, |
502 | "%s - nonzero urb status received: %d\n", | 502 | "%s - nonzero urb status received: %d\n", |
@@ -505,20 +505,29 @@ static void acm_read_bulk_callback(struct urb *urb) | |||
505 | } | 505 | } |
506 | 506 | ||
507 | /* | 507 | /* |
508 | * Unthrottle may run on another CPU which needs to see events | 508 | * Make sure URB processing is done before marking as free to avoid |
509 | * in the same order. Submission has an implict barrier | 509 | * racing with unthrottle() on another CPU. Matches the barriers |
510 | * implied by the test_and_clear_bit() in acm_submit_read_urb(). | ||
510 | */ | 511 | */ |
511 | smp_mb__before_atomic(); | 512 | smp_mb__before_atomic(); |
513 | set_bit(rb->index, &acm->read_urbs_free); | ||
514 | /* | ||
515 | * Make sure URB is marked as free before checking the throttled flag | ||
516 | * to avoid racing with unthrottle() on another CPU. Matches the | ||
517 | * smp_mb() in unthrottle(). | ||
518 | */ | ||
519 | smp_mb__after_atomic(); | ||
512 | 520 | ||
513 | /* throttle device if requested by tty */ | 521 | if (stopped || stalled) { |
514 | spin_lock_irqsave(&acm->read_lock, flags); | 522 | if (stalled) |
515 | acm->throttled = acm->throttle_req; | 523 | schedule_work(&acm->work); |
516 | if (!acm->throttled) { | 524 | return; |
517 | spin_unlock_irqrestore(&acm->read_lock, flags); | ||
518 | acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); | ||
519 | } else { | ||
520 | spin_unlock_irqrestore(&acm->read_lock, flags); | ||
521 | } | 525 | } |
526 | |||
527 | if (test_bit(ACM_THROTTLED, &acm->flags)) | ||
528 | return; | ||
529 | |||
530 | acm_submit_read_urb(acm, rb->index, GFP_ATOMIC); | ||
522 | } | 531 | } |
523 | 532 | ||
524 | /* data interface wrote those outgoing bytes */ | 533 | /* data interface wrote those outgoing bytes */ |
@@ -655,10 +664,7 @@ static int acm_port_activate(struct tty_port *port, struct tty_struct *tty) | |||
655 | /* | 664 | /* |
656 | * Unthrottle device in case the TTY was closed while throttled. | 665 | * Unthrottle device in case the TTY was closed while throttled. |
657 | */ | 666 | */ |
658 | spin_lock_irq(&acm->read_lock); | 667 | clear_bit(ACM_THROTTLED, &acm->flags); |
659 | acm->throttled = 0; | ||
660 | acm->throttle_req = 0; | ||
661 | spin_unlock_irq(&acm->read_lock); | ||
662 | 668 | ||
663 | retval = acm_submit_read_urbs(acm, GFP_KERNEL); | 669 | retval = acm_submit_read_urbs(acm, GFP_KERNEL); |
664 | if (retval) | 670 | if (retval) |
@@ -826,24 +832,19 @@ static void acm_tty_throttle(struct tty_struct *tty) | |||
826 | { | 832 | { |
827 | struct acm *acm = tty->driver_data; | 833 | struct acm *acm = tty->driver_data; |
828 | 834 | ||
829 | spin_lock_irq(&acm->read_lock); | 835 | set_bit(ACM_THROTTLED, &acm->flags); |
830 | acm->throttle_req = 1; | ||
831 | spin_unlock_irq(&acm->read_lock); | ||
832 | } | 836 | } |
833 | 837 | ||
834 | static void acm_tty_unthrottle(struct tty_struct *tty) | 838 | static void acm_tty_unthrottle(struct tty_struct *tty) |
835 | { | 839 | { |
836 | struct acm *acm = tty->driver_data; | 840 | struct acm *acm = tty->driver_data; |
837 | unsigned int was_throttled; | ||
838 | 841 | ||
839 | spin_lock_irq(&acm->read_lock); | 842 | clear_bit(ACM_THROTTLED, &acm->flags); |
840 | was_throttled = acm->throttled; | 843 | |
841 | acm->throttled = 0; | 844 | /* Matches the smp_mb__after_atomic() in acm_read_bulk_callback(). */ |
842 | acm->throttle_req = 0; | 845 | smp_mb(); |
843 | spin_unlock_irq(&acm->read_lock); | ||
844 | 846 | ||
845 | if (was_throttled) | 847 | acm_submit_read_urbs(acm, GFP_KERNEL); |
846 | acm_submit_read_urbs(acm, GFP_KERNEL); | ||
847 | } | 848 | } |
848 | 849 | ||
849 | static int acm_tty_break_ctl(struct tty_struct *tty, int state) | 850 | static int acm_tty_break_ctl(struct tty_struct *tty, int state) |
diff --git a/drivers/usb/class/cdc-acm.h b/drivers/usb/class/cdc-acm.h index 515aad0847ee..ca1c026382c2 100644 --- a/drivers/usb/class/cdc-acm.h +++ b/drivers/usb/class/cdc-acm.h | |||
@@ -108,6 +108,7 @@ struct acm { | |||
108 | unsigned long flags; | 108 | unsigned long flags; |
109 | # define EVENT_TTY_WAKEUP 0 | 109 | # define EVENT_TTY_WAKEUP 0 |
110 | # define EVENT_RX_STALL 1 | 110 | # define EVENT_RX_STALL 1 |
111 | # define ACM_THROTTLED 2 | ||
111 | struct usb_cdc_line_coding line; /* bits, stop, parity */ | 112 | struct usb_cdc_line_coding line; /* bits, stop, parity */ |
112 | struct work_struct work; /* work queue entry for line discipline waking up */ | 113 | struct work_struct work; /* work queue entry for line discipline waking up */ |
113 | unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ | 114 | unsigned int ctrlin; /* input control lines (DCD, DSR, RI, break, overruns) */ |
@@ -122,8 +123,6 @@ struct acm { | |||
122 | unsigned int ctrl_caps; /* control capabilities from the class specific header */ | 123 | unsigned int ctrl_caps; /* control capabilities from the class specific header */ |
123 | unsigned int susp_count; /* number of suspended interfaces */ | 124 | unsigned int susp_count; /* number of suspended interfaces */ |
124 | unsigned int combined_interfaces:1; /* control and data collapsed */ | 125 | unsigned int combined_interfaces:1; /* control and data collapsed */ |
125 | unsigned int throttled:1; /* actually throttled */ | ||
126 | unsigned int throttle_req:1; /* throttle requested */ | ||
127 | u8 bInterval; | 126 | u8 bInterval; |
128 | struct usb_anchor delayed; /* writes queued for a device about to be woken */ | 127 | struct usb_anchor delayed; /* writes queued for a device about to be woken */ |
129 | unsigned long quirks; | 128 | unsigned long quirks; |