All of lore.kernel.org
 help / color / mirror / Atom feed
From: Guan-Yu Lin <guanyulin@google.com>
To: gregkh@linuxfoundation.org, mathias.nyman@intel.com,
	 stern@rowland.harvard.edu, royluo@google.com,
	benjamin.tissoires@redhat.com,  hadess@hadess.net,
	heikki.krogerus@linux.intel.com, grundler@chromium.org,
	 oneukum@suse.com, dianders@chromium.org, yajun.deng@linux.dev
Cc: linux-kernel@vger.kernel.org, linux-usb@vger.kernel.org,
	 Guan-Yu Lin <guanyulin@google.com>
Subject: [PATCH] usb: host: enable suspend-to-RAM control in userspace
Date: Fri,  2 Feb 2024 08:42:35 +0000	[thread overview]
Message-ID: <20240202084815.3064391-1-guanyulin@google.com> (raw)

In systems with both a main processor and a co-processor, asynchronous
controller management can lead to conflicts. For example, the main
processor might attempt to suspend a USB device while the co-processor
is actively transferring data. To address this, we introduce a new
sysfs entry, "disable_suspend2ram", which allows userspace to control
the suspend-to-RAM functionality of devices on a specific USB bus.
Since the userspace has visibility into the activities of both
processors, it can resolve potential conflicts.

Signed-off-by: Guan-Yu Lin <guanyulin@google.com>
---
 Documentation/ABI/testing/sysfs-bus-usb | 10 +++++++
 drivers/usb/core/driver.c               | 12 ++++++++
 drivers/usb/core/sysfs.c                | 29 ++++++++++++++++++
 drivers/usb/host/xhci-plat.c            | 40 ++++++++++++++++++++++---
 include/linux/usb.h                     |  4 +++
 5 files changed, 91 insertions(+), 4 deletions(-)

diff --git a/Documentation/ABI/testing/sysfs-bus-usb b/Documentation/ABI/testing/sysfs-bus-usb
index 2b7108e21977..f6a4f496c8f6 100644
--- a/Documentation/ABI/testing/sysfs-bus-usb
+++ b/Documentation/ABI/testing/sysfs-bus-usb
@@ -19,6 +19,16 @@ Description:
 		would be authorized by default.
 		The value can be 1 or 0. It's by default 1.
 
+What:		/sys/bus/usb/devices/usbX/disable_suspend2ram
+Date:		January 2024
+Contact:	Guan-Yu Lin <guanyulin@google.com>
+Description:
+		Disables "suspend-to-RAM" system power management in USB devices
+		and their host contorller under this usb bus. The modification of
+		this entry should be done when the system is active to ensure the
+		correctness of system power state transitions.
+		The value can be 1 or 0. It's by default 0.
+
 What:		/sys/bus/usb/device/.../authorized
 Date:		July 2008
 KernelVersion:	2.6.26
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index e02ba15f6e34..c555e2b1ddda 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1569,6 +1569,12 @@ int usb_suspend(struct device *dev, pm_message_t msg)
 	struct usb_device	*udev = to_usb_device(dev);
 	int r;
 
