From: Guan-Yu Lin <guanyulin@google.com>
To: gregkh@linuxfoundation.org, mathias.nyman@intel.com,
gargaditya08@live.com, kekrby@gmail.com,
jeff.johnson@oss.qualcomm.com, quic_zijuhu@quicinc.com,
andriy.shevchenko@linux.intel.com, ben@decadent.org.uk,
broonie@kernel.org, quic_wcheng@quicinc.com,
krzysztof.kozlowski@linaro.org, sumit.garg@kernel.org
Cc: linux-usb@vger.kernel.org, linux-kernel@vger.kernel.org,
Guan-Yu Lin <guanyulin@google.com>
Subject: [PATCH v14 2/4] usb: offload: add apis for offload usage tracking
Date: Wed, 4 Jun 2025 08:23:08 +0000 [thread overview]
Message-ID: <20250604082449.2029156-3-guanyulin@google.com> (raw)
In-Reply-To: <20250604082449.2029156-1-guanyulin@google.com>
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>
---
drivers/usb/core/Kconfig | 10 +++
drivers/usb/core/Makefile | 1 +
drivers/usb/core/offload.c | 136 +++++++++++++++++++++++++++++++++++++
drivers/usb/core/usb.c | 1 +
include/linux/usb.h | 18 +++++
5 files changed, 166 insertions(+)
create mode 100644 drivers/usb/core/offload.c
diff --git a/drivers/usb/core/Kconfig b/drivers/usb/core/Kconfig
index 58e3ca7e4793..d5d38657f929 100644
--- a/drivers/usb/core/Kconfig
+++ b/drivers/usb/core/Kconfig
@@ -143,3 +143,13 @@ config USB_DEFAULT_AUTHORIZATION_MODE
ACPI selecting value 2 is analogous to selecting value 0.
If unsure, keep the default value.
+
+config USB_OFFLOAD
+ bool "Enable USB offload feature"
+ depends on USB
+ depends on USB_XHCI_SIDEBAND_SUSPEND
+ help
+ Offload denotes that there is another co-processor accessing the
+ USB device via the same USB host controller, creating the
+ "offloaded USB transfers". Say Y to allow offloaded USB
+ transfers during system sleep (Suspend-to-RAM).
diff --git a/drivers/usb/core/Makefile b/drivers/usb/core/Makefile
index ac006abd13b3..df26c2885dd8 100644
--- a/drivers/usb/core/Makefile
+++ b/drivers/usb/core/Makefile
@@ -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_OFFLOAD) += offload.o
usbcore-$(CONFIG_USB_PCI) += hcd-pci.o
usbcore-$(CONFIG_ACPI) += usb-acpi.o
diff --git a/drivers/usb/core/offload.c b/drivers/usb/core/offload.c
new file mode 100644
index 000000000000..7c699f1b8d2b
--- /dev/null
+++ b/drivers/usb/core/offload.c
@@ -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);
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c
index 118fa4c93a79..43efd9d939c0 100644
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -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);
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 1b2545b4363b..86922f1233ef 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -635,6 +635,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.
@@ -723,6 +725,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;
int slot_id;
@@ -839,6 +843,20 @@ static inline void usb_mark_last_busy(struct usb_device *udev)
{ }
#endif
+#if IS_ENABLED(CONFIG_USB_OFFLOAD)
+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. */
--
2.49.0.1204.g71687c7c1d-goog
next prev parent reply other threads:[~2025-06-04 8:25 UTC|newest]
Thread overview: 11+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-04 8:23 [PATCH v14 0/4] Support system sleep with offloaded usb transfers Guan-Yu Lin
2025-06-04 8:23 ` [PATCH v14 1/4] usb: xhci-plat: separate dev_pm_ops for each pm_event Guan-Yu Lin
2025-06-04 8:23 ` Guan-Yu Lin [this message]
2025-06-04 8:23 ` [PATCH v14 3/4] xhci: sideband: add api to trace sideband usage Guan-Yu Lin
2025-06-04 8:23 ` [PATCH v14 4/4] usb: host: enable USB offload during system sleep Guan-Yu Lin
2025-06-04 10:56 ` [PATCH v14 0/4] Support system sleep with offloaded usb transfers Greg KH
2025-06-04 12:32 ` Guan-Yu Lin
2025-06-19 9:27 ` Greg KH
2025-07-01 15:05 ` Guan-Yu Lin
2025-07-02 4:33 ` Guan-Yu Lin
2025-07-02 5:11 ` Jung Daehwan
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20250604082449.2029156-3-guanyulin@google.com \
--to=guanyulin@google.com \
--cc=andriy.shevchenko@linux.intel.com \
--cc=ben@decadent.org.uk \
--cc=broonie@kernel.org \
--cc=gargaditya08@live.com \
--cc=gregkh@linuxfoundation.org \
--cc=jeff.johnson@oss.qualcomm.com \
--cc=kekrby@gmail.com \
--cc=krzysztof.kozlowski@linaro.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-usb@vger.kernel.org \
--cc=mathias.nyman@intel.com \
--cc=quic_wcheng@quicinc.com \
--cc=quic_zijuhu@quicinc.com \
--cc=sumit.garg@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox