diff options
author | Inaky Perez-Gonzalez <inaky@linux.intel.com> | 2008-11-13 13:31:35 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2009-01-07 12:59:53 -0500 |
commit | dc023dceec861c60bc1d1a17a2c6496ddac26ee7 (patch) | |
tree | 908529b3eb975acb2582f679d20a81bfadc95bf4 /include/linux/usb.h | |
parent | 9ac39f28b5237a629e41ccfc1f73d3a55723045c (diff) |
USB: Introduce usb_queue_reset() to do resets from atomic contexts
This patch introduces a new call to be able to do a USB reset from an
atomic contect. This is quite helpful in USB callbacks to handle
errors (when the only thing that can be done is to do a device
reset).
It is done queuing a work struct that will do the actual reset. The
struct is "attached" to an interface so pending requests from an
interface are removed when said interface is unbound from the driver.
The call flow then becomes:
usb_queue_reset_device()
__usb_queue_reset_device() [workqueue]
usb_reset_device()
usb_probe_interface()
usb_cancel_queue_reset() [error path]
usb_unbind_interface()
usb_cancel_queue_reset()
usb_driver_release_interface()
usb_cancel_queue_reset()
Note usb_cancel_queue_reset() needs smarts to try not to unqueue when
it is actually being executed. This happens when we run the reset from
the workqueue: usb_reset_device() is called and on interface unbind
time, usb_cancel_queue_reset() would be called. That would deadlock on
cancel_work_sync(). To avoid that, we set (before running
usb_reset_device()) usb_intf->reset_running and clear it inmediately
after returning.
Patch is against 2.6.28-rc2 and depends on
http://marc.info/?l=linux-usb&m=122581634925308&w=2 (as submitted by
Alan Stern).
Signed-off-by: Inaky Perez-Gonzalez <inaky@linux.intel.com>
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'include/linux/usb.h')
-rw-r--r-- | include/linux/usb.h | 8 |
1 files changed, 8 insertions, 0 deletions
diff --git a/include/linux/usb.h b/include/linux/usb.h index 859a88e6ce9c..c8e55aa979de 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h | |||
@@ -120,6 +120,11 @@ enum usb_interface_condition { | |||
120 | * to the sysfs representation for that device. | 120 | * to the sysfs representation for that device. |
121 | * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not | 121 | * @pm_usage_cnt: PM usage counter for this interface; autosuspend is not |
122 | * allowed unless the counter is 0. | 122 | * allowed unless the counter is 0. |
123 | * @reset_ws: Used for scheduling resets from atomic context. | ||
124 | * @reset_running: set to 1 if the interface is currently running a | ||
125 | * queued reset so that usb_cancel_queued_reset() doesn't try to | ||
126 | * remove from the workqueue when running inside the worker | ||
127 | * thread. See __usb_queue_reset_device(). | ||
123 | * | 128 | * |
124 | * USB device drivers attach to interfaces on a physical device. Each | 129 | * USB device drivers attach to interfaces on a physical device. Each |
125 | * interface encapsulates a single high level function, such as feeding | 130 | * interface encapsulates a single high level function, such as feeding |
@@ -168,10 +173,12 @@ struct usb_interface { | |||
168 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ | 173 | unsigned needs_remote_wakeup:1; /* driver requires remote wakeup */ |
169 | unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ | 174 | unsigned needs_altsetting0:1; /* switch to altsetting 0 is pending */ |
170 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ | 175 | unsigned needs_binding:1; /* needs delayed unbind/rebind */ |
176 | unsigned reset_running:1; | ||
171 | 177 | ||
172 | struct device dev; /* interface specific device info */ | 178 | struct device dev; /* interface specific device info */ |
173 | struct device *usb_dev; | 179 | struct device *usb_dev; |
174 | int pm_usage_cnt; /* usage counter for autosuspend */ | 180 | int pm_usage_cnt; /* usage counter for autosuspend */ |
181 | struct work_struct reset_ws; /* for resets in atomic context */ | ||
175 | }; | 182 | }; |
176 | #define to_usb_interface(d) container_of(d, struct usb_interface, dev) | 183 | #define to_usb_interface(d) container_of(d, struct usb_interface, dev) |
177 | #define interface_to_usbdev(intf) \ | 184 | #define interface_to_usbdev(intf) \ |
@@ -507,6 +514,7 @@ extern int usb_lock_device_for_reset(struct usb_device *udev, | |||
507 | 514 | ||
508 | /* USB port reset for device reinitialization */ | 515 | /* USB port reset for device reinitialization */ |
509 | extern int usb_reset_device(struct usb_device *dev); | 516 | extern int usb_reset_device(struct usb_device *dev); |
517 | extern void usb_queue_reset_device(struct usb_interface *dev); | ||
510 | 518 | ||
511 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); | 519 | extern struct usb_device *usb_find_device(u16 vendor_id, u16 product_id); |
512 | 520 | ||