summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2019-06-18 10:27:47 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2019-06-18 12:14:38 -0400
commitb8c3b718087bf7c3c8e388eb1f72ac1108a4926e (patch)
treea512906e7d6484f04f3272d869a337e7f4a9a56e
parentc19dffc0a9511a7d7493ec21019aefd97e9a111b (diff)
usb: xhci: Don't try to recover an endpoint if port is in error state.
A USB3 device needs to be reset and re-enumarated if the port it connects to goes to a error state, with link state inactive. There is no use in trying to recover failed transactions by resetting endpoints at this stage. Tests show that in rare cases, after multiple endpoint resets of a roothub port the whole host controller might stop completely. Several retries to recover from transaction error can happen as it can take a long time before the hub thread discovers the USB3 port error and inactive link. We can't reliably detect the port error from slot or endpoint context due to a limitation in xhci, see xhci specs section 4.8.3: "There are several cases where the EP State field in the Output Endpoint Context may not reflect the current state of an endpoint" and "Software should maintain an accurate value for EP State, by tracking it with an internal variable that is driven by Events and Doorbell accesses" Same appears to be true for slot state. set a flag to the corresponding slot if a USB3 roothub port link goes inactive to prevent both queueing new URBs and resetting endpoints. Reported-by: Rapolu Chiranjeevi <chiranjeevi.rapolu@intel.com> Tested-by: Rapolu Chiranjeevi <chiranjeevi.rapolu@intel.com> Cc: <stable@vger.kernel.org> Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/host/xhci-ring.c15
-rw-r--r--drivers/usb/host/xhci.c5
-rw-r--r--drivers/usb/host/xhci.h9
3 files changed, 28 insertions, 1 deletions
diff --git a/drivers/usb/host/xhci-ring.c b/drivers/usb/host/xhci-ring.c
index feffceb31e8a..121782e22c01 100644
--- a/drivers/usb/host/xhci-ring.c
+++ b/drivers/usb/host/xhci-ring.c
@@ -1612,8 +1612,13 @@ static void handle_port_status(struct xhci_hcd *xhci,
1612 usb_hcd_resume_root_hub(hcd); 1612 usb_hcd_resume_root_hub(hcd);
1613 } 1613 }
1614 1614
1615 if (hcd->speed >= HCD_USB3 && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) 1615 if (hcd->speed >= HCD_USB3 &&
1616 (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) {
1617 slot_id = xhci_find_slot_id_by_port(hcd, xhci, hcd_portnum + 1);
1618 if (slot_id && xhci->devs[slot_id])
1619 xhci->devs[slot_id]->flags |= VDEV_PORT_ERROR;
1616 bus_state->port_remote_wakeup &= ~(1 << hcd_portnum); 1620 bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
1621 }
1617 1622
1618 if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { 1623 if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
1619 xhci_dbg(xhci, "port resume event for port %d\n", port_id); 1624 xhci_dbg(xhci, "port resume event for port %d\n", port_id);
@@ -1801,6 +1806,14 @@ static void xhci_cleanup_halted_endpoint(struct xhci_hcd *xhci,
1801{ 1806{
1802 struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index]; 1807 struct xhci_virt_ep *ep = &xhci->devs[slot_id]->eps[ep_index];
1803 struct xhci_command *command; 1808 struct xhci_command *command;
1809
1810 /*
1811 * Avoid resetting endpoint if link is inactive. Can cause host hang.
1812 * Device will be reset soon to recover the link so don't do anything
1813 */
1814 if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR)
1815 return;
1816
1804 command = xhci_alloc_command(xhci, false, GFP_ATOMIC); 1817 command = xhci_alloc_command(xhci, false, GFP_ATOMIC);
1805 if (!command) 1818 if (!command)
1806 return; 1819 return;
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 20db378a6012..78a2a937dd83 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -1466,6 +1466,10 @@ static int xhci_urb_enqueue(struct usb_hcd *hcd, struct urb *urb, gfp_t mem_flag
1466 xhci_dbg(xhci, "urb submitted during PCI suspend\n"); 1466 xhci_dbg(xhci, "urb submitted during PCI suspend\n");
1467 return -ESHUTDOWN; 1467 return -ESHUTDOWN;
1468 } 1468 }
1469 if (xhci->devs[slot_id]->flags & VDEV_PORT_ERROR) {
1470 xhci_dbg(xhci, "Can't queue urb, port error, link inactive\n");
1471 return -ENODEV;
1472 }
1469 1473
1470 if (usb_endpoint_xfer_isoc(&urb->ep->desc)) 1474 if (usb_endpoint_xfer_isoc(&urb->ep->desc))
1471 num_tds = urb->number_of_packets; 1475 num_tds = urb->number_of_packets;
@@ -3754,6 +3758,7 @@ static int xhci_discover_or_reset_device(struct usb_hcd *hcd,
3754 } 3758 }
3755 /* If necessary, update the number of active TTs on this root port */ 3759 /* If necessary, update the number of active TTs on this root port */
3756 xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps); 3760 xhci_update_tt_active_eps(xhci, virt_dev, old_active_eps);
3761 virt_dev->flags = 0;
3757 ret = 0; 3762 ret = 0;
3758 3763
3759command_cleanup: 3764command_cleanup:
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 7f8b950d1a73..92e764c54154 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -1010,6 +1010,15 @@ struct xhci_virt_device {
1010 u8 real_port; 1010 u8 real_port;
1011 struct xhci_interval_bw_table *bw_table; 1011 struct xhci_interval_bw_table *bw_table;
1012 struct xhci_tt_bw_info *tt_info; 1012 struct xhci_tt_bw_info *tt_info;
1013 /*
1014 * flags for state tracking based on events and issued commands.
1015 * Software can not rely on states from output contexts because of
1016 * latency between events and xHC updating output context values.
1017 * See xhci 1.1 section 4.8.3 for more details
1018 */
1019 unsigned long flags;
1020#define VDEV_PORT_ERROR BIT(0) /* Port error, link inactive */
1021
1013 /* The current max exit latency for the enabled USB3 link states. */ 1022 /* The current max exit latency for the enabled USB3 link states. */
1014 u16 current_mel; 1023 u16 current_mel;
1015 /* Used for the debugfs interfaces. */ 1024 /* Used for the debugfs interfaces. */