aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/core
diff options
context:
space:
mode:
authorAlan Stern <stern@rowland.harvard.edu>2006-09-19 10:14:07 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2006-09-28 18:36:44 -0400
commitbd859281c09b4318153dc7222b5e9052aad83b61 (patch)
treec56c116b7720aae3dc5192f115444d16a5c21e58 /drivers/usb/core
parent6a9fb060393e04a79973f95925f4f6587442e9c7 (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')
-rw-r--r--drivers/usb/core/driver.c11
-rw-r--r--drivers/usb/core/usb.c42
-rw-r--r--drivers/usb/core/usb.h1
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
48static int nousb; /* Disable USB when built into kernel image */ 49static int nousb; /* Disable USB when built into kernel image */
49 50
51struct 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
190static 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
198static 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 */
188static void usb_autosuspend_work(void *_udev) 213static 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
225static 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();
1015host_init_failed: 1048host_init_failed:
1016 bus_unregister(&usb_bus_type); 1049 bus_unregister(&usb_bus_type);
1050bus_register_failed:
1051 ksuspend_usb_cleanup();
1017out: 1052out:
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
1040subsys_initcall(usb_init); 1076subsys_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
65extern struct workqueue_struct *ksuspend_usb_wq;
65extern struct bus_type usb_bus_type; 66extern struct bus_type usb_bus_type;
66extern struct usb_device_driver usb_generic_driver; 67extern struct usb_device_driver usb_generic_driver;
67 68