From: Greg Kroah-Hartman Date: Wed, 28 Nov 2018 13:20:00 +0000 (+0100) Subject: 4.9-stable patches X-Git-Tag: v4.19.6~44 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=a9e8ebad4d4af3d476c2e4ef9857acc726fddc54;p=thirdparty%2Fkernel%2Fstable-queue.git 4.9-stable patches added patches: usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch --- diff --git a/queue-4.9/series b/queue-4.9/series index bf765586d32..25a1062cc77 100644 --- a/queue-4.9/series +++ b/queue-4.9/series @@ -24,3 +24,4 @@ can-dev-__can_get_echo_skb-print-error-message-if-trying-to-echo-non-existing-sk acpica-aml-interpreter-add-region-addresses-in-global-list-during-initialization.patch ib-core-fix-for-core-panic.patch ib-hfi1-eliminate-races-in-the-sdma-send-error-path.patch +usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch diff --git a/queue-4.9/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch b/queue-4.9/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch new file mode 100644 index 00000000000..b5fa611218f --- /dev/null +++ b/queue-4.9/usb-xhci-prevent-bus-suspend-if-a-port-connect-change-or-polling-state-is-detected.patch @@ -0,0 +1,146 @@ +From 2f31a67f01a8beb22cae754c53522cb61a005750 Mon Sep 17 00:00:00 2001 +From: Mathias Nyman +Date: Thu, 15 Nov 2018 11:38:41 +0200 +Subject: usb: xhci: Prevent bus suspend if a port connect change or polling state is detected + +From: Mathias Nyman + +commit 2f31a67f01a8beb22cae754c53522cb61a005750 upstream. + +USB3 roothub might autosuspend before a plugged USB3 device is detected, +causing USB3 device enumeration failure. + +USB3 devices don't show up as connected and enabled until USB3 link trainig +completes. On a fast booting platform with a slow USB3 link training the +link might reach the connected enabled state just as the bus is suspending. + +If this device is discovered first time by the xhci_bus_suspend() routine +it will be put to U3 suspended state like the other ports which failed to +suspend earlier. + +The hub thread will notice the connect change and resume the bus, +moving the port back to U0 + +This U0 -> U3 -> U0 transition right after being connected seems to be +too much for some devices, causing them to first go to SS.Inactive state, +and finally end up stuck in a polling state with reset asserted + +Fix this by failing the bus suspend if a port has a connect change or is +in a polling state in xhci_bus_suspend(). + +Don't do any port changes until all ports are checked, buffer all port +changes and only write them in the end if suspend can proceed + +Cc: stable@vger.kernel.org +Signed-off-by: Mathias Nyman +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/host/xhci-hub.c | 60 +++++++++++++++++++++++++++++++++----------- + 1 file changed, 46 insertions(+), 14 deletions(-) + +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -1322,13 +1322,16 @@ int xhci_bus_suspend(struct usb_hcd *hcd + __le32 __iomem **port_array; + struct xhci_bus_state *bus_state; + unsigned long flags; ++ u32 portsc_buf[USB_MAXCHILDREN]; ++ bool wake_enabled; + + max_ports = xhci_get_ports(hcd, &port_array); + bus_state = &xhci->bus_state[hcd_index(hcd)]; ++ wake_enabled = hcd->self.root_hub->do_remote_wakeup; + + spin_lock_irqsave(&xhci->lock, flags); + +- if (hcd->self.root_hub->do_remote_wakeup) { ++ if (wake_enabled) { + if (bus_state->resuming_ports || /* USB2 */ + bus_state->port_remote_wakeup) { /* USB3 */ + spin_unlock_irqrestore(&xhci->lock, flags); +@@ -1336,26 +1339,36 @@ int xhci_bus_suspend(struct usb_hcd *hcd + return -EBUSY; + } + } +- +- port_index = max_ports; ++ /* ++ * Prepare ports for suspend, but don't write anything before all ports ++ * are checked and we know bus suspend can proceed ++ */ + bus_state->bus_suspended = 0; ++ port_index = max_ports; + while (port_index--) { +- /* suspend the port if the port is not suspended */ + u32 t1, t2; +- int slot_id; + + t1 = readl(port_array[port_index]); + t2 = xhci_port_state_to_neutral(t1); ++ portsc_buf[port_index] = 0; + +- if ((t1 & PORT_PE) && !(t1 & PORT_PLS_MASK)) { +- xhci_dbg(xhci, "port %d not suspended\n", port_index); +- slot_id = xhci_find_slot_id_by_port(hcd, xhci, +- port_index + 1); +- if (slot_id) { ++ /* Bail out if a USB3 port has a new device in link training */ ++ if ((t1 & PORT_PLS_MASK) == XDEV_POLLING) { ++ bus_state->bus_suspended = 0; ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ xhci_dbg(xhci, "Bus suspend bailout, port in polling\n"); ++ return -EBUSY; ++ } ++ ++ /* suspend ports in U0, or bail out for new connect changes */ ++ if ((t1 & PORT_PE) && (t1 & PORT_PLS_MASK) == XDEV_U0) { ++ if ((t1 & PORT_CSC) && wake_enabled) { ++ bus_state->bus_suspended = 0; + spin_unlock_irqrestore(&xhci->lock, flags); +- xhci_stop_device(xhci, slot_id, 1); +- spin_lock_irqsave(&xhci->lock, flags); ++ xhci_dbg(xhci, "Bus suspend bailout, port connect change\n"); ++ return -EBUSY; + } ++ xhci_dbg(xhci, "port %d not suspended\n", port_index); + t2 &= ~PORT_PLS_MASK; + t2 |= PORT_LINK_STROBE | XDEV_U3; + set_bit(port_index, &bus_state->bus_suspended); +@@ -1364,7 +1377,7 @@ int xhci_bus_suspend(struct usb_hcd *hcd + * including the USB 3.0 roothub, but only if CONFIG_PM + * is enabled, so also enable remote wake here. + */ +- if (hcd->self.root_hub->do_remote_wakeup) { ++ if (wake_enabled) { + if (t1 & PORT_CONNECT) { + t2 |= PORT_WKOC_E | PORT_WKDISC_E; + t2 &= ~PORT_WKCONN_E; +@@ -1377,7 +1390,26 @@ int xhci_bus_suspend(struct usb_hcd *hcd + + t1 = xhci_port_state_to_neutral(t1); + if (t1 != t2) +- writel(t2, port_array[port_index]); ++ portsc_buf[port_index] = t2; ++ } ++ ++ /* write port settings, stopping and suspending ports if needed */ ++ port_index = max_ports; ++ while (port_index--) { ++ if (!portsc_buf[port_index]) ++ continue; ++ if (test_bit(port_index, &bus_state->bus_suspended)) { ++ int slot_id; ++ ++ slot_id = xhci_find_slot_id_by_port(hcd, xhci, ++ port_index + 1); ++ if (slot_id) { ++ spin_unlock_irqrestore(&xhci->lock, flags); ++ xhci_stop_device(xhci, slot_id, 1); ++ spin_lock_irqsave(&xhci->lock, flags); ++ } ++ } ++ writel(portsc_buf[port_index], port_array[port_index]); + } + hcd->state = HC_STATE_SUSPENDED; + bus_state->next_statechange = jiffies + msecs_to_jiffies(10);