Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

usb: hub: Fail fast in USB3 link power management enable path

Enabling LPM is done in hub workqueue, often in paths handling possible
link issues. So fail immediately on USB3 LPM issues and avoid hub wq
from unnecessary blocking, thus allowing it to handle other port events
faster.

Detect errors when enabling U1/U2 link states, and return immediately
if there is an issue.

Signed-off-by: Mathias Nyman <mathias.nyman@linux.intel.com>
Link: https://lore.kernel.org/r/20250314142000.93090-6-mathias.nyman@linux.intel.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>

authored by

Mathias Nyman and committed by
Greg Kroah-Hartman
a02dcd3b bf11662f

+12 -8
+12 -8
drivers/usb/core/hub.c
··· 4292 4292 * driver know about it. If that call fails, it should be harmless, and just 4293 4293 * take up more slightly more bus bandwidth for unnecessary U1/U2 exit latency. 4294 4294 */ 4295 - static void usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, 4295 + static int usb_enable_link_state(struct usb_hcd *hcd, struct usb_device *udev, 4296 4296 enum usb3_link_state state) 4297 4297 { 4298 4298 int timeout; ··· 4301 4301 4302 4302 /* Skip if the device BOS descriptor couldn't be read */ 4303 4303 if (!udev->bos) 4304 - return; 4304 + return -EINVAL; 4305 4305 4306 4306 u1_mel = udev->bos->ss_cap->bU1devExitLat; 4307 4307 u2_mel = udev->bos->ss_cap->bU2DevExitLat; ··· 4312 4312 */ 4313 4313 if ((state == USB3_LPM_U1 && u1_mel == 0) || 4314 4314 (state == USB3_LPM_U2 && u2_mel == 0)) 4315 - return; 4315 + return -EINVAL; 4316 4316 4317 4317 /* We allow the host controller to set the U1/U2 timeout internally 4318 4318 * first, so that it can change its schedule to account for the ··· 4323 4323 4324 4324 /* xHCI host controller doesn't want to enable this LPM state. */ 4325 4325 if (timeout == 0) 4326 - return; 4326 + return -EINVAL; 4327 4327 4328 4328 if (timeout < 0) { 4329 4329 dev_warn(&udev->dev, "Could not enable %s link state, " 4330 4330 "xHCI error %i.\n", usb3_lpm_names[state], 4331 4331 timeout); 4332 - return; 4332 + return timeout; 4333 4333 } 4334 4334 4335 4335 if (usb_set_lpm_timeout(udev, state, timeout)) { ··· 4338 4338 * host know that this link state won't be enabled. 4339 4339 */ 4340 4340 hcd->driver->disable_usb3_lpm_timeout(hcd, udev, state); 4341 - return; 4341 + return -EBUSY; 4342 4342 } 4343 4343 4344 4344 if (state == USB3_LPM_U1) 4345 4345 udev->usb3_lpm_u1_enabled = 1; 4346 4346 else if (state == USB3_LPM_U2) 4347 4347 udev->usb3_lpm_u2_enabled = 1; 4348 + 4349 + return 0; 4348 4350 } 4349 4351 /* 4350 4352 * Disable the hub-initiated U1/U2 idle timeouts, and disable device-initiated ··· 4499 4497 port_dev = hub->ports[udev->portnum - 1]; 4500 4498 4501 4499 if (port_dev->usb3_lpm_u1_permit) 4502 - usb_enable_link_state(hcd, udev, USB3_LPM_U1); 4500 + if (usb_enable_link_state(hcd, udev, USB3_LPM_U1)) 4501 + return; 4503 4502 4504 4503 if (port_dev->usb3_lpm_u2_permit) 4505 - usb_enable_link_state(hcd, udev, USB3_LPM_U2); 4504 + if (usb_enable_link_state(hcd, udev, USB3_LPM_U2)) 4505 + return; 4506 4506 4507 4507 /* 4508 4508 * Enable device initiated U1/U2 with a SetFeature(U1/U2_ENABLE) request