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 | |
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')
-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 | ||