We can't have 2 target types in the same module without breaking the
automatic loading of target modules.  eg, if 'iostat_avg' is requested
and not registered the kernel will try and load a module called
'dm-iostat_avg'.

So this changes the code back to a single target, with an extra
parameter which states whether we want latency info or not.
--- diff/drivers/md/dm-iostats.c	2002-12-16 13:22:12.000000000 +0000
+++ source/drivers/md/dm-iostats.c	2002-12-16 13:22:03.000000000 +0000
@@ -11,6 +11,7 @@
 #include <linux/blkdev.h>
 #include <linux/mempool.h>
 #include <linux/slab.h>
+#include <linux/ctype.h>
 
 static kmem_cache_t *_iostats_cache;
 mempool_t *_iostats_pool;
@@ -28,23 +29,24 @@
 };
 
 /*
- * Linear: io status for a linear range of a device.
+ * io status for a linear range of a device.
  */
-struct iostats_c {
-	struct dm_dev *dev;
-	uint32_t reads;
-	uint32_t writes;
-};
-
 typedef	uint16_t	count_t;
 #define	MAX_COUNT	((count_t) -1)
+#define MIN_IOSTATS 128
+#define IOF_LATENCY 1
 
-struct iostats_avg_c {
-	spinlock_t lock;
-
+struct iostats_c {
+	unsigned long flags;
 	struct dm_dev *dev;
 	uint32_t reads;
 	uint32_t writes;
+
+	/*
+	 * These fields are only present if we are recording the
+	 * average completion times.
+	 */
+	spinlock_t lock;
 	jiffies_t total_r;
 	jiffies_t total_w;
 	count_t count_r;
@@ -62,106 +64,70 @@
 	mempool_free(io, _iostats_pool);
 }
 
-
 /*
- * Construct a linear io status mapping: <dev_path>
+ * Construct a io status mapping: <dev_path> <get averages y/n>
  */
-static int _iostats_ctr(struct dm_target *ti, int argc, char **argv, int size)
+static int iostats_ctr(struct dm_target *ti, int argc, char **argv)
 {
+	size_t size;
+	int averaging;
 	struct iostats_c *ic;
 
-	if (argc != 1) {
-		ti->error = "dm-iostats: Wrong argument count";
+	if (argc != 2) {
+		ti->error = "incorrect number of arguments";
 		return -EINVAL;
 	}
 
-	ic = kmalloc(size, GFP_KERNEL);
-	if (ic == NULL) {
-		ti->error = "dm-iostats: Cannot allocate io status context";
-		return -ENOMEM;
-	}
-
-	if (dm_get_device(ti, argv[0], ti->begin, ti->len,
-			  dm_table_get_mode(ti->table), &ic->dev)) {
-		ti->error = "dm-iostats: Device lookup failed";
-		goto bad;
+	if (strlen(argv[1]) != 1) {
+		ti->error = "invalid 'should average' flag";
+		return -EINVAL;
 	}
 
-	ti->private = ic;
-	return 0;
-
-      bad:
-	kfree(ic);
-	return -EINVAL;
-}
-
-/* read/write statistics constructor */
-static int iostats_ctr(struct dm_target *ti, int argc, char **argv)
-{
-	int r = _iostats_ctr(ti, argc, argv, sizeof(struct iostats_c));
+	averaging = (toupper(argv[1][0]) == 'Y');
 
-	if (!r) {
-		struct iostats_c *ic = ti->private;
+	if (averaging)
+		size = sizeof(*ic);
+	else
+		size = (size_t) &((struct iostats_c *) NULL)->lock;
 
-		ic->reads = 0;
-		ic->writes = 0;
+	ic = kmalloc(size, GFP_KERNEL);
+	if (!ic) {
+		ti->error = "out of memory";
+		kfree(ic);
+		return -ENOMEM;
 	}
 
-	return r;
-}
-
-/* average io latency statistics constuctor */
-static int iostats_avg_ctr(struct dm_target *ti, int argc, char **argv)
-{
-	int r = _iostats_ctr(ti, argc, argv, sizeof(struct iostats_avg_c));
-
-	if (!r) {
-		struct iostats_avg_c *ic = ti->private;
-
+	memset(ic, 0, size);
+	if (averaging) {
+		set_bit(IOF_LATENCY, &ic->flags);
 		ic->lock = SPIN_LOCK_UNLOCKED;
-		ic->reads = 0;
-		ic->writes = 0;
-		ic->total_r = 0;
-		ic->total_w = 0;
-		ic->count_r = 0;
-		ic->count_w = 0;
 	}
 
-	return r;
+	if (dm_get_device(ti, argv[0], ti->begin, ti->len,
+			  dm_table_get_mode(ti->table), &ic->dev)) {
+		ti->error = "device lookup failed";
+		kfree(ic);
+		return -ENXIO;
+	}
+
+	return 0;
 }
 
-/* common destructor */
 static void iostats_dtr(struct dm_target *ti)
 {
 	struct iostats_c *ic = (struct iostats_c *) ti->private;
-
 	dm_put_device(ti, ic->dev);
 	kfree(ic);
 }
 
-/* read/write statistics mapping */
-static int iostats_map(struct dm_target *ti, struct buffer_head *bh, int rw)
-{
-	struct iostats_c *ic = (struct iostats_c *) ti->private;
-
-	if (rw == WRITE)
-		ic->writes++;
-	else
-		ic->reads++;
-
-	/* map */
-	bh->b_rdev = ic->dev->dev;
-
-	return 1;
-}
-
-/* average latency stats end_io function */
+/*
+ * average latency stats end_io function */
 static void iostats_avg_end_io(struct buffer_head *bh, int uptodate)
 {
 	int flags;
 	jiffies_t j;
 	struct dm_iostats *io = bh->b_private;
-	struct iostats_avg_c *ic = io->ti->private;
+	struct iostats_c *ic = io->ti->private;
 
 	spin_lock_irqsave(&ic->lock, flags);
 
@@ -198,12 +164,15 @@
 	bh->b_end_io(bh, uptodate);
 }
 
-/* average io latency statistics mapping */
-static int iostats_avg_map(struct dm_target *ti, struct buffer_head *bh, int rw)
+/*
+ * Hook the io so we can work get the io completion time.
+ */
+static int _hook_io(struct iostats_c *stats, struct dm_target *ti,
+		    struct buffer_head *bh, int rw)
 {
-	struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private;
-	struct dm_iostats *io = alloc_io();
+	struct dm_iostats *io;
 
+	io = alloc_io();
 	if (!io)
 		return -ENOMEM;
 
@@ -217,31 +186,26 @@
 	bh->b_end_io = iostats_avg_end_io;
 	bh->b_private = io;
 
-	/* map */
-	bh->b_rdev = ic->dev->dev;
-
-	return 1;
+	return 0;
 }
 
 /* read/write statistics mapping */
-static int iostats_status(struct dm_target *ti, status_type_t type,
-			 char *result, int maxlen)
+static int iostats_map(struct dm_target *ti, struct buffer_head *bh, int rw)
 {
 	struct iostats_c *ic = (struct iostats_c *) ti->private;
 
-	switch (type) {
-	case STATUSTYPE_INFO:
-		snprintf(result, maxlen, "%s %u %u",
-			 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)),
-			 ic->reads, ic->writes);
-		break;
+	if (rw == WRITE)
+		ic->writes++;
+	else
+		ic->reads++;
 
-	case STATUSTYPE_TABLE:
-		snprintf(result, maxlen, "%s",
-			 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)));
-		break;
-	}
-	return 0;
+	if (test_bit(IOF_LATENCY, &ic->flags))
+		_hook_io(ic, ti, bh, rw);
+
+	/* map */
+	bh->b_rdev = ic->dev->dev;
+
+	return 1;
 }
 
 static inline uint _calc(jiffies_t j, count_t c)
@@ -251,15 +215,19 @@
 	return (c ? (j * m / c) : (j * m));
 }
 
