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 15:36:44 -0700 |
commit | bd859281c09b4318153dc7222b5e9052aad83b61 (patch) | |
tree | c56c116b7720aae3dc5192f115444d16a5c21e58 /drivers/usb | |
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')
-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 c2aad6a4d49..ee18d187ca1 100644 --- a/drivers/usb/core/driver.c +++ b/drivers/usb/core/driver.c @@ -24,6 +24,7 @@ #include <linux/device.h> #include <linux/usb.h> +#include <linux/workqueue.h> #include "hcd.h" #include "usb.h" @@ -1131,7 +1132,7 @@ void usb_autosuspend_device(struct usb_device *udev, int dec_usage_cnt) mutex_lock_nested(&udev->pm_mutex, udev->level); udev->pm_usage_cnt -= dec_usage_cnt; if (udev->pm_usage_cnt <= 0) - schedule_delayed_work(&udev->autosuspend, + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, USB_AUTOSUSPEND_DELAY); mutex_unlock(&udev->pm_mutex); // dev_dbg(&udev->dev, "%s: cnt %d\n", @@ -1215,10 +1216,10 @@ void usb_autopm_put_interface(struct usb_interface *intf) struct usb_device *udev = interface_to_usbdev(intf); mutex_lock_nested(&udev->pm_mutex, udev->level); - if (intf->condition != USB_INTERFACE_UNBOUND) { - if (--intf->pm_usage_cnt <= 0) - schedule_delayed_work(&udev->autosuspend, - USB_AUTOSUSPEND_DELAY); + if (intf->condition != USB_INTERFACE_UNBOUND && + --intf->pm_usage_cnt <= 0) { + queue_delayed_work(ksuspend_usb_wq, &udev->autosuspend, + USB_AUTOSUSPEND_DELAY); } mutex_unlock(&udev->pm_mutex); // dev_dbg(&intf->dev, "%s: cnt %d\n", diff --git a/drivers/usb/core/usb.c b/drivers/usb/core/usb.c index 60ef4ef0101..239f8e5d247 100644 --- a/drivers/usb/core/usb.c +++ b/drivers/usb/core/usb.c @@ -33,6 +33,7 @@ #include <linux/smp_lock.h> #include <linux/usb.h> #include <linux/mutex.h> +#include <linux/workqueue.h> #include <asm/io.h> #include <asm/scatterlist.h> @@ -47,6 +48,8 @@ const char *usbcore_name = "usbcore"; static int nousb; /* Disable USB when built into kernel image */ +struct workqueue_struct *ksuspend_usb_wq; /* For autosuspend */ + /** * 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) udev = to_usb_device(dev); -#ifdef CONFIG_PM +#ifdef CONFIG_USB_SUSPEND cancel_delayed_work(&udev->autosuspend); - flush_scheduled_work(); + flush_workqueue(ksuspend_usb_wq); #endif usb_destroy_configuration(udev); usb_put_hcd(bus_to_hcd(udev->bus)); @@ -184,6 +187,28 @@ static void usb_release_dev(struct device *dev) #ifdef CONFIG_PM +static int ksuspend_usb_init(void) +{ + ksuspend_usb_wq = create_singlethread_workqueue("ksuspend_usbd"); + if (!ksuspend_usb_wq) + return -ENOMEM; + return 0; +} + +static void ksuspend_usb_cleanup(void) +{ + destroy_workqueue(ksuspend_usb_wq); +} + +#else + +#define ksuspend_usb_init() 0 +#define ksuspend_usb_cleanup() do {} while (0) + +#endif + +#ifdef CONFIG_USB_SUSPEND + /* usb_autosuspend_work - callback routine to autosuspend a USB device */ static void usb_autosuspend_work(void *_udev) { @@ -195,6 +220,11 @@ static void usb_autosuspend_work(void *_udev) mutex_unlock(&udev->pm_mutex); } +#else + +static void usb_autosuspend_work(void *_udev) +{} + #endif /** @@ -976,9 +1006,12 @@ static int __init usb_init(void) return 0; } + retval = ksuspend_usb_init(); + if (retval) + goto out; retval = bus_register(&usb_bus_type); if (retval) - goto out; + goto bus_register_failed; retval = usb_host_init(); if (retval) goto host_init_failed; @@ -1014,6 +1047,8 @@ major_init_failed: usb_host_cleanup(); host_init_failed: bus_unregister(&usb_bus_type); +bus_register_failed: + ksuspend_usb_cleanup(); out: return retval; } @@ -1035,6 +1070,7 @@ static void __exit usb_exit(void) usb_hub_cleanup(); usb_host_cleanup(); bus_unregister(&usb_bus_type); + ksuspend_usb_cleanup(); } subsys_initcall(usb_init); diff --git a/drivers/usb/core/usb.h b/drivers/usb/core/usb.h index 0c09ecced6e..fb6eb41c374 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); #endif +extern struct workqueue_struct *ksuspend_usb_wq; extern struct bus_type usb_bus_type; extern struct usb_device_driver usb_generic_driver; |