Commit c9f89fa4 authored by David Brownell's avatar David Brownell Committed by Greg Kroah-Hartman

[PATCH] remove suspend-path recursion

This patch removes some recursion in the CONFIG_USB_SUSPEND logic, which
suspended children (of devices or hubs) that weren't already suspended.
When it sees such cases, suspend now just fails cleanly.

That logic was not needed during system-wide sleep state transitions; and
given the current notions of how to manage selective suspend transitions,
we don't want it there either.  Where it was particularly handy was coping
with various limitations of the sysfs "echo -n N > power/state" support.
(These include assuming that "N" is always meaningful to the driver; and
that drivers can only transition to state N from state zero.)
Signed-off-by: default avatarDavid Brownell <dbrownell@users.sourceforge.net>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent db690874
...@@ -453,6 +453,7 @@ static void hub_quiesce(struct usb_hub *hub) ...@@ -453,6 +453,7 @@ static void hub_quiesce(struct usb_hub *hub)
{ {
/* stop khubd and related activity */ /* stop khubd and related activity */
hub->quiescing = 1; hub->quiescing = 1;
hub->activating = 0;
usb_kill_urb(hub->urb); usb_kill_urb(hub->urb);
if (hub->has_indicators) if (hub->has_indicators)
cancel_delayed_work(&hub->leds); cancel_delayed_work(&hub->leds);
...@@ -1613,68 +1614,21 @@ static int __usb_suspend_device (struct usb_device *udev, int port1, ...@@ -1613,68 +1614,21 @@ static int __usb_suspend_device (struct usb_device *udev, int port1,
return 0; return 0;
} }
/* suspend interface drivers; if this is a hub, it /* all interfaces must already be suspended */
* suspends the child devices
*/
if (udev->actconfig) { if (udev->actconfig) {
int i; int i;
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
struct usb_interface *intf; struct usb_interface *intf;
struct usb_driver *driver;
intf = udev->actconfig->interface[i]; intf = udev->actconfig->interface[i];
if (!is_active(intf)) if (is_active(intf)) {
continue; dev_dbg(&intf->dev, "nyet suspended\n");
if (!intf->dev.driver) return -EBUSY;
continue;
driver = to_usb_driver(intf->dev.driver);
if (driver->suspend) {
status = driver->suspend(intf, state);
if (status == 0)
mark_quiesced(intf);
else
dev_err(&intf->dev,
"suspend error %d\n",
status);
}
/* only drivers with suspend() can ever resume();
* and after power loss, even they won't.
* bus_rescan_devices() can rebind drivers later.
*
* FIXME the PM core self-deadlocks when unbinding
* drivers during suspend/resume ... everything grabs
* dpm_sem (not a spinlock, ugh). we want to unbind,
* since we know every driver's probe/disconnect works
* even for drivers that can't suspend.
*/
if (!driver->suspend || state.event > PM_EVENT_FREEZE) {
#if 1
dev_warn(&intf->dev, "resume is unsafe!\n");
#else
down_write(&usb_bus_type.rwsem);
device_release_driver(&intf->dev);
up_write(&usb_bus_type.rwsem);
#endif
} }
} }
} }
/*
* FIXME this needs port power off call paths too, to help force
* USB into the "generic" PM model. At least for devices on
* ports that aren't using ganged switching (usually root hubs).
*
* NOTE: SRP-capable links should adopt more aggressive poweroff
* policies (when HNP doesn't apply) once we have mechanisms to
* turn power back on! (Likely not before 2.7...)
*/
if (state.event > PM_EVENT_FREEZE) {
dev_warn(&udev->dev, "no poweroff yet, suspending instead\n");
}
/* "global suspend" of the HC-to-USB interface (root hub), or /* "global suspend" of the HC-to-USB interface (root hub), or
* "selective suspend" of just one hub-device link. * "selective suspend" of just one hub-device link.
*/ */
...@@ -1960,26 +1914,20 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg) ...@@ -1960,26 +1914,20 @@ static int hub_suspend(struct usb_interface *intf, pm_message_t msg)
struct usb_hub *hub = usb_get_intfdata (intf); struct usb_hub *hub = usb_get_intfdata (intf);
struct usb_device *hdev = hub->hdev; struct usb_device *hdev = hub->hdev;
unsigned port1; unsigned port1;
int status;
/* stop khubd and related activity */ /* fail if children aren't already suspended */
hub_quiesce(hub);
/* then suspend every port */
for (port1 = 1; port1 <= hdev->maxchild; port1++) { for (port1 = 1; port1 <= hdev->maxchild; port1++) {
struct usb_device *udev; struct usb_device *udev;
udev = hdev->children [port1-1]; udev = hdev->children [port1-1];
if (!udev) if (udev && udev->state != USB_STATE_SUSPENDED) {
continue; dev_dbg(&intf->dev, "port %d nyet suspended\n", port1);
down(&udev->serialize); return -EBUSY;
status = __usb_suspend_device(udev, port1, msg); }
up(&udev->serialize);
if (status < 0)
dev_dbg(&intf->dev, "suspend port %d --> %d\n",
port1, status);
} }
/* stop khubd and related activity */
hub_quiesce(hub);
return 0; return 0;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment