aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2014-05-23 10:45:54 -0400
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>2014-05-23 15:06:00 -0400
commit8ef42ddd9a53b73e6fc3934278710c27f80f324f (patch)
tree9d4d1f587f69191b78c0db033d229e8bdfc7eb7a
parentd6d211db37e75de2ddc3a4f979038c40df7cc79c (diff)
USB: Avoid runtime suspend loops for HCDs that can't handle suspend/resume
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@rowland.harvard.edu> Reported-by: Will Deacon <will.deacon@arm.com> Tested-by: Will Deacon <will.deacon@arm.com> CC: <stable@vger.kernel.org> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
-rw-r--r--drivers/usb/core/driver.c9
-rw-r--r--drivers/usb/core/hub.c15
2 files changed, 19 insertions, 5 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 888881e5f292..4aeb10034de7 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1822,10 +1822,13 @@ int usb_runtime_suspend(struct device *dev)
1822 if (status == -EAGAIN || status == -EBUSY) 1822 if (status == -EAGAIN || status == -EBUSY)
1823 usb_mark_last_busy(udev); 1823 usb_mark_last_busy(udev);
1824 1824
1825 /* The PM core reacts badly unless the return code is 0, 1825 /*
1826 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error. 1826 * The PM core reacts badly unless the return code is 0,
1827 * -EAGAIN, or -EBUSY, so always return -EBUSY on an error
1828 * (except for root hubs, because they don't suspend through
1829 * an upstream port like other USB devices).
1827 */ 1830 */
1828 if (status != 0) 1831 if (status != 0 && udev->parent)
1829 return -EBUSY; 1832 return -EBUSY;
1830 return status; 1833 return status;
1831} 1834}
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 090469ebfcff..229a73f64304 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1691,8 +1691,19 @@ static int hub_probe(struct usb_interface *intf, const struct usb_device_id *id)
1691 */ 1691 */
1692 pm_runtime_set_autosuspend_delay(&hdev->dev, 0); 1692 pm_runtime_set_autosuspend_delay(&hdev->dev, 0);
1693 1693
1694 /* Hubs have proper suspend/resume support. */ 1694 /*
1695 usb_enable_autosuspend(hdev); 1695 * Hubs have proper suspend/resume support, except for root hubs
1696 * where the controller driver doesn't have bus_suspend and
1697 * bus_resume methods.
1698 */
1699 if (hdev->parent) { /* normal device */
1700 usb_enable_autosuspend(hdev);
1701 } else { /* root hub */
1702 const struct hc_driver *drv = bus_to_hcd(hdev->bus)->driver;
1703
1704 if (drv->bus_suspend && drv->bus_resume)
1705 usb_enable_autosuspend(hdev);
1706 }
1696 1707
1697 if (hdev->level == MAX_TOPO_LEVEL) { 1708 if (hdev->level == MAX_TOPO_LEVEL) {
1698 dev_err(&intf->dev, 1709 dev_err(&intf->dev,