[PATCH/RFC] Re: pre_reset() in bas-gigaset.c

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Am 13.10.2012 05:01, schrieb Ming Lei:
> On Sat, Oct 13, 2012 at 6:11 AM, Tilman Schmidt <tilman@xxxxxxx> wrote:
>>
>> There are two cases I have to worry about:
>> (1) a reset triggered by int_in_work()
>> (2) a reset triggered by some other source external to the driver
>>
>> In case (1) the current design will deadlock because pre_reset() is
>> indirectly called from int_in_work() but calls cancel_work_sync()
>> which will wait for this very work item to complete. This would be
>> fixed by pre_reset() not calling cancel_work_sync() at all.
> 
> Maybe you need to set a flag in int_in_work() to indicate that
> current reset is from itself, so you may fix both two cases.
> 
>> int_in_work() will then run to completion without resubmitting
>> urb_int_in because it's already past that point once it calls
>> usb_reset_device(), so there's no conflict with post_reset() doing
>> that.
>>
>> In case (2) the current design will work fine, but if I remove the
>> call to cancel_work_sync() from pre_reset() there's a slight chance
>> that an uncompleted int_in_work() work item remains uncancelled.
>> So any part of int_in_work() might run asynchronously after
>> pre_reset(). The question is whether this might cause a problem.
>>
>> Which of the two cases does your argument address?
> 
> I am saying the case 2, and the current design should be fine
> if the 1sec timeout can be tolerated in int_in_work().
> 
> You are right on case 1.

Ok, what do you think about this patch? It uses the fact that
pre_reset() calls the suspend method with message=PMSG_ON, which
the USB framework never does, to skip the cancel_work_sync() in that
case. It also avoids the extraneous reset if usb_submit_urb() fails
because post_reset() has already submitted the interrupt URB.

-- 
Tilman Schmidt                    E-Mail: tilman@xxxxxxx
Bonn, Germany
Diese Nachricht besteht zu 100% aus wiederverwerteten Bits.
Ungeöffnet mindestens haltbar bis: (siehe Rückseite)
diff --git a/drivers/isdn/gigaset/bas-gigaset.c b/drivers/isdn/gigaset/bas-gigaset.c
index 5275887..3c69dbf 100644
--- a/drivers/isdn/gigaset/bas-gigaset.c
+++ b/drivers/isdn/gigaset/bas-gigaset.c
@@ -617,7 +617,11 @@ 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) {
+
+	/* try to recover from failures by resetting the device, except
+	 * for -ENODEV (device gone) and -EINVAL (URB already resubmitted)
+	 */
+	if (rc != 0 && rc != -EINVAL && rc != -ENODEV) {
 		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 +2446,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 +2504,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;

Attachment: signature.asc
Description: OpenPGP digital signature


[Index of Archives]     [Linux Media]     [Linux Input]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [Old Linux USB Devel Archive]

  Powered by Linux