Change the multipath target interface yet again.
--- diff/drivers/md/dm-mpath.c	2003-12-29 10:16:34.000000000 +0000
+++ source/drivers/md/dm-mpath.c	2003-12-29 10:16:39.000000000 +0000
@@ -1,12 +1,7 @@
 /*
  * Copyright (C) 2003 Sistina Software Limited.
  *
- * Module Author: Heinz Mauelshagen
- *
  * This file is released under the GPL.
- *
- * device-mapper multipathing target
- *
  */
 
 #include "dm.h"
@@ -27,25 +22,35 @@
 	struct list_head list;
 
 	struct dm_dev *dev;
-	int fail_limit;
+	struct priority_group *pg;
 
 	int has_failed;
 	jiffy_t fail_time;
 	atomic_t fail_count;
 	atomic_t fail_total;
 
-	unsigned failback_interval;
-
 	sector_t test_sector;
 };
 
+struct priority_group {
+	struct list_head list;
+
+	unsigned priority;
+	struct path_selector ps;
+	struct list_head valid_paths;
+	struct list_head invalid_paths;
+};
+
 /* Multipath context */
 struct multipath {
 	struct list_head list;
 	struct dm_target *ti;
-	struct path_selector ps;
 
-	struct list_head paths;
+	struct rw_semaphore path_lock;
+	struct list_head priority_groups;
+	struct path *current_path;
+	atomic_t count;
+	unsigned min_io;
 
 	spinlock_t failed_lock;
 	struct bio *failed_ios;
@@ -73,38 +78,68 @@
 	kfree(p);
 }
 
-static struct multipath *alloc_multipath(void)
+static struct priority_group *alloc_priority_group(void)
 {
-	struct multipath *m;
+	struct priority_group *pg;
 
-	m = kmalloc(sizeof(*m), GFP_KERNEL);
-	if (m) {
-		memset(m, 0, sizeof(*m));
-		INIT_LIST_HEAD(&m->paths);
+	pg = kmalloc(sizeof(*pg), GFP_KERNEL);
+	if (pg) {
+		INIT_LIST_HEAD(&pg->valid_paths);
+		INIT_LIST_HEAD(&pg->invalid_paths);
 	}
 
-	return m;
+	return pg;
 }
 
-static void free_multipath(struct multipath *m)
+static void free_paths(struct list_head *paths, struct dm_target *ti)
 {
-	struct path_selector *ps;
 	struct path *path, *tmp;
 
-	if (!m)
-		return;
+	list_for_each_entry_safe (path, tmp, paths, list) {
+		list_del(&path->list);
+		dm_put_device(ti, path->dev);
+		free_path(path);
+	}
+}
 
-	ps = &m->ps;
+static void free_priority_group(struct priority_group *pg,
+				struct dm_target *ti)
+{
+	struct path_selector *ps = &pg->ps;
 
-	if (ps) {
+???	if (ps) {
 		ps->type->dtr(ps);
 		dm_put_path_selector(ps->type);
 	}
 
-	list_for_each_entry_safe (path, tmp, &m->paths, list) {
-		list_del(&path->list);
-		dm_put_device(m->ti, path->dev);
-		free_path(path);
+	free_paths(&pg->valid_paths, ti);
+	free_paths(&pg->invalid_paths, ti);
+	kfree(pg);
+}
+
+static struct multipath *alloc_multipath(void)
+{
+	struct multipath *m;
+
+	m = kmalloc(sizeof(*m), GFP_KERNEL);
+	if (m) {
+		memset(m, 0, sizeof(*m));
+		init_rwsem(&m->path_lock);
+		INIT_LIST_HEAD(&m->priority_groups);
+		m->failed_lock = SPIN_LOCK_UNLOCKED;
+		m->min_io = 1000; /* FIXME: arbitrary number */
+	}
+
+	return m;
+}
+
+static void free_multipath(struct multipath *m)
+{
+	struct priority_group *pg, *tmp;
+
+	list_for_each_entry_safe (pg, tmp, &m->priority_groups, list) {
+		list_del(&pg->list);
+		free_priority_group(pg, m->ti);
 	}
 
 	kfree(m);
@@ -159,12 +194,9 @@
 }
 
 /*-----------------------------------------------------------------
- * Constructor/argument parsing
- *	<nr paths> <nr path parms> <path_test_interval>
- *	<path_selector_name> <num_ps_parms>
- *	[<device_path> <failback interval> <io failures>
- *	 <ps path args>{num_ps_parms}
- *	]{nr path parms}
+ * Constructor/argument parsing:
+ * <poll interval??> [<priority> <selector> <selector args>
+ * <num paths> [<path> <arg>]]
  *---------------------------------------------------------------*/
 struct param {
 	unsigned min;
@@ -176,7 +208,8 @@
 
 static int read_param(struct param *param, char *str, unsigned *v, char **error)
 {
-	if ((sscanf(str, "%u", v) != 1) ||
+	if (!str ||
+	    (sscanf(str, "%u", v) != 1) ||
 	    (*v < param->min) ||
 	    (*v > param->max)) {
 		*error = param->error;
@@ -186,135 +219,208 @@
 	return 0;
 }
 
-static int parse_path(struct multipath *m, int argc, char **argv, struct dm_target *ti)
+struct arg_set {
+	unsigned argc;
+	char **argv;
+};
+
+static char *shift(struct arg_set *as)
 {
-	/* path parameters */
-	static struct param _params[] = {
-		{0, 1024, ESTR("invalid path reactivation interval")},
-		{0, 1024, ESTR("invalid io failures")}
-	};
+	if (as->argc) {
+		as->argc--;
+		return *as->argv++;
+	}
+
+	return NULL;
+}
 
+static void consume(struct arg_set *as, unsigned n)
+{
+	BUG_ON (as->argc < n);
+	as->argc -= n;
+	as->argv += n;
+}
 
+static struct path *parse_path(struct arg_set *as, struct path_selector *ps,
+			       struct dm_target *ti)
+{
 	int r;
 	struct path *p;
 
+	/* we need at least a path arg */
+	if (as->argc < 1) {
+		ti->error = ESTR("no device given");
+		return NULL;
+	}
+
 	p = alloc_path();
 	if (!p)
-		return -ENOMEM;
+		return NULL;
 
-	r = dm_get_device(ti, argv[0], ti->begin, ti->len,
+	r = dm_get_device(ti, shift(as), ti->begin, ti->len,
 			  dm_table_get_mode(ti->table), &p->dev);
 	if (r) {
-		ti->error = "dm-multipath: error getting device";
+		ti->error = ESTR("error getting device");
 		goto bad;
 	}
 
-	r = read_param(_params, argv[1], &p->failback_interval, &ti->error);
-	if (r)
-		goto bad;
-
-	r = read_param(_params + 1, argv[2], &p->fail_limit, &ti->error);
-	if (r)
+	r = ps->type->add_path(ps, p, as->argc, as->argv, &ti->error);
+	if (r) {
+		dm_put_device(ti, p->dev);
 		goto bad;
-	atomic_set(&p->fail_count, p->fail_limit);
+	}
 
-	r = m->ps.type->add_path(&m->ps, p, argc - 3, argv + 3, &ti->error);
-	if (r)
-		goto bad;
-
-	list_add_tail(&p->list, &m->paths);
-	return 0;
+	return p;
 
  bad:
 	free_path(p);
-	return r;
+	return NULL;
 }
 
-#define	MIN_PARMS	5
-static int multipath_ctr(struct dm_target *ti, unsigned int argc,
-			 char **argv)
+/*
+ * Returns the number of arguments consumed, or an error.
+ */
+static struct priority_group *parse_priority_group(struct arg_set *as,
+						   struct multipath *m,
+						   struct dm_target *ti)
 {
-	/* target parameters */
 	static struct param _params[] = {
-		{2, 1024, ESTR("invalid number of paths")},
-		{2, 32, ESTR("invalid number of path parameters")},
-		{1, 24*60*60, ESTR("invalid path test interval")},
-		{0, 1024, ESTR("invalid path selector parameters")}
+		{0, 1024, ESTR("invalid priority")},
+		{0, 1024, ESTR("invalid number of selector args")},
+		{1, 1024, ESTR("invalid number of paths")}
 	};
 
-	int r, i;
-	struct multipath *m;
+	int r;
+	unsigned i, nr_paths, nr_selector_args, nr_params;
+	struct priority_group *pg;
 	struct path_selector_type *pst;
-	unsigned nr_paths, nr_params, nr_ps_params;
-
-	/* Check minimum argument count */
-	if (argc < MIN_PARMS) {
-		ti->error = ESTR("not enough arguments");
-		return -EINVAL;
-	}
-
-	m = alloc_multipath();
-	if (!m) {
-		ti->error = ESTR("can't allocate multipath context");
-		return -EINVAL;
-	}
-	m->failed_lock = SPIN_LOCK_UNLOCKED;
-
-	r = read_param(_params, argv[0], &nr_paths, &ti->error);
-	if (r)
-		goto bad;
 
-	/* there must be at least 2 paths */
-	if (nr_paths < 2) {
-		ti->error = ESTR("not enough paths");
-		goto bad;
+	if (as->argc < 3) {
+		ti->error = ESTR("not enough priority group aruments");
+		return NULL;
 	}
 
-	r = read_param(_params + 1, argv[1], &nr_params, &ti->error);
-	if (r)
-		goto bad;
-
-	if (nr_params != 2) {
-		ti->error = ESTR("invalid number of path args");
-		goto bad;
+	pg = alloc_priority_group();
+	if (!pg) {
+		ti->error = ESTR("couldn't allocate priority group");
+		return NULL;
 	}
 
-	r = read_param(_params + 2, argv[2], &m->test_interval, &ti->error);
+	r = read_param(_params, shift(as), &pg->priority, &ti->error);
 	if (r)
 		goto bad;
 
-	pst = dm_get_path_selector(argv[3]);
+	pst = dm_get_path_selector(shift(as));
 	if (!pst) {
 		ti->error = ESTR("unknown path selector type");
 		goto bad;
 	}
-	m->ps.type = pst;
+	pg->ps.type = pst;
 
-	r = pst->ctr(&m->ps);
+	r = pst->ctr(&pg->ps);
 	if (r) {
-		/* FIXME: put the pst ? */
+		/* FIXME: need to put the pst ? fix after
+		 * factoring out the register */
 		goto bad;
 	}
 
-	r = read_param(_params + 3, argv[4], &nr_ps_params, &ti->error);
+	r = read_param(_params + 1, shift(as), &nr_selector_args, &ti->error);
+	if (r)
+		goto bad;
+
+	/*
+	 * read the paths
+	 */
+	nr_params = 1 + nr_selector_args;
+	r = read_param(_params + 2, shift(as), &nr_paths, &ti->error);
 	if (r)
 		goto bad;
 
-	/* Loop through all paths parsing their parameters */
-	argc -= 5; argv += 5;
-	nr_params += nr_ps_params + 1;
 	for (i = 0; i < nr_paths; i++) {
+		struct path *path;
+		struct arg_set path_args;
 
-		if (argc < nr_params) {
-			ti->error = ESTR("insufficient arguments");
+		if (as->argc < nr_params)
 			goto bad;
-		}
 
-		r = parse_path(m, nr_params, argv, ti);
-		if (r)
+		path_args.argc = nr_params;
+		path_args.argv = as->argv;
+
+		path = parse_path(&path_args, &pg->ps, ti);
+		if (!path)
 			goto bad;
 
-		argc -= nr_params; argv += nr_params;
+		path->pg = pg;
+		list_add_tail(&path->list, &pg->valid_paths);
+		consume(as, nr_params);
+	}
+
+	return pg;
+
+ bad:
+	free_priority_group(pg, ti);
+	return NULL;
+}
+
+/*
+ * Debug only.
+ */
+static void __check_ordered(struct list_head *head)
+{
+	struct priority_group *pg;
+	unsigned last = 0;
+
+	list_for_each_entry (pg, head, list) {
+		BUG_ON (pg->priority < last);
+		last = pg->priority;
+	}
+}
+
+static void __insert_priority_group(struct multipath *m,
+				    struct priority_group *pg)
+{
+	struct priority_group *tmp;
+
+	list_for_each_entry (tmp, &m->priority_groups, list)
+		if (tmp->priority > pg->priority)
+			break;
+
+	list_add_tail(&pg->list, &tmp->list);
+
+	/* FIXME: remove debug later */
+	__check_ordered(&m->priority_groups);
+}
+
+static int multipath_ctr(struct dm_target *ti, unsigned int argc,
+			 char **argv)
+{
+	/* target parameters */
+	static struct param _params[] = {
+		{1, 60 * 60, ESTR("invalid path test interval")},
+	};
+
+	int r;
+	struct multipath *m;
+	struct arg_set as;
+
+	as.argc = argc;
+	as.argv = argv;
+
+	m = alloc_multipath();
+	if (!m) {
+		ti->error = ESTR("can't allocate multipath");
+		return -EINVAL;
+	}
+
+	r = read_param(_params, shift(&as), &m->test_interval, &ti->error);
+	if (r)
+		goto bad;
+
+	/* parse the priority groups */
+	while (as.argc) {
+		struct priority_group *pg = parse_priority_group(&as, m, ti);
+		if (pg)
+			__insert_priority_group(m, pg);
 	}
 
 	ti->private = m;
@@ -331,7 +437,6 @@
 	return -EINVAL;
 }
 
-/* Destruct a multipath mapping */
 static void multipath_dtr(struct dm_target *ti)
 {
 	struct multipath *m = (struct multipath *) ti->private;
@@ -344,103 +449,170 @@
 	free_multipath(m);
 }
 
-/* Set a path to "failed" */
-static inline void set_failed(struct path_selector *ps, struct path *path, sector_t sector)
+static struct priority_group *__find_priority_group(struct list_head *pgs)
 {
-	if (path->has_failed)
-		return;
+	struct priority_group *pg;
 
-	/* FIXME: need locking ? */
-	path->fail_time = jiffies;
-	atomic_inc(&path->fail_total);
-	path->test_sector = sector;
-	ps->type->set_path_state(ps, path, 0);
-//	queue_table_event(io);
+	list_for_each_entry (pg, pgs, list) {
+		if (!list_empty(&pg->valid_paths))
+			return pg;
+	}
+
+	return NULL;
+}
+
+static int __choose_path(struct multipath *m)
+{
+	struct priority_group *pg;
+	struct path *path;
+
+	/* get the priority group */
+	pg = __find_priority_group(&m->priority_groups);
+	if (!pg)
+		return -EIO;
+
+	/* select a path */
+	path = pg->ps.type->select_path(&pg->ps);
+	if (!path)
+		return -EIO;	/* No valid path found */
+
+	m->current_path = path;
+	atomic_set(&m->count, m->min_io);
+	return 0;
+}
+
+static int multipath_map(struct dm_target *ti, struct bio *bio,
+			 union map_info *map_context)
+{
+	struct multipath *m = (struct multipath *) ti->private;
+	struct path *path;
+
+ retry:
+	down_read(&m->path_lock);
+
+	/*
+	 * Do we need to choose a new path?
+	 */
+	if (m->current_path && atomic_dec_and_test(&m->count)) {
+		path = m->current_path;
+		up_read(&m->path_lock);
+
+	} else {
+		/* promote to write lock */
+		up_read(&m->path_lock);
+		down_write(&m->path_lock);
+
+		if (m->current_path && atomic_read(&m->count)) {
+			up_write(&m->path_lock);
+			goto retry;
+		}
+
+		if (__choose_path(m)) {
+			/* no paths */
+			up_write(&m->path_lock);
+			return -EIO;
+		}
+
+		path = m->current_path;
+		up_write(&m->path_lock);
+	}
+
+	/* map */
+	bio->bi_rw |= (1 << BIO_RW_FAILFAST);
+	bio->bi_bdev = path->dev->bdev;
+	return 1;
 }
 
 /*
- * Only called on the slow, error path.
+ * Only called on the error path.
  */
-static struct path *find_path(struct multipath *m, struct block_device *bdev)
+static struct path *__lookup_path(struct list_head *head,
+				  struct block_device *bdev)
 {
 	struct path *p;
 
-	list_for_each_entry(p, &m->paths, list)
+	list_for_each_entry (p, head, list)
 		if (p->dev->bdev == bdev)
 			return p;
 
 	return NULL;
 }
 
-static int multipath_end_io(struct dm_target *ti, struct bio *bio,
-			    int error, union map_info *map_context)
+static struct path *__find_path(struct multipath *m, struct block_device *bdev)
 {
-	struct multipath *m = (struct multipath *) ti->private;
-	struct path_selector *ps = &m->ps;
-	struct path_selector_type *pst = ps->type;
-	ps_endio_fn endio = pst->endio;
-	unsigned long flags;
-
-	if (error) {
-		struct path *path = find_path(m, bio->bi_bdev);
-
-		if (atomic_dec_and_test(&path->fail_count))
-			set_failed(ps, path, bio->bi_sector);
+	struct path *p;
+	struct priority_group *pg;
 
-		/* choose a new path */
-		path = pst->select_path(ps, bio, map_context);
-		if (path) {
-			bio->bi_bdev = path->dev->bdev;
-			spin_lock_irqsave(&m->failed_lock, flags);
-			bio->bi_next = m->failed_ios;
-			m->failed_ios = bio;
-			spin_unlock_irqrestore(&m->failed_lock, flags);
+	list_for_each_entry (pg, &m->priority_groups, list) {
+		p = __lookup_path(&pg->valid_paths, bdev);
+		if (p)
+			return p;
 
-			dm_daemon_wake(&_kmpathd);
-			return 1;	/* io not complete */
-		}
+		p = __lookup_path(&pg->invalid_paths, bdev);
+		if (p)
+			return p;
 	}
 
-	if (endio)
-		endio(ps, bio, error, map_context);
-
-	return 0;		/* io complete */
+	return NULL;
 }
 
-/* Suspend */
-static void multipath_suspend(struct dm_target *ti)
+static void __fail_path(struct path *path)
 {
-	struct multipath *m = (struct multipath *) ti->private;
+	if (path->has_failed)
+		return;
 
-	//atomic_set(&m->suspended, 1);
-	//wait_for_scrub_ios(m);
+	if (!atomic_dec_and_test(&path->fail_count))
+		return;
+
+	path->has_failed = 1;
+	path->fail_time = jiffies;
+	atomic_inc(&path->fail_total);
+//	path->test_sector = sector;
+	list_del(&path->list);
+	list_add(&path->list, &path->pg->invalid_paths);
+	path->pg->ps.type->set_path_state(&path->pg->ps, path, 0);
 }
 
-/* Resume */
-static void multipath_resume(struct dm_target *ti)
+static int __resubmit_io(struct multipath *m, struct bio *bio)
 {
-	struct multipath *m = (struct multipath *) ti->private;
+	int r;
+	unsigned long flags;
+
+	r = __choose_path(m);
+	if (r)
+		return r;
+
+	/* remap */
+	bio->bi_bdev = m->current_path->dev->bdev;
+
+	/* queue for the daemon to resubmit */
+	spin_lock_irqsave(&m->failed_lock, flags);
+	bio->bi_next = m->failed_ios;
+	m->failed_ios = bio;
+	spin_unlock_irqrestore(&m->failed_lock, flags);
 
-	//atomic_set(&m->suspended, 0);
 	dm_daemon_wake(&_kmpathd);
+	return 1;	/* io not complete */
 }
 
-/* Multipath mapping */
-static int multipath_map(struct dm_target *ti, struct bio *bio,
-			 union map_info *map_context)
+static int multipath_end_io(struct dm_target *ti, struct bio *bio,
+			    int error, union map_info *map_context)
 {
+	int r = 0;
 	struct multipath *m = (struct multipath *) ti->private;
-	struct path *path;
 
-	/* Ask path selector for a path */
-	memset(map_context, 0, sizeof(*map_context));
-	path = m->ps.type->select_path(&m->ps, bio, map_context);
-	if (!path)
-		return -1;	/* No valid path found */
+	if (error) {
+		struct path *path;
 
-	bio->bi_rw |= (1 << BIO_RW_FAILFAST);
-	bio->bi_bdev = path->dev->bdev;
-	return 1;
+		down_write(&m->path_lock);
+		path = __find_path(m, bio->bi_bdev);
+		__fail_path(path);
+		up_write(&m->path_lock);
+
+		r = __resubmit_io(m, bio);
+	}
+
+	return r;
 }
 
 /* Multipath status */
@@ -460,8 +632,6 @@
 	.dtr = multipath_dtr,
 	.map = multipath_map,
 	.end_io = multipath_end_io,
-	.suspend = multipath_suspend,
-	.resume = multipath_resume,
 	.status = multipath_status,
 };
 
@@ -483,7 +653,8 @@
 
 	r = dm_daemon_start(&_kmpathd, "kpathd", do_work);
 	if (r) {
-//		dm_unregister_path_selectors();
+		/* FIXME: remove this */
+		dm_unregister_path_selectors();
 		dm_unregister_target(&multipath_target);
 	} else
 		DMINFO("dm_multipath v0.2.0");
@@ -508,7 +679,7 @@
 module_exit(dm_multipath_exit);
 
 MODULE_DESCRIPTION(DM_NAME " multipath target");
-MODULE_AUTHOR("Heinz Mauelshagen <mge@sistina.com>");
+MODULE_AUTHOR("Sistina software <dm@uk.sistina.com>");
 MODULE_LICENSE("GPL");
 
 
@@ -518,6 +689,12 @@
 
 
 
+
+
+
+
+
+
 #ifdef SCRUB_STUFF
 /* Reset failure information on a path */
 static inline void reset_failures(struct path *path)
@@ -824,3 +1001,26 @@
 
 	return 0;
 #endif
+
+
+
+#if 0
+/* Suspend */
+static void multipath_suspend(struct dm_target *ti)
+{
+	struct multipath *m = (struct multipath *) ti->private;
+
+	//atomic_set(&m->suspended, 1);
+	//wait_for_scrub_ios(m);
+}
+
+/* Resume */
+static void multipath_resume(struct dm_target *ti)
+{
+	struct multipath *m = (struct multipath *) ti->private;
+
+	//atomic_set(&m->suspended, 0);
+	dm_daemon_wake(&_kmpathd);
+}
+
+#endif
--- diff/drivers/md/dm-path-selector.c	2003-12-29 10:16:34.000000000 +0000
+++ source/drivers/md/dm-path-selector.c	2003-12-29 10:16:39.000000000 +0000
@@ -40,6 +40,9 @@
 {
 	struct path_selector_type *lb;
 
+	if (!name)
+		return NULL;
+
 	down(&_lock);
 	lb = __find_path_selector_type(name);
 	if (lb) {
@@ -133,58 +136,13 @@
 }
 
 /*-----------------------------------------------------------------
- * Path handling code, paths are held in lists ordered by
- * priority.
+ * Path handling code, paths are held in lists
  *---------------------------------------------------------------*/
 struct path_info {
 	struct list_head list;
 	struct path *path;
-
-	unsigned min_io;
-	unsigned priority;
-
-	/* count how much has been sent to the path */
-	atomic_t io_count;
 };
 
-/*
- * Debug only.
- */
-static void path_ordered(struct list_head *head, struct path_info *pi)
-{
-	struct path_info *cursor;
-	unsigned last = 0;
-	int seen = 0;
-
-	list_for_each_entry (cursor, head, list) {
-		BUG_ON (cursor->priority < last);
-
-		last = cursor->priority;
-		if (cursor == pi)
-			seen = 1;
-	}
-
-	BUG_ON(!seen);
-}
-
-/*
- * IMPORTANT: we rely on this function inserting the new path at
- * the _back_ of its priority group.
- */
-static void path_insert(struct list_head *head, struct path_info *pi)
-{
-	struct path_info *cursor;
-
-	list_for_each_entry (cursor, head, list)
-		if (cursor->priority > pi->priority)
-			break;
-
-	list_add_tail(&pi->list, &cursor->list);
-
-	/* FIXME: remove debug later */
-	path_ordered(head, pi);
-}
-
 static struct path_info *path_lookup(struct list_head *head, struct path *p)
 {
 	struct path_info *pi;
@@ -257,24 +215,13 @@
 {
 	struct selector *s = (struct selector *) ps->context;
 	struct path_info *pi;
-	unsigned priority, min_io;
 
 	/* parse the path arguments */
-	if (argc != 2) {
+	if (argc != 0) {
 		*error = "round-robin ps: incorrect number of arguments";
 		return -EINVAL;
 	}
 
-	if (sscanf(argv[0], "%u", &priority) != 1) {
-		*error = "round-robin ps: Invalid priority";
-		return -EINVAL;
-	}
-
-	if (sscanf(argv[1], "%u", &min_io) != 1) {
-		*error = "round-robin ps: Invalid min_io";
-		return -EINVAL;
-	}
-
 	/* allocate the path */
 	pi = kmalloc(sizeof(*pi), GFP_KERNEL);
 	if (!pi) {
@@ -283,18 +230,14 @@
 	}
 
 	pi->path = path;
-	pi->min_io = min_io;
-	pi->priority = priority;
-	atomic_set(&pi->io_count, min_io);
 
 	spin_lock(&s->lock);
-	path_insert(&s->valid_paths, pi);
+	list_add(&pi->list, &s->valid_paths);
 	spin_unlock(&s->lock);
 
 	return 0;
 }
 
-
 static void rr_set_path_state(struct path_selector *ps,
 			      struct path *p, int valid)
 {
@@ -314,33 +257,26 @@
 	if (!pi)
 		DMWARN("asked to change the state of an unknown path");
 
-	else
-		path_insert(valid ? &s->valid_paths : &s->invalid_paths, pi);
+	else {
+		list_del(&pi->list);
+		list_add(&pi->list, valid ? &s->valid_paths : &s->invalid_paths);
+	}
 
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
 /* Path selector */
-static struct path *rr_select_path(struct path_selector *ps,
-				   struct bio *bio,
-				   union map_info *map_context)
+static struct path *rr_select_path(struct path_selector *ps)
 {
 	unsigned long flags;
 	struct selector *s = (struct selector *) ps->context;
-	struct list_head *list = &s->valid_paths;
 	struct path_info *pi = NULL;
 
 	spin_lock_irqsave(&s->lock, flags);
-	if (!list_empty(list))
-		pi = list_entry(list->next, struct path_info, list);
-
-	/* have we done enough io on this path? */
-	if (pi && atomic_dec_and_test(&pi->io_count)) {
+	if (!list_empty(&s->valid_paths)) {
+		pi = list_entry(s->valid_paths.next, struct path_info, list);
 		list_del(&pi->list);
-		atomic_set(&pi->io_count, pi->min_io);
-
-		/* move to the back of its priority group */
-		path_insert(&s->valid_paths, pi);
+		list_add_tail(&pi->list, &s->valid_paths);
 	}
 	spin_unlock_irqrestore(&s->lock, flags);
 
@@ -354,8 +290,6 @@
 	.add_path = rr_add_path,
 	.set_path_state = rr_set_path_state,
 	.select_path = rr_select_path,
-	.endio = NULL,
-	.status = NULL,
 };
 
 int dm_register_rr_ps(void)
--- diff/drivers/md/dm-path-selector.h	2003-12-29 10:16:16.000000000 +0000
+++ source/drivers/md/dm-path-selector.h	2003-12-29 10:16:39.000000000 +0000
@@ -49,18 +49,7 @@
  * reused or reallocated because an endio call (which needs to free it)
  * might happen after a couple of select calls.
  */
-typedef	struct path *(*ps_select_path_fn) (struct path_selector *ps,
-					   struct bio *bio,
-					   union map_info *map_context);
-
-/*
- * Hook the end of the io, path throughput/failure can be
- * detected through this. Must ensure, that any dynamically allocated
- * IO context gets freed.
- */
-typedef	void (*ps_endio_fn) (struct path_selector *ps,
-			     struct bio *bio, int error,
-			     union map_info *map_context);
+typedef	struct path *(*ps_select_path_fn) (struct path_selector *ps);
 
 /*
  * Notify the selector that a path has failed.
@@ -85,10 +74,7 @@
 
 	ps_add_path_fn add_path;
 	ps_set_path_state_fn set_path_state;
-
 	ps_select_path_fn select_path;
-	ps_endio_fn endio;
-
 	ps_status_fn status;
 };
 
@@ -117,3 +103,26 @@
 void dm_unregister_latency_ps(void);
 
 #endif
+
+
+
+#if 0
+/*
+ * An optional function usd if the selector wishes to record the
+ * start of every io to a path.
+ */
+typedef struct path *(*path_startio_fn) (struct path_selector *ps,
+					 struct path *path,
+					 struct bio *bio,
+					 union map_info *map_context);
+
+/*
+ * Hook the end of the io, path throughput/failure can be
+ * detected through this. Must ensure, that any dynamically allocated
+ * IO context gets freed.
+ */
+typedef	void (*ps_endio_fn) (struct path_selector *ps,
+			     const struct bio *bio, int error,
+			     union map_info *map_context);
+#endif
+
--- diff/drivers/md/dm.h	2003-12-29 10:15:50.000000000 +0000
+++ source/drivers/md/dm.h	2003-12-29 10:16:39.000000000 +0000
@@ -123,6 +123,24 @@
 void dm_table_resume_targets(struct dm_table *t);
 
 /*-----------------------------------------------------------------
+ * Gerneral purpose registry code
+ *---------------------------------------------------------------*/
+struct dm_registry;
+struct dm_registry_entry {
+	const char *name;
+	struct module *m;
+};
+
+struct dm_register *dm_registry_create(void);
+void dm_registry_destroy(struct dm_register *reg);
+
+int dm_registry_add(struct dm_register *reg, struct dm_registry_entry *re);
+int dm_registry_remove(struct dm_register *reg, struct dm_registry_entry *re);
+
+struct dm_registry_entry *dm_register_get(struct dm_register *reg, const char *name);
+void dm_register_put(struct dm_register *reg, struct dm_registry_entry *re);
+
+/*-----------------------------------------------------------------
  * A registry of target types.
  *---------------------------------------------------------------*/
 int dm_target_init(void);
@@ -130,7 +148,6 @@
 struct target_type *dm_get_target_type(const char *name);
 void dm_put_target_type(struct target_type *t);
 
-
 /*-----------------------------------------------------------------
  * Useful inlines.
  *---------------------------------------------------------------*/
--- diff/drivers/md/kcopyd.c	2003-12-29 10:12:47.000000000 +0000
+++ source/drivers/md/kcopyd.c	2003-12-29 10:16:39.000000000 +0000
@@ -398,7 +398,7 @@
 /*
  * kcopyd does this every time it's woken up.
  */
-static void do_work(void)
+static jiffy_t do_work(void)
 {
 	/*
 	 * The order that these are called is *very* important.
@@ -412,6 +412,7 @@
 	process_jobs(&_io_jobs, run_io_job);
 
 	blk_run_queues();
+	return (jiffy_t) 0;
 }
 
 /*
--- diff/drivers/md/dm-register.h	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-register.h	2003-12-29 10:16:39.000000000 +0000
@@ -0,0 +1,13 @@
+/*
+ * Copyright (C) 2003 Sistina Software (UK) Limited
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_REGISTER_H
+#define DM_REGISTER_H
+
+struct dm_register;
+
+
+#endif