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>

diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 46df5e6..e46d38b 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1039,6 +1039,8 @@
 		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;
 }
 
@@ -1228,6 +1230,14 @@
 	*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);
 }
 
@@ -1356,6 +1366,10 @@
 		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;