diff -ruN -X /home/joe/packages/2.4/dontdiff linux-last/drivers/md/Makefile linux/drivers/md/Makefile
--- linux-last/drivers/md/Makefile	Wed Nov 28 17:09:15 2001
+++ linux/drivers/md/Makefile	Wed Nov 28 17:09:49 2001
@@ -7,7 +7,8 @@
 export-objs	:= md.o xor.o dm-table.o dm-target.o
 list-multi	:= lvm-mod.o
 lvm-mod-objs	:= lvm.o lvm-snap.o
-dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-ioctl.o dm-linear.o
+dm-mod-objs	:= dm.o dm-table.o dm-target.o dm-ioctl.o \
+			dm-linear.o dm-stripe.o
 
 # Note: link order is important.  All raid personalities
 # and xor.o must come before md.o, as they each initialise 
diff -ruN -X /home/joe/packages/2.4/dontdiff linux-last/drivers/md/dm-stripe.c linux/drivers/md/dm-stripe.c
--- linux-last/drivers/md/dm-stripe.c	Thu Jan  1 01:00:00 1970
+++ linux/drivers/md/dm-stripe.c	Wed Nov 28 17:34:39 2001
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2001 Sistina Software (UK) Limited.
+ *
+ * This file is released under the GPL.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/device-mapper.h>
+
+#include "dm.h"
+
+struct stripe {
+	struct dm_dev *dev;
+	offset_t physical_start;
+};
+
+struct stripe_c {
+	offset_t logical_start;
+	uint32_t stripes;
+
+	/* The size of this target / num. stripes */
+	uint32_t stripe_width;
+
+	/* eg, we stripe in 64k chunks */
+	uint32_t chunk_shift;
+	offset_t chunk_mask;
+
+	struct stripe stripe[0];
+};
+
+
+static inline struct stripe_c *alloc_context(int stripes)
+{
+	size_t len = sizeof(struct stripe_c) +
+		(sizeof(struct stripe) * stripes);
+	return kmalloc(len, GFP_KERNEL);
+}
+
+/*
+ * parses a single <dev> <sector> pair.
+ */
+static int get_stripe(struct dm_table *t, struct stripe_c *sc,
+		      int stripe, const char *args)
+{
+	int n, r;
+	char path[256];		/* FIXME: buffer overrun risk */
+	unsigned long start;
+
+	if (sscanf(args, "%s %lu %n", path, &start, &n) != 2)
+		return -EINVAL;
+
+	if ((r = dm_table_get_device(t, path, start, sc->stripe_width,
+				     &sc->stripe[stripe].dev)))
+		return -ENXIO;
+
+	sc->stripe[stripe].physical_start = start;
+	return n;
+}
+
+/*
+ * construct a striped mapping.
+ * <number of stripes> <chunk size (2^^n)> [<dev_path> <offset>]+
+ */
+static int stripe_ctr(struct dm_table *t, offset_t b, offset_t l,
+		      const char *args, void **context,
+		      dm_error_fn err, void *e_private)
+{
+	struct stripe_c *sc;
+	uint32_t stripes;
+	uint32_t chunk_size;
+	int n, i;
+
+	if (sscanf(args, "%u %u %n", &stripes, &chunk_size, &n) != 2) {
+		err("couldn't parse <stripes> <chunk size>", e_private);
+		return -EINVAL;
+	}
+
+	if (l % stripes) {
+		err("target length is not divisable by the number of stripes",
+		    e_private);
+		return -EINVAL;
+	}
+
+	if (!(sc = alloc_context(stripes))) {
+		err("couldn't allocate memory for striped context", e_private);
+		return -ENOMEM;
+	}
+
+	sc->logical_start = b;
+	sc->stripes = stripes;
+	sc->stripe_width = l / stripes;
+
+	/*
+	 * chunk_size is a power of two.  We only
+	 * that power and the mask.
+	 */
+	if (!chunk_size) {
+		err("invalid chunk size", e_private);
+		return -EINVAL;
+	}
+
+	sc->chunk_mask = chunk_size - 1;
+	for (sc->chunk_shift = 0; chunk_size; sc->chunk_shift++)
+		chunk_size >>= 1;
+	sc->chunk_shift--;
+
+	/*
+	 * Get the stripe destinations.
+	 */
+	for (i = 0; i < stripes; i++) {
+		args += n;
+		n = get_stripe(t, sc, i, args);
+
+		if (n < 0) {
+			err("couldn't parse stripe destination", e_private);
+			kfree(sc);
+			return n;
+		}
+	}
+
+
+	*context = sc;
+	return 0;
+}
+
+static void stripe_dtr(struct dm_table *t, void *c)
+{
+	unsigned int i;
+	struct stripe_c *sc = (struct stripe_c *) c;
+
+	for (i = 0; i < sc->stripes; i++)
+		dm_table_put_device(t, sc->stripe[i].dev);
+
+	kfree(sc);
+}
+
+static int stripe_map(struct buffer_head *bh, int rw, void *context)
+{
+	struct stripe_c *sc = (struct stripe_c *) context;
+
+	offset_t offset = bh->b_rsector - sc->logical_start;
+	uint32_t chunk = (uint32_t) (offset >> sc->chunk_shift);
+	uint32_t stripe = chunk % sc->stripes; /* 32bit modulus */
+	chunk = chunk / sc->stripes;
+
+	bh->b_rdev = sc->stripe[stripe].dev->dev;
+	bh->b_rsector = sc->stripe[stripe].physical_start +
+		(chunk << sc->chunk_shift) +
+		(offset & sc->chunk_mask);
+	return 1;
+}
+
+static struct target_type stripe_target = {
+	name: "striped",
+	module: THIS_MODULE,
+	ctr: stripe_ctr,
+	dtr: stripe_dtr,
+	map: stripe_map,
+};
+
+static int __init stripe_init(void)
+{
+	int r;
+
+	if ((r = dm_register_target(&stripe_target)) < 0)
+		WARN("linear target register failed");
+
+	return r;
+}
+
+static void __exit stripe_exit(void)
+{
+	if (dm_unregister_target(stripe_target.name))
+		WARN("striped target unregister failed");
+}
+
+module_init(stripe_init);
+module_exit(stripe_exit);
+
+MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
+MODULE_DESCRIPTION("Device Mapper: Striped mapping");
+
+#ifdef MODULE_LICENSE
+MODULE_LICENSE("GPL");
+#endif
