From: Greg Kroah-Hartman Date: Thu, 19 Dec 2019 15:05:08 +0000 (+0100) Subject: 4.14-stable patches X-Git-Tag: v4.4.207~8 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=aaa4233bdab2512c2f360ae25dbd2d52be80935a;p=thirdparty%2Fkernel%2Fstable-queue.git 4.14-stable patches added patches: xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch --- diff --git a/queue-4.14/series b/queue-4.14/series index ec553913b1b..019daa28344 100644 --- a/queue-4.14/series +++ b/queue-4.14/series @@ -32,3 +32,4 @@ drm-meson-venc-cvbs-fix-cvbs-mode-matching.patch dm-btree-increase-rebalance-threshold-in-__rebalance2.patch scsi-iscsi-fix-a-potential-deadlock-in-the-timeout-handler.patch drm-radeon-fix-r1xx-r2xx-register-checker-for-pot-textures.patch +xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch diff --git a/queue-4.14/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch b/queue-4.14/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch new file mode 100644 index 00000000000..194890dcc69 --- /dev/null +++ b/queue-4.14/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch @@ -0,0 +1,104 @@ +From 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 Mon Sep 17 00:00:00 2001 +From: Mathias Nyman +Date: Wed, 11 Dec 2019 16:20:03 +0200 +Subject: xhci: fix USB3 device initiated resume race with roothub autosuspend + +From: Mathias Nyman + +commit 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 upstream. + +A race in xhci USB3 remote wake handling may force device back to suspend +after it initiated resume siganaling, causing a missed resume event or warm +reset of device. + +When a USB3 link completes resume signaling and goes to enabled (UO) +state a interrupt is issued and the interrupt handler will clear the +bus_state->port_remote_wakeup resume flag, allowing bus suspend. + +If the USB3 roothub thread just finished reading port status before +the interrupt, finding ports still in suspended (U3) state, but hasn't +yet started suspending the hub, then the xhci interrupt handler will clear +the flag that prevented roothub suspend and allow bus to suspend, forcing +all port links back to suspended (U3) state. + +Example case: +usb_runtime_suspend() # because all ports still show suspended U3 + usb_suspend_both() + hub_suspend(); # successful as hub->wakeup_bits not set yet +==> INTERRUPT +xhci_irq() + handle_port_status() + clear bus_state->port_remote_wakeup + usb_wakeup_notification() + sets hub->wakeup_bits; + kick_hub_wq() +<== END INTERRUPT + hcd_bus_suspend() + xhci_bus_suspend() # success as port_remote_wakeup bits cleared + +Fix this by increasing roothub usage count during port resume to prevent +roothub autosuspend, and by making sure bus_state->port_remote_wakeup +flag is only cleared after resume completion is visible, i.e. +after xhci roothub returned U0 or other non-U3 link state link on a +get port status request. + +Issue rootcaused by Chiasheng Lee + +Cc: +Cc: Lee, Hou-hsun +Reported-by: Lee, Chiasheng +Signed-off-by: Mathias Nyman +Link: https://lore.kernel.org/r/20191211142007.8847-3-mathias.nyman@linux.intel.com +Signed-off-by: Greg Kroah-Hartman + +--- + drivers/usb/host/xhci-hub.c | 8 ++++++++ + drivers/usb/host/xhci-ring.c | 6 +----- + 2 files changed, 9 insertions(+), 5 deletions(-) + +--- a/drivers/usb/host/xhci-hub.c ++++ b/drivers/usb/host/xhci-hub.c +@@ -887,6 +887,14 @@ static u32 xhci_get_port_status(struct u + status |= USB_PORT_STAT_C_BH_RESET << 16; + if ((raw_port_status & PORT_CEC)) + status |= USB_PORT_STAT_C_CONFIG_ERROR << 16; ++ ++ /* USB3 remote wake resume signaling completed */ ++ if (bus_state->port_remote_wakeup & (1 << wIndex) && ++ (raw_port_status & PORT_PLS_MASK) != XDEV_RESUME && ++ (raw_port_status & PORT_PLS_MASK) != XDEV_RECOVERY) { ++ bus_state->port_remote_wakeup &= ~(1 << wIndex); ++ usb_hcd_end_port_resume(&hcd->self, wIndex); ++ } + } + + if (hcd->speed < HCD_USB3) { +--- a/drivers/usb/host/xhci-ring.c ++++ b/drivers/usb/host/xhci-ring.c +@@ -1679,9 +1679,6 @@ static void handle_port_status(struct xh + usb_hcd_resume_root_hub(hcd); + } + +- if (hcd->speed >= HCD_USB3 && (portsc & PORT_PLS_MASK) == XDEV_INACTIVE) +- bus_state->port_remote_wakeup &= ~(1 << faked_port_index); +- + if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) { + xhci_dbg(xhci, "port resume event for port %d\n", port_id); + +@@ -1700,6 +1697,7 @@ static void handle_port_status(struct xh + bus_state->port_remote_wakeup |= 1 << faked_port_index; + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); ++ usb_hcd_start_port_resume(&hcd->self, faked_port_index); + xhci_set_link_state(xhci, port_array, faked_port_index, + XDEV_U0); + /* Need to wait until the next link state change +@@ -1737,8 +1735,6 @@ static void handle_port_status(struct xh + if (slot_id && xhci->devs[slot_id]) + xhci_ring_device(xhci, slot_id); + if (bus_state->port_remote_wakeup & (1 << faked_port_index)) { +- bus_state->port_remote_wakeup &= +- ~(1 << faked_port_index); + xhci_test_and_clear_bit(xhci, port_array, + faked_port_index, PORT_PLC); + usb_wakeup_notification(hcd->self.root_hub,