aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/class
diff options
context:
space:
mode:
authorBjørn Mork <bjorn@mork.no>2013-12-20 08:07:24 -0500
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2013-12-20 15:06:46 -0500
commit8dd5cd5395b90070d98149d0a94e5981a74cd2ec (patch)
treee4c61bc41c7b2c580fedc55056cfa74939500958 /drivers/usb/class
parentbee53637e5836a7cc642ca036e44bd77caf7bc35 (diff)
usb: cdc-wdm: avoid hanging on zero length reads
commit 73e06865ead1 ("USB: cdc-wdm: support back-to-back USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications") implemented queued response handling. This added a new requirement: The read urb must be resubmitted every time we clear the WDM_READ flag if the response counter indicates that the device is waiting for a read. Fix by factoring out the code handling the WMD_READ clearing and possible urb submission, calling it everywhere we clear the flag. Without this fix, the driver ends up in a state where the read urb is inactive, but the response counter is positive after a zero length read. This prevents the read urb from ever being submitted again and the driver appears to be hanging. Fixes: 73e06865ead1 ("USB: cdc-wdm: support back-to-back USB_CDC_NOTIFY_RESPONSE_AVAILABLE notifications") Cc: Greg Suarez <gsuarez@smithmicro.com> Signed-off-by: Bjørn Mork <bjorn@mork.no> Cc: stable <stable@vger.kernel.org> # 3.13 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/class')
-rw-r--r--drivers/usb/class/cdc-wdm.c70
1 files changed, 38 insertions, 32 deletions
diff --git a/drivers/usb/class/cdc-wdm.c b/drivers/usb/class/cdc-wdm.c
index 4d387596f3f0..750afa9774b4 100644
--- a/drivers/usb/class/cdc-wdm.c
+++ b/drivers/usb/class/cdc-wdm.c
@@ -432,6 +432,38 @@ outnl:
432 return rv < 0 ? rv : count; 432 return rv < 0 ? rv : count;
433} 433}
434 434
435/*
436 * clear WDM_READ flag and possibly submit the read urb if resp_count
437 * is non-zero.
438 *
439 * Called with desc->iuspin locked
440 */
441static int clear_wdm_read_flag(struct wdm_device *desc)
442{
443 int rv = 0;
444
445 clear_bit(WDM_READ, &desc->flags);
446
447 /* submit read urb only if the device is waiting for it */
448 if (!--desc->resp_count)
449 goto out;
450
451 set_bit(WDM_RESPONDING, &desc->flags);
452 spin_unlock_irq(&desc->iuspin);
453 rv = usb_submit_urb(desc->response, GFP_KERNEL);
454 spin_lock_irq(&desc->iuspin);
455 if (rv) {
456 dev_err(&desc->intf->dev,
457 "usb_submit_urb failed with result %d\n", rv);
458
459 /* make sure the next notification trigger a submit */
460 clear_bit(WDM_RESPONDING, &desc->flags);
461 desc->resp_count = 0;
462 }
463out:
464 return rv;
465}
466
435static ssize_t wdm_read 467static ssize_t wdm_read
436(struct file *file, char __user *buffer, size_t count, loff_t *ppos) 468(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
437{ 469{
@@ -503,8 +535,10 @@ retry:
503 535
504 if (!desc->reslength) { /* zero length read */ 536 if (!desc->reslength) { /* zero length read */
505 dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__); 537 dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
506 clear_bit(WDM_READ, &desc->flags); 538 rv = clear_wdm_read_flag(desc);
507 spin_unlock_irq(&desc->iuspin); 539 spin_unlock_irq(&desc->iuspin);
540 if (rv < 0)
541 goto err;
508 goto retry; 542 goto retry;
509 } 543 }
510 cntr = desc->length; 544 cntr = desc->length;
@@ -526,37 +560,9 @@ retry:
526 560
527 desc->length -= cntr; 561 desc->length -= cntr;
528 /* in case we had outstanding data */ 562 /* in case we had outstanding data */
529 if (!desc->length) { 563 if (!desc->length)
530 clear_bit(WDM_READ, &desc->flags); 564 clear_wdm_read_flag(desc);
531 565 spin_unlock_irq(&desc->iuspin);
532 if (--desc->resp_count) {
533 set_bit(WDM_RESPONDING, &desc->flags);
534 spin_unlock_irq(&desc->iuspin);
535
536 rv = usb_submit_urb(desc->response, GFP_KERNEL);
537 if (rv) {
538 dev_err(&desc->intf->dev,
539 "%s: usb_submit_urb failed with result %d\n",
540 __func__, rv);
541 spin_lock_irq(&desc->iuspin);
542 clear_bit(WDM_RESPONDING, &desc->flags);
543 spin_unlock_irq(&desc->iuspin);
544
545 if (rv == -ENOMEM) {
546 rv = schedule_work(&desc->rxwork);
547 if (rv)
548 dev_err(&desc->intf->dev, "Cannot schedule work\n");
549 } else {
550 spin_lock_irq(&desc->iuspin);
551 desc->resp_count = 0;
552 spin_unlock_irq(&desc->iuspin);
553 }
554 }
555 } else
556 spin_unlock_irq(&desc->iuspin);
557 } else
558 spin_unlock_irq(&desc->iuspin);
559
560 rv = cntr; 566 rv = cntr;
561 567
562err: 568err: