]> git.ipfire.org Git - thirdparty/kernel/stable-queue.git/commitdiff
4.14-stable patches
authorGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 Dec 2019 15:05:08 +0000 (16:05 +0100)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Thu, 19 Dec 2019 15:05:08 +0000 (16:05 +0100)
added patches:
xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch

queue-4.14/series
queue-4.14/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch [new file with mode: 0644]

index ec553913b1bbad06902c96be8aed0b23e432e7ce..019daa28344ecae6479db084a6744f4af75fc6b7 100644 (file)
@@ -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 (file)
index 0000000..194890d
--- /dev/null
@@ -0,0 +1,104 @@
+From 057d476fff778f1d3b9f861fdb5437ea1a3cfc99 Mon Sep 17 00:00:00 2001
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+Date: Wed, 11 Dec 2019 16:20:03 +0200
+Subject: xhci: fix USB3 device initiated resume race with roothub autosuspend
+
+From: Mathias Nyman <mathias.nyman@linux.intel.com>
+
+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: <stable@vger.kernel.org>
+Cc: Lee, Hou-hsun <hou-hsun.lee@intel.com>
+Reported-by: Lee, Chiasheng <chiasheng.lee@intel.com>
+Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
+Link: https://lore.kernel.org/r/20191211142007.8847-3-mathias.nyman@linux.intel.com
+Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+
+---
+ 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,