diff options
| author | Alan Stern <stern@rowland.harvard.edu> | 2006-09-19 10:14:07 -0400 |
|---|---|---|
| committer | Greg Kroah-Hartman <gregkh@suse.de> | 2006-09-28 18:36:44 -0400 |
| commit | bd859281c09b4318153dc7222b5e9052aad83b61 (patch) | |
| tree | c56c116b7720aae3dc5192f115444d16a5c21e58 | |
| parent | 6a9fb060393e04a79973f95925f4f6587442e9c7 (diff) | |
USB: create new workqueue thread for USB autosuspend
This patch (as787) creates a new workqueue thread to handle delayed
USB autosuspend requests. Previously the code used keventd. However
it turns out that the hub driver's suspend routine calls
flush_scheduled_work(), making it a poor candidate for running in
keventd (the call immediately deadlocks). The solution is to use a
new thread instead of keventd.
Signed-off-by: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
| -rw-r--r-- | drivers/usb/core/driver.c | 11 | ||||
| -rw-r--r-- | drivers/usb/core/usb.c | 42 | ||||
| -rw-r--r-- | drivers/usb/core/usb.h | 1 |
3 files changed, 46 insertions, 8 deletions
diff --git a/drivers/usb/core/driver.c b/drivers/usb/core/driver.c index c2aad6a4d49f..ee18d187ca17 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c | |||
| @@ -24,6 +24,7 @@ | |||
| 24 | 24 | ||
| 25 | #include <linux/device.h> | 25 | #include <linux/device.h> |
| 26 | #include <linux/usb.h> | 26 | #include <linux/usb.h> |
| 27 | #include <linux/workqueue.h> | ||
| 27 | #include "hcd.h" | 28 | #include "hcd.h" |
| 28 | #include "usb.h" | 29 | #include "usb.h" |
| 29 | 30 | ||
| @@ -1131,7 +1132,7 @@ void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) | |||
| 1131 | mutex_lock_nested(&udev->pm_mutex, udev->level); | 1132 | mutex_lock_nested(&udev->pm_mutex, udev->level); |
| 1132 | udev->pm_usage_cnt -= dec_usage_cnt; | 1133 | udev->pm_usage_cnt -= dec_usage_cnt; |
| 1133 | if (udev->pm_usage_cnt <= 0) | 1134 | if (udev->pm_usage_cnt <= 0) |
| 1134 | schedule_delayed_work(&udev->autosuspend, | 1135 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, |
| 1135 | USB_AUTOSUSPEND_DELAY); | 1136 | USB_AUTOSUSPEND_DELAY); |
| 1136 | mutex_unlock(&udev->pm_mutex); | 1137 | mutex_unlock(&udev->pm_mutex); |
| 1137 | // dev_dbg(&udev->dev, "%s: cnt %d\n", | 1138 | // dev_dbg(&udev->dev, "%s: cnt %d\n", |
| @@ -1215,10 +1216,10 @@ void usb_autopm_put_interface(struct usb_interface *intf) | |||
| 1215 | struct usb_device *udev = interface_to_usbdev(intf); | 1216 | struct usb_device *udev = interface_to_usbdev(intf); |
| 1216 | 1217 | ||
| 1217 | mutex_lock_nested(&udev->pm_mutex, udev->level); | 1218 | mutex_lock_nested(&udev->pm_mutex, udev->level); |
| 1218 | if (intf->condition != USB_INTERFACE_UNBOUND) { | 1219 | if (intf->condition != USB_INTERFACE_UNBOUND && |
| 1219 | if (--intf->pm_usage_cnt <= 0) | 1220 | --intf->pm_usage_cnt <= 0) { |
| 1220 | schedule_delayed_work(&udev->autosuspend, | 1221 | queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, |
| 1221 | USB_AUTOSUSPEND_DELAY); | 1222 | USB_AUTOSUSPEND_DELAY); |
| 1222 | } | 1223 | } |
| 1223 | mutex_unlock(&udev->pm_mutex); | 1224 | mutex_unlock(&udev->pm_mutex); |
| 1224 | // dev_dbg(&intf->dev, "%s: cnt %d\n", | 1225 | // dev_dbg(&intf->dev, "%s: cnt %d\n", |
diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 60ef4ef0101a..239f8e5d247f 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c | |||
| @@ -33,6 +33,7 @@ | |||
| 33 | #include <linux/smp_lock.h> | 33 | #include <linux/smp_lock.h> |
| 34 | #include <linux/usb.h> | 34 | #include <linux/usb.h> |
| 35 | #include <linux/mutex.h> | 35 | #include <linux/mutex.h> |
| 36 | #include <linux/workqueue.h> | ||
| 36 | 37 | ||
| 37 | #include <asm/io.h> | 38 | #include <asm/io.h> |
| 38 | #include <asm/scatterlist.h> | 39 | #include <asm/scatterlist.h> |
| @@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore"; | |||
| 47 | 48 | ||
| 48 | static int nousb; /* Disable USB when built into kernel image */ | 49 | static int nousb; /* Disable USB when built into kernel image */ |
| 49 | 50 | ||
| 51 | struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ | ||
| 52 | |||
| 50 | 53 | ||
| 51 | /** | 54 | /** |
| 52 | * usb_ifnum_to_if - get the interface object with a given interface number | 55 | * usb_ifnum_to_if - get the interface object with a given interface number |
| @@ -170,9 +173,9 @@ static void usb_release_dev(struct device *dev) | |||
| 170 | 173 | ||
| 171 | udev = to_usb_device(dev); | 174 | udev = to_usb_device(dev); |
| 172 | 175 | ||
| 173 | #ifdef CONFIG_PM | 176 | #ifdef CONFIG_USB_SUSPEND |
| 174 | cancel_delayed_work(&udev->autosuspend); | 177 | cancel_delayed_work(&udev->autosuspend); |
| 175 | flush_scheduled_work(); | 178 | flush_workqueue(ksuspend_usb_wq); |
| 176 | #endif | 179 | #endif |
| 177 | usb_destroy_configuration(udev); | 180 | usb_destroy_configuration(udev); |
| 178 | usb_put_hcd(bus_to_hcd(udev->bus)); | 181 | usb_put_hcd(bus_to_hcd(udev->bus)); |
| @@ -184,6 +187,28 @@ static void usb_release_dev(struct device *dev) | |||
| 184 | 187 | ||
| 185 | #ifdef CONFIG_PM | 188 | #ifdef CONFIG_PM |
| 186 | 189 | ||
| 190 | static int ksuspend_usb_init(void) | ||
| 191 | { | ||
| 192 | ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd"); | ||
| 193 | if (!ksuspend_usb_wq) | ||
| 194 | return -ENOMEM; | ||
| 195 | return 0; | ||
| 196 | } | ||
| 197 | |||
| 198 | static void ksuspend_usb_cleanup(void) | ||
| 199 | { | ||
| 200 | destroy_workqueue(ksuspend_usb_wq); | ||
| 201 | } | ||
| 202 | |||
| 203 | #else | ||
| 204 | |||
| 205 | #define ksuspend_usb_init() 0 | ||
| 206 | #define ksuspend_usb_cleanup() do {} while (0) | ||
| 207 | |||
| 208 | #endif | ||
| 209 | |||
| 210 | #ifdef CONFIG_USB_SUSPEND | ||
| 211 | |||
| 187 | /* usb_autosuspend_work - callback routine to autosuspend a USB device */ | 212 | /* usb_autosuspend_work - callback routine to autosuspend a USB device */ |
| 188 | static void usb_autosuspend_work(void *_udev) | 213 | static void usb_autosuspend_work(void *_udev) |
| 189 | { | 214 | { |
| @@ -195,6 +220,11 @@ static void usb_autosuspend_work(void *_udev) | |||
| 195 | mutex_unlock(&udev->pm_mutex); | 220 | mutex_unlock(&udev->pm_mutex); |
| 196 | } | 221 | } |
| 197 | 222 | ||
| 223 | #else | ||
| 224 | |||
| 225 | static void usb_autosuspend_work(void *_udev) | ||
| 226 | {} | ||
| 227 | |||
| 198 | #endif | 228 | #endif |
| 199 | 229 | ||
| 200 | /** | 230 | /** |
| @@ -976,9 +1006,12 @@ static int __init usb_init(void) | |||
| 976 | return 0; | 1006 | return 0; |
| 977 | } | 1007 | } |
| 978 | 1008 | ||
| 1009 | retval = ksuspend_usb_init(); | ||
| 1010 | if (retval) | ||
| 1011 | goto out; | ||
| 979 | retval = bus_register(&usb_bus_type); | 1012 | retval = bus_register(&usb_bus_type); |
| 980 | if (retval) | 1013 | if (retval) |
| 981 | goto out; | 1014 | goto bus_register_failed; |
| 982 | retval = usb_host_init(); | 1015 | retval = usb_host_init(); |
| 983 | if (retval) | 1016 | if (retval) |
| 984 | goto host_init_failed; | 1017 | goto host_init_failed; |
| @@ -1014,6 +1047,8 @@ major_init_failed: | |||
| 1014 | usb_host_cleanup(); | 1047 | usb_host_cleanup(); |
| 1015 | host_init_failed: | 1048 | host_init_failed: |
| 1016 | bus_unregister(&usb_bus_type); | 1049 | bus_unregister(&usb_bus_type); |
| 1050 | bus_register_failed: | ||
| 1051 | ksuspend_usb_cleanup(); | ||
| 1017 | out: | 1052 | out: |
| 1018 | return retval; | 1053 | return retval; |
| 1019 | } | 1054 | } |
| @@ -1035,6 +1070,7 @@ static void __exit usb_exit(void) | |||
| 1035 | usb_hub_cleanup(); | 1070 | usb_hub_cleanup(); |
| 1036 | usb_host_cleanup(); | 1071 | usb_host_cleanup(); |
| 1037 | bus_unregister(&usb_bus_type); | 1072 | bus_unregister(&usb_bus_type); |
| 1073 | ksuspend_usb_cleanup(); | ||
| 1038 | } | 1074 | } |
| 1039 | 1075 | ||
| 1040 | subsys_initcall(usb_init); | 1076 | subsys_initcall(usb_init); |
diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0c09ecced6e1..fb6eb41c374f 100644 --- a/drivers/usb/core/usb.h +++ b/drivers/usb/core/usb.h | |||
| @@ -62,6 +62,7 @@ extern int usb_autoresume_device(struct usb_device *udev, int inc_busy_cnt); | |||
| 62 | 62 | ||
| 63 | #endif | 63 | #endif |
| 64 | 64 | ||
| 65 | extern struct workqueue_struct *ksuspend_usb_wq; | ||
| 65 | extern struct bus_type usb_bus_type; | 66 | extern struct bus_type usb_bus_type; |
| 66 | extern struct usb_device_driver usb_generic_driver; | 67 | extern struct usb_device_driver usb_generic_driver; |
| 67 | 68 | ||
