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:
Родитель
d25450c687
Коммит
ee49fb5dc8
|
@ -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 */
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче