diff options
author | Oliver Neukum <oneukum@suse.de> | 2015-03-20 04:24:24 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2015-03-26 05:50:52 -0400 |
commit | 36e59e0d70d6150e7a2155c54612ea875e88ce8d (patch) | |
tree | 210a79c37d9a262b1d6e8f37db2429c1c21edef7 | |
parent | c0ab6bb0597363532f178f3cd7b7fb527eef39e2 (diff) |
cdc-acm: fix race between callback and unthrottle
Abn URB may be may marked free only after the buffer has been
processed or there is a small window during which it could
be submitted on another CPU and overwrite an unprocessed buffer
Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r-- | drivers/usb/class/cdc-acm.c | 12 |
1 files changed, 10 insertions, 2 deletions
diff --git a/drivers/usb/class/cdc-acm.c b/drivers/usb/class/cdc-acm.c index 683617714e7c..58241ec1e91d 100644 --- a/drivers/usb/class/cdc-acm.c +++ b/drivers/usb/class/cdc-acm.c | |||
@@ -417,25 +417,33 @@ static void acm_read_bulk_callback(struct urb *urb) | |||
417 | struct acm_rb *rb = urb->context; | 417 | struct acm_rb *rb = urb->context; |
418 | struct acm *acm = rb->instance; | 418 | struct acm *acm = rb->instance; |
419 | unsigned long flags; | 419 | unsigned long flags; |
420 | int status = urb->status; | ||
420 | 421 | ||
421 | dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, | 422 | dev_vdbg(&acm->data->dev, "%s - urb %d, len %d\n", __func__, |
422 | rb->index, urb->actual_length); | 423 | rb->index, urb->actual_length); |
423 | set_bit(rb->index, &acm->read_urbs_free); | ||
424 | 424 | ||
425 | if (!acm->dev) { | 425 | if (!acm->dev) { |
426 | set_bit(rb->index, &acm->read_urbs_free); | ||
426 | dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); | 427 | dev_dbg(&acm->data->dev, "%s - disconnected\n", __func__); |
427 | return; | 428 | return; |
428 | } | 429 | } |
429 | 430 | ||
430 | if (urb->status) { | 431 | if (urb->status) { |
432 | set_bit(rb->index, &acm->read_urbs_free); | ||
431 | dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", | 433 | dev_dbg(&acm->data->dev, "%s - non-zero urb status: %d\n", |
432 | __func__, urb->status); | 434 | __func__, status); |
433 | return; | 435 | return; |
434 | } | 436 | } |
435 | 437 | ||
436 | usb_mark_last_busy(acm->dev); | 438 | usb_mark_last_busy(acm->dev); |
437 | 439 | ||
438 | acm_process_read_urb(acm, urb); | 440 | acm_process_read_urb(acm, urb); |
441 | /* | ||
442 | * Unthrottle may run on another CPU which needs to see events | ||
443 | * in the same order. Submission has an implict barrier | ||
444 | */ | ||
445 | smp_mb__before_atomic(); | ||
446 | set_bit(rb->index, &acm->read_urbs_free); | ||
439 | 447 | ||
440 | /* throttle device if requested by tty */ | 448 | /* throttle device if requested by tty */ |
441 | spin_lock_irqsave(&acm->read_lock, flags); | 449 | spin_lock_irqsave(&acm->read_lock, flags); |