Commit c6fdd8e5d0c65bb8821dc6da26ee1a2ddd58b3cc
Committed by
David S. Miller
1 parent
910a578f7e
Exists in
master
and in
20 other branches
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. Signed-off-by: Tilman Schmidt <tilman@imap.cc> Signed-off-by: David S. Miller <davem@davemloft.net>
Showing 1 changed file with 16 additions and 3 deletions Side-by-side Diff
drivers/isdn/gigaset/bas-gigaset.c
... | ... | @@ -617,7 +617,13 @@ |
617 | 617 | if (rc == 0) |
618 | 618 | /* success, resubmit interrupt read URB */ |
619 | 619 | rc = usb_submit_urb(urb, GFP_ATOMIC); |
620 | - if (rc != 0 && rc != -ENODEV) { | |
620 | + | |
621 | + switch (rc) { | |
622 | + case 0: /* success */ | |
623 | + case -ENODEV: /* device gone */ | |
624 | + case -EINVAL: /* URB already resubmitted, or terminal badness */ | |
625 | + break; | |
626 | + default: /* failure: try to recover by resetting the device */ | |
621 | 627 | dev_err(cs->dev, "clear halt failed: %s\n", get_usb_rcmsg(rc)); |
622 | 628 | rc = usb_lock_device_for_reset(ucs->udev, ucs->interface); |
623 | 629 | if (rc == 0) { |
... | ... | @@ -2442,7 +2448,9 @@ |
2442 | 2448 | } |
2443 | 2449 | |
2444 | 2450 | /* gigaset_suspend |
2445 | - * This function is called before the USB connection is suspended. | |
2451 | + * This function is called before the USB connection is suspended | |
2452 | + * or before the USB device is reset. | |
2453 | + * In the latter case, message == PMSG_ON. | |
2446 | 2454 | */ |
2447 | 2455 | static int gigaset_suspend(struct usb_interface *intf, pm_message_t message) |
2448 | 2456 | { |
... | ... | @@ -2498,7 +2506,12 @@ |
2498 | 2506 | del_timer_sync(&ucs->timer_atrdy); |
2499 | 2507 | del_timer_sync(&ucs->timer_cmd_in); |
2500 | 2508 | del_timer_sync(&ucs->timer_int_in); |
2501 | - cancel_work_sync(&ucs->int_in_wq); | |
2509 | + | |
2510 | + /* don't try to cancel int_in_wq from within reset as it | |
2511 | + * might be the one requesting the reset | |
2512 | + */ | |
2513 | + if (message.event != PM_EVENT_ON) | |
2514 | + cancel_work_sync(&ucs->int_in_wq); | |
2502 | 2515 | |
2503 | 2516 | gig_dbg(DEBUG_SUSPEND, "suspend complete"); |
2504 | 2517 | return 0; |