aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMathias Nyman <mathias.nyman@linux.intel.com>2018-06-21 09:19:41 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2018-06-25 09:16:27 -0400
commit229bc19fd7aca4f37964af06e3583c1c8f36b5d6 (patch)
treeba29eec42c20d7627d9006ffb07987ce82382a5a
parent9d1a68c458c71171f8670d7e994ba1d135790fe7 (diff)
xhci: Fix perceived dead host due to runtime suspend race with event handler
Don't rely on event interrupt (EINT) bit alone to detect pending port change in resume. If no change event is detected the host may be suspended again, oterwise roothubs are resumed. There is a lag in xHC setting EINT. If we don't notice the pending change in resume, and the controller is runtime suspeded again, it causes the event handler to assume host is dead as it will fail to read xHC registers once PCI puts the controller to D3 state. [ 268.520969] xhci_hcd: xhci_resume: starting port polling. [ 268.520985] xhci_hcd: xhci_hub_status_data: stopping port polling. [ 268.521030] xhci_hcd: xhci_suspend: stopping port polling. [ 268.521040] xhci_hcd: // Setting command ring address to 0x349bd001 [ 268.521139] xhci_hcd: Port Status Change Event for port 3 [ 268.521149] xhci_hcd: resume root hub [ 268.521163] xhci_hcd: port resume event for port 3 [ 268.521168] xhci_hcd: xHC is not running. [ 268.521174] xhci_hcd: handle_port_status: starting port polling. [ 268.596322] xhci_hcd: xhci_hc_died: xHCI host controller not responding, assume dead The EINT lag is described in a additional note in xhci specs 4.19.2: "Due to internal xHC scheduling and system delays, there will be a lag between a change bit being set and the Port Status Change Event that it generated being written to the Event Ring. If SW reads the PORTSC and sees a change bit set, there is no guarantee that the corresponding Port Status Change Event has already been written into the Event Ring." 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.c40
-rw-r--r--drivers/usb/host/xhci.h4
2 files changed, 41 insertions, 3 deletions
diff --git a/drivers/usb/host/xhci.c b/drivers/usb/host/xhci.c
index 8c8da2d657fa..f11ec61bcc7d 100644
--- a/drivers/usb/host/xhci.c
+++ b/drivers/usb/host/xhci.c
@@ -908,6 +908,41 @@ static void xhci_disable_port_wake_on_bits(struct xhci_hcd *xhci)
908 spin_unlock_irqrestore(&xhci->lock, flags); 908 spin_unlock_irqrestore(&xhci->lock, flags);
909} 909}
910 910
911static bool xhci_pending_portevent(struct xhci_hcd *xhci)
912{
913 struct xhci_port **ports;
914 int port_index;
915 u32 status;
916 u32 portsc;
917
918 status = readl(&xhci->op_regs->status);
919 if (status & STS_EINT)
920 return true;
921 /*
922 * Checking STS_EINT is not enough as there is a lag between a change
923 * bit being set and the Port Status Change Event that it generated
924 * being written to the Event Ring. See note in xhci 1.1 section 4.19.2.
925 */
926
927 port_index = xhci->usb2_rhub.num_ports;
928 ports = xhci->usb2_rhub.ports;
929 while (port_index--) {
930 portsc = readl(ports[port_index]->addr);
931 if (portsc & PORT_CHANGE_MASK ||
932 (portsc & PORT_PLS_MASK) == XDEV_RESUME)
933 return true;
934 }
935 port_index = xhci->usb3_rhub.num_ports;
936 ports = xhci->usb3_rhub.ports;
937 while (port_index--) {
938 portsc = readl(ports[port_index]->addr);
939 if (portsc & PORT_CHANGE_MASK ||
940 (portsc & PORT_PLS_MASK) == XDEV_RESUME)
941 return true;
942 }
943 return false;
944}
945
911/* 946/*
912 * Stop HC (not bus-specific) 947 * Stop HC (not bus-specific)
913 * 948 *
@@ -1009,7 +1044,7 @@ EXPORT_SYMBOL_GPL(xhci_suspend);
1009 */ 1044 */
1010int xhci_resume(struct xhci_hcd *xhci, bool hibernated) 1045int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
1011{ 1046{
1012 u32 command, temp = 0, status; 1047 u32 command, temp = 0;
1013 struct usb_hcd *hcd = xhci_to_hcd(xhci); 1048 struct usb_hcd *hcd = xhci_to_hcd(xhci);
1014 struct usb_hcd *secondary_hcd; 1049 struct usb_hcd *secondary_hcd;
1015 int retval = 0; 1050 int retval = 0;
@@ -1134,8 +1169,7 @@ int xhci_resume(struct xhci_hcd *xhci, bool hibernated)
1134 done: 1169 done:
1135 if (retval == 0) { 1170 if (retval == 0) {
1136 /* Resume root hubs only when have pending events. */ 1171 /* Resume root hubs only when have pending events. */
1137 status = readl(&xhci->op_regs->status); 1172 if (xhci_pending_portevent(xhci)) {
1138 if (status & STS_EINT) {
1139 usb_hcd_resume_root_hub(xhci->shared_hcd); 1173 usb_hcd_resume_root_hub(xhci->shared_hcd);
1140 usb_hcd_resume_root_hub(hcd); 1174 usb_hcd_resume_root_hub(hcd);
1141 } 1175 }
diff --git a/drivers/usb/host/xhci.h b/drivers/usb/host/xhci.h
index 939e2f86b595..841e89ffe2e9 100644
--- a/drivers/usb/host/xhci.h
+++ b/drivers/usb/host/xhci.h
@@ -382,6 +382,10 @@ struct xhci_op_regs {
382#define PORT_PLC (1 << 22) 382#define PORT_PLC (1 << 22)
383/* port configure error change - port failed to configure its link partner */ 383/* port configure error change - port failed to configure its link partner */
384#define PORT_CEC (1 << 23) 384#define PORT_CEC (1 << 23)
385#define PORT_CHANGE_MASK (PORT_CSC | PORT_PEC | PORT_WRC | PORT_OCC | \
386 PORT_RC | PORT_PLC | PORT_CEC)
387
388
385/* Cold Attach Status - xHC can set this bit to report device attached during 389/* Cold Attach Status - xHC can set this bit to report device attached during
386 * Sx state. Warm port reset should be perfomed to clear this bit and move port 390 * Sx state. Warm port reset should be perfomed to clear this bit and move port
387 * to connected state. 391 * to connected state.