Skip to content

Commit 8306095

Browse files
author
Sarah Sharp
committed
USB: Disable USB 3.0 LPM in critical sections.
There are several places where the USB core needs to disable USB 3.0 Link PM: - usb_bind_interface - usb_unbind_interface - usb_driver_claim_interface - usb_port_suspend/usb_port_resume - usb_reset_and_verify_device - usb_set_interface - usb_reset_configuration - usb_set_configuration Use the new LPM disable/enable functions to temporarily disable LPM around these critical sections. We need to protect the critical section around binding and unbinding USB interface drivers. USB drivers may want to disable hub-initiated USB 3.0 LPM, which will change the value of the U1/U2 timeouts that the xHCI driver will install. We need to disable LPM completely until the driver is bound to the interface, and the driver has a chance to enable whatever alternate interface setting it needs in its probe routine. Then re-enable USB3 LPM, and recalculate the U1/U2 timeout values. We also need to disable LPM in usb_driver_claim_interface, because drivers like usbfs can bind to an interface through that function. Note, there is no way currently for userspace drivers to disable hub-initiated USB 3.0 LPM. Revisit this later. When a driver is unbound, the U1/U2 timeouts may change because we are unbinding the last driver that needed hub-initiated USB 3.0 LPM to be disabled. USB LPM must be disabled when a USB device is going to be suspended. The USB 3.0 spec does not define a state transition from U1 or U2 into U3, so we need to bring the device into U0 by disabling LPM before we can place it into U3. Therefore, call usb_unlocked_disable_lpm() in usb_port_suspend(), and call usb_unlocked_enable_lpm() in usb_port_resume(). If the port suspend fails, make sure to re-enable LPM by calling usb_unlocked_enable_lpm(), since usb_port_resume() will not be called on a failed port suspend. USB 3.0 devices lose their USB 3.0 LPM settings (including whether USB device-initiated LPM is enabled) across device suspend. Therefore, disable LPM before the device will be reset in usb_reset_and_verify_device(), and re-enable LPM after the reset is complete and the configuration/alt settings are re-installed. The calculated U1/U2 timeout values are heavily dependent on what USB device endpoints are currently enabled. When any of the enabled endpoints on the device might change, due to a new configuration, or new alternate interface setting, we need to first disable USB 3.0 LPM, add or delete endpoints from the xHCI schedule, install the new interfaces and alt settings, and then re-enable LPM. Do this in usb_set_interface, usb_reset_configuration, and usb_set_configuration. Basically, there is a call to disable and then enable LPM in all functions that lock the bandwidth_mutex. One exception is usb_disable_device, because the device is disconnecting or otherwise going away, and we should not care about whether USB 3.0 LPM is enabled. Signed-off-by: Sarah Sharp <[email protected]>
1 parent 1ea7e0e commit 8306095

File tree

4 files changed

+120
-1
lines changed

4 files changed

+120
-1
lines changed

drivers/usb/core/driver.c

Lines changed: 53 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,7 @@ static int usb_probe_interface(struct device *dev)
288288
struct usb_device *udev = interface_to_usbdev(intf);
289289
const struct usb_device_id *id;
290290
int error = -ENODEV;
291+
int lpm_disable_error;
291292

292293
dev_dbg(dev, "%s\n", __func__);
293294

@@ -324,6 +325,25 @@ static int usb_probe_interface(struct device *dev)
324325
if (driver->supports_autosuspend)
325326
pm_runtime_enable(dev);
326327

