usb: host: enable suspend-to-RAM control in userspace

Message ID 20240202084815.3064391-1-guanyulin@google.com
State New
Headers
Series usb: host: enable suspend-to-RAM control in userspace |

Commit Message

Guan-Yu Lin Feb. 2, 2024, 8:42 a.m. UTC
  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(-)
  

Comments

Mathias Nyman Feb. 2, 2024, 2:51 p.m. UTC | #1
On 2.2.2024 10.42, Guan-Yu Lin wrote:
> 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>
> ---

Doesn't setting this new disable_suspend2ram break system suspend on all
other systems except this one?

On any system with a PCI xHC we end up trying to suddenly stop the xHC
host with all connected usb devices still fully up and running.

In the xhci platform device case again nothing will be stopped or suspended,
but PM framework assumes everything is suspended correctly.

So then xHC either continues running and generates interrupts, or it might
be abruptly powered off if the bus above it suspends.
(For example if the xhci platform device is created by a PCI DWC3, and it
goes to D3 state)

EHCI and other hosts face similar issues with trying to suspend the
controller when the devices connected to it are fully up and running.

To me it looks like this whole co-processor case needs to be developed and
designed into the pm framework

Thanks
Mathias
  
Greg KH Feb. 2, 2024, 2:55 p.m. UTC | #2
On Fri, Feb 02, 2024 at 08:42:35AM +0000, Guan-Yu Lin wrote:
> 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(-)

Hi,

This is the friendly patch-bot of Greg Kroah-Hartman.  You have sent him
a patch that has triggered this response.  He used to manually respond
to these common problems, but in order to save his sanity (he kept
writing the same thing over and over, yet to different people), I was
created.  Hopefully you will not take offence and will fix the problem
in your patch and resubmit it so that it can be accepted into the Linux
kernel tree.

You are receiving this message because of the following common error(s)
as indicated below:

- This looks like a new version of a previously submitted patch, but you
  did not list below the --- line any changes from the previous version.
  Please read the section entitled "The canonical patch format" in the
  kernel file, Documentation/process/submitting-patches.rst for what
  needs to be done here to properly describe this.

If you wish to discuss this problem further, or you have questions about
how to resolve this issue, please feel free to respond to this email and
Greg will reply once he has dug out from the pending patches received
from other developers.

thanks,

greg k-h's patch email bot
  
Greg KH Feb. 2, 2024, 2:57 p.m. UTC | #3
On Fri, Feb 02, 2024 at 04:51:06PM +0200, Mathias Nyman wrote:
> To me it looks like this whole co-processor case needs to be developed and
> designed into the pm framework

That is what was said for the last time this patch was submitted, but
for some reason the author ignored those review comments :(

thanks for saying it again, don't worry, I'm not taking this change.

thanks,

greg k-h
  

Patch

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 */