When David mentioned that unlink_urb tests urb->hcpriv to see whether the
low-level driver has finished processing the urb, I realized that my patch
needed to be fixed. Here's the new version.
Alan Stern
===== include/linux/usb.h 1.124 vs edited =====
--- 1.124/include/linux/usb.h Wed Jan 22 12:38:08 2003
+++ edited/include/linux/usb.h Thu Jan 30 15:37:47 2003
@@ -554,6 +554,7 @@
#define URB_NO_FSBR 0x0020 /* UHCI-specific */
#define URB_ZERO_PACKET 0x0040 /* Finish bulk OUTs with short packet
*/
#define URB_NO_INTERRUPT 0x0080 /* HINT: no non-error interrupt needed */
+#define URB_ALIVE 0x2000 /* Completion handler not yet called */
struct usb_iso_packet_descriptor {
unsigned int offset;
@@ -727,6 +728,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 */
===== drivers/usb/core/hcd.c 1.90 vs edited =====
--- 1.90/drivers/usb/core/hcd.c Wed Jan 22 13:02:11 2003
+++ edited/drivers/usb/core/hcd.c Thu Jan 30 15:36:53 2003
@@ -1105,6 +1105,7 @@
unsigned long flags;
struct completion_splice splice;
int retval;
+ int just_waiting = 0;
if (!urb)
return -EINVAL;
@@ -1119,8 +1120,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) {
@@ -1136,19 +1142,24 @@
goto done;
}
- if (!urb->hcpriv) {
- retval = -EINVAL;
- goto done;
- }
-
/* 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. Likewise, if hcpriv is not
+ * set then the low-level driver is done with this URB.
+ * If the completion handler has run or the request is async then
+ * there's no more work to do. Otherwise we must wait for the
+ * handler.
*
* FIXME use better explicit urb state
*/
- if (urb->status != -EINPROGRESS) {
+ if (urb->status != -EINPROGRESS || !urb->hcpriv) {
+ if (!(urb->transfer_flags & URB_ALIVE)) {
+ retval = -EINVAL;
+ goto done;
+ }
retval = -EBUSY;
- goto done;
+ if (urb->transfer_flags & URB_ASYNC_UNLINK)
+ goto done;
+ just_waiting = 1;
}
/* maybe set up to block until the urb's completion fires. the
@@ -1167,15 +1178,23 @@
splice.context = urb->context;
urb->complete = unlink_complete;
urb->context = &splice;
- urb->status = -ENOENT;
+ /* Only indicate that the URB was unlinked if the request
+ * arrived before the URB had finished anyway.
+ */
+ 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 {
@@ -1199,11 +1218,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);
@@ -1310,7 +1330,10 @@
}
/* pass ownership to the completion handler */
+ spin_lock (&urb->handler_lock);
+ urb->transfer_flags &= ~URB_ALIVE;
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 30 15:37:03 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_ALIVE) {
+ 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_ALIVE;
+ return status;
}
/*-------------------------------------------------------------------*/
-------------------------------------------------------
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