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

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

index 2eb2acfb86ef91b6b943eb76a1ef4071ca6c33e5..94657c5545e03b2784551664410c4b2abf721e94 100644 (file)
@@ -44,3 +44,4 @@ dm-btree-increase-rebalance-threshold-in-__rebalance2.patch
 scsi-iscsi-fix-a-potential-deadlock-in-the-timeout-handler.patch
 scsi-qla2xxx-change-discovery-state-before-plogi.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.19/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch b/queue-4.19/xhci-fix-usb3-device-initiated-resume-race-with-roothub-autosuspend.patch
new file mode 100644 (file)
index 0000000..7772db9
--- /dev/null
@@ -0,0 +1,101 @@
+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 |    3 +--
+ 2 files changed, 9 insertions(+), 2 deletions(-)
+
+--- a/drivers/usb/host/xhci-hub.c
++++ b/drivers/usb/host/xhci-hub.c
+@@ -868,6 +868,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
+@@ -1609,7 +1609,6 @@ static void handle_port_status(struct xh
+               slot_id = xhci_find_slot_id_by_port(hcd, xhci, hcd_portnum + 1);
+               if (slot_id && xhci->devs[slot_id])
+                       xhci->devs[slot_id]->flags |= VDEV_PORT_ERROR;
+-              bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
+       }
+       if ((portsc & PORT_PLC) && (portsc & PORT_PLS_MASK) == XDEV_RESUME) {
+@@ -1630,6 +1629,7 @@ static void handle_port_status(struct xh
+                       bus_state->port_remote_wakeup |= 1 << hcd_portnum;
+                       xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+                       xhci_set_link_state(xhci, port, XDEV_U0);
++                      usb_hcd_start_port_resume(&hcd->self, hcd_portnum);
+                       /* Need to wait until the next link state change
+                        * indicates the device is actually in U0.
+                        */
+@@ -1669,7 +1669,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 << hcd_portnum)) {
+-                      bus_state->port_remote_wakeup &= ~(1 << hcd_portnum);
+                       xhci_test_and_clear_bit(xhci, port, PORT_PLC);
+                       usb_wakeup_notification(hcd->self.root_hub,
+                                       hcd_portnum + 1);