Skip to content

Commit

Permalink
USB: keep count of unsuspended children
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Alan Stern authored and Greg Kroah-Hartman committed Dec 1, 2006
1 parent d25450c commit ee49fb5
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 11 deletions.
34 changes: 23 additions & 11 deletions drivers/usb/core/driver.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
} else if (parent)
usb_autosuspend_device(parent, 0);
usb_autosuspend_device(parent, 1);

// dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
return status;
Expand Down Expand Up @@ -1096,9 +1096,25 @@ int usb_resume_both(struct usb_device *udev)
/* Propagate the resume up the tree, if necessary */
if (udev->state == USB_STATE_SUSPENDED) {
if (parent) {
usb_pm_lock(parent);
parent->auto_pm = 1;
status = usb_resume_both(parent);
status = usb_autoresume_device(parent, 1);
if (status == 0) {
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 {

/* We can't progagate beyond the USB subsystem,
Expand All @@ -1107,20 +1123,16 @@ int usb_resume_both(struct usb_device *udev)
if (udev->dev.parent->power.power_state.event !=
PM_EVENT_ON)
status = -EHOSTUNREACH;
}
if (status == 0)
status = usb_resume_device(udev);
if (parent)
usb_pm_unlock(parent);
else
status = usb_resume_device(udev);
}
} else {

/* Needed only for setting udev->dev.power.power_state.event
* and for possible debugging message. */
status = usb_resume_device(udev);
}

/* Now the parent won't suspend until we are finished */

if (status == 0 && udev->actconfig) {
for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
intf = udev->actconfig->interface[i];
Expand Down
14 changes: 14 additions & 0 deletions drivers/usb/core/hub.c
Original file line number Diff line number Diff line change
Expand Up @@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
if (udev->children[i])
recursively_mark_NOTATTACHED(udev->children[i]);
}
if (udev->state == USB_STATE_SUSPENDED)
udev->discon_suspended = 1;
udev->state = USB_STATE_NOTATTACHED;
}

Expand Down Expand Up @@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **pdev)
*pdev = NULL;
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);
}

Expand Down Expand Up @@ -1356,6 +1366,10 @@ static int __usb_new_device(void *void_data)
goto fail;
}

/* Increment the parent's count of unsuspended children */
if (udev->parent)
usb_autoresume_device(udev->parent, 1);

exit:
module_put(THIS_MODULE);
return err;
Expand Down
1 change: 1 addition & 0 deletions include/linux/usb.h
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,7 @@ struct usb_device {
u8 portnum; /* Parent port number (origin 1) */
u8 level; /* Number of USB hub ancestors */

unsigned discon_suspended:1; /* Disconnected while suspended */
unsigned have_langid:1; /* whether string_langid is valid */
int string_langid; /* language ID for strings */

Expand Down

0 comments on commit ee49fb5

Please sign in to comment.