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 /drivers/usb/core/usb.c | |
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>
Diffstat (limited to 'drivers/usb/core/usb.c')
-rw-r--r-- | drivers/usb/core/usb.c | 42 |
1 files changed, 39 insertions, 3 deletions
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); |