-/* average io status */
-static int iostats_avg_status(struct dm_target *ti, status_type_t type,
-			      char *result, int maxlen)
+/* read/write statistics mapping */
+static int iostats_status(struct dm_target *ti, status_type_t type,
+			 char *result, int maxlen)
 {
-	struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private;
+	struct iostats_c *ic = (struct iostats_c *) ti->private;
 
 	switch (type) {
 	case STATUSTYPE_INFO:
-		{
+		if (test_bit(IOF_LATENCY, &ic->flags)) {
+			snprintf(result, maxlen, "%s %u %u",
+				 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)),
+				 ic->reads, ic->writes);
+		} else {
 			uint avg_r, avg_w;
 
 			avg_r = _calc(ic->total_r, ic->count_r);
@@ -267,19 +235,19 @@
 			snprintf(result, maxlen, "%s %u %u %u %u",
 				 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)),
 				 ic->reads, ic->writes, avg_r, avg_w);
-
-			break;
 		}
 		break;
 
 	case STATUSTYPE_TABLE:
-		snprintf(result, maxlen, "%s",
-			 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)));
+		snprintf(result, maxlen, "%s %s",
+			 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)),
+			 test_bit(IOF_LATENCY, &ic->flags) ? "y" : "n");
+		break;
 	}
 	return 0;
 }
 
