usb: cdc-wdm: avoid hanging on zero length reads
commit73e06865ea
("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:73e06865ea
("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>
This commit is contained in:
parent
bee53637e5
commit
8dd5cd5395
|
@ -432,6 +432,38 @@ outnl:
|
||||||
return rv < 0 ? rv : count;
|
return rv < 0 ? rv : count;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* clear WDM_READ flag and possibly submit the read urb if resp_count
|
||||||
|
* is non-zero.
|
||||||
|
*
|
||||||
|
* Called with desc->iuspin locked
|
||||||
|
*/
|
||||||
|
static int clear_wdm_read_flag(struct wdm_device *desc)
|
||||||
|
{
|
||||||
|
int rv = 0;
|
||||||
|
|
||||||
|
clear_bit(WDM_READ, &desc->flags);
|
||||||
|
|
||||||
|
/* submit read urb only if the device is waiting for it */
|
||||||
|
if (!--desc->resp_count)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
set_bit(WDM_RESPONDING, &desc->flags);
|
||||||
|
spin_unlock_irq(&desc->iuspin);
|
||||||
|
rv = usb_submit_urb(desc->response, GFP_KERNEL);
|
||||||
|
spin_lock_irq(&desc->iuspin);
|
||||||
|
if (rv) {
|
||||||
|
dev_err(&desc->intf->dev,
|
||||||
|
"usb_submit_urb failed with result %d\n", rv);
|
||||||
|
|
||||||
|
/* make sure the next notification trigger a submit */
|
||||||
|
clear_bit(WDM_RESPONDING, &desc->flags);
|
||||||
|
desc->resp_count = 0;
|
||||||
|
}
|
||||||
|
out:
|
||||||
|
return rv;
|
||||||
|
}
|
||||||
|
|
||||||
static ssize_t wdm_read
|
static ssize_t wdm_read
|
||||||
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
|
||||||
{
|
{
|
||||||
|
@ -503,8 +535,10 @@ retry:
|
||||||
|
|
||||||
if (!desc->reslength) { /* zero length read */
|
if (!desc->reslength) { /* zero length read */
|
||||||
dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
|
dev_dbg(&desc->intf->dev, "%s: zero length - clearing WDM_READ\n", __func__);
|
||||||
clear_bit(WDM_READ, &desc->flags);
|
rv = clear_wdm_read_flag(desc);
|
||||||
spin_unlock_irq(&desc->iuspin);
|
spin_unlock_irq(&desc->iuspin);
|
||||||
|
if (rv < 0)
|
||||||
|
goto err;
|
||||||
goto retry;
|
goto retry;
|
||||||
}
|
}
|
||||||
cntr = desc->length;
|
cntr = desc->length;
|
||||||
|
@ -526,37 +560,9 @@ retry:
|
||||||
|
|
||||||
desc->length -= cntr;
|
desc->length -= cntr;
|
||||||
/* in case we had outstanding data */
|
/* in case we had outstanding data */
|
||||||
if (!desc->length) {
|
if (!desc->length)
|
||||||
clear_bit(WDM_READ, &desc->flags);
|
clear_wdm_read_flag(desc);
|
||||||
|
spin_unlock_irq(&desc->iuspin);
|
||||||
if (--desc->resp_count) {
|
|
||||||
set_bit(WDM_RESPONDING, &desc->flags);
|
|
||||||
spin_unlock_irq(&desc->iuspin);
|
|
||||||
|
|
||||||
rv = usb_submit_urb(desc->response, GFP_KERNEL);
|
|
||||||
if (rv) {
|
|
||||||
dev_err(&desc->intf->dev,
|
|
||||||
"%s: usb_submit_urb failed with result %d\n",
|
|
||||||
__func__, rv);
|
|
||||||
spin_lock_irq(&desc->iuspin);
|
|
||||||
clear_bit(WDM_RESPONDING, &desc->flags);
|
|
||||||
spin_unlock_irq(&desc->iuspin);
|
|
||||||
|
|
||||||
if (rv == -ENOMEM) {
|
|
||||||
rv = schedule_work(&desc->rxwork);
|
|
||||||
if (rv)
|
|
||||||
dev_err(&desc->intf->dev, "Cannot schedule work\n");
|
|
||||||
} else {
|
|
||||||
spin_lock_irq(&desc->iuspin);
|
|
||||||
desc->resp_count = 0;
|
|
||||||
spin_unlock_irq(&desc->iuspin);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
spin_unlock_irq(&desc->iuspin);
|
|
||||||
} else
|
|
||||||
spin_unlock_irq(&desc->iuspin);
|
|
||||||
|
|
||||||
rv = cntr;
|
rv = cntr;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
|
Loading…
Reference in New Issue