+	if (msg.event == PM_EVENT_SUSPEND && udev->bus->disable_suspend2ram) {
+		/* Skip "suspend-to-RAM" process under the same USB bus */
+		dev_dbg(dev, "suspend-to-RAM disabled, skipping dev_pm_ops.suspend\n");
+		return 0;
+	}
+
 	unbind_no_pm_drivers_interfaces(udev);
 
 	/* From now on we are sure all drivers support suspend/resume
@@ -1605,6 +1611,12 @@ int usb_resume(struct device *dev, pm_message_t msg)
 	struct usb_device	*udev = to_usb_device(dev);
 	int			status;
 
+	if (msg.event == PM_EVENT_RESUME && udev->bus->disable_suspend2ram) {
+		/* Skip "suspend-to-RAM" process under the same USB bus */
+		dev_dbg(dev, "suspend-to-RAM disabled, skipping dev_pm_ops.resume\n");
+		return 0;
+	}
+
 	/* For all calls, take the device back to full power and
 	 * tell the PM core in case it was autosuspended previously.
 	 * Unbind the interfaces that will need rebinding later,
diff --git a/drivers/usb/core/sysfs.c b/drivers/usb/core/sysfs.c
index 5d21718afb05..7a228ea95506 100644
--- a/drivers/usb/core/sysfs.c
+++ b/drivers/usb/core/sysfs.c
@@ -68,6 +68,34 @@ static ssize_t bMaxPower_show(struct device *dev,
 }
 static DEVICE_ATTR_RO(bMaxPower);
 
+static ssize_t disable_suspend2ram_show(struct device *dev,
+					struct device_attribute *attr, char *buf)
+{
+	struct usb_device *udev;
+
+	udev = to_usb_device(dev);
+	return sysfs_emit(buf, "%d\n", !!(udev->bus->disable_suspend2ram));
+}
+
+static ssize_t disable_suspend2ram_store(struct device *dev,
+					 struct device_attribute *attr,
+					 const char *buf, size_t count)
+{
+	struct usb_device *udev = to_usb_device(dev);
+	bool val;
+	int rc;
+
+	if (kstrtobool(buf, &val) != 0)
+		return -EINVAL;
+	rc = usb_lock_device_interruptible(udev);
+	if (rc < 0)
+		return -EINTR;
+	udev->bus->disable_suspend2ram = !!(val);
+	usb_unlock_device(udev);
+	return count;
+}
+static DEVICE_ATTR_RW(disable_suspend2ram);
+
 static ssize_t configuration_show(struct device *dev,
 		struct device_attribute *attr, char *buf)
 {
@@ -984,6 +1012,7 @@ static DEVICE_ATTR_RW(interface_authorized_default);
 static struct attribute *usb_bus_attrs[] = {
 		&dev_attr_authorized_default.attr,
 		&dev_attr_interface_authorized_default.attr,
+		&dev_attr_disable_suspend2ram.attr,
 		NULL,
 };
 
diff --git a/drivers/usb/host/xhci-plat.c b/drivers/usb/host/xhci-plat.c
index f04fde19f551..86733249fcdc 100644
--- a/drivers/usb/host/xhci-plat.c
+++ b/drivers/usb/host/xhci-plat.c
@@ -436,12 +436,18 @@ void xhci_plat_remove(struct platform_device *dev)
 }
 EXPORT_SYMBOL_GPL(xhci_plat_remove);
 
-static int xhci_plat_suspend(struct device *dev)
+static int xhci_plat_suspend_common(struct device *dev, struct pm_message pmsg)
 {
 	struct usb_hcd	*hcd = dev_get_drvdata(dev);
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	int ret;
 
+	if (pmsg.event == PM_EVENT_SUSPEND && hcd->self.disable_suspend2ram) {
+		/* Skip "suspend-to-RAM" process under the same USB bus */
+		dev_dbg(dev, "suspend-to-RAM disabled, skipping dev_pm_ops.suspend\n");
+		return 0;
+	}
+
 	if (pm_runtime_suspended(dev))
 		pm_runtime_resume(dev);
 
@@ -464,12 +470,33 @@ static int xhci_plat_suspend(struct device *dev)
 	return 0;
 }
 
