[3.11.y.z extended stable] Patch "USB: Avoid runtime suspend loops for HCDs that can't handle suspend/resume" has been added to staging queue

Luis Henriques luis.henriques at canonical.com
Thu Jun 12 14:00:14 UTC 2014


This is a note to let you know that I have just added a patch titled

    USB: Avoid runtime suspend loops for HCDs that can't handle suspend/resume

to the linux-3.11.y-queue branch of the 3.11.y.z extended stable tree 
which can be found at:

 http://kernel.ubuntu.com/git?p=ubuntu/linux.git;a=shortlog;h=refs/heads/linux-3.11.y-queue

If you, or anyone else, feels it should not be added to this tree, please 
reply to this email.

For more information about the 3.11.y.z tree, see
https://wiki.ubuntu.com/Kernel/Dev/ExtendedStable

Thanks.
-Luis

------

>From f655c654723df3b91d651044d9e98540cc4e2ab4 Mon Sep 17 00:00:00 2001
From: Alan Stern <stern at rowland.harvard.edu>
Date: Fri, 23 May 2014 10:45:54 -0400
Subject: USB: Avoid runtime suspend loops for HCDs that can't handle
 suspend/resume

commit 8ef42ddd9a53b73e6fc3934278710c27f80f324f upstream.

Not all host controller drivers have bus-suspend and bus-resume
methods.  When one doesn't, it will cause problems if runtime PM is
enabled in the kernel.  The PM core will attempt to suspend the
controller's root hub, the suspend will fail because there is no
bus-suspend routine, and a -EBUSY error code will be returned to the
PM core.  This will cause the suspend attempt to be repeated shortly
thereafter, in a never-ending loop.

Part of the problem is that the original error code -ENOENT gets
changed to -EBUSY in usb_runtime_suspend(), on the grounds that the PM
core will interpret -ENOENT as meaning that the root hub has gotten
into a runtime-PM error state.  While this change is appropriate for
real USB devices, it's not such a good idea for a root hub.  In fact,
considering the root hub to be in a runtime-PM error state would not
be far from the truth.  Therefore this patch updates
usb_runtime_suspend() so that it adjusts error codes only for
non-root-hub devices.

Furthermore, the patch attempts to prevent the problem from occurring
in the first place by not enabling runtime PM by default for root hubs
whose host controller driver doesn't have bus_suspend and bus_resume
methods.

Signed-off-by: Alan Stern <stern at rowland.harvard.edu>
Reported-by: Will Deacon <will.deacon at arm.com>
Tested-by: Will Deacon <will.deacon at arm.com>
Signed-off-by: Greg Kroah-Hartman <gregkh at linuxfoundation.org>
Signed-off-by: Luis Henriques <luis.henriques at canonical.com>
---
 drivers/usb/core/driver.c |  9 ++++++---
 drivers/usb/core/hub.c    | 15 +++++++++++++--
 2 files changed, 19 insertions(+), 5 deletions(-)

diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 5c592c0364ae..86894faad404 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1754,10 +1754,13 @@ int usb_runtime_suspend(struct device *dev)
 	if (status == -EAGAIN || status == -EBUSY)
 		usb_mark_last_busy(udev);

-	/* The PM core reacts badly unless the return code is 0,
-	 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error.
+	/*
+	 * The PM core reacts badly unless the return code is 0,
+	 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error
+	 * (except for root hubs, because they don't suspend through
+	 * an upstream port like other USB devices).
 	 */
-	if (status != 0)
+	if (status != 0 && udev->parent)
 		return -EBUSY;
 	return status;
 }
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 5c75492ea8f4..5e0925a01475 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1683,8 +1683,19 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
 	 */
 	pm_runtime_set_autosuspend_delay(&hdev->dev, 0);

-	/* Hubs have proper suspend/resume support. */
-	usb_enable_autosuspend(hdev);
+	/*
+	 * Hubs have proper suspend/resume support, except for root hubs
+	 * where the controller driver doesn't have bus_suspend and
+	 * bus_resume methods.
+	 */
+	if (hdev->parent) {		/* normal device */
+		usb_enable_autosuspend(hdev);
+	} else {			/* root hub */
+		const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
+
+		if (drv->bus_suspend && drv->bus_resume)
+			usb_enable_autosuspend(hdev);
+	}

 	if (hdev->level == MAX_TOPO_LEVEL) {
 		dev_err(&intf->dev,
--
1.9.1





More information about the kernel-team mailing list