import dm-iostats.c
--- diff/drivers/md/dm-iostats.c	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-iostats.c	2002-12-16 10:45:56.000000000 +0000
@@ -0,0 +1,383 @@
+/*
+ * Copyright (C) 2002 Sistina Software Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/slab.h>
+
+#define	MIN_IOSTATS	256
+static kmem_cache_t *_iostats_cache;
+mempool_t *_iostats_pool;
+
+struct dm_iostats {
+	struct dm_target *ti;
+
+	int rw;
+	typeof(jiffies) jiffies_start_io;
+
+	void (*end_io) (struct buffer_head * bh, int uptodate);
+	void *context;
+};
+
+/*
+ * Linear: 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)
+
+struct iostats_avg_c {
+	struct dm_dev *dev;
+	uint32_t reads;
+	uint32_t writes;
+	typeof(jiffies) jiffies_r;
+	typeof(jiffies) jiffies_w;
+	count_t count_r;
+	count_t count_w;
+};
+
+
+static inline struct dm_iostats *alloc_io(void)
+{
+	return mempool_alloc(_iostats_pool, GFP_NOIO);
+}
+
+static inline void free_io(struct dm_iostats *io)
+{
+	mempool_free(io, _iostats_pool);
+}
+
+
+/*
+ * Construct a linear io status mapping: <dev_path>
+ */
+static int _iostats_ctr(struct dm_target *ti, int argc, char **argv, int size)
+{
+	struct iostats_c *ic;
+
+	if (argc != 1) {
+		ti->error = "dm-iostats: Wrong argument count";
+		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;
+	}
+
+	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));
+
+	if (!r) {
+		struct iostats_c *ic = ti->private;
+
+		ic->reads = 0;
+		ic->writes = 0;
+	}
+
+	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;
+
+		ic->reads = 0;
+		ic->writes = 0;
+		ic->jiffies_r = 0;
+		ic->jiffies_w = 0;
+		ic->count_r = 0;
+		ic->count_w = 0;
+	}
+
+	return r;
+}
+
+/* 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;
+}
+
+/* FIXME: should be a target lock */
+static spinlock_t	_io_lock = SPIN_LOCK_UNLOCKED;
+
+/* average latency stats end_io function */
+static void iostats_avg_end_io(struct buffer_head *bh, int uptodate)
+{
+	int flags;
+	typeof(jiffies) j;
+	struct dm_iostats *io = bh->b_private;
+	struct iostats_avg_c *ic = io->ti->private;
+
+	spin_lock_irqsave(&_io_lock, flags);
+
+	/* average io latency; ignore an io on jiffies overflow */
+	if (jiffies >= io->jiffies_start_io) {
+		j = jiffies - io->jiffies_start_io;
+
+		if (io->rw == WRITE) {
+			ic->writes++;
+			ic->jiffies_w += j;
+			ic->count_w++;
+			if (ic->count_w > MAX_COUNT) {
+				ic->jiffies_w /= ic->count_w;
+				ic->count_w = 1;
+			}
+		} else {
+			ic->reads++;
+			ic->jiffies_r += j;
+			ic->count_r++;
+			if (ic->count_r > MAX_COUNT) {
+				ic->jiffies_r /= ic->count_r;
+				ic->count_r = 1;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&_io_lock, flags);
+
+	bh->b_end_io = io->end_io;
+	bh->b_private = io->context;
+
+	free_io(io);
+
+	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)
+{
+	struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private;
+	struct dm_iostats *io = alloc_io();
+
+	if (!io)
+		return -ENOMEM;
+	
+	io->ti = ti;
+	io->rw = rw;
+	io->jiffies_start_io = jiffies;
+
+	/* hook the end io request fn */
+	io->end_io = bh->b_end_io;
+	io->context = bh->b_private;
+	bh->b_end_io = iostats_avg_end_io;
+	bh->b_private = io;
+
+	/* map */
+	bh->b_rdev = ic->dev->dev;
+
+	return 1;
+}
+
+/* read/write statistics mapping */
+static int iostats_status(struct dm_target *ti, status_type_t type,
+			 char *result, int maxlen)
+{
+	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;
+
+	case STATUSTYPE_TABLE:
+		snprintf(result, maxlen, "%s",
+			 kdevname(to_kdev_t(ic->dev->bdev->bd_dev)));
+		break;
+	}
+	return 0;
+}
+
+static inline uint _calc(typeof(jiffies) j, count_t c)
+{
+	typeof(jiffies) const m = 1000 / HZ;
+
+	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)
+{
+	struct iostats_avg_c *ic = (struct iostats_avg_c *) ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		{
+			uint avg_r, avg_w;
+
+			avg_r = _calc(ic->jiffies_r, ic->count_r);
+			avg_w = _calc(ic->jiffies_w, ic->count_w);
+			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)));
+	}
+	return 0;
+}
+
+static struct target_type iostats_target = {
+	.name   = "iostats",
+	.module = THIS_MODULE,
+	.ctr    = iostats_ctr,
+	.dtr    = iostats_dtr,
+	.map    = iostats_map,
+	.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;
+
+	/* allocate a slab for the dm_iostats */
+	_iostats_cache = kmem_cache_create("dm iostats io",
+					    sizeof(struct dm_iostats), 0, 0,
+					    NULL, NULL);
+
+	if (!_iostats_cache)
+		return -ENOMEM;
+
+	/* Create _iostats_pool mempool */
+	_iostats_pool = mempool_create(MIN_IOSTATS, mempool_alloc_slab,
+				       mempool_free_slab, _iostats_cache);
+
+	if (!_iostats_pool) {
+		kmem_cache_destroy(_iostats_cache);
+		return -ENOMEM;
+	}
+
+	for (i = 0; i < count; i++, tti++) {
+		int r = dm_register_target(*tti);
+
+		if (r) {
+			DMERR("%s: register failed %d", (*tti)->name, r);
+
+			for (tti--; i; i--, tti--) {
+				r = dm_unregister_target(*tti);
+
+				if (r)
+					DMERR("%s: unregister failed %d",
+					      (*tti)->name, r);
+			}
+			return r;
+		}
+	}
+
+	DMINFO("dm_iostats_init v0.0.29");
+	return 0;
+}
+
+/*
+ * module hooks
+ */
+#if	1
+module_init(dm_iostats_init);
+module_exit(dm_iostats_exit);
+
+MODULE_DESCRIPTION(DM_NAME " iostats target");
+MODULE_AUTHOR("Heinz Mauelshagen <mge@sistina.com>");
+MODULE_LICENSE("GPL");
+#endif