Oliver: In case my message from last Friday fell through the cracks, I'm resending it. This patch should do what you want:
Calls to usb_unlink_urb() while the completion handler is running won't do anything until the handler has returned. Synchronous calls to usb_unlink_urb() are guaranteed not to return until the completion handler for the urb being cancelled has returned. The one disadvantage to this scheme (not a big one) is that if a completion handler calls usb_unlink_urb() for its own urb (following a resubmit, for example), the call will spin indefinitely. Device drivers should just be careful not to do this. Let me know what you think. Alan ===== drivers/usb/core/hcd.c 1.89 vs edited ===== --- 1.89/drivers/usb/core/hcd.c Thu Jan 16 04:29:28 2003 +++ edited/drivers/usb/core/hcd.c Thu Jan 23 16:40:12 2003 @@ -1102,6 +1102,7 @@ unsigned long flags; struct completion_splice splice; int retval; + int just_waiting = 0; if (!urb) return -EINVAL; @@ -1116,8 +1117,13 @@ * lock sequence needed for the usb_hcd_giveback_urb() code paths * (urb lock, then hcd_data_lock) in case some other CPU is now * unlinking it. + * + * In case the completion handler is already running, we first + * acquire urb->handler_lock; this will cause us to wait until + * the handler has returned. */ - spin_lock_irqsave (&urb->lock, flags); + spin_lock_irqsave (&urb->handler_lock, flags); + spin_lock (&urb->lock); spin_lock (&hcd_data_lock); if (!urb->dev || !urb->dev->bus) { @@ -1139,13 +1145,19 @@ } /* Any status except -EINPROGRESS means something already started to - * unlink this URB from the hardware. So there's no more work to do. + * unlink this URB from the hardware. For an async request there's + * no more work to do. For a synchronous request, if the completion + * handler has not yet run then we must still wait for it. * * FIXME use better explicit urb state */ if (urb->status != -EINPROGRESS) { retval = -EBUSY; - goto done; + if (urb->transfer_flags & URB_ASYNC_UNLINK) + goto done; + if (!(urb->transfer_flags & URB_NOT_COMPLETED)) + goto done; + just_waiting = 1; } /* maybe set up to block until the urb's completion fires. the @@ -1164,15 +1176,23 @@ splice.context = urb->context; urb->complete = unlink_complete; urb->context = &splice; - urb->status = -ENOENT; + /* If the request arrived after the urb had finished anyway, + * don't indicate that it was unlinked -- it wasn't. + */ + if (!just_waiting) + urb->status = -ENOENT; } else { /* asynchronous unlink */ urb->status = -ECONNRESET; } spin_unlock (&hcd_data_lock); - spin_unlock_irqrestore (&urb->lock, flags); + spin_unlock (&urb->lock); + spin_unlock_irqrestore (&urb->handler_lock, flags); - if (urb == (struct urb *) hcd->rh_timer.data) { + if (just_waiting) + /* no need to unlink; the urb is already finishing up */ + ; + else if (urb == (struct urb *) hcd->rh_timer.data) { usb_rh_status_dequeue (hcd, urb); retval = 0; } else { @@ -1196,11 +1216,12 @@ return -EINPROGRESS; wait_for_completion (&splice.done); - return 0; + return retval; done: spin_unlock (&hcd_data_lock); - spin_unlock_irqrestore (&urb->lock, flags); + spin_unlock (&urb->lock); + spin_unlock_irqrestore (&urb->handler_lock, flags); bye: if (retval && sys) dev_dbg (sys, "hcd_unlink_urb %p fail %d\n", urb, retval); @@ -1307,7 +1328,10 @@ } /* pass ownership to the completion handler */ + spin_lock (&urb->handler_lock); + urb->transfer_flags &= ~URB_NOT_COMPLETED; urb->complete (urb, regs); + spin_unlock (&urb->handler_lock); usb_put_urb (urb); } EXPORT_SYMBOL (usb_hcd_giveback_urb); ===== drivers/usb/core/urb.c 1.19 vs edited ===== --- 1.19/drivers/usb/core/urb.c Wed Oct 30 22:55:08 2002 +++ edited/drivers/usb/core/urb.c Thu Jan 23 16:36:13 2003 @@ -44,6 +44,7 @@ memset(urb, 0, sizeof(*urb)); urb->count = (atomic_t)ATOMIC_INIT(1); spin_lock_init(&urb->lock); + spin_lock_init(&urb->handler_lock); return urb; } @@ -192,9 +193,14 @@ struct usb_device *dev; struct usb_operations *op; int is_out; + int status; if (!urb || urb->hcpriv || !urb->complete) return -EINVAL; + if (urb->transfer_flags & URB_NOT_COMPLETED) { + BUG(); + return -EINPROGRESS; + } if (!(dev = urb->dev) || !dev->bus || dev->devnum <= 0) return -ENODEV; if (!(op = dev->bus->op) || !op->submit_urb) @@ -346,7 +352,10 @@ urb->interval = temp; } - return op->submit_urb (urb, mem_flags); + status = op->submit_urb (urb, mem_flags); + if (!status) + urb->transfer_flags |= URB_NOT_COMPLETED; + return status; } /*-------------------------------------------------------------------*/ ===== include/linux/usb.h 1.123 vs edited ===== --- 1.123/include/linux/usb.h Fri Jan 17 13:21:23 2003 +++ edited/include/linux/usb.h Thu Jan 23 16:35:51 2003 @@ -555,6 +555,7 @@ #define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet */ #define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */ #define URB_TIMEOUT_KILLED 0x1000 /* only set by HCD! */ +#define URB_NOT_COMPLETED 0x2000 /* only set by HCD! */ struct usb_iso_packet_descriptor { unsigned int offset; @@ -728,6 +729,7 @@ { spinlock_t lock; /* lock for the URB */ atomic_t count; /* reference count of the URB */ + spinlock_t handler_lock; /* lock during completion routine */ void *hcpriv; /* private data for host controller */ struct list_head urb_list; /* list pointer to all active urbs */ struct usb_device *dev; /* (in) pointer to associated device */ ------------------------------------------------------- This SF.NET email is sponsored by: SourceForge Enterprise Edition + IBM + LinuxWorld = Something 2 See! http://www.vasoftware.com _______________________________________________ [EMAIL PROTECTED] To unsubscribe, use the last form field at: https://lists.sourceforge.net/lists/listinfo/linux-usb-devel