-static struct target_type iostats_target = {
+static struct target_type _iostats_target = {
 	.name   = "iostats",
 	.module = THIS_MODULE,
 	.ctr    = iostats_ctr,
@@ -288,50 +256,9 @@
 	.status = iostats_status,
 };
 
-static struct target_type iostats_avg_target = {
-	.name   = "iostats_avg",
-	.module = THIS_MODULE,
-	.ctr    = iostats_avg_ctr,
-	.dtr    = iostats_dtr,
-	.map    = iostats_avg_map,
-	.status = iostats_avg_status,
-};
-
-/* Keep the target pointers in an array for (de)registration */
-static struct target_type *target_type_init[] = {
-	&iostats_target,
-	&iostats_avg_target,
-};
-
-void __exit dm_iostats_exit(void)
-{
-	int i;
-	const int count = ARRAY_SIZE(target_type_init);
-	struct target_type **tti = &target_type_init[count-1];
-
-	for (i = count; i; i--, tti--) {
-		int r = dm_unregister_target(*tti);
-
-		if (r)
-			DMERR("%s: unregister failed %d", (*tti)->name, r);
-	}
-
-	if (_iostats_pool) {
-		mempool_destroy(_iostats_pool);
-		_iostats_pool = NULL;
-	}
-
-	if (_iostats_cache) {
-		kmem_cache_destroy(_iostats_cache);
-		_iostats_cache = NULL;
-	}
-}
-
 int __init dm_iostats_init(void)
 {
-	int i;
-	const int count = ARRAY_SIZE(target_type_init);
-	struct target_type **tti = target_type_init;
+	int r;
 
 	/* allocate a slab for the dm_iostats */
 	_iostats_cache = kmem_cache_create("dm iostats io",
@@ -350,25 +277,30 @@
 		return -ENOMEM;
 	}
 
-	for (i = 0; i < count; i++, tti++) {
-		int r = dm_register_target(*tti);
+	r = dm_register_target(&_iostats_target);
+	if (r) {
+		mempool_destroy(_iostats_pool);
+		kmem_cache_destroy(_iostats_cache);
+	}
 
-		if (r) {
-			DMERR("%s: register failed %d", (*tti)->name, r);
+	return 0;
+}
 
-			for (tti--; i; i--, tti--) {
-				r = dm_unregister_target(*tti);
+void __exit dm_iostats_exit(void)
+{
+	int r = dm_unregister_target(&_iostats_target);
+	if (r)
+		DMERR("dm-iostats unregister failed %d", r);
 
-				if (r)
-					DMERR("%s: unregister failed %d",
-					      (*tti)->name, r);
-			}
-			return r;
-		}
+	if (_iostats_pool) {
+		mempool_destroy(_iostats_pool);
+		_iostats_pool = NULL;
 	}
 
-	DMINFO("dm_iostats_init v0.0.29");
-	return 0;
+	if (_iostats_cache) {
+		kmem_cache_destroy(_iostats_cache);
+		_iostats_cache = NULL;
+	}
 }
 
 /*