bas_gigaset: fix pre_reset handling
Subsystems (2)
PATCH 1/1 bas_gigaset: fix pre_reset handling
The delayed work function int_in_work() may call usb_reset_device() and thus, indirectly, the driver's pre_reset method. Trying to cancel the work synchronously in that situation would deadlock. Fix by avoiding cancel_work_sync() in the pre_reset method.
If the reset was NOT initiated by int_in_work() this might cause int_in_work() to run after the post_reset method, with urb_int_in already resubmitted, so handle that case gracefully.
--- drivers/isdn/gigaset/bas-gigaset.c | 19 ++++++++++++++++--- 1 files changed, 16 insertions(+), 3 deletions(-)
drivers/isdn/gigaset/bas-gigaset.c +16 -3
@@ -617,7 +617,13 @@ static void int_in_work(struct work_struct *work)if (rc ==0 )/* success, resubmit interrupt read URB */ rc =usb_submit_urb (urb,GFP_ATOMIC ); -if (rc !=0 && rc != -ENODEV ) { + +switch (rc) { +case 0 :/* success */ +case -ENODEV :/* device gone */ +case -EINVAL :/* URB already resubmitted, or terminal badness */ +break ; +default :/* failure: try to recover by resetting the device */ dev_err (cs->dev,"clear halt failed: %s\n" ,get_usb_rcmsg (rc)); rc =usb_lock_device_for_reset (ucs->udev, ucs->interface);if (rc ==0 ) {
@@ -2442,7 +2448,9 @@ static void gigaset_disconnect(struct usb_interface *interface) }/* gigaset_suspend -* This function is called before the USB connection is suspended. +* This function is called before the USB connection is suspended +* or before the USB device is reset. +* In the latter case, message == PMSG_ON. */ static int gigaset_suspend (struct usb_interface *intf,pm_message_t message) {
@@ -2498,7 +2506,12 @@ static int gigaset_suspend(struct usb_interface *intf, pm_message_t message)del_timer_sync (&ucs->timer_atrdy);del_timer_sync (&ucs->timer_cmd_in);del_timer_sync (&ucs->timer_int_in); -cancel_work_sync (&ucs->int_in_wq); + +/* don't try to cancel int_in_wq from within reset as it +* might be the one requesting the reset +*/ +if (message.event !=PM_EVENT_ON ) +cancel_work_sync (&ucs->int_in_wq);gig_dbg (DEBUG_SUSPEND ,"suspend complete" );return 0 ;
Signature · 1 line
1.7.3.4
From: Tilman Schmidt <[email protected]>
Date: Wed, 24 Oct 2012 20:44:32 +0200
Quoted 10 lines
The delayed work function
int_in_work()may callusb_reset_device()and thus, indirectly, the driver'spre_resetmethod. Trying to cancel the work synchronously in that situation would deadlock. Fix by avoidingcancel_work_sync()in thepre_resetmethod.If the reset was NOT initiated by
int_in_work()this might causeint_in_work()to run after thepost_resetmethod, withurb_int_inalready resubmitted, so handle that case gracefully.Signed-off-by Tilman Schmidt <[email protected]>
Applied, thanks.