328+
/* If the new driver doesn't allow hub-initiated LPM, and we can't
329+
* disable hub-initiated LPM, then fail the probe.
330+
*
331+
* Otherwise, leaving LPM enabled should be harmless, because the
332+
* endpoint intervals should remain the same, and the U1/U2 timeouts
333+
* should remain the same.
334+
*
335+
* If we need to install alt setting 0 before probe, or another alt
336+
* setting during probe, that should also be fine. usb_set_interface()
337+
* will attempt to disable LPM, and fail if it can't disable it.
338+
*/
339+
lpm_disable_error = usb_unlocked_disable_lpm(udev);
340+
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
341+
dev_err(&intf->dev, "%s Failed to disable LPM for driver %s\n.",
342+
__func__, driver->name);
343+
error = lpm_disable_error;
344+
goto err;
345+
}
346+
327347
/* Carry out a deferred switch to altsetting 0 */
328348
if (intf->needs_altsetting0) {
329349
error = usb_set_interface(udev, intf->altsetting[0].
@@ -338,6 +358,11 @@ static int usb_probe_interface(struct device *dev)
338358
goto err;
339359

340360
intf->condition = USB_INTERFACE_BOUND;
361+
362+
/* If the LPM disable succeeded, balance the ref counts. */
363+
if (!lpm_disable_error)
364+
usb_unlocked_enable_lpm(udev);
365+
341366
usb_autosuspend_device(udev);
342367
return error;
343368

@@ -361,14 +386,21 @@ static int usb_unbind_interface(struct device *dev)
361386
struct usb_driver *driver = to_usb_driver(dev->driver);
362387
struct usb_interface *intf = to_usb_interface(dev);
363388
struct usb_device *udev;
364-
int error, r;
389+
int error, r, lpm_disable_error;
365390

366391
intf->condition = USB_INTERFACE_UNBINDING;
367392

368393
/* Autoresume for set_interface call below */
369394
udev = interface_to_usbdev(intf);
370395
error = usb_autoresume_device(udev);
371396

397+
/* Hub-initiated LPM policy may change, so attempt to disable LPM until
398+
* the driver is unbound. If LPM isn't disabled, that's fine because it
399+
* wouldn't be enabled unless all the bound interfaces supported
400+
* hub-initiated LPM.
401+
*/
402+
lpm_disable_error = usb_unlocked_disable_lpm(udev);
403+
372404
/* Terminate all URBs for this interface unless the driver
373405
* supports "soft" unbinding.
374406
*/
@@ -402,6 +434,10 @@ static int usb_unbind_interface(struct device *dev)
402434
intf->condition = USB_INTERFACE_UNBOUND;
403435
intf->needs_remote_wakeup = 0;
404436

437+
/* Attempt to re-enable USB3 LPM, if the disable succeeded. */
438+
if (!lpm_disable_error)
439+
usb_unlocked_enable_lpm(udev);
440+
405441
/* Unbound interfaces are always runtime-PM-disabled and -suspended */
406442
if (driver->supports_autosuspend)
407443
pm_runtime_disable(dev);
@@ -442,17 +478,29 @@ int usb_driver_claim_interface(struct usb_driver *driver,
442478
struct usb_interface *iface, void *priv)
443479
{
444480
struct device *dev = &iface->dev;
481+
struct usb_device *udev;
445482
int retval = 0;
483+
int lpm_disable_error;
446484

447485
if (dev->driver)
448486
return -EBUSY;
449487

488+
udev = interface_to_usbdev(iface);
489+
450490
dev->driver = &driver->drvwrap.driver;
451491
usb_set_intfdata(iface, priv);
452492
iface->needs_binding = 0;
453493

454494
iface->condition = USB_INTERFACE_BOUND;
455495

496+
/* Disable LPM until this driver is bound. */
497+
lpm_disable_error = usb_unlocked_disable_lpm(udev);
498+
if (lpm_disable_error && driver->disable_hub_initiated_lpm) {
499+
dev_err(&iface->dev, "%s Failed to disable LPM for driver %s\n.",
500+
__func__, driver->name);
501+
return -ENOMEM;
502+
}
503+
456504
/* Claimed interfaces are initially inactive (suspended) and
457505
* runtime-PM-enabled, but only if the driver has autosuspend
458506
* support. Otherwise they are marked active, to prevent the
@@ -471,6 +519,10 @@ int usb_driver_claim_interface(struct usb_driver *driver,
471519
if (device_is_registered(dev))
472520
retval = device_bind_driver(dev);
473521

522+
/* Attempt to re-enable USB3 LPM, if the disable was successful. */
523+
if (!lpm_disable_error)
524+
usb_unlocked_enable_lpm(udev);
525+
474526
return retval;
475527
}
476528
EXPORT_SYMBOL_GPL(usb_driver_claim_interface);

drivers/usb/core/hub.c

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2702,6 +2702,12 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
27022702
if (udev->usb2_hw_lpm_enabled == 1)
27032703
usb_set_usb2_hardware_lpm(udev, 0);
27042704

2705+
if (usb_unlocked_disable_lpm(udev)) {
2706+
dev_err(&udev->dev, "%s Failed to disable LPM before suspend\n.",
2707+
__func__);
2708+
return -ENOMEM;
2709+
}
2710+
27052711
/* see 7.1.7.6 */
27062712
if (hub_is_superspeed(hub->hdev))
27072713
status = set_port_feature(hub->hdev,
@@ -2725,6 +2731,9 @@ int usb_port_suspend(struct usb_device *udev, pm_message_t msg)
27252731
if (udev->usb2_hw_lpm_capable == 1)
27262732
usb_set_usb2_hardware_lpm(udev, 1);
27272733

2734+
/* Try to enable USB3 LPM again */
2735+
usb_unlocked_enable_lpm(udev);
2736+
27282737
/* System sleep transitions should never fail */
27292738
if (!PMSG_IS_AUTO(msg))
27302739
status = 0;
@@ -2922,6 +2931,9 @@ int usb_port_resume(struct usb_device *udev, pm_message_t msg)
29222931
/* Try to enable USB2 hardware LPM */
29232932
if (udev->usb2_hw_lpm_capable == 1)
29242933
usb_set_usb2_hardware_lpm(udev, 1);
2934+
2935+
/* Try to enable USB3 LPM */
2936+
usb_unlocked_enable_lpm(udev);
29252937
}
29262938

29272939
return status;
@@ -4681,11 +4693,22 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
46814693
goto done;
46824694

46834695
mutex_lock(hcd->bandwidth_mutex);
4696+
/* Disable LPM while we reset the device and reinstall the alt settings.
4697+
* Device-initiated LPM settings, and system exit latency settings are
4698+
* cleared when the device is reset, so we have to set them up again.
4699+
*/
4700+
ret = usb_disable_lpm(udev);
4701+
if (ret) {
4702+
dev_err(&udev->dev, "%s Failed to disable LPM\n.", __func__);
4703+
mutex_unlock(hcd->bandwidth_mutex);
4704+
goto done;
4705+
}
46844706
ret = usb_hcd_alloc_bandwidth(udev, udev->actconfig, NULL, NULL);
46854707
if (ret < 0) {
46864708
dev_warn(&udev->dev,
46874709
"Busted HC? Not enough HCD resources for "
46884710
"old configuration.\n");
4711+
usb_enable_lpm(udev);
46894712
mutex_unlock(hcd->bandwidth_mutex);
46904713
goto re_enumerate;
46914714
}
@@ -4697,6 +4720,7 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
46974720
dev_err(&udev->dev,
46984721
"can't restore configuration #%d (error=%d)\n",
46994722
udev->actconfig->desc.bConfigurationValue, ret);
4723+
usb_enable_lpm(udev);
47004724
mutex_unlock(hcd->bandwidth_mutex);
47014725
goto re_enumerate;
47024726
}
@@ -4735,10 +4759,13 @@ static int usb_reset_and_verify_device(struct usb_device *udev)
47354759
desc->bInterfaceNumber,
47364760
desc->bAlternateSetting,
47374761
ret);
4762+
usb_unlocked_enable_lpm(udev);
47384763
goto re_enumerate;
47394764
}
47404765
}
47414766

