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

Reply via email to