usb: core: hub: Disable hub-initiated U1/U2 [Linux 4.4.187]

This Linux kernel change "usb: core: hub: Disable hub-initiated U1/U2" is included in the Linux 4.4.187 release. This change is authored by Thinh Nguyen <Thinh.Nguyen [at] synopsys.com> on Tue May 14 14:38:38 2019 -0700. The commit for this change in Linux stable tree is d04ba4c (patch) which is from upstream commit 5617592. The same Linux upstream change may have been applied to various maintained Linux releases and you can find all Linux releases containing changes from upstream 5617592.

usb: core: hub: Disable hub-initiated U1/U2

[ Upstream commit 561759292774707b71ee61aecc07724905bb7ef1 ]

If the device rejects the control transfer to enable device-initiated
U1/U2 entry, then the device will not initiate U1/U2 transition. To
improve the performance, the downstream port should not initate
transition to U1/U2 to avoid the delay from the device link command
response (no packet can be transmitted while waiting for a response from
the device). If the device has some quirks and does not implement U1/U2,
it may reject all the link state change requests, and the downstream
port may resend and flood the bus with more requests. This will affect
the device performance even further. This patch disables the
hub-initated U1/U2 if the device-initiated U1/U2 entry fails.

Reference: USB 3.2 spec 7.2.4.2.3

Signed-off-by: Thinh Nguyen <[email protected]>
Signed-off-by: Greg Kroah-Hartman <[email protected]>
Signed-off-by: Sasha Levin <[email protected]>

There are 28 lines of Linux source code added/deleted in this change. Code changes to Linux kernel are as follows.

 drivers/usb/core/hub.c | 28 ++++++++++++++++------------
 1 file changed, 16 insertions(+), 12 deletions(-)

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 3d714da..5c274c5 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -3823,6 +3823,9 @@ static int usb_set_lpm_timeout(struct usb_device *udev,
  * control transfers to set the hub timeout or enable device-initiated U1/U2
  * will be successful.
  *
+ * If the control transfer to enable device-initiated U1/U2 entry fails, then
+ * hub-initiated U1/U2 will be disabled.
+ *
  * If we cannot set the parent hub U1/U2 timeout, we attempt to let the xHCI
  * driver know about it.  If that call fails, it should be harmless, and just
  * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency.
@@ -3877,23 +3880,24 @@ static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
         * host know that this link state won't be enabled.
         */
        hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
-   } else {
-       /* Only a configured device will accept the Set Feature
-        * U1/U2_ENABLE
-        */
-       if (udev->actconfig)
-           usb_set_device_initiated_lpm(udev, state, true);
+       return;
+   }

-       /* As soon as usb_set_lpm_timeout(timeout) returns 0, the
-        * hub-initiated LPM is enabled. Thus, LPM is enabled no
-        * matter the result of usb_set_device_initiated_lpm().
-        * The only difference is whether device is able to initiate
-        * LPM.
-        */
+   /* Only a configured device will accept the Set Feature
+    * U1/U2_ENABLE
+    */
+   if (udev->actconfig &&
+       usb_set_device_initiated_lpm(udev, state, true) == 0) {
        if (state == USB3_LPM_U1)
            udev->usb3_lpm_u1_enabled = 1;
        else if (state == USB3_LPM_U2)
            udev->usb3_lpm_u2_enabled = 1;
+   } else {
+       /* Don't request U1/U2 entry if the device
+        * cannot transition to U1/U2.
+        */
+       usb_set_lpm_timeout(udev, state, 0);
+       hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state);
    }
 }

Leave a Reply

Your email address will not be published. Required fields are marked *