--- diff/drivers/md/dm-recorder.c	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-recorder.c	2004-02-10 14:45:45.000000000 +0000
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2004 Red Hat Inc
+ *
+ * This file is released under the GPL.
+ */
+
+#include "dm.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/slab.h>
+
+/*
+ * The recorder target is designed to be layered over another
+ * block device, it also writes to a seperate log device.  It
+ * simply records details about every io to the origin device
+ * (ie. time, location, size).  The actual data in the ios is not
+ * recorded.  This should give us a way a generating more
+ * realistic test scenarios.  Once the log device is full an
+ * event is triggered, and logging stops.  If the system crashes
+ * some log data will be lost.
+ */
+
+/*----------------------------------------------------------------
+ * On disk structures
+ * FIXME: we can come up with a much more space efficient format.
+ *--------------------------------------------------------------*/
+struct header {
+	u32_t magic;
+	u32_t format_version;
+	u64_t sector_start;
+	u32_t hz;
+};
+
+struct atom {
+	u64_t time;
+	u64_t sector;
+	u32_t write;
+	u32_t length;
+};
+
+/*----------------------------------------------------------------
+ * Recorder target
+ *--------------------------------------------------------------*/
+#define NR_SECTORS 256
+#define NR_ATOMS ((NR_SECTORS << SECTOR_SHIFT) / sizeof(struct atom))
+struct recorder {
+	struct dm_dev *origin;
+	struct dm_dev *log;
+
+	sector_t log_cursor;
+
+	unsigned current_atom;
+	struct atom *atoms;
+};
+
+#define ESTR(x) ("dm-recorder: " x)
+
+/*
+ * Construct a recorder mapping: <origin dev> <log dev>
+ */
+static int recorder_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct recorder *rc;
+
+	if (argc != 2) {
+		ti->error = ESTR("invalid argument count");
+		return -EINVAL;
+	}
+
+	rc = kmalloc(sizeof(*rc), GFP_KERNEL);
+	if (!rc) {
+		ti->error = ESTR("cannot allocate linear context");
+		return -ENOMEM;
+	}
+
+	if (dm_get_device(ti, argv[0], rc->start, ti->len,
+			  dm_table_get_mode(ti->table), &rc->origin)) {
+		ti->error = ESTR("origin device lookup failed");
+		goto bad1;
+	}
+
+	/*
+	 * We don't mind how big the log is, as long as it has
+	 * room for the header.
+	 */
+	if (dm_get_device(ti, argv[1], rc->start, 1,
+			  dm_table_get_mode(ti->table), &rc->log)) {
+		ti->error = ESTR("log device lookup failed");
+		goto bad2;
+	}
+
+	rc->log_cursor = 1;
+	rc->current_atom = 0;
+	rc->atoms = dm_vcalloc(NR_ATOMS, sizeof(*rc->atoms));
+	if (!rc->atoms) {
+		ti->error = ESTR("couln't allocate atoms array");
+		goto bad3;
+	}
+
+	ti->private = rc;
+	return 0;
+
+ bad3:
+	dm_put_device(ti, rc->log);
+ bad2:
+	dm_put_device(ti, rc->origin);
+ bad1:
+	kfree(rc);
+	return -EINVAL;
+}
+
+static void recorder_dtr(struct dm_target *ti)
+{
+	struct recorder *rc = (struct recorder *) ti->private;
+
+	vfree(rc->atoms);
+	dm_put_device(ti, rc->log);
+	dm_put_device(ti, rc->origin);
+	kfree(rc);
+}
+
+static int write_atoms(struct recorder *rc)
+{
+	int r;
+	unsigned long errors;
+	struct io_region where;
+
+	where.bdev = rc->log;
+	where.sector = current_sector;
+	where.count = NR_SECTORS;
+
+	r = dm_io_sync(1, &where, WRITE, pages, 0, errors);
+	if (!r) {
+		rc->current_atom = 0;
+		rc->current_sector += NR_SECTORS;
+	}
+
+	/* FIXME: detect end of log device, and trigger event if neccessary */
+
+	return r;
+}
+
+static void atom_to_disk(struct atom *a)
+{
+	a->time = cpu_to_le64(a->time);
+	a->sector = cpu_to_le64(a->sector);
+	a->write = cpu_to_le32(a->write);
+	a->length = cpu_to_le32(a->length);
+}
+
+static u64_t now(void)
+{
+
+}
+
+static int recorder_map(struct dm_target *ti, struct bio *bio,
+			union map_info *map_context)
+{
+	struct recorder *rc = (struct recorder *) ti->private;
+	struct atom *a = rc->atoms + rc->current_atom++;
+
+	/* fill in the atom */
+	a->time = now();
+	a->write = test_bit(BIO_RW, bio->bi_rw);
+	a->sector = bio->bi_sector;
+	a->length = to_sector(bio->bi_size);
+	atom_to_disk(a);
+
+	if (rc->current_atom = NR_ATOMS)
+		write_atoms(rc);
+
+	bio->bi_bdev = rc->origin->bdev;
+	return 1;
+}
+
+static void recorder_suspend(struct dm_target *ti)
+{
+	struct recorder *rc = (struct recorder *) ti->private;
+}
+
+static void recorder_resume(struct dm_target *ti)
+{
+	struct recorder *rc = (struct recorder *) ti->private;
+
+}
+
+static int recorder_status(struct dm_target *ti, status_type_t type,
+			   char *result, unsigned int maxlen)
+{
+	struct linear_c *lc = (struct linear_c *) ti->private;
+	char origin_buffer[32], log_buffer[32];
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		format_dev_t(origin_buffer, rc->origin_dev->bdev->bd_dev);
+		format_dev_t(log_buffer, rc->log_dev->bdev->bd_dev);
+		snprintf(result, maxlen, "%s %s", origin_buffer, log_buffer);
+		break;
+	}
+	return 0;
+}
+
+static struct target_type recorder_target = {
+	.name   = "recorder",
+	.version= {1, 0, 0},
+	.module = THIS_MODULE,
+	.ctr    = recorder_ctr,
+	.dtr    = recorder_dtr,
+	.map    = recorder_map,
+	.suspend = recorder_suspend,
+	.resume	 = recorder_resume,
+	.status = recorder_status,
+};
+
+int __init dm_recorder_init(void)
+{
+	int r = dm_register_target(&linear_target);
+
+	if (r < 0)
+		DMERR(ESTR("register target failed %d", r));
+
+	return r;
+}
+
+void __exit dm_recorder_exit(void)
+{
+	int r = dm_unregister_target(&linear_target);
+
+	if (r < 0)
+		DMERR(ESTR("unregister target failed %d", r));
+}
+
+module_init(dm_recorder_init);
+module_exit(dm_recorder_exit);
+
+MODULE_DESCRIPTION(DM_NAME " mirror target");
+MODULE_AUTHOR("Joe Thornber");
+MODULE_LICENSE("GPL");
