mirror of
https://github.com/torvalds/linux.git
synced 2026-06-04 04:23:35 +02:00
usb: offload: add apis for offload usage tracking
Introduce offload_usage and corresponding apis to track offload usage on each USB device. Offload denotes that there is another co-processor accessing the USB device via the same USB host controller. To optimize power usage, it's essential to monitor whether the USB device is actively used by other co-processor. This information is vital when determining if a USB device can be safely suspended during system power state transitions. Signed-off-by: Guan-Yu Lin <guanyulin@google.com> Link: https://lore.kernel.org/r/20250911142051.90822-3-guanyulin@google.com Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org> Link: https://lore.kernel.org/r/20250911142051.90822-3-guanyulin@google.com
This commit is contained in:
parent
ddb473a51b
commit
7f70b89b2b
|
|
@ -9,6 +9,7 @@ usbcore-y += devio.o notify.o generic.o quirks.o devices.o
|
|||
usbcore-y += phy.o port.o
|
||||
|
||||
usbcore-$(CONFIG_OF) += of.o
|
||||
usbcore-$(CONFIG_USB_XHCI_SIDEBAND) += offload.o
|
||||
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
|
||||
usbcore-$(CONFIG_ACPI) += usb-acpi.o
|
||||
|
||||
|
|
|
|||
136
drivers/usb/core/offload.c
Normal file
136
drivers/usb/core/offload.c
Normal file
|
|
@ -0,0 +1,136 @@
|
|||
// SPDX-License-Identifier: GPL-2.0
|
||||
|
||||
/*
|
||||
* offload.c - USB offload related functions
|
||||
*
|
||||
* Copyright (c) 2025, Google LLC.
|
||||
*
|
||||
* Author: Guan-Yu Lin
|
||||
*/
|
||||
|
||||
#include <linux/usb.h>
|
||||
|
||||
#include "usb.h"
|
||||
|
||||
/**
|
||||
* usb_offload_get - increment the offload_usage of a USB device
|
||||
* @udev: the USB device to increment its offload_usage
|
||||
*
|
||||
* Incrementing the offload_usage of a usb_device indicates that offload is
|
||||
* 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.
|
||||
*
|
||||
* Return: 0 on success. A negative error code otherwise.
|
||||
*/
|
||||
int usb_offload_get(struct usb_device *udev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usb_lock_device(udev);
|
||||
if (udev->state == USB_STATE_NOTATTACHED) {
|
||||
usb_unlock_device(udev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (udev->state == USB_STATE_SUSPENDED ||
|
||||
udev->offload_at_suspend) {
|
||||
usb_unlock_device(udev);
|
||||
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);
|
||||
return ret;
|
||||
}
|
||||
|
||||
udev->offload_usage++;
|
||||
usb_autosuspend_device(udev);
|
||||
usb_unlock_device(udev);
|
||||
|
||||
return ret;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_offload_get);
|
||||
|
||||
/**
|
||||
* usb_offload_put - drop the offload_usage of a USB device
|
||||
* @udev: the USB device to drop its offload_usage
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Return: 0 on success. A negative error code otherwise.
|
||||
*/
|
||||
int usb_offload_put(struct usb_device *udev)
|
||||
{
|
||||
int ret;
|
||||
|
||||
usb_lock_device(udev);
|
||||
if (udev->state == USB_STATE_NOTATTACHED) {
|
||||
usb_unlock_device(udev);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (udev->state == USB_STATE_SUSPENDED ||
|
||||
udev->offload_at_suspend) {
|
||||
usb_unlock_device(udev);
|
||||
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);
|
||||
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;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_offload_put);
|
||||
|
||||
/**
|
||||
* usb_offload_check - check offload activities on a USB device
|
||||
* @udev: the USB device to check its offload activity.
|
||||
*
|
||||
* Check if there are any offload activity on the USB device right now. This
|
||||
* information could be used for power management or other forms of resource
|
||||
* management.
|
||||
*
|
||||
* The caller must hold @udev's device lock. In addition, the caller should
|
||||
* ensure downstream usb devices are all either suspended or marked as
|
||||
* "offload_at_suspend" to ensure the correctness of the return value.
|
||||
*
|
||||
* Returns true on any offload activity, false otherwise.
|
||||
*/
|
||||
bool usb_offload_check(struct usb_device *udev) __must_hold(&udev->dev->mutex)
|
||||
{
|
||||
struct usb_device *child;
|
||||
bool active;
|
||||
int port1;
|
||||
|
||||
usb_hub_for_each_child(udev, port1, child) {
|
||||
usb_lock_device(child);
|
||||
active = usb_offload_check(child);
|
||||
usb_unlock_device(child);
|
||||
if (active)
|
||||
return true;
|
||||
}
|
||||
|
||||
return !!udev->offload_usage;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(usb_offload_check);
|
||||
|
|
@ -670,6 +670,7 @@ struct usb_device *usb_alloc_dev(struct usb_device *parent,
|
|||
set_dev_node(&dev->dev, dev_to_node(bus->sysdev));
|
||||
dev->state = USB_STATE_ATTACHED;
|
||||
dev->lpm_disable_count = 1;
|
||||
dev->offload_usage = 0;
|
||||
atomic_set(&dev->urbnum, 0);
|
||||
|
||||
INIT_LIST_HEAD(&dev->ep0.urb_list);
|
||||
|
|
|
|||
|
|
@ -636,6 +636,8 @@ struct usb3_lpm_parameters {
|
|||
* @do_remote_wakeup: remote wakeup should be enabled
|
||||
* @reset_resume: needs reset instead of resume
|
||||
* @port_is_suspended: the upstream port is suspended (L2 or U3)
|
||||
* @offload_at_suspend: offload activities during suspend is enabled.
|
||||
* @offload_usage: number of offload activities happening on this usb device.
|
||||
* @slot_id: Slot ID assigned by xHCI
|
||||
* @l1_params: best effor service latency for USB2 L1 LPM state, and L1 timeout.
|
||||
* @u1_params: exit latencies for USB3 U1 LPM state, and hub-initiated timeout.
|
||||
|
|
@ -724,6 +726,8 @@ struct usb_device {
|
|||
unsigned do_remote_wakeup:1;
|
||||
unsigned reset_resume:1;
|
||||
unsigned port_is_suspended:1;
|
||||
unsigned offload_at_suspend:1;
|
||||
int offload_usage;
|
||||
enum usb_link_tunnel_mode tunnel_mode;
|
||||
struct device_link *usb4_link;
|
||||
|
||||
|
|
@ -841,6 +845,20 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
|
|||
{ }
|
||||
#endif
|
||||
|
||||
#if IS_ENABLED(CONFIG_USB_XHCI_SIDEBAND)
|
||||
int usb_offload_get(struct usb_device *udev);
|
||||
int usb_offload_put(struct usb_device *udev);
|
||||
bool usb_offload_check(struct usb_device *udev);
|
||||
#else
|
||||
|
||||
static inline int usb_offload_get(struct usb_device *udev)
|
||||
{ return 0; }
|
||||
static inline int usb_offload_put(struct usb_device *udev)
|
||||
{ return 0; }
|
||||
static inline bool usb_offload_check(struct usb_device *udev)
|
||||
{ return false; }
|
||||
#endif
|
||||
|
||||
extern int usb_disable_lpm(struct usb_device *udev);
|
||||
extern void usb_enable_lpm(struct usb_device *udev);
|
||||
/* Same as above, but these functions lock/unlock the bandwidth_mutex. */
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user