anchors and their use

Oliver Neukum <[email protected]>
Newsgroups gmane.linux.usb.devel
Organization Novell
Message-ID <[email protected]>
Hi Alan, list,

here's the patch to introduce anchors for use with "fire-and-forget"
techniques and a modification for the skeleton driver to use them.
This should make implementing pre/post_reset and suspend/resume
much easier.

	Regards
		Oliver
-- 

--- a/include/linux/usb.h	2007-05-04 10:50:18.000000000 +0200
+++ b/include/linux/usb.h	2007-05-08 12:47:27.000000000 +0200
@@ -958,11 +958,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"
@@ -1135,6 +1150,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 */
@@ -1270,6 +1287,10 @@ extern struct urb *usb_get_urb(struct ur
 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, int timeout);
 
 void *usb_buffer_alloc (struct usb_device *dev, size_t size,
 	gfp_t mem_flags, dma_addr_t *dma);
--- a/drivers/usb/core/hcd.c	2007-05-04 12:04:29.000000000 +0200
+++ b/drivers/usb/core/hcd.c	2007-05-07 15:19:01.000000000 +0200
@@ -1408,6 +1408,8 @@ void usb_hcd_giveback_urb (struct usb_hc
 	}
 
 	usbmon_urb_complete (&hcd->self, urb);
+	usb_unanchor_urb(urb);
+
 	/* pass ownership to the completion handler */
 	urb->complete (urb);
 	atomic_dec (&urb->use_count);
--- a/drivers/usb/core/urb.c	2007-05-04 10:53:14.000000000 +0200
+++ b/drivers/usb/core/urb.c	2007-05-16 11:32:15.000000000 +0200
@@ -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,55 @@ 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);
+}
+
+/**
+ * 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);
+	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);
+}
+
 /*-------------------------------------------------------------------*/
 
 /**
@@ -477,12 +527,56 @@ void usb_kill_urb(struct urb *urb)
 	--urb->reject;
 	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);
+}
+
+/**
+ * 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 jiffies
+ *
+ * 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, int timeout)
+{
+	int res;
+
+	res = wait_event_timeout(anchor->wait, list_empty(&anchor->urb_list), timeout);
+	return res;
+}
 
 EXPORT_SYMBOL(usb_init_urb);
 EXPORT_SYMBOL(usb_alloc_urb);
 EXPORT_SYMBOL(usb_free_urb);
 EXPORT_SYMBOL(usb_get_urb);
+EXPORT_SYMBOL(usb_anchor_urb);
 EXPORT_SYMBOL(usb_submit_urb);
 EXPORT_SYMBOL(usb_unlink_urb);
 EXPORT_SYMBOL(usb_kill_urb);
-
+EXPORT_SYMBOL(usb_kill_anchored_urbs);
+EXPORT_SYMBOL(usb_wait_anchor_empty_timeout);
--- a/drivers/usb/usb-skeleton.c	2007-05-04 13:07:30.000000000 +0200
+++ b/drivers/usb/usb-skeleton.c	2007-05-16 11:18:22.000000000 +0200
@@ -54,16 +54,20 @@ struct usb_skel {
 	struct usb_device	*udev;			/* the usb device for this device */
 	struct usb_interface	*interface;		/* the interface for this device */
 	struct semaphore	limit_sem;		/* limiting the number of writes in progress */
+	struct usb_anchor	submitted;		/* in case we need to retract our submissions */
 	unsigned char           *bulk_in_buffer;	/* the buffer to receive data */
 	size_t			bulk_in_size;		/* the size of the receive buffer */
 	__u8			bulk_in_endpointAddr;	/* the address of the bulk in endpoint */
 	__u8			bulk_out_endpointAddr;	/* the address of the bulk out endpoint */
+	int			errors;			/* the last request tanked */
+	spinlock_t		err_lock;		/* lock for errors */
 	struct kref		kref;
 	struct mutex		io_mutex;		/* synchronize I/O with disconnect */
 };
 #define to_skel_dev(d) container_of(d, struct usb_skel, kref)
 
 static struct usb_driver skel_driver;
