summaryrefslogtreecommitdiff
path: root/sys/dev/usb/usbdi.c
diff options
context:
space:
mode:
authorMartin Pieuchot <mpi@cvs.openbsd.org>2013-09-24 09:01:42 +0000
committerMartin Pieuchot <mpi@cvs.openbsd.org>2013-09-24 09:01:42 +0000
commit1e00e83898179e4997632256fc4767062e7ff3a8 (patch)
treedfb456cb9f70d46843ced4682a7028be77035aa3 /sys/dev/usb/usbdi.c
parente45e1a140aee7cc92b359b5521ad1b80eaf8c60e (diff)
Stop generating traffic for a given endpoint when a transfer reported
an I/O error. When such thing happens, the device is more likely to be already gone or non responding, but the task responsible for aborting the pipe has not been executed. Fix a problem where the ehci(4) controller would vomit^Wprint a lot of "ehci_idone:" messages in loop leaving the machine unusable, initially reported by ajacoutot@ and later by RD Thrush. Fix tested by RD Thrush and Markus Bergkvist, thanks! ok miod@
Diffstat (limited to 'sys/dev/usb/usbdi.c')
-rw-r--r--sys/dev/usb/usbdi.c25
1 files changed, 17 insertions, 8 deletions
diff --git a/sys/dev/usb/usbdi.c b/sys/dev/usb/usbdi.c
index 8e515a16c69..768bdbdbbb6 100644
--- a/sys/dev/usb/usbdi.c
+++ b/sys/dev/usb/usbdi.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: usbdi.c,v 1.59 2013/09/20 15:34:51 mpi Exp $ */
+/* $OpenBSD: usbdi.c,v 1.60 2013/09/24 09:01:41 mpi Exp $ */
/* $NetBSD: usbdi.c,v 1.103 2002/09/27 15:37:38 provos Exp $ */
/* $FreeBSD: src/sys/dev/usb/usbdi.c,v 1.28 1999/11/17 22:33:49 n_hibma Exp $ */
@@ -731,7 +731,6 @@ usb_transfer_complete(struct usbd_xfer *xfer)
{
struct usbd_pipe *pipe = xfer->pipe;
struct usb_dma *dmap = &xfer->dmabuf;
- int repeat = pipe->repeat;
int polling;
SPLUSBCHECK;
@@ -771,14 +770,14 @@ usb_transfer_complete(struct usbd_xfer *xfer)
/* if we allocated the buffer in usbd_transfer() we free it here. */
if (xfer->rqflags & URQ_AUTO_DMABUF) {
- if (!repeat) {
+ if (!pipe->repeat) {
struct usbd_bus *bus = pipe->device->bus;
usb_freemem(bus, dmap);
xfer->rqflags &= ~URQ_AUTO_DMABUF;
}
}
- if (!repeat) {
+ if (!pipe->repeat) {
/* Remove request from queue. */
#ifdef DIAGNOSTIC
if (xfer != SIMPLEQ_FIRST(&pipe->queue))
@@ -788,8 +787,8 @@ usb_transfer_complete(struct usbd_xfer *xfer)
#endif
SIMPLEQ_REMOVE_HEAD(&pipe->queue, next);
}
- DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n", repeat,
- SIMPLEQ_FIRST(&pipe->queue)));
+ DPRINTFN(5,("usb_transfer_complete: repeat=%d new head=%p\n",
+ pipe->repeat, SIMPLEQ_FIRST(&pipe->queue)));
/* Count completed transfers. */
++pipe->device->bus->stats.uds_requests
@@ -803,7 +802,16 @@ usb_transfer_complete(struct usbd_xfer *xfer)
xfer->status = USBD_SHORT_XFER;
}
- if (repeat) {
+ if (pipe->repeat) {
+ /*
+ * If we already got an I/O error that generally means
+ * the device is gone or not responding, so don't try
+ * to enqueue a new transfer as it will more likely
+ * results in the same error.
+ */
+ if (xfer->status == USBD_IOERROR)
+ pipe->repeat = 0;
+
if (xfer->callback)
xfer->callback(xfer, xfer->priv, xfer->status);
pipe->methods->done(xfer);
@@ -816,9 +824,10 @@ usb_transfer_complete(struct usbd_xfer *xfer)
if ((xfer->flags & USBD_SYNCHRONOUS) && !polling)
wakeup(xfer);
- if (!repeat) {
+ if (!pipe->repeat) {
/* XXX should we stop the queue on all errors? */
if ((xfer->status == USBD_CANCELLED ||
+ xfer->status == USBD_IOERROR ||
xfer->status == USBD_TIMEOUT) &&
pipe->iface != NULL) /* not control pipe */
pipe->running = 0;