Module Name: src
Committed By: skrll
Date: Thu Dec 29 09:52:00 UTC 2016
Modified Files:
src/sys/dev/usb [nick-nhusb]: xhci.c
Log Message:
Mark device transfers as USBD_IN_PROGRESS appropriately and improve
abort handling
To generate a diff of this commit:
cvs rdiff -u -r1.28.2.77 -r1.28.2.78 src/sys/dev/usb/xhci.c
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/usb/xhci.c
diff -u src/sys/dev/usb/xhci.c:1.28.2.77 src/sys/dev/usb/xhci.c:1.28.2.78
--- src/sys/dev/usb/xhci.c:1.28.2.77 Wed Oct 5 20:55:59 2016
+++ src/sys/dev/usb/xhci.c Thu Dec 29 09:52:00 2016
@@ -1,4 +1,4 @@
-/* $NetBSD: xhci.c,v 1.28.2.77 2016/10/05 20:55:59 skrll Exp $ */
+/* $NetBSD: xhci.c,v 1.28.2.78 2016/12/29 09:52:00 skrll Exp $ */
/*
* Copyright (c) 2013 Jonathan A. Kollasch
@@ -34,7 +34,7 @@
*/
#include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.77 2016/10/05 20:55:59 skrll Exp $");
+__KERNEL_RCSID(0, "$NetBSD: xhci.c,v 1.28.2.78 2016/12/29 09:52:00 skrll Exp $");
#ifdef _KERNEL_OPT
#include "opt_usb.h"
@@ -1560,12 +1560,13 @@ xhci_abort_xfer(struct usbd_xfer *xfer,
xfer, xfer->ux_pipe, status, 0);
KASSERT(mutex_owned(&sc->sc_lock));
+ ASSERT_SLEEPABLE();
if (sc->sc_dying) {
/* If we're dying, just do the software part. */
DPRINTFN(4, "xfer %p dying %u", xfer, xfer->ux_status, 0, 0);
xfer->ux_status = status;
- callout_stop(&xfer->ux_callout);
+ callout_halt(&xfer->ux_callout, &sc->sc_lock);
usb_transfer_complete(xfer);
return;
}
@@ -1592,10 +1593,16 @@ xhci_abort_xfer(struct usbd_xfer *xfer,
xfer->ux_hcflags |= UXFER_ABORTING;
/*
- * Step 1: Stop xfer timeout timer.
+ * Step 1: When cancelling a transfer make sure the timeout handler
+ * didn't run or ran to the end and saw the USBD_CANCELLED status.
+ * Otherwise we must have got here via a timeout.
*/
- xfer->ux_status = status;
- callout_stop(&xfer->ux_callout);
+ if (status == USBD_CANCELLED) {
+ xfer->ux_status = status;
+ callout_halt(&xfer->ux_callout, &sc->sc_lock);
+ } else {
+ KASSERT(xfer->ux_status == USBD_TIMEOUT);
+ }
/*
* Step 2: Stop execution of TD on the ring.
@@ -1879,7 +1886,7 @@ xhci_event_transfer(struct xhci_softc *
* UF_ENDPOINT_HALT).
*/
xfer->ux_status = err;
- callout_stop(&xfer->ux_callout);
+ callout_halt(&xfer->ux_callout, &sc->sc_lock);
xhci_clear_endpoint_stall_async(xfer);
return;
default:
@@ -3629,17 +3636,18 @@ xhci_device_ctrl_start(struct usbd_xfer
XHCI_TRB_3_IOC_BIT;
xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+ if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
+ callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
+ xhci_timeout, xfer);
+ }
+ xfer->ux_status = USBD_IN_PROGRESS;
+
mutex_enter(&tr->xr_lock);
xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
mutex_exit(&tr->xr_lock);
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
- if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
- callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
- xhci_timeout, xfer);
- }
-
return USBD_IN_PROGRESS;
}
@@ -3745,17 +3753,18 @@ xhci_device_bulk_start(struct usbd_xfer
XHCI_TRB_3_IOC_BIT;
xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+ if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
+ callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
+ xhci_timeout, xfer);
+ }
+ xfer->ux_status = USBD_IN_PROGRESS;
+
mutex_enter(&tr->xr_lock);
xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
mutex_exit(&tr->xr_lock);
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
- if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
- callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
- xhci_timeout, xfer);
- }
-
return USBD_IN_PROGRESS;
}
@@ -3851,17 +3860,18 @@ xhci_device_intr_start(struct usbd_xfer
XHCI_TRB_3_IOC_BIT;
xhci_trb_put(&xx->xx_trb[i++], parameter, status, control);
+ if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
+ callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
+ xhci_timeout, xfer);
+ }
+ xfer->ux_status = USBD_IN_PROGRESS;
+
mutex_enter(&tr->xr_lock);
xhci_ring_put(sc, tr, xfer, xx->xx_trb, i);
mutex_exit(&tr->xr_lock);
xhci_db_write_4(sc, XHCI_DOORBELL(xs->xs_idx), dci);
- if (xfer->ux_timeout && !sc->sc_bus.ub_usepolling) {
- callout_reset(&xfer->ux_callout, mstohz(xfer->ux_timeout),
- xhci_timeout, xfer);
- }
-
return USBD_IN_PROGRESS;
}
@@ -3914,31 +3924,42 @@ xhci_device_intr_close(struct usbd_pipe
static void
xhci_timeout(void *addr)
{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
struct xhci_xfer * const xx = addr;
struct usbd_xfer * const xfer = &xx->xx_xfer;
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
+ bool timeout = false;
- XHCIHIST_FUNC(); XHCIHIST_CALLED();
-
+ mutex_enter(&sc->sc_lock);
if (sc->sc_dying) {
+ mutex_exit(&sc->sc_lock);
return;
}
+ if (xfer->ux_status != USBD_CANCELLED) {
+ xfer->ux_status = USBD_TIMEOUT;
+ timeout = true;
+ }
+ mutex_exit(&sc->sc_lock);
- usb_init_task(&xfer->ux_aborttask, xhci_timeout_task, addr,
- USB_TASKQ_MPSAFE);
- usb_add_task(xx->xx_xfer.ux_pipe->up_dev, &xfer->ux_aborttask,
- USB_TASKQ_HC);
+ if (timeout) {
+ struct usbd_device *dev = xfer->ux_pipe->up_dev;
+
+ /* Execute the abort in a process context. */
+ usb_init_task(&xfer->ux_aborttask, xhci_timeout_task, xfer,
+ USB_TASKQ_MPSAFE);
+ usb_add_task(dev, &xfer->ux_aborttask, USB_TASKQ_HC);
+ }
}
static void
xhci_timeout_task(void *addr)
{
+ XHCIHIST_FUNC(); XHCIHIST_CALLED();
struct usbd_xfer * const xfer = addr;
struct xhci_softc * const sc = XHCI_XFER2SC(xfer);
- XHCIHIST_FUNC(); XHCIHIST_CALLED();
-
mutex_enter(&sc->sc_lock);
+ KASSERT(xfer->ux_status == USBD_TIMEOUT);
xhci_abort_xfer(xfer, USBD_TIMEOUT);
mutex_exit(&sc->sc_lock);
}