diff options
Diffstat (limited to 'drivers/usb/class/cdc-acm.c')
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 63 |
1 files changed, 32 insertions, 31 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) |