Skip to content

Commit

Permalink
wimax/i2400m: handle USB stalls
Browse files Browse the repository at this point in the history
When the device stalls, clear it and retry; if it keeps failing too
often, reset the device.

This specially happens when running on virtual machines; the real
hardware doesn't seem to trip on stalls too much, except for a few
reports in the mailing list (still to be confirmed this is the cause,
although it seems likely.

NOTE: it is not clear if the URB has to be resubmitted fully or start
only at the offset of the first transaction sent. Can't find
documentation to clarify one end or the other.

Tests that just resubmit the whole URB seemed to work in my
environment.

Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
  • Loading branch information
Inaky Perez-Gonzalez committed Nov 3, 2009
1 parent fae9221 commit faf5716
Show file tree
Hide file tree
Showing 4 changed files with 116 additions and 6 deletions.
22 changes: 22 additions & 0 deletions drivers/net/wimax/i2400m/usb-fw.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,28 @@ ssize_t i2400mu_tx_bulk_out(struct i2400mu *i2400mu, void *buf, size_t buf_size)
}
result = len;
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
/* fallthrough */
} else {
usb_clear_halt(i2400mu->usb_dev, pipe);
msleep(10); /* give the device some time */
goto retry;
}
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
Expand Down
21 changes: 21 additions & 0 deletions drivers/net/wimax/i2400m/usb-rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,26 @@ struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)
goto retry; /* ZLP, just resubmit */
skb_put(rx_skb, read_size);
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
goto do_reset;
}
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
msleep(10); /* give the device some time */
goto retry;
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
Expand Down Expand Up @@ -283,6 +303,7 @@ struct sk_buff *i2400mu_rx(struct i2400mu *i2400mu, struct sk_buff *rx_skb)
error_reset:
dev_err(dev, "RX: maximum errors in URB exceeded; "
"resetting device\n");
do_reset:
usb_queue_reset_device(i2400mu->usb_iface);
rx_skb = ERR_PTR(result);
goto out;
Expand Down
22 changes: 22 additions & 0 deletions drivers/net/wimax/i2400m/usb-tx.c
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,28 @@ int i2400mu_tx(struct i2400mu *i2400mu, struct i2400m_msg_hdr *tx_msg,
result = -EIO;
}
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "BM-CMD: too many stalls in "
"URB; resetting device\n");
usb_queue_reset_device(i2400mu->usb_iface);
/* fallthrough */
} else {
usb_clear_halt(i2400mu->usb_dev, usb_pipe);
msleep(10); /* give the device some time */
goto retry;
}
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
Expand Down
57 changes: 51 additions & 6 deletions drivers/net/wimax/i2400m/usb.c
Original file line number Diff line number Diff line change
Expand Up @@ -172,14 +172,59 @@ int __i2400mu_send_barker(struct i2400mu *i2400mu,
epd = usb_get_epd(i2400mu->usb_iface, endpoint);
pipe = usb_sndbulkpipe(i2400mu->usb_dev, epd->bEndpointAddress);
memcpy(buffer, barker, barker_size);
retry:
ret = usb_bulk_msg(i2400mu->usb_dev, pipe, buffer, barker_size,
&actual_len, 200);
if (ret < 0) {
if (ret != -EINVAL)
dev_err(dev, "E: barker error: %d\n", ret);
} else if (actual_len != barker_size) {
dev_err(dev, "E: only %d bytes transmitted\n", actual_len);
ret = -EIO;
switch (ret) {
case 0:
if (actual_len != barker_size) { /* Too short? drop it */
dev_err(dev, "E: %s: short write (%d B vs %zu "
"expected)\n",
__func__, actual_len, barker_size);
ret = -EIO;
}
break;
case -EPIPE:
/*
* Stall -- maybe the device is choking with our
* requests. Clear it and give it some time. If they
* happen to often, it might be another symptom, so we
* reset.
*
* No error handling for usb_clear_halt(0; if it
* works, the retry works; if it fails, this switch
* does the error handling for us.
*/
if (edc_inc(&i2400mu->urb_edc,
10 * EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "E: %s: too many stalls in "
"URB; resetting device\n", __func__);
usb_queue_reset_device(i2400mu->usb_iface);
/* fallthrough */
} else {
usb_clear_halt(i2400mu->usb_dev, pipe);
msleep(10); /* give the device some time */
goto retry;
}
case -EINVAL: /* while removing driver */
case -ENODEV: /* dev disconnect ... */
case -ENOENT: /* just ignore it */
case -ESHUTDOWN: /* and exit */
case -ECONNRESET:
ret = -ESHUTDOWN;
break;
default: /* Some error? */
if (edc_inc(&i2400mu->urb_edc,
EDC_MAX_ERRORS, EDC_ERROR_TIMEFRAME)) {
dev_err(dev, "E: %s: maximum errors in URB "
"exceeded; resetting device\n",
__func__);
usb_queue_reset_device(i2400mu->usb_iface);
} else {
dev_warn(dev, "W: %s: cannot send URB: %d\n",
__func__, ret);
goto retry;
}
}
kfree(buffer);
error_kzalloc:
Expand Down

0 comments on commit faf5716

Please sign in to comment.