summaryrefslogtreecommitdiffstats
path: root/drivers/usb/host/xhci.c
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2017-04-07 17:57:01 +0300
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2017-04-08 12:17:41 +0200
commitd9f11ba9f107aa335091ab8d7ba5eea714e46e8b (patch)
treefdcd52b0fbd2fb43f2b27f210f380211e449b51f /drivers/usb/host/xhci.c
parentfe190ed0d60260e44f48d8b0b04f26a8c8898a02 (diff)
downloadop-kernel-dev-d9f11ba9f107aa335091ab8d7ba5eea714e46e8b.zip
op-kernel-dev-d9f11ba9f107aa335091ab8d7ba5eea714e46e8b.tar.gz
xhci: Rework how we handle unresponsive or hoptlug removed hosts
Introduce a new xhci_hc_died() function that takes care of handling pending commands and URBs if a host controller becomes unresponsive. This addresses issues on hotpluggable xhci controllers that disappear from the bus suddenly, often while the bus (PCI) remove function is still being processed. xhci_hc_died() sets a XHCI_STATUS_DYING flag to prevent new URBs and commands or to be queued. The flag also ensures xhci_hc_died() will give back pending commands and URBs once. Host is considered dead if register read returns 0xffffffff, or host fails to abort the command ring, or fails stopping an endpoint after trying for 5 seconds. Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/usb/host/xhci.c')
-rw-r--r--drivers/usb/host/xhci.c16
1 files changed, 14 insertions, 2 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 5ecba7c..3e5d89b 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1504,10 +1504,16 @@ int xhci_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status)
if (!ep || !ep_ring)
goto err_giveback;
+ /* If xHC is dead take it down and return ALL URBs in xhci_hc_died() */
temp = readl(&xhci->op_regs->status);
- if (temp == 0xffffffff || (xhci->xhc_state & XHCI_STATE_HALTED)) {
+ if (temp == ~(u32)0 || xhci->xhc_state & XHCI_STATE_DYING) {
+ xhci_hc_died(xhci);
+ goto done;
+ }
+
+ if (xhci->xhc_state & XHCI_STATE_HALTED) {
xhci_dbg_trace(xhci, trace_xhci_dbg_cancel_urb,
- "HW died, freeing TD.");
+ "HC halted, freeing TD manually.");
for (i = urb_priv->num_tds_done;
i < urb_priv->num_tds;
i++) {
@@ -2598,6 +2604,12 @@ static int xhci_configure_endpoint(struct xhci_hcd *xhci,
return -EINVAL;
spin_lock_irqsave(&xhci->lock, flags);
+
+ if (xhci->xhc_state & XHCI_STATE_DYING) {
+ spin_unlock_irqrestore(&xhci->lock, flags);
+ return -ESHUTDOWN;
+ }
+
virt_dev = xhci->devs[udev->slot_id];
ctrl_ctx = xhci_get_input_control_ctx(command->in_ctx);
OpenPOWER on IntegriCloud