[PATCH v1 1/2] usb: offload: move device locking to callers in offload.c

From: Guan-Yu Lin

Date: Wed Feb 25 2026 - 01:50:54 EST


Update usb_offload_get() and usb_offload_put() to require that the
caller holds the USB device lock. Remove the internal call to
usb_lock_device() and add device_lock_assert() to ensure synchronization
is handled by the caller. These functions continue to manage the
device's power state via autoresume/autosuspend and update the
offload_usage counter.

Additionally, decouple the xHCI sideband interrupter lifecycle from the
offload usage counter by removing the calls to usb_offload_get() and
usb_offload_put() from the interrupter creation and removal paths. This
allows interrupters to be managed independently of the device's offload
activity status.

Cc: stable@xxxxxxxxxxxxxxx
Fixes: ef82a4803aab ("xhci: sideband: add api to trace sideband usage")
Signed-off-by: Guan-Yu Lin <guanyulin@xxxxxxxxxx>
---
drivers/usb/core/offload.c | 34 +++++++++++---------------------
drivers/usb/host/xhci-sideband.c | 14 +------------
2 files changed, 13 insertions(+), 35 deletions(-)

diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
index 7c699f1b8d2b..e13a4c21d61b 100644
--- a/drivers/usb/core/offload.c
+++ b/drivers/usb/core/offload.c
@@ -20,6 +20,7 @@
* enabled on this usb_device; that is, another entity is actively handling USB
* transfers. This information allows the USB driver to adjust its power
* management policy based on offload activity.
+ * The caller must hold @udev's device lock.
*
* Return: 0 on success. A negative error code otherwise.
*/
@@ -27,31 +28,25 @@ int usb_offload_get(struct usb_device *udev)
{
int ret;

- usb_lock_device(udev);
- if (udev->state == USB_STATE_NOTATTACHED) {
- usb_unlock_device(udev);
+ device_lock_assert(&udev->dev);
+
+ if (udev->state == USB_STATE_NOTATTACHED)
return -ENODEV;
- }

if (udev->state == USB_STATE_SUSPENDED ||
- udev->offload_at_suspend) {
- usb_unlock_device(udev);
+ udev->offload_at_suspend)
return -EBUSY;
- }

/*
* offload_usage could only be modified when the device is active, since
* it will alter the suspend flow of the device.
*/
ret = usb_autoresume_device(udev);
- if (ret < 0) {
- usb_unlock_device(udev);
+ if (ret < 0)
return ret;
- }

udev->offload_usage++;
usb_autosuspend_device(udev);
- usb_unlock_device(udev);

return ret;
}
@@ -64,6 +59,7 @@ EXPORT_SYMBOL_GPL(usb_offload_get);
* The inverse operation of usb_offload_get, which drops the offload_usage of
* a USB device. This information allows the USB driver to adjust its power
* management policy based on offload activity.
+ * The caller must hold @udev's device lock.
*
* Return: 0 on success. A negative error code otherwise.
*/
@@ -71,33 +67,27 @@ int usb_offload_put(struct usb_device *udev)
{
int ret;

- usb_lock_device(udev);
- if (udev->state == USB_STATE_NOTATTACHED) {
- usb_unlock_device(udev);
+ device_lock_assert(&udev->dev);
+
+ if (udev->state == USB_STATE_NOTATTACHED)
return -ENODEV;
- }

if (udev->state == USB_STATE_SUSPENDED ||
- udev->offload_at_suspend) {
- usb_unlock_device(udev);
+ udev->offload_at_suspend)
return -EBUSY;
- }

/*
* offload_usage could only be modified when the device is active, since
* it will alter the suspend flow of the device.
*/
ret = usb_autoresume_device(udev);
- if (ret < 0) {
- usb_unlock_device(udev);
+ if (ret < 0)
return ret;
- }

/* Drop the count when it wasn't 0, ignore the operation otherwise. */
if (udev->offload_usage)
udev->offload_usage--;
usb_autosuspend_device(udev);
- usb_unlock_device(udev);

return ret;
}
diff --git a/drivers/usb/host/xhci-sideband.c b/drivers/usb/host/xhci-sideband.c
index 2bd77255032b..6fc0ad658d66 100644
--- a/drivers/usb/host/xhci-sideband.c
+++ b/drivers/usb/host/xhci-sideband.c
@@ -93,8 +93,6 @@ __xhci_sideband_remove_endpoint(struct xhci_sideband *sb, struct xhci_virt_ep *e
static void
__xhci_sideband_remove_interrupter(struct xhci_sideband *sb)
{
- struct usb_device *udev;
-
lockdep_assert_held(&sb->mutex);

if (!sb->ir)
@@ -102,10 +100,6 @@ __xhci_sideband_remove_interrupter(struct xhci_sideband *sb)

xhci_remove_secondary_interrupter(xhci_to_hcd(sb->xhci), sb->ir);
sb->ir = NULL;
- udev = sb->vdev->udev;
-
- if (udev->state != USB_STATE_NOTATTACHED)
- usb_offload_put(udev);
}

/* sideband api functions */
@@ -328,9 +322,6 @@ int
xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
bool ip_autoclear, u32 imod_interval, int intr_num)
{
- int ret = 0;
- struct usb_device *udev;
-
if (!sb || !sb->xhci)
return -ENODEV;

@@ -348,12 +339,9 @@ xhci_sideband_create_interrupter(struct xhci_sideband *sb, int num_seg,
if (!sb->ir)
return -ENOMEM;

- udev = sb->vdev->udev;
- ret = usb_offload_get(udev);
-
sb->ir->ip_autoclear = ip_autoclear;

- return ret;
+ return 0;
}
EXPORT_SYMBOL_GPL(xhci_sideband_create_interrupter);

--
2.53.0.414.gf7e9f6c205-goog