diff mbox series

[1/5] usb: hub: Block less in USB3 link power management LPM disable path

Message ID 20250314142000.93090-2-mathias.nyman@linux.intel.com (mailing list archive)
State New
Headers show
Series usb: hub: Fail fast on USB3 LPM requests issues | expand

Commit Message

Mathias Nyman March 14, 2025, 2:19 p.m. UTC
Several usb requests are needed to allow or forbid a USB3 link from
going into U1 or U2 hardware link power management (LPM) states.

Fail fast on issues in LPM disabling path. LPM disabling is done in hub
workqueue paths that are often already handling possible link issues.

Enabling and disabling LPM involves four usb requests.
Two requests sent to the upstream hub of the connected device:
SetPortFeature(U1_TIMEOUT)
SetPortFeature(U2_TIMEOUT)

And two to the device itself:
SetFeature(U1_ENABLE)
SetFeature(U2_ENABLE)

The requests to the hub sets the inactivity timeout used by the hub to
know when to initiate U1 and U2 LPM link state transitions.
These requests are also used prevent U1/U2 LPM transitions completely
by passing zero timeout value.

The requsts sent to the device only controls if device is allowed to
initiate U1/U2 transitions. If not enabled then only hub initiates U1/U2
transitions. Hub may block these device initiated attempts.

Reorder and send the hub requests first, these are more likely to succeed
due to shorter path, and we can consider LPM disabled if these succeed
as U1/U2 link state can not be entered after that.

Fail immediately if a request fails, and don't try to enable back LPM
after a failed request, that will just send more LPM requests over a
bad link.

If a device request controlling device initiateed LPM fails then exit
immediately, but consider LPM disabled at this stage.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
---
 drivers/usb/core/hub.c | 16 ++++++++++------
 1 file changed, 10 insertions(+), 6 deletions(-)
diff mbox series

Patch

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 8c7f9cc785bb..a901d1b55856 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -4393,8 +4393,6 @@  static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
 	if (usb_set_lpm_timeout(udev, state, 0))
 		return -EBUSY;
 
-	usb_set_device_initiated_lpm(udev, state, false);
-
 	if (hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state))
 		dev_warn(&udev->dev, "Could not disable xHCI %s timeout, "
 				"bus schedule bandwidth may be impacted.\n",
@@ -4424,6 +4422,7 @@  static int usb_disable_link_state(struct usb_hcd *hcd, struct usb_device *udev,
 int usb_disable_lpm(struct usb_device *udev)
 {
 	struct usb_hcd *hcd;
+	int err;
 
 	if (!udev || !udev->parent ||
 			udev->speed < USB_SPEED_SUPER ||
@@ -4441,14 +4440,19 @@  int usb_disable_lpm(struct usb_device *udev)
 
 	/* If LPM is enabled, attempt to disable it. */
 	if (usb_disable_link_state(hcd, udev, USB3_LPM_U1))
-		goto enable_lpm;
+		goto disable_failed;
 	if (usb_disable_link_state(hcd, udev, USB3_LPM_U2))
-		goto enable_lpm;
+		goto disable_failed;
+
+	err = usb_set_device_initiated_lpm(udev, USB3_LPM_U1, false);
+	if (!err)
+		usb_set_device_initiated_lpm(udev, USB3_LPM_U2, false);
 
 	return 0;
 
-enable_lpm:
-	usb_enable_lpm(udev);
+disable_failed:
+	udev->lpm_disable_count--;
+
 	return -EBUSY;
 }
 EXPORT_SYMBOL_GPL(usb_disable_lpm);