diff options
author | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-09-24 09:01:42 +0000 |
---|---|---|
committer | Martin Pieuchot <mpi@cvs.openbsd.org> | 2013-09-24 09:01:42 +0000 |
commit | 1e00e83898179e4997632256fc4767062e7ff3a8 (patch) | |
tree | dfb456cb9f70d46843ced4682a7028be77035aa3 /sys/dev/usb/usbdi.c | |
parent | e45e1a140aee7cc92b359b5521ad1b80eaf8c60e (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.c | 25 |
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; |