USB: introduce usb_anchor
authorOliver Neukum <oneukum@suse.de>
Fri, 25 May 2007 11:40:56 +0000 (13:40 +0200)
committerGreg Kroah-Hartman <gregkh@suse.de>
Thu, 12 Jul 2007 23:29:51 +0000 (16:29 -0700)
- introduction of usb_anchor and its methods

Signed-off-by: Oliver Neukum <oneukum@suse.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
drivers/usb/core/hcd.c
drivers/usb/core/urb.c
include/linux/usb.h

index c5a2f83..87d6edf 100644 (file)
@@ -1410,6 +1410,8 @@ void usb_hcd_giveback_urb (struct usb_hcd *hcd, struct urb *urb)
        }
 
        usbmon_urb_complete (&hcd->self, urb);
+       usb_unanchor_urb(urb);
+
        /* pass ownership to the completion handler */
        urb->complete (urb);
        atomic_dec (&urb->use_count);
index 94ea972..ac4273d 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/usb.h>
+#include <linux/wait.h>
 #include "hcd.h"
 
 #define to_urb(d) container_of(d, struct urb, kref)
@@ -11,6 +12,7 @@
 static void urb_destroy(struct kref *kref)
 {
        struct urb *urb = to_urb(kref);
+
        kfree(urb);
 }
 
@@ -34,6 +36,7 @@ void usb_init_urb(struct urb *urb)
                memset(urb, 0, sizeof(*urb));
                kref_init(&urb->kref);
                spin_lock_init(&urb->lock);
+               INIT_LIST_HEAD(&urb->anchor_list);
        }
 }
 
@@ -100,8 +103,60 @@ struct urb * usb_get_urb(struct urb *urb)
                kref_get(&urb->kref);
        return urb;
 }
-               
-               
+
+/**
+ * usb_anchor_urb - anchors an URB while it is processed
+ * @urb: pointer to the urb to anchor
+ * @anchor: pointer to the anchor
+ *
+ * This can be called to have access to URBs which are to be executed
+ * without bothering to track them
+ */
+void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&anchor->lock, flags);
+       usb_get_urb(urb);
+       list_add_tail(&urb->anchor_list, &anchor->urb_list);
+       urb->anchor = anchor;
+       spin_unlock_irqrestore(&anchor->lock, flags);
+}
+EXPORT_SYMBOL_GPL(usb_anchor_urb);
+
+/**
+ * usb_unanchor_urb - unanchors an URB
+ * @urb: pointer to the urb to anchor
+ *
+ * Call this to stop the system keeping track of this URB
+ */
+void usb_unanchor_urb(struct urb *urb)
+{
+       unsigned long flags;
+       struct usb_anchor *anchor;
+
+       if (!urb)
+               return;
+
+       anchor = urb->anchor;
+       if (!anchor)
+               return;
+
+       spin_lock_irqsave(&anchor->lock, flags);
+       if (unlikely(anchor != urb->anchor)) {
+               /* we've lost the race to another thread */
+               spin_unlock_irqrestore(&anchor->lock, flags);
+               return;
+       }
+       urb->anchor = NULL;
+       list_del(&urb->anchor_list);
+       spin_unlock_irqrestore(&anchor->lock, flags);
+       usb_put_urb(urb);
+       if (list_empty(&anchor->urb_list))
+               wake_up(&anchor->wait);
+}
+EXPORT_SYMBOL_GPL(usb_unanchor_urb);
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -478,6 +533,48 @@ void usb_kill_urb(struct urb *urb)
        spin_unlock_irq(&urb->lock);
 }
 
+/**
+ * usb_kill_anchored_urbs - cancel transfer requests en masse
+ * @anchor: anchor the requests are bound to
+ *
+ * this allows all outstanding URBs to be killed starting
+ * from the back of the queue
+ */
+void usb_kill_anchored_urbs(struct usb_anchor *anchor)
+{
+       struct urb *victim;
+
+       spin_lock_irq(&anchor->lock);
+       while (!list_empty(&anchor->urb_list)) {
+               victim = list_entry(anchor->urb_list.prev, struct urb, anchor_list);
+               /* we must make sure the URB isn't freed before we kill it*/
+               usb_get_urb(victim);
+               spin_unlock_irq(&anchor->lock);
+               /* this will unanchor the URB */
+               usb_kill_urb(victim);
+               usb_put_urb(victim);
+               spin_lock_irq(&anchor->lock);
+       }
+       spin_unlock_irq(&anchor->lock);
+}
+EXPORT_SYMBOL_GPL(usb_kill_anchored_urbs);
+
+/**
+ * usb_wait_anchor_empty_timeout - wait for an anchor to be unused
+ * @anchor: the anchor you want to become unused
+ * @timeout: how long you are willing to wait in milliseconds
+ *
+ * Call this is you want to be sure all an anchor's
+ * URBs have finished
+ */
+int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
+                                 unsigned int timeout)
+{
+       return wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list),
+                                 msecs_to_jiffies(timeout));
+}
+EXPORT_SYMBOL_GPL(usb_wait_anchor_empty_timeout);
+
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
@@ -485,4 +582,3 @@ EXPORT_SYMBOL(usb_get_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
-
index 98e0338..0873c62 100644 (file)
@@ -1000,11 +1000,26 @@ struct usb_iso_packet_descriptor {
 
 struct urb;
 
+struct usb_anchor {
+       struct list_head urb_list;
+       wait_queue_head_t wait;
+       spinlock_t lock;
+};
+
+static inline void init_usb_anchor(struct usb_anchor *anchor)
+{
+       INIT_LIST_HEAD(&anchor->urb_list);
+       init_waitqueue_head(&anchor->wait);
+       spin_lock_init(&anchor->lock);
+}
+
 typedef void (*usb_complete_t)(struct urb *);
 
 /**
  * struct urb - USB Request Block
  * @urb_list: For use by current owner of the URB.
+ * @anchor_list: membership in the list of an anchor
+ * @anchor: to anchor URBs to a common mooring
  * @pipe: Holds endpoint number, direction, type, and more.
  *     Create these values with the eight macros available;
  *     usb_{snd,rcv}TYPEpipe(dev,endpoint), where the TYPE is "ctrl"
@@ -1177,6 +1192,8 @@ struct urb
        /* public: documented fields in the urb that can be used by drivers */
        struct list_head urb_list;      /* list head for use by the urb's
                                         * current owner */
+       struct list_head anchor_list;   /* the URB may be anchored by the driver */
+       struct usb_anchor *anchor;
        struct usb_device *dev;         /* (in) pointer to associated device */
        unsigned int pipe;              /* (in) pipe information */
        int status;                     /* (return) non-ISO status */
@@ -1312,6 +1329,11 @@ extern struct urb *usb_get_urb(struct urb *urb);
 extern int usb_submit_urb(struct urb *urb, gfp_t mem_flags);
 extern int usb_unlink_urb(struct urb *urb);
 extern void usb_kill_urb(struct urb *urb);
+extern void usb_kill_anchored_urbs(struct usb_anchor *anchor);
+extern void usb_anchor_urb(struct urb *urb, struct usb_anchor *anchor);
+extern void usb_unanchor_urb(struct urb *urb);
+extern int usb_wait_anchor_empty_timeout(struct usb_anchor *anchor,
+                                        unsigned int timeout);
 
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
        gfp_t mem_flags, dma_addr_t *dma);