+static void skel_draw_down(struct usb_skel *dev);
 
 static void skel_delete(struct kref *kref)
 {
@@ -102,18 +106,22 @@ static int skel_open(struct inode *inode
 
 	/* increment our usage count for the device */
 	kref_get(&dev->kref);
-	/* now we can drop the lock */
+	/* to guard against an ongoing reset */
+	mutex_lock(&dev->io_mutex);
+	/* now we can drop the common lock */
 	mutex_unlock(&skel_open_lock);
 
 	/* prevent the device from being autosuspended */
 	retval = usb_autopm_get_interface(interface);
 	if (retval) {
+		mutex_unlock(&dev->io_mutex);
 		kref_put(&dev->kref, skel_delete);
 		goto exit;
 	}
 
 	/* save our object in the file's private structure */
 	file->private_data = dev;
+	mutex_unlock(&dev->io_mutex);
 
 exit:
 	return retval;
@@ -138,6 +146,29 @@ static int skel_release(struct inode *in
 	return 0;
 }
 
+static int skel_flush(struct file *file, fl_owner_t id)
+{
+	struct usb_skel *dev;
+	unsigned long flags;
+	int res;
+
+	dev = (struct usb_skel *)file->private_data;
+	if (dev == NULL)
+		return -ENODEV;
+
+	/* wait for io to stop */
+	skel_draw_down(dev);
+
+	/* read out errors, leave subsequent opens a clean slate */
+	spin_lock_irqsave(&dev->err_lock, flags);
+	res = dev->errors ? -EIO : 0;
+	if (res)
+		dev->errors = 0;
+	spin_unlock_irqrestore(&dev->err_lock, flags);
+
+	return res;
+}
+
 static ssize_t skel_read(struct file *file, char *buffer, size_t count, loff_t *ppos)
 {
 	struct usb_skel *dev;
@@ -179,14 +210,17 @@ static void skel_write_bulk_callback(str
 	dev = (struct usb_skel *)urb->context;
 
 	/* sync/async unlink faults aren't errors */
-	if (urb->status &&
-	    !(urb->status == -ENOENT ||
-	      urb->status == -ECONNRESET ||
-	      urb->status == -ESHUTDOWN)) {
-		err("%s - nonzero write bulk status received: %d",
-		    __FUNCTION__, urb->status);
+	if (urb->status) {
+		if (!(urb->status == -ENOENT ||
+		      urb->status == -ECONNRESET ||
+		      urb->status == -ESHUTDOWN)) {
+			err("%s - nonzero write bulk status received: %d",
+			__FUNCTION__, urb->status);
+		}
+		spin_lock(&dev->err_lock);
+		dev->errors = urb->status;
+		spin_unlock(&dev->err_lock);
 	}
-
 	/* free up our allocated buffer */
 	usb_buffer_free(urb->dev, urb->transfer_buffer_length,
 			urb->transfer_buffer, urb->transfer_dma);
@@ -213,6 +247,15 @@ static ssize_t skel_write(struct file *f
 		goto exit;
 	}
 
+	spin_lock_irq(&dev->err_lock);
+	if (dev->errors < 0) {
+		dev->errors = 0;
+		retval = -EIO;
+	}
+	spin_unlock_irq(&dev->err_lock);
+	if (retval < 0)
+		goto error;
+
 	/* create a urb, and a buffer for it, and copy the data to the urb */
 	urb = usb_alloc_urb(0, GFP_KERNEL);
 	if (!urb) {
@@ -244,13 +287,14 @@ static ssize_t skel_write(struct file *f
 			  usb_sndbulkpipe(dev->udev, dev->bulk_out_endpointAddr),
 			  buf, writesize, skel_write_bulk_callback, dev);
 	urb->transfer_flags |= URB_NO_TRANSFER_DMA_MAP;
+	usb_anchor_urb(urb, &dev->submitted);
 
 	/* send the data out the bulk port */
 	retval = usb_submit_urb(urb, GFP_KERNEL);
 	mutex_unlock(&dev->io_mutex);
 	if (retval) {
 		err("%s - failed submitting write urb, error %d", __FUNCTION__, retval);
-		goto error;
+		goto error_unanchor;
 	}
 
 	/* release our reference to this urb, the USB core will eventually free it entirely */
@@ -259,6 +303,8 @@ static ssize_t skel_write(struct file *f
 
 	return writesize;
 
+error_unanchor:
+	usb_unanchor_urb(urb);
 error:
 	if (urb) {
 		usb_buffer_free(dev->udev, writesize, buf, urb->transfer_dma);
@@ -276,6 +322,7 @@ static const struct file_operations skel
 	.write =	skel_write,
 	.open =		skel_open,
 	.release =	skel_release,
+	.flush =	skel_flush,
 };
 
 /*
@@ -306,6 +353,8 @@ static int skel_probe(struct usb_interfa
 	kref_init(&dev->kref);
 	sema_init(&dev->limit_sem, WRITES_IN_FLIGHT);
 	mutex_init(&dev->io_mutex);
+	spin_lock_init(&dev->err_lock);
+	init_usb_anchor(&dev->submitted);
 
 	dev->udev = usb_get_dev(interface_to_usbdev(interface));
 	dev->interface = interface;
@@ -383,7 +432,7 @@ static void skel_disconnect(struct usb_i
 	dev->interface = NULL;
 	mutex_unlock(&dev->io_mutex);
 
-
+	usb_kill_anchored_urbs(&dev->submitted);
 
 	/* decrement our usage count */
 	kref_put(&dev->kref, skel_delete);
@@ -391,10 +440,60 @@ static void skel_disconnect(struct usb_i
 	info("USB Skeleton #%d now disconnected", minor);
 }
 
+static void __skel_draw_down(struct usb_skel *dev)
+{
+	int time;
+
+	time = usb_wait_anchor_empty_timeout(&dev->submitted, 1 * HZ);
+	if (!time)
+		usb_kill_anchored_urbs(&dev->submitted);
+}
+
+static void skel_draw_down(struct usb_skel *dev)
+{
+	mutex_lock(&dev->io_mutex);
+	__skel_draw_down(dev);
+	mutex_unlock(&dev->io_mutex);
+}
+
+static int skel_suspend(struct usb_interface *intf, pm_message_t message)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	__skel_draw_down(dev);
+	return 0;
+}
+
+static void skel_pre_reset(struct usb_interface *intf)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	mutex_lock(&dev->io_mutex);
+	__skel_draw_down(dev);
+}
+
+static int skel_resume (struct usb_interface *intf)
+{
+	return 0;
+}
+
+static void skel_post_reset(struct usb_interface *intf)
+{
+	struct usb_skel *dev = usb_get_intfdata(intf);
+
+	/* we are sure no URBs are active - no locking needed */
+	dev->errors = -EPIPE;
+	mutex_unlock(&dev->io_mutex);
+}
+
 static struct usb_driver skel_driver = {
 	.name =		"skeleton",
 	.probe =	skel_probe,
 	.disconnect =	skel_disconnect,
+	.suspend =	skel_suspend,
+	.resume =	skel_resume,
+	.pre_reset =	skel_pre_reset,
+	.post_reset =	skel_post_reset,
 	.id_table =	skel_table,
 	.supports_autosuspend = 1,
 };


-------------------------------------------------------------------------
This SF.net email is sponsored by DB2 Express
Download DB2 Express C - the FREE version of DB2 express and take
control of your XML. No limits. Just data. Click to get it now.
http://sourceforge.net/powerbar/db2/
_______________________________________________
[email protected]
To unsubscribe, use the last form field at:
https://lists.sourceforge.net/lists/listinfo/linux-usb-devel
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.