USB: keep count of unsuspended children

This patch (as818b) simplifies autosuspend processing by keeping track
of the number of unsuspended children of each USB hub.  This will
permit us to avoid a good deal of unnecessary work all the time; we
will no longer have to create a bunch of workqueue entries to carry
out autosuspend requests, only to have them fail because one of the
hub's children isn't suspended.

The basic idea is simple.  There already is a usage counter in the
usb_device structure for preventing autosuspends.  The patch just
increments that counter for every unsuspended child.  There's only one
tricky part: When a device disconnects we need to remember whether it
was suspended at the time (leave the counter alone) or not (decrement
the counter).

Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
Alan Stern 2006-11-22 16:55:54 -05:00 коммит произвёл Greg Kroah-Hartman
Родитель d25450c687
Коммит ee49fb5dc8
3 изменённых файлов: 38 добавлений и 11 удалений

Просмотреть файл

@ -1048,7 +1048,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
/* If the suspend succeeded, propagate it up the tree */ /* If the suspend succeeded, propagate it up the tree */
} else if (parent) } else if (parent)
usb_autosuspend_device(parent, 0); usb_autosuspend_device(parent, 1);
// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status; return status;
@ -1096,9 +1096,25 @@ int usb_resume_both(struct usb_device *udev)
/* Propagate the resume up the tree, if necessary */ /* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) { if (udev->state == USB_STATE_SUSPENDED) {
if (parent) { if (parent) {
usb_pm_lock(parent); status = usb_autoresume_device(parent, 1);
parent->auto_pm = 1; if (status == 0) {
status = usb_resume_both(parent); status = usb_resume_device(udev);
if (status) {
usb_autosuspend_device(parent, 1);
/* It's possible usb_resume_device()
* failed after the port was
* unsuspended, causing udev to be
* logically disconnected. We don't
* want usb_disconnect() to autosuspend
* the parent again, so tell it that
* udev disconnected while still
* suspended. */
if (udev->state ==
USB_STATE_NOTATTACHED)
udev->discon_suspended = 1;
}
}
} else { } else {
/* We can't progagate beyond the USB subsystem, /* We can't progagate beyond the USB subsystem,
@ -1107,11 +1123,9 @@ int usb_resume_both(struct usb_device *udev)
if (udev->dev.parent->power.power_state.event != if (udev->dev.parent->power.power_state.event !=
PM_EVENT_ON) PM_EVENT_ON)
status = -EHOSTUNREACH; status = -EHOSTUNREACH;
} else
if (status == 0) status = usb_resume_device(udev);
status = usb_resume_device(udev); }
if (parent)
usb_pm_unlock(parent);
} else { } else {
/* Needed only for setting udev->dev.power.power_state.event /* Needed only for setting udev->dev.power.power_state.event
@ -1119,8 +1133,6 @@ int usb_resume_both(struct usb_device *udev)
status = usb_resume_device(udev); status = usb_resume_device(udev);
} }
/* Now the parent won't suspend until we are finished */
if (status == 0 && udev->actconfig) { if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i]; intf = udev->actconfig->interface[i];

Просмотреть файл

@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (udev->children[i]) if (udev->children[i])
recursively_mark_NOTATTACHED(udev->children[i]); recursively_mark_NOTATTACHED(udev->children[i]);
} }
if (udev->state == USB_STATE_SUSPENDED)
udev->discon_suspended = 1;
udev->state = USB_STATE_NOTATTACHED; udev->state = USB_STATE_NOTATTACHED;
} }
@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL; *pdev = NULL;
spin_unlock_irq(&device_state_lock); spin_unlock_irq(&device_state_lock);
/* Decrement the parent's count of unsuspended children */
if (udev->parent) {
usb_pm_lock(udev);
if (!udev->discon_suspended)
usb_autosuspend_device(udev->parent, 1);
usb_pm_unlock(udev);
}
put_device(&udev->dev); put_device(&udev->dev);
} }
@ -1356,6 +1366,10 @@ static int __usb_new_device(void *void_data)
goto fail; goto fail;
} }
/* Increment the parent's count of unsuspended children */
if (udev->parent)
usb_autoresume_device(udev->parent, 1);
exit: exit:
module_put(THIS_MODULE); module_put(THIS_MODULE);
return err; return err;

Просмотреть файл

@ -362,6 +362,7 @@ struct usb_device {
u8 portnum; /* Parent port number (origin 1) */ u8 portnum; /* Parent port number (origin 1) */
u8 level; /* Number of USB hub ancestors */ u8 level; /* Number of USB hub ancestors */
unsigned discon_suspended:1; /* Disconnected while suspended */
unsigned have_langid:1; /* whether string_langid is valid */ unsigned have_langid:1; /* whether string_langid is valid */
int string_langid; /* language ID for strings */ int string_langid; /* language ID for strings */