Index: linux-2.6.16-rc1/drivers/md/Kconfig
===================================================================
--- linux-2.6.16-rc1.orig/drivers/md/Kconfig
+++ linux-2.6.16-rc1/drivers/md/Kconfig
@@ -236,5 +236,14 @@ config DM_MULTIPATH_EMC
 	---help---
 	  Multipath support for EMC CX/AX series hardware.
 
+config DM_RWSPLIT
+	tristate "Read/Write splitting target (EXPERIMENTAL)"
+	depends on BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  This device-mapper target allows you to send reads and
+	  writes to different devices.
+
+	  If unsure, say N.
+
 endmenu
 
Index: linux-2.6.16-rc1/drivers/md/dm-rwsplit.c
===================================================================
--- /dev/null
+++ linux-2.6.16-rc1/drivers/md/dm-rwsplit.c
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2005 Red Hat GmbH
+ *
+ * Target reads off one device and writes to another.
+ *
+ * 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>
+
+/*
+ * Split: maps a split range of a device
+ */
+struct split_c {
+	struct dm_dev *dev_read;
+	struct dm_dev *dev_write;
+	sector_t start_read;
+	sector_t start_write;
+};
+
+/*
+ * Construct a split mapping:
+ *
+ *	<dev_read_path> <read_offset> <dev_write_path> <write_offset>
+ */
+static int rwsplit_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct split_c *sc;
+
+	if (argc != 4) {
+		ti->error = "dm-rwsplit: Invalid argument count";
+		return -EINVAL;
+	}
+
+	sc = kmalloc(sizeof(*sc), GFP_KERNEL);
+	if (sc == NULL) {
+		ti->error = "dm-rwsplit: Cannot allocate linear context";
+		return -ENOMEM;
+	}
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &sc->start_read) != 1) {
+		ti->error = "dm-rwsplit: Invalid read device sector";
+		goto bad;
+	}
+
+	if (sscanf(argv[3], SECTOR_FORMAT, &sc->start_write) != 1) {
+		ti->error = "dm-rwsplit: Invalid write device sector";
+		goto bad;
+	}
+
+	if (dm_get_device(ti, argv[0], sc->start_read, ti->len,
+			  dm_table_get_mode(ti->table), &sc->dev_read)) {
+		ti->error = "dm-rwsplit: Read device lookup failed";
+		goto bad;
+	}
+
+	if (dm_get_device(ti, argv[2], sc->start_write, ti->len,
+			  dm_table_get_mode(ti->table), &sc->dev_write)) {
+		ti->error = "dm-rwsplit: Write device lookup failed";
+		dm_put_device(ti, sc->dev_read);
+		goto bad;
+	}
+
+	ti->private = sc;
+	return 0;
+
+      bad:
+	kfree(sc);
+	return -EINVAL;
+}
+
+static void rwsplit_dtr(struct dm_target *ti)
+{
+	struct split_c *sc = ti->private;
+
+	dm_put_device(ti, sc->dev_read);
+	dm_put_device(ti, sc->dev_write);
+	kfree(sc);
+}
+
+static int rwsplit_map(struct dm_target *ti, struct bio *bio,
+		      union map_info *map_context)
+{
+	struct split_c *sc = ti->private;
+
+	if (bio_data_dir(bio) == WRITE) {
+		bio->bi_bdev = sc->dev_write->bdev;
+		bio->bi_sector = sc->start_write +
+				 (bio->bi_sector - ti->begin);;
+	} else {
+		bio->bi_bdev = sc->dev_read->bdev;
+		bio->bi_sector = sc->start_read +
+				 (bio->bi_sector - ti->begin);;
+	}
+
+	return 1;
+}
+
+static int rwsplit_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct split_c *sc = ti->private;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		snprintf(result, maxlen, "%s " SECTOR_FORMAT
+					 " %s " SECTOR_FORMAT,
+			 sc->dev_read->name, sc->start_read,
+			 sc->dev_write->name, sc->start_write);
+		break;
+	}
+
+	return 0;
+}
+
+static struct target_type rwsplit_target = {
+	.name   = "rwsplit",
+	.version= {1, 0, 1},
+	.module = THIS_MODULE,
+	.ctr    = rwsplit_ctr,
+	.dtr    = rwsplit_dtr,
+	.map    = rwsplit_map,
+	.status = rwsplit_status,
+};
+
+int __init dm_rwsplit_init(void)
+{
+	int r = dm_register_target(&rwsplit_target);
+
+	if (r < 0)
+		DMERR("rwsplit: register failed %d", r);
+
+	return r;
+}
+
+void dm_rwsplit_exit(void)
+{
+	int r = dm_unregister_target(&rwsplit_target);
+
+	if (r < 0)
+		DMERR("rwsplit: unregister failed %d", r);
+}
+
+/* Module hooks */
+module_init(dm_rwsplit_init);
+module_exit(dm_rwsplit_exit);
+
+MODULE_DESCRIPTION(DM_NAME " rwsplit target");
+MODULE_AUTHOR("Heinz Mauelshagen <mauelshagen@redhat.com>");
+MODULE_LICENSE("GPL");
+
+/*
+ * Overrides for Emacs so that we follow Linus's tabbing style.
+ * Emacs will notice this stuff at the end of the file and automatically
+ * adjust the settings for this buffer only.  This must remain at the end
+ * of the file.
+ * ---------------------------------------------------------------------------
+ * Local variables:
+ * c-file-style: "linux"
+ * End:
+ */
Index: linux-2.6.16-rc1/drivers/md/Makefile
===================================================================
--- linux-2.6.16-rc1.orig/drivers/md/Makefile
+++ linux-2.6.16-rc1/drivers/md/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipa
 obj-$(CONFIG_DM_MULTIPATH_EMC)	+= dm-emc.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
+obj-$(CONFIG_DM_RWSPLIT)	+= dm-rwsplit.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
 
 quiet_cmd_unroll = UNROLL  $@