+static int xhci_plat_suspend(struct device *dev)
+{
+	return xhci_plat_suspend_common(dev, PMSG_SUSPEND);
+}
+
+static int xhci_plat_freeze(struct device *dev)
+{
+	return xhci_plat_suspend_common(dev, PMSG_FREEZE);
+}
+
+static int xhci_plat_poweroff(struct device *dev)
+{
+	return xhci_plat_suspend_common(dev, PMSG_HIBERNATE);
+}
+
 static int xhci_plat_resume_common(struct device *dev, struct pm_message pmsg)
 {
 	struct usb_hcd	*hcd = dev_get_drvdata(dev);
 	struct xhci_hcd	*xhci = hcd_to_xhci(hcd);
 	int ret;
 
+	if (pmsg.event == PM_EVENT_RESUME && hcd->self.disable_suspend2ram) {
+		/* Skip "suspend-to-RAM" process under the same USB bus */
+		dev_dbg(dev, "suspend-to-RAM disabled, skipping dev_pm_ops.resume\n");
+		return 0;
+	}
+
 	if (!device_may_wakeup(dev) && (xhci->quirks & XHCI_SUSPEND_RESUME_CLKS)) {
 		ret = clk_prepare_enable(xhci->clk);
 		if (ret)
@@ -510,6 +537,11 @@ static int xhci_plat_resume(struct device *dev)
 	return xhci_plat_resume_common(dev, PMSG_RESUME);
 }
 
+static int xhci_plat_thaw(struct device *dev)
+{
+	return xhci_plat_resume_common(dev, PMSG_THAW);
+}
+
 static int xhci_plat_restore(struct device *dev)
 {
 	return xhci_plat_resume_common(dev, PMSG_RESTORE);
@@ -539,9 +571,9 @@ static int __maybe_unused xhci_plat_runtime_resume(struct device *dev)
 const struct dev_pm_ops xhci_plat_pm_ops = {
 	.suspend = pm_sleep_ptr(xhci_plat_suspend),
 	.resume = pm_sleep_ptr(xhci_plat_resume),
-	.freeze = pm_sleep_ptr(xhci_plat_suspend),
-	.thaw = pm_sleep_ptr(xhci_plat_resume),
-	.poweroff = pm_sleep_ptr(xhci_plat_suspend),
+	.freeze = pm_sleep_ptr(xhci_plat_freeze),
+	.thaw = pm_sleep_ptr(xhci_plat_thaw),
+	.poweroff = pm_sleep_ptr(xhci_plat_poweroff),
 	.restore = pm_sleep_ptr(xhci_plat_restore),
 
 	SET_RUNTIME_PM_OPS(xhci_plat_runtime_suspend,
diff --git a/include/linux/usb.h b/include/linux/usb.h
index 9e52179872a5..a3b8f799c5a4 100644
--- a/include/linux/usb.h
+++ b/include/linux/usb.h
@@ -465,6 +465,10 @@ struct usb_bus {
 					 * the ep queue on a short transfer
 					 * with the URB_SHORT_NOT_OK flag set.
 					 */
+	unsigned disable_suspend2ram:1; /* Disables the "suspend-to-RAM" system
+					 * power management of USB devices on this
+					 * bus and their hcd.
+					 */
 	unsigned no_sg_constraint:1;	/* no sg constraint */
 	unsigned sg_tablesize;		/* 0 or largest number of sg list entries */
 
-- 
2.43.0.594.gd9cf4e227d-goog


             reply	other threads:[~2024-02-02  8:48 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-02-02  8:42 Guan-Yu Lin [this message]
2024-02-02 14:51 ` [PATCH] usb: host: enable suspend-to-RAM control in userspace Mathias Nyman
2024-02-02 14:57   ` Greg KH
2024-02-02 14:55 ` Greg KH

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=20240202084815.3064391-1-guanyulin@google.com \
    --to=guanyulin@google.com \
    --cc=benjamin.tissoires@redhat.com \
    --cc=dianders@chromium.org \
    --cc=gregkh@linuxfoundation.org \
    --cc=grundler@chromium.org \
    --cc=hadess@hadess.net \
    --cc=heikki.krogerus@linux.intel.com \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-usb@vger.kernel.org \
    --cc=mathias.nyman@intel.com \
    --cc=oneukum@suse.com \
    --cc=royluo@google.com \
    --cc=stern@rowland.harvard.edu \
    --cc=yajun.deng@linux.dev \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.