4767+
/* Now that the alt settings are re-installed, enable LPM. */
4768+
usb_unlocked_enable_lpm(udev);
47424769
done:
47434770
return 0;
47444771

drivers/usb/core/message.c

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1308,10 +1308,19 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
13081308
* Remove the current alt setting and add the new alt setting.
13091309
*/
13101310
mutex_lock(hcd->bandwidth_mutex);
1311+
/* Disable LPM, and re-enable it once the new alt setting is installed,
1312+
* so that the xHCI driver can recalculate the U1/U2 timeouts.
1313+
*/
1314+
if (usb_disable_lpm(dev)) {
1315+
dev_err(&iface->dev, "%s Failed to disable LPM\n.", __func__);
1316+
mutex_unlock(hcd->bandwidth_mutex);
1317+
return -ENOMEM;
1318+
}
13111319
ret = usb_hcd_alloc_bandwidth(dev, NULL, iface->cur_altsetting, alt);
13121320
if (ret < 0) {
13131321
dev_info(&dev->dev, "Not enough bandwidth for altsetting %d\n",
13141322
alternate);
1323+
usb_enable_lpm(dev);
13151324
mutex_unlock(hcd->bandwidth_mutex);
13161325
return ret;
13171326
}
@@ -1334,6 +1343,7 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
13341343
} else if (ret < 0) {
13351344
/* Re-instate the old alt setting */
13361345
usb_hcd_alloc_bandwidth(dev, NULL, alt, iface->cur_altsetting);
1346+
usb_enable_lpm(dev);
13371347
mutex_unlock(hcd->bandwidth_mutex);
13381348
return ret;
13391349
}
@@ -1354,6 +1364,9 @@ int usb_set_interface(struct usb_device *dev, int interface, int alternate)
13541364

13551365
iface->cur_altsetting = alt;
13561366

1367+
/* Now that the interface is installed, re-enable LPM. */
1368+
usb_unlocked_enable_lpm(dev);
1369+
13571370
/* If the interface only has one altsetting and the device didn't
13581371
* accept the request, we attempt to carry out the equivalent action
13591372
* by manually clearing the HALT feature for each endpoint in the
@@ -1437,6 +1450,14 @@ int usb_reset_configuration(struct usb_device *dev)
14371450
config = dev->actconfig;
14381451
retval = 0;
14391452
mutex_lock(hcd->bandwidth_mutex);
1453+
/* Disable LPM, and re-enable it once the configuration is reset, so
1454+
* that the xHCI driver can recalculate the U1/U2 timeouts.
1455+
*/
1456+
if (usb_disable_lpm(dev)) {
1457+
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
1458+
mutex_unlock(hcd->bandwidth_mutex);
1459+
return -ENOMEM;
1460+
}
14401461
/* Make sure we have enough bandwidth for each alternate setting 0 */
14411462
for (i = 0; i < config->desc.bNumInterfaces; i++) {
14421463
struct usb_interface *intf = config->interface[i];
@@ -1465,6 +1486,7 @@ int usb_reset_configuration(struct usb_device *dev)
14651486
usb_hcd_alloc_bandwidth(dev, NULL,
14661487
alt, intf->cur_altsetting);
14671488
}
1489+
usb_enable_lpm(dev);
14681490
mutex_unlock(hcd->bandwidth_mutex);
14691491
return retval;
14701492
}
@@ -1502,6 +1524,8 @@ int usb_reset_configuration(struct usb_device *dev)
15021524
create_intf_ep_devs(intf);
15031525
}
15041526
}
1527+
/* Now that the interfaces are installed, re-enable LPM. */
1528+
usb_unlocked_enable_lpm(dev);
15051529
return 0;
15061530
}
15071531
EXPORT_SYMBOL_GPL(usb_reset_configuration);
@@ -1763,8 +1787,18 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
17631787
* this call fails, the device state is unchanged.
17641788
*/
17651789
mutex_lock(hcd->bandwidth_mutex);
1790+
/* Disable LPM, and re-enable it once the new configuration is
1791+
* installed, so that the xHCI driver can recalculate the U1/U2
1792+
* timeouts.
1793+
*/
1794+
if (usb_disable_lpm(dev)) {
1795+
dev_err(&dev->dev, "%s Failed to disable LPM\n.", __func__);
1796+
mutex_unlock(hcd->bandwidth_mutex);
1797+
return -ENOMEM;
1798+
}
17661799
ret = usb_hcd_alloc_bandwidth(dev, cp, NULL, NULL);
17671800
if (ret < 0) {
1801+
usb_enable_lpm(dev);
17681802
mutex_unlock(hcd->bandwidth_mutex);
17691803
usb_autosuspend_device(dev);
17701804
goto free_interfaces;
@@ -1784,6 +1818,7 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
17841818
if (!cp) {
17851819
usb_set_device_state(dev, USB_STATE_ADDRESS);
17861820
usb_hcd_alloc_bandwidth(dev, NULL, NULL, NULL);
1821+
usb_enable_lpm(dev);
17871822
mutex_unlock(hcd->bandwidth_mutex);
17881823
usb_autosuspend_device(dev);
17891824
goto free_interfaces;
@@ -1838,6 +1873,9 @@ int usb_set_configuration(struct usb_device *dev, int configuration)
18381873
!(dev->quirks & USB_QUIRK_CONFIG_INTF_STRINGS))
18391874
cp->string = usb_cache_string(dev, cp->desc.iConfiguration);
18401875

1876+
/* Now that the interfaces are installed, re-enable LPM. */
1877+
usb_unlocked_enable_lpm(dev);
1878+
18411879
/* Now that all the interfaces are set up, register them
18421880
* to trigger binding of drivers to interfaces. probe()
18431881
* routines may install different altsettings and may

include/linux/usb.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,7 @@ struct usb_device {
526526
unsigned lpm_capable:1;
527527
unsigned usb2_hw_lpm_capable:1;
528528
unsigned usb2_hw_lpm_enabled:1;
529+
unsigned usb3_lpm_enabled:1;
529530
int string_langid;
530531

531532
/* static strings from the device */
@@ -555,6 +556,7 @@ struct usb_device {
555556
struct usb3_lpm_parameters u1_params;
556557
struct usb3_lpm_parameters u2_params;
557558
unsigned lpm_disable_count;
559+
unsigned hub_initiated_lpm_disable_count;
558560
};
559561
#define to_usb_device(d) container_of(d, struct usb_device, dev)
560562

0 commit comments

Comments
 (0)