aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-11-22 16:55:54 -0500
committerGreg Kroah-Hartman <gregkh@suse.de>2006-12-01 17:25:52 -0500
commitee49fb5dc89d34f1794ac9362fa97c1a640f7ddd (patch)
tree310d11b695b631c091c9f442d566400ba9a86de9 /drivers/usb
parentd25450c68767481f7c9cc4823a6da8235db40be6 (diff)
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>
Diffstat (limited to 'drivers/usb')
-rw-r--r--drivers/usb/core/driver.c34
-rw-r--r--drivers/usb/core/hub.c14
2 files changed, 37 insertions, 11 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c
index 40c1bf09b2b7..0fa15bd62c48 100644
--- a/drivers/usb/core/driver.c
+++ b/drivers/usb/core/driver.c
@@ -1048,7 +1048,7 @@ int usb_suspend_both(struct usb_device *udev, pm_message_t msg)
1048 1048
1049 /* If the suspend succeeded, propagate it up the tree */ 1049 /* If the suspend succeeded, propagate it up the tree */
1050 } else if (parent) 1050 } else if (parent)
1051 usb_autosuspend_device(parent, 0); 1051 usb_autosuspend_device(parent, 1);
1052 1052
1053 // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status); 1053 // dev_dbg(&udev->dev, "%s: status %d\n", __FUNCTION__, status);
1054 return status; 1054 return status;
@@ -1096,9 +1096,25 @@ int usb_resume_both(struct usb_device *udev)
1096 /* Propagate the resume up the tree, if necessary */ 1096 /* Propagate the resume up the tree, if necessary */
1097 if (udev->state == USB_STATE_SUSPENDED) { 1097 if (udev->state == USB_STATE_SUSPENDED) {
1098 if (parent) { 1098 if (parent) {
1099 usb_pm_lock(parent); 1099 status = usb_autoresume_device(parent, 1);
1100 parent->auto_pm = 1; 1100 if (status == 0) {
1101 status = usb_resume_both(parent); 1101 status = usb_resume_device(udev);
1102 if (status) {
1103 usb_autosuspend_device(parent, 1);
1104
1105 /* It's possible usb_resume_device()
1106 * failed after the port was
1107 * unsuspended, causing udev to be
1108 * logically disconnected. We don't
1109 * want usb_disconnect() to autosuspend
1110 * the parent again, so tell it that
1111 * udev disconnected while still
1112 * suspended. */
1113 if (udev->state ==
1114 USB_STATE_NOTATTACHED)
1115 udev->discon_suspended = 1;
1116 }
1117 }
1102 } else { 1118 } else {
1103 1119
1104 /* We can't progagate beyond the USB subsystem, 1120 /* We can't progagate beyond the USB subsystem,
@@ -1107,11 +1123,9 @@ int usb_resume_both(struct usb_device *udev)
1107 if (udev->dev.parent->power.power_state.event != 1123 if (udev->dev.parent->power.power_state.event !=
1108 PM_EVENT_ON) 1124 PM_EVENT_ON)
1109 status = -EHOSTUNREACH; 1125 status = -EHOSTUNREACH;
1110 } 1126 else
1111 if (status == 0) 1127 status = usb_resume_device(udev);
1112 status = usb_resume_device(udev); 1128 }
1113 if (parent)
1114 usb_pm_unlock(parent);
1115 } else { 1129 } else {
1116 1130
1117 /* Needed only for setting udev->dev.power.power_state.event 1131 /* Needed only for setting udev->dev.power.power_state.event
@@ -1119,8 +1133,6 @@ int usb_resume_both(struct usb_device *udev)
1119 status = usb_resume_device(udev); 1133 status = usb_resume_device(udev);
1120 } 1134 }
1121 1135
1122 /* Now the parent won't suspend until we are finished */
1123
1124 if (status == 0 && udev->actconfig) { 1136 if (status == 0 && udev->actconfig) {
1125 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) { 1137 for (i = 0; i < udev->actconfig->desc.bNumInterfaces; i++) {
1126 intf = udev->actconfig->interface[i]; 1138 intf = udev->actconfig->interface[i];
diff --git a/drivers/usb/core/hub.c b/drivers/usb/core/hub.c
index 46df5e60764b..e46d38b18249 100644
--- a/drivers/usb/core/hub.c
+++ b/drivers/usb/core/hub.c
@@ -1039,6 +1039,8 @@ static void recursively_mark_NOTATTACHED(struct usb_device *udev)
1039 if (udev->children[i]) 1039 if (udev->children[i])
1040 recursively_mark_NOTATTACHED(udev->children[i]); 1040 recursively_mark_NOTATTACHED(udev->children[i]);
1041 } 1041 }
1042 if (udev->state == USB_STATE_SUSPENDED)
1043 udev->discon_suspended = 1;
1042 udev->state = USB_STATE_NOTATTACHED; 1044 udev->state = USB_STATE_NOTATTACHED;
1043} 1045}
1044 1046
@@ -1228,6 +1230,14 @@ void usb_disconnect(struct usb_device **pdev)
1228 *pdev = NULL; 1230 *pdev = NULL;
1229 spin_unlock_irq(&device_state_lock); 1231 spin_unlock_irq(&device_state_lock);
1230 1232
1233 /* Decrement the parent's count of unsuspended children */
1234 if (udev->parent) {
1235 usb_pm_lock(udev);
1236 if (!udev->discon_suspended)
1237 usb_autosuspend_device(udev->parent, 1);
1238 usb_pm_unlock(udev);
1239 }
1240
1231 put_device(&udev->dev); 1241 put_device(&udev->dev);
1232} 1242}
1233 1243
@@ -1356,6 +1366,10 @@ static int __usb_new_device(void *void_data)
1356 goto fail; 1366 goto fail;
1357 } 1367 }
1358 1368
1369 /* Increment the parent's count of unsuspended children */
1370 if (udev->parent)
1371 usb_autoresume_device(udev->parent, 1);
1372
1359exit: 1373exit:
1360 module_put(THIS_MODULE); 1374 module_put(THIS_MODULE);
1361 return err; 1375 return err;