Delete null selector.

Add a round robin selector to dm-path-selector.c
--- diff/drivers/md/Kconfig	2003-12-29 10:16:08.000000000 +0000
+++ source/drivers/md/Kconfig	2003-12-29 10:16:16.000000000 +0000
@@ -162,5 +162,19 @@
 	---help---
 	  Allow volume managers to support multipath hardware.
 
+config DM_FAILURE
+	tristate "Failure target (EXPERIMENTAL)"
+	depends on BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  A debug only target that attempts to change the
+          characteristics of a block device.  Most notably its
+          reliability.
+
+config DM_FLAKEY
+	tristate "Flakey target (EXPERIMENTAL)"
+	depends on BLK_DEV_DM && EXPERIMENTAL
+	---help---
+	  A debug only target that intermittently fails.
+
 endmenu
 
--- diff/drivers/md/Makefile	2003-12-29 10:16:08.000000000 +0000
+++ source/drivers/md/Makefile	2003-12-29 10:16:16.000000000 +0000
@@ -9,8 +9,7 @@
 
 dm-mirror-objs	:= dm-log.o dm-raid1.o
 
-dm-multipath-objs := dm-path-selector.o dm-null-ps.o	\
-		     dm-latency-ps.o dm-mpath.o
+dm-multipath-objs := dm-path-selector.o dm-mpath.o
 
 # Note: link order is important.  All raid personalities
 # and xor.o must come before md.o, as they each initialise 
@@ -26,4 +25,6 @@
 obj-$(CONFIG_BLK_DEV_DM)	+= dm-mod.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o
-obj-$(CONFIG_DM_MULTIPATH)		+= dm-multipath.o
+obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o
+obj-$(CONFIG_DM_FAILURE)	+= dm-failure.o
+obj-$(CONFIG_DM_FLAKEY)		+= dm-flakey.o
--- diff/drivers/md/dm-mpath.c	2003-12-29 10:16:08.000000000 +0000
+++ source/drivers/md/dm-mpath.c	2003-12-29 10:16:16.000000000 +0000
@@ -217,7 +217,7 @@
 	if (r)
 		goto bad;
 
-	r = m->ps.type->add_path(&m->ps, p, argc - 2, argv + 2, &ti->error);
+	r = m->ps.type->add_path(&m->ps, p, argc - 3, argv + 3, &ti->error);
 	if (r)
 		goto bad;
 
@@ -257,6 +257,7 @@
 		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)
@@ -286,6 +287,7 @@
 		ti->error = ESTR("unknown path selector type");
 		goto bad;
 	}
+	m->ps.type = pst;
 
 	r = pst->ctr(&m->ps);
 	if (r) {
@@ -318,7 +320,7 @@
 	m->ti = ti;
 
 	spin_lock(&_mpath_lock);
-	list_add(&_mpaths, &m->list);
+	list_add(&m->list, &_mpaths);
 	spin_unlock(&_mpath_lock);
 
 	return 0;
@@ -430,6 +432,7 @@
 	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 */
@@ -478,7 +481,7 @@
 
 	r = dm_daemon_start(&_kmpathd, "kpathd", do_work);
 	if (r) {
-		dm_unregister_path_selectors();
+//		dm_unregister_path_selectors();
 		dm_unregister_target(&multipath_target);
 	} else
 		DMINFO("dm_multipath v0.2.0");
--- diff/drivers/md/dm-path-selector.c	2003-12-29 10:15:37.000000000 +0000
+++ source/drivers/md/dm-path-selector.c	2003-12-29 10:16:16.000000000 +0000
@@ -28,7 +28,7 @@
 {
 	struct ps_internal *li;
 
-	list_for_each_entry(li, &_path_selectors, list) {
+	list_for_each_entry (li, &_path_selectors, list) {
 		if (!strcmp(name, li->pt.name))
 			return &li->pt;
 	}
@@ -124,17 +124,247 @@
  */
 void dm_unregister_path_selectors(void)
 {
-	dm_unregister_null_ps();
-	dm_unregister_latency_ps();
+	dm_unregister_rr_ps();
 }
 
 int dm_register_path_selectors(void)
 {
-	int r;
+	return dm_register_rr_ps();
+}
 
-	r = dm_register_null_ps();
-	if (!r || r == -EEXIST)
-		dm_register_latency_ps();
+/*-----------------------------------------------------------------
+ * Path handling code, paths are held in lists ordered by
+ * priority.
+ *---------------------------------------------------------------*/
+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;
+};
 
-	return r;
+/*
+ * 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;
+
+	list_for_each_entry (pi, head, list)
+		if (pi->path == p)
+			return pi;
+
+	return NULL;
+}
+
+/*-----------------------------------------------------------------
+ * Round robin selector
+ *---------------------------------------------------------------*/
+struct selector {
+	spinlock_t lock;
+
+	struct list_head valid_paths;
+	struct list_head invalid_paths;
+};
+
+static struct selector *alloc_selector(void)
+{
+	struct selector *s = kmalloc(sizeof(*s), GFP_KERNEL);
+
+	if (s) {
+		INIT_LIST_HEAD(&s->valid_paths);
+		INIT_LIST_HEAD(&s->invalid_paths);
+		s->lock = SPIN_LOCK_UNLOCKED;
+	}
+
+	return s;
+}
+
+/* Path selector constructor */
+static int rr_ctr(struct path_selector *ps)
+{
+	struct selector *s;
+
+	s = alloc_selector();
+	if (!s)
+		return -ENOMEM;
+
+	ps->context = s;
+	return 0;
+}
+
+static void free_paths(struct list_head *paths)
+{
+	struct path_info *pi, *next;
+
+	list_for_each_entry_safe (pi, next, paths, list) {
+		list_del(&pi->list);
+		kfree(pi);
+	}
+}
+
+/* Path selector destructor */
+static void rr_dtr(struct path_selector *ps)
+{
+	struct selector *s = (struct selector *) ps->context;
+	free_paths(&s->valid_paths);
+	free_paths(&s->invalid_paths);
+	kfree(s);
+}
+
+/* Path add context */
+static int rr_add_path(struct path_selector *ps, struct path *path,
+		       int argc, char **argv, char **error)
+{
+	struct selector *s = (struct selector *) ps->context;
+	struct path_info *pi;
+	unsigned priority, min_io;
+
+	/* parse the path arguments */
+	if (argc != 2) {
+		*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) {
+		*error = "round-robin ps: Error allocating path context";
+		return -ENOMEM;
+	}
+
+	pi->path = path;
+	pi->min_io = min_io;
+	pi->priority = priority;
+	atomic_set(&pi->io_count, min_io);
+
+	spin_lock(&s->lock);
+	list_add_tail(&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)
+{
+	unsigned long flags;
+	struct selector *s = (struct selector *) ps->context;
+	struct path_info *pi;
+
+	/*
+	 * This function will be called infrequently so we don't
+	 * mind the expense of these searches.
+	 */
+	spin_lock_irqsave(&s->lock, flags);
+	pi = path_lookup(&s->valid_paths, p);
+	if (!pi)
+		pi = path_lookup(&s->invalid_paths, p);
+
+	if (!pi)
+		DMWARN("asked to change the state of an unknown path");
+
+	else
+		list_move_tail(&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)
+{
+	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)) {
+		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);
+	}
+	spin_unlock_irqrestore(&s->lock, flags);
+
+	return pi ? pi->path : NULL;
+}
+
+static struct path_selector_type rr_ps = {
+	.name = "round-robin",
+	.ctr = rr_ctr,
+	.dtr = rr_dtr,
+	.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)
+{
+	return dm_register_path_selector(&rr_ps);
+}
+
+void dm_unregister_rr_ps(void)
+{
+	dm_unregister_path_selector(&rr_ps);
 }
--- diff/drivers/md/dm-path-selector.h	2003-12-29 10:16:08.000000000 +0000
+++ source/drivers/md/dm-path-selector.h	2003-12-29 10:16:16.000000000 +0000
@@ -111,8 +111,8 @@
 /* FIXME: remove these 6 after tests */
 int dm_register_path_selectors(void);
 void dm_unregister_path_selectors(void);
-int dm_register_null_ps(void);
-void dm_unregister_null_ps(void);
+int dm_register_rr_ps(void);
+void dm_unregister_rr_ps(void);
 int dm_register_latency_ps(void);
 void dm_unregister_latency_ps(void);
 
--- diff/drivers/md/dm-failure.c	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-failure.c	2003-12-29 10:16:16.000000000 +0000
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2003 Sistina Software Limited.
+ *
+ * This file is released under the GPL.
+ *
+ */
+
+#include "dm.h"
+#include "dm-daemon.h"
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/mempool.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+
+#define ARG_FORMAT	"%ld"
+
+#define FAILURED_RTPRIO	20
+
+/* slab for the io jobs */
+static kmem_cache_t *_failure_cache;
+mempool_t *_failure_pool;
+
+static int ios = 0;
+#define	DEFAULT_IOS	256
+#define	MIN_IOS	16
+#define	MAX_IOS	32768	/* maximum on 32 bit hw with mempool_create() */
+
+/* context of every delayed io */
+struct failure_io {
+	struct list_head list;
+
+	struct dm_target	*ti;
+	unsigned long		flags;
+	jiffy_t			delay;
+
+	struct bio *bio;
+};
+
+/* processing categories */
+enum {
+	DELAY,
+	ERROR,
+	CATEGORY_SIZE, /* MUST BE LAST */
+};
+
+/* category properties */
+enum {
+	IOS_MIN,
+	IOS_MAX,
+	IOS_DELTA,
+	DELAY_MIN,
+	DELAY_MAX,
+	MIN,
+	MAX,
+	DELTA,
+	W_IOS_MIN,
+	W_IOS_MAX,
+	W_IOS_RAND_MAX,
+	W_DELAY_MIN,
+	W_DELAY_MAX,
+	W_MIN,
+	W_MAX,
+	W_IOS,
+	W_RAND,
+	W_DELAYS_C,
+	W_ERRORS_C,
+	W_REAL_ERRORS_C,
+	PARAM_SIZE, /* MUST BE LAST */
+};
+
+/* global flags */
+static int f[] = { 1, 2};
+
+/* failure context */
+struct failure_c {
+	struct dm_dev	*dev;
+	unsigned long	flags;
+	long		ios[CATEGORY_SIZE][PARAM_SIZE];
+};
+
+static spinlock_t _job_lock = SPIN_LOCK_UNLOCKED;
+static spinlock_t _io_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(_delay_jobs);
+static LIST_HEAD(_request_jobs);
+
+/* io job allocation/deallocation */
+static inline struct failure_io *alloc_io(void)
+{
+	return mempool_alloc(_failure_pool, GFP_NOIO);
+}
+
+static inline void free_io(struct failure_io *io)
+{
+	mempool_free(io, _failure_pool);
+}
+
+/* push a job onto the daemon job list */
+static inline void push(struct failure_io *io)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&_job_lock, flags);
+	list_add_tail(&io->list, &_delay_jobs);
+	spin_unlock_irqrestore(&_job_lock, flags);
+}
+
+static inline long _timeout(struct failure_io *io)
+{
+	return io->delay - jiffies;
+}
+
+static inline int _delay_reached(struct failure_io *io)
+{
+	return jiffies >= io->delay;
+}
+
+/*
+ * Daemon
+ */
+static unsigned long _failured_flags = 0;
+enum {
+	RUN = 1,
+	RERUN,
+};
+#define	_RUN	test_bit(RUN, &_failured_flags)
+
+static inline void _do_ios(void)
+{
+	struct list_head *jobs = &_request_jobs;
+
+	if (list_empty(jobs))
+		return;
+
+	while (!list_empty(jobs)) {
+		struct failure_io *io =
+			list_entry(jobs->next, struct failure_io, list);
+		generic_make_request(io->bio);
+		list_del(&io->list);
+	}
+
+	blk_run_queues();
+}
+
+/* failured does this every time it runs */
+static jiffy_t _do_work(void)
+{
+	unsigned long flags;
+	long timeout = MAX_SCHEDULE_TIMEOUT;
+	struct failure_io *io;
+	struct list_head *job, *tmp;
+
+	spin_lock_irqsave(&_job_lock, flags);
+
+	if (!list_empty(&_delay_jobs)) {
+		list_for_each_safe(job, tmp, &_delay_jobs) {
+			io = list_entry(job, struct failure_io, list);
+
+			if (_delay_reached(io) || !_RUN)
+				list_move_tail(&io->list, &_request_jobs);
+			else {
+				long t = _timeout(io);
+
+				if (t > 0 && t < timeout)
+					timeout = t;
+			}
+		}
+	}
+
+	spin_unlock_irqrestore(&_job_lock, flags);
+
+	_do_ios();
+
+	if (test_bit(RERUN, &_failured_flags)) {
+		timeout = 0;
+		clear_bit(RERUN, &_failured_flags);
+	}
+
+	return timeout;
+}
+
+
+/*-----------------------------------------------------------------
+ * Daemon
+ *---------------------------------------------------------------*/
+struct dm_daemon _failured;
+static DECLARE_WAIT_QUEUE_HEAD(_failured_queue);
+
+static inline long _to_jiffies(long v)
+{
+	long r = v * HZ / 1000;
+
+	/* < 1 jiffy make it 1 */
+	return v && !r ? 1 : r;
+}
+
+/*
+ * Parse a single
+ *
+ *	<path>
+ *
+ *	<ios min delay> <ios max delay> <ios delta>
+ *	<delay min[ms]> <delay max[ms]>
+ *	<min delays> <max delays> <delays delta>
+ *
+ *      <ios min error> <ios max error> <ios delta>
+ *	<delay min[ms]> <delay max[ms]>
+ *	<min errors> <max errors> <errors delta>
+ *
+ * parameter set
+ *
+ */
+#define xx(a, s, v) \
+	ti->error = "dm-failure: " s; \
+	if (sscanf(argv[a], ARG_FORMAT, & v) != 1) \
+		return -EINVAL;
+
+#define	yy(s, v, min, max) \
+	ti->error = "dm-failure: minimum or maximum " s " < 0"; \
+	if (fc->ios[v][max] < 0 || fc->ios[v][min] < 0) \
+		return -EINVAL; \
+\
+	ti->error = "dm-failure: maximum " s " != 0 and <= minimum"; \
+	if (fc->ios[v][max] && fc->ios[v][max] <= fc->ios[v][min]) \
+		return -EINVAL;
+
+#define	zz(c) \
+	fc->ios[c][W_IOS_MIN] = fc->ios[c][IOS_MIN]; \
+	fc->ios[c][W_IOS_MAX] = fc->ios[c][IOS_MAX]; \
+	fc->ios[c][W_DELAY_MIN] = _to_jiffies(fc->ios[c][DELAY_MIN]); \
+	fc->ios[c][W_DELAY_MAX] = _to_jiffies(fc->ios[c][DELAY_MAX]); \
+	fc->ios[c][W_MIN] = fc->ios[c][MIN]; \
+	fc->ios[c][W_MAX] = fc->ios[c][MAX];
+
+#define	PATH_ARGS	17	/* _get_path() must check for this amount */
+
+static int _get_path(struct dm_target *ti, struct failure_c *fc, char **argv)
+{
+	/* delay parameters */
+	xx(1, "minimum ios before delay", fc->ios[DELAY][IOS_MIN])
+	xx(2, "maximum ios before delay", fc->ios[DELAY][IOS_MAX])
+	yy("ios before delay", DELAY, IOS_MIN, IOS_MAX)
+
+	xx(3, "ios delay delta", fc->ios[DELAY][IOS_DELTA])
+
+	ti->error = "dm-failure: ios delay delta too high";
+	if (fc->ios[DELAY][IOS_DELTA] < 0 &&
+	    abs(fc->ios[DELAY][IOS_DELTA]) >= fc->ios[DELAY][IOS_MIN])
+		return -EINVAL;
+
+	xx(4, "delay time minimum", fc->ios[DELAY][DELAY_MIN])
+	xx(5, "delay time maximum", fc->ios[DELAY][DELAY_MAX])
+	yy("delay", DELAY, DELAY_MIN, DELAY_MAX)
+
+	ti->error = "dm-failure: ios before delay without delay";
+	if (!fc->ios[DELAY][IOS_MIN] && fc->ios[DELAY][DELAY_MIN])
+		return -EINVAL;
+
+	xx(6, "minimum delays", fc->ios[DELAY][MIN])
+	xx(7, "maximum delays", fc->ios[DELAY][MAX])
+	yy("delays", DELAY, MIN, MAX)
+
+	xx(8, "delays delta", fc->ios[DELAY][DELTA])
+
+	ti->error = "dm-failure: delay delta without delay";
+	/* FIXME: allow 0 delay in case maximum dleay given ? */
+	if (!fc->ios[DELAY][MIN] &&
+	    fc->ios[DELAY][DELTA])
+		return -EINVAL;
+
+	ti->error = "dm-failure: delay delta too high";
+	if (fc->ios[DELAY][DELTA] < 0 &&
+	    abs(fc->ios[DELAY][DELTA]) >= fc->ios[DELAY][MIN])
+		return -EINVAL;
+
+	/* error parameters */
+	xx(9, "minimum ios before error", fc->ios[ERROR][IOS_MIN])
+	xx(10, "maximum ios before error", fc->ios[ERROR][IOS_MAX])
+	yy("ios before error", ERROR, IOS_MIN, IOS_MAX)
+
+	xx(11, "ios error delta", fc->ios[ERROR][IOS_DELTA]);
+
+	ti->error = "dm-failure: ios error delta too high";
+	if (fc->ios[ERROR][IOS_DELTA] < 0 &&
+	    abs(fc->ios[ERROR][IOS_DELTA]) >= fc->ios[ERROR][IOS_MIN])
+		return -EINVAL;
+
+	xx(12, "error time minimum", fc->ios[ERROR][DELAY_MIN])
+	xx(13, "error time maximum", fc->ios[ERROR][DELAY_MAX])
+	yy("error", ERROR, DELAY_MIN, DELAY_MAX)
+
+	ti->error = "dm-failure: ios before error without error";
+	/* FIXME: allow 0 delay in case maximum dleay given ? */
+	if (!fc->ios[ERROR][IOS_MIN] &&
+	    fc->ios[ERROR][DELAY_MIN])
+		return -EINVAL;
+
+	xx(14, "minimum errors", fc->ios[ERROR][MIN])
+	xx(15, "maximum errors", fc->ios[ERROR][MAX])
+	yy("errors", ERROR, MIN, MAX)
+
+	xx(16, "errors delta", fc->ios[ERROR][DELTA])
+
+	ti->error = "dm-failure: error delta without error";
+	if (!fc->ios[ERROR][MIN] && fc->ios[ERROR][DELTA])
+		return -EINVAL;
+
+	ti->error = "dm-failure: error delta too high";
+	if (fc->ios[ERROR][DELTA] < 0 &&
+	    abs(fc->ios[ERROR][DELTA]) >= fc->ios[ERROR][MIN])
+		return -EINVAL;
+
+	ti->error = "dm-failure: device lookup failure";
+	if (dm_get_device(ti, argv[0], ti->begin, ti->len,
+			  dm_table_get_mode(ti->table),
+			  &fc->dev))
+		return -ENXIO;
+
+	ti->error = NULL;
+
+	zz(DELAY)
+	zz(ERROR)
+
+	if (fc->ios[DELAY][IOS_MIN] && fc->ios[DELAY][DELAY_MIN])
+		set_bit(f[DELAY], &fc->flags);
+
+	if (fc->ios[ERROR][IOS_MIN])
+		set_bit(f[ERROR], &fc->flags);
+
+	return 0;
+}
+#undef zz
+#undef yy
+#undef xx
+
+/* Construct a failure mapping */
+static int failure_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r;
+	struct failure_c *fc;
+
+	ti->error = "dm-failure: Wrong argument count";
+	if (argc != PATH_ARGS)
+		return -EINVAL;
+
+	ti->error = "dm-failure: Cannot allocate failure context";
+	fc = kmalloc(sizeof(*fc), GFP_NOIO);
+	if (!fc)
+		return -ENOMEM;
+
+	memset(fc, 0, sizeof(*fc));
+
+	r = _get_path(ti, fc, argv);
+	if (r) {
+		kfree(fc);
+		return r;
+	}
+
+	ti->private = fc;
+
+	return 0;
+
+}
+
+/* Destruct a failure mapping */
+static void failure_dtr(struct dm_target *ti)
+{
+	struct failure_c *fc = ti->private;
+
+	dm_put_device(ti, fc->dev);
+	kfree(fc);
+}
+
+static inline int _is_delay_io(struct failure_io *io) {
+	return test_bit(f[DELAY], &io->flags);
+}
+
+static inline int _is_error_io(struct failure_io *io) {
+	return test_bit(f[ERROR], &io->flags);
+}
+
+static int failure_end_io(struct dm_target *ti, struct bio *bio,
+			  int error, union map_info *map_context)
+{
+	int r = 0;
+	unsigned long flags;
+	struct failure_io *io = (struct failure_io *) map_context->ptr;
+	struct failure_c *fc = (struct failure_c *) io->ti->private;
+	int cat = _is_delay_io(io) ? 0 : 1;
+
+	if (error || _is_error_io(io)) {
+		/* FIXME: failures on non delayed/errored io count here too */
+		spin_lock_irqsave(&_io_lock, flags);
+		fc->ios[cat][W_REAL_ERRORS_C]++;
+		spin_unlock_irqrestore(&_io_lock, flags);
+		r = -1;
+		dm_table_event(io->ti->table);
+	} else if (_is_delay_io(io)) {
+		spin_lock_irqsave(&_io_lock, flags);
+		fc->ios[cat][W_DELAYS_C]++;
+		spin_unlock_irqrestore(&_io_lock, flags);
+		dm_table_event(io->ti->table);
+	}
+
+	free_io(io);
+
+	return r;
+}
+
+/* return min if max <= min or a random number between min and max */
+static inline long _random(long min, long max)
+{
+	if (min >= abs(max))
+		return min;
+	else {
+		unsigned short rand;
+
+		get_random_bytes(&rand, sizeof(rand));
+		if (!rand)
+			rand = 1;
+
+		return min + ((max - min) * rand / ((typeof(rand)) -1));
+	}
+}
+
+static inline long _get_delta(long min, long max, long delta)
+{
+	long t = _random(0, delta);
+
+	if (t) {
+		long iosmin, iosmax;
+
+		iosmin = min + t;
+		iosmax = max + t;
+		if (iosmin > 0 &&
+		    iosmin < LONG_MAX &&
+		    iosmax < LONG_MAX)
+			return t;
+	}
+
+	return 0;
+}
+
+#define _i	fc->ios[cat]
+static inline void _delta(struct failure_c *fc, int cat,
+			  int min, int max, int delta)
+{
+	if (_i[delta]) {
+		long d = _get_delta(_i[min], _i[max], _i[delta]);
+
+		if (d) {
+			_i[min] += d;
+			if (_i[max])
+				_i[max] += d;
+		}
+	}
+}
+
+static inline int _prepare_io(struct failure_io *io, int cat)
+{
+	unsigned long flags;
+	struct failure_c *fc = io->ti->private;
+
+	io->bio->bi_bdev = fc->dev->bdev;
+
+	if (!test_bit(f[cat], &fc->flags))
+		return 0;
+
+	spin_lock_irqsave(&_io_lock, flags);
+
+	if (!_i[W_IOS_RAND_MAX]) {
+		_i[W_RAND] = _random(_i[W_MIN], _i[W_MAX]);
+		_i[W_IOS_RAND_MAX] = _random(_i[W_IOS_MIN], _i[W_IOS_MAX]);
+	}
+
+	if (++_i[W_IOS] < _i[W_IOS_RAND_MAX])
+		goto out;
+
+	if (!_i[W_RAND]) {
+		_i[W_IOS] = 0;
+		_i[W_IOS_RAND_MAX] = 0;
+		_delta(fc, cat, W_MIN, W_MAX, DELTA);
+		_delta(fc, cat, W_IOS_MIN, W_IOS_MAX, IOS_DELTA);
+		goto out;
+	} else
+		_i[W_RAND]--;
+
+	spin_unlock_irqrestore(&_io_lock, flags);
+
+	set_bit(f[cat], &io->flags);
+	io->delay = jiffies + _random(_i[W_DELAY_MIN], _i[W_DELAY_MAX]);
+	push(io);
+	return 1;
+
+   out:
+	spin_unlock_irqrestore(&_io_lock, flags);
+
+	return 0;
+}
+#undef _i
+
+/* failure mapping */
+static int failure_map(struct dm_target *ti, struct bio *bio,
+		       union map_info *map_context)
+{
+	struct failure_io *io = alloc_io();
+
+	io->ti = ti;
+	io->bio = bio;
+	io->flags = 0;
+
+	/* pointer to be handed over to end_io function */
+	map_context->ptr = (void *) io;
+
+	if (_prepare_io(io, DELAY) ||
+	    _prepare_io(io, ERROR)) {
+		dm_daemon_wake(&_failured);
+		return 0; /* handle later */
+	}
+
+	return 1; /* regular map */
+}
+
+/* failure status */
+static int failure_status(struct dm_target *ti, status_type_t type,
+			  char *result, unsigned int maxlen)
+{
+	int sz = 0;
+	struct failure_c *fc = (struct failure_c *) ti->private;
+	char buffer[32];
+
+	format_dev_t(buffer, fc->dev->bdev->bd_dev);
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		sz += snprintf(result + sz, maxlen - sz,
+			       "%s "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT,
+			       buffer,
+			       fc->ios[DELAY][W_IOS_MIN],
+			       fc->ios[DELAY][W_IOS_MAX],
+			       fc->ios[DELAY][W_MIN],
+			       fc->ios[DELAY][W_MAX],
+			       fc->ios[DELAY][W_DELAYS_C],
+			       fc->ios[DELAY][W_ERRORS_C],
+			       fc->ios[DELAY][W_REAL_ERRORS_C],
+			       fc->ios[ERROR][W_IOS_MIN],
+			       fc->ios[ERROR][W_IOS_MAX],
+			       fc->ios[ERROR][W_MIN],
+			       fc->ios[ERROR][W_MAX],
+			       fc->ios[ERROR][W_DELAYS_C],
+			       fc->ios[ERROR][W_ERRORS_C],
+			       fc->ios[ERROR][W_REAL_ERRORS_C]);
+
+		break;
+
+	case STATUSTYPE_TABLE:
+		sz += snprintf(result + sz, maxlen - sz,
+			       "%s "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT " " ARG_FORMAT " " ARG_FORMAT " "
+			       ARG_FORMAT,
+			       buffer,
+			       fc->ios[DELAY][IOS_MIN],
+			       fc->ios[DELAY][IOS_MAX],
+			       fc->ios[DELAY][IOS_DELTA],
+			       fc->ios[DELAY][DELAY_MIN],
+			       fc->ios[DELAY][DELAY_MAX],
+			       fc->ios[DELAY][MIN],
+			       fc->ios[DELAY][MAX],
+			       fc->ios[DELAY][DELTA],
+			       fc->ios[ERROR][IOS_MIN],
+			       fc->ios[ERROR][IOS_MAX],
+			       fc->ios[ERROR][IOS_DELTA],
+			       fc->ios[ERROR][DELAY_MIN],
+			       fc->ios[ERROR][DELAY_MAX],
+			       fc->ios[ERROR][MIN],
+			       fc->ios[ERROR][MAX],
+			       fc->ios[ERROR][DELTA]);
+		break;
+	}
+
+	return 0;
+}
+
+static struct target_type failure_target = {
+	.name = "failure",
+	.module = THIS_MODULE,
+	.ctr = failure_ctr,
+	.dtr = failure_dtr,
+	.map = failure_map,
+	.end_io = failure_end_io,
+	.status = failure_status,
+};
+
+int __init dm_failure_init(void)
+{
+	int r = -EINVAL;
+	INIT_LIST_HEAD(&_delay_jobs);
+	INIT_LIST_HEAD(&_request_jobs);
+
+	if (!ios)
+		ios = DEFAULT_IOS;
+	else if (ios < MIN_IOS ||
+		 ios > MAX_IOS)
+			goto bad;
+
+	/* allocate a slab for the failure ios */
+	r = -ENOMEM;
+	_failure_cache = kmem_cache_create("dm failure io",
+					   sizeof(struct failure_io),
+					   0, 0, NULL, NULL);
+
+	if (!_failure_cache)
+		goto bad;
+
+	/* Create failure io mempool */
+	_failure_pool = mempool_create(ios, mempool_alloc_slab,
+				       mempool_free_slab, _failure_cache);
+
+	if (!_failure_pool)
+		goto bad1;
+
+	r = dm_register_target(&failure_target);
+	if (r < 0) {
+		DMERR("%s: register failed %d", failure_target.name, r);
+		goto bad2;
+	}
+
+	r = dm_daemon_start(&_failured, "failured", _do_work);
+	if (!r) {
+		DMINFO("dm_failure v0.1.2 (%d io contexts preallocated)", ios);
+		return 0;
+	}
+
+	dm_unregister_target(&failure_target);
+
+   bad2:
+	mempool_destroy(_failure_pool);
+
+   bad1:
+	kmem_cache_destroy(_failure_cache);
+
+   bad:
+	return r;
+}
+
+void __exit dm_failure_exit(void)
+{
+	int r;
+
+	dm_daemon_stop(&_failured);
+
+	r = dm_unregister_target(&failure_target);
+	if (r < 0)
+		DMERR("%s: unregister failed %d", failure_target.name, r);
+
+	mempool_destroy(_failure_pool);
+	kmem_cache_destroy(_failure_cache);
+}
+
+/*
+ * module hooks
+ */
+module_init(dm_failure_init);
+module_exit(dm_failure_exit);
+
+MODULE_DESCRIPTION(DM_NAME " failure target");
+MODULE_AUTHOR("Heinz Mauelshagen <mge@sistina.com>");
+MODULE_LICENSE("GPL");
+MODULE_PARM(ios, "i");
+MODULE_PARM_DESC(min_ios, "number of preallocated io contexts");
--- diff/drivers/md/dm-flakey.c	1970-01-01 01:00:00.000000000 +0100
+++ source/drivers/md/dm-flakey.c	2003-12-29 10:16:16.000000000 +0000
@@ -0,0 +1,155 @@
+/*
+ * Copyright (C) 2003 Sistina Software (UK) 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/bio.h>
+#include <linux/slab.h>
+
+/*
+ * Flakey: Used for testing only, simulates intermittent,
+ * catastrophic device failure.
+ */
+struct flakey {
+	struct dm_dev *dev;
+	jiffy_t start_time;
+	sector_t start;
+	unsigned up_interval;
+	unsigned down_interval;
+};
+
+/*
+ * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval>
+ */
+static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	struct flakey *f;
+
+	if (argc != 4) {
+		ti->error = "dm-flakey: Invalid argument count";
+		return -EINVAL;
+	}
+
+	f = kmalloc(sizeof(*f), GFP_KERNEL);
+	if (!f) {
+		ti->error = "dm-flakey: Cannot allocate linear context";
+		return -ENOMEM;
+	}
+	f->start_time = jiffies;
+
+	if (sscanf(argv[1], SECTOR_FORMAT, &f->start) != 1) {
+		ti->error = "dm-flakey: Invalid device sector";
+		goto bad;
+	}
+
+	if (sscanf(argv[2], "%u", &f->up_interval) != 1) {
+		ti->error = "dm-flakey: Invalid up interval";
+		goto bad;
+	}
+
+	if (sscanf(argv[3], "%u", &f->down_interval) != 1) {
+		ti->error = "dm-flakey: Invalid down interval";
+		goto bad;
+	}
+
+	if (dm_get_device(ti, argv[0], f->start, ti->len,
+			  dm_table_get_mode(ti->table), &f->dev)) {
+		ti->error = "dm-flakey: Device lookup failed";
+		goto bad;
+	}
+
+	ti->private = f;
+	return 0;
+
+ bad:
+	kfree(f);
+	return -EINVAL;
+}
+
+static void flakey_dtr(struct dm_target *ti)
+{
+	struct flakey *f = (struct flakey *) ti->private;
+
+	dm_put_device(ti, f->dev);
+	kfree(f);
+}
+
+static int flakey_map(struct dm_target *ti, struct bio *bio,
+		      union map_info *map_context)
+{
+	struct flakey *f = (struct flakey *) ti->private;
+	unsigned elapsed;
+
+	/* are we alive ? */
+	elapsed = (jiffies - f->start_time) / HZ;
+	elapsed %= (f->up_interval + f->down_interval);
+	if (elapsed > f->up_interval)
+		return -EIO;
+
+	else {
+		bio->bi_bdev = f->dev->bdev;
+		bio->bi_sector = f->start + (bio->bi_sector - ti->begin);
+	}
+
+	return 1;
+}
+
+static int flakey_status(struct dm_target *ti, status_type_t type,
+			 char *result, unsigned int maxlen)
+{
+	struct flakey *f = (struct flakey *) ti->private;
+	char buffer[32];
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		result[0] = '\0';
+		break;
+
+	case STATUSTYPE_TABLE:
+		format_dev_t(buffer, f->dev->bdev->bd_dev);
+		snprintf(result, maxlen, "%s " SECTOR_FORMAT, buffer, f->start);
+		break;
+	}
+	return 0;
+}
+
+static struct target_type flakey_target = {
+	.name   = "flakey",
+	.module = THIS_MODULE,
+	.ctr    = flakey_ctr,
+	.dtr    = flakey_dtr,
+	.map    = flakey_map,
+	.status = flakey_status,
+};
+
+int __init dm_flakey_init(void)
+{
+	int r = dm_register_target(&flakey_target);
+
+	if (r < 0)
+		DMERR("flakey: register failed %d", r);
+
+	return r;
+}
+
+void dm_flakey_exit(void)
+{
+	int r = dm_unregister_target(&flakey_target);
+
+	if (r < 0)
+		DMERR("flakey: unregister failed %d", r);
+}
+
+/* Module hooks */
+module_init(dm_flakey_init);
+module_exit(dm_flakey_exit);
+
+MODULE_DESCRIPTION(DM_NAME " flakey target");
+MODULE_AUTHOR("Joe Thornber <thornber@sistina.com>");
+MODULE_LICENSE("GPL");
--- diff/drivers/md/dm-null-ps.c	2003-12-29 10:16:08.000000000 +0000
+++ source/drivers/md/dm-null-ps.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,182 +0,0 @@
-/*
- * Copyright (C) 2003 Sistina Software.
- *
- * Module Author: Heinz Mauelshagen
- *
- * This file is released under the GPL.
- *
- *
- * "Null" Path Selector
- *
- *	Returns any path unless failed.
- *
- */
-
-#include "dm.h"
-#include "dm-path-selector.h"
-
-#include <linux/slab.h>
-
-/* Path selector context */
-struct null_c {
-	spinlock_t lock;
-
-	struct list_head valid_paths;
-	struct list_head invalid_paths;
-};
-
-/* We keep the paths on linked lists */
-struct path_list {
-	struct list_head list;
-	struct path *path;
-};
-
-/* Allocate null context */
-static struct null_c *alloc_null_c(void)
-{
-	struct null_c *nc = kmalloc(sizeof(*nc), GFP_KERNEL);
-
-	if (nc) {
-		INIT_LIST_HEAD(&nc->valid_paths);
-		INIT_LIST_HEAD(&nc->invalid_paths);
-		nc->lock = SPIN_LOCK_UNLOCKED;
-	}
-
-	return nc;
-}
-
-/* Path selector constructor */
-static int null_ctr(struct path_selector *ps)
-{
-	struct null_c *nc;
-
-	nc = alloc_null_c();
-	if (!nc)
-		return -ENOMEM;
-
-	ps->context = nc;
-	return 0;
-}
-
-static void free_paths(struct list_head *paths)
-{
-	struct path_list *pl, *next;
-
-	list_for_each_entry_safe (pl, next, paths, list) {
-		list_del(&pl->list);
-		kfree(pl);
-	}
-}
-
-/* Path selector destructor */
-static void null_dtr(struct path_selector *ps)
-{
-	struct null_c *nc = (struct null_c *) ps->context;
-	free_paths(&nc->valid_paths);
-	free_paths(&nc->invalid_paths);
-	kfree(nc);
-}
-
-/* Path add context */
-static int null_add_path(struct path_selector *ps, struct path *path,
-			 int argc, char **argv, char **error)
-{
-	struct null_c *nc = (struct null_c *) ps->context;
-	struct path_list *pl;
-
-	if (argc) {
-		*error = "null path selector: No path arguments allowed";
-		return -EINVAL;
-	}
-
-	pl = kmalloc(sizeof(*pl), GFP_KERNEL);
-	if (!pl) {
-		*error = "null path selector: Error allocating path context";
-		return -ENOMEM;
-	}
-
-	pl->path = path;
-
-	spin_lock(&nc->lock);
-	list_add_tail(&pl->list, &nc->valid_paths);
-	spin_unlock(&nc->lock);
-
-	return 0;
-}
-
-/*
- * Search a list for a particular path.
- */
-static struct path_list *__find_path(struct list_head *head, struct path *p)
-{
-	struct path_list *pl;
-
-	list_for_each_entry (pl, head, list)
-		if (pl->path == p)
-			return pl;
-
-	return NULL;
-}
-
-static void null_set_path_state(struct path_selector *ps,
-				struct path *p, int valid)
-{
-	unsigned long flags;
-	struct null_c *nc = (struct null_c *) ps->context;
-	struct path_list *pl;
-
-	/*
-	 * This function will be called infrequently so we don't
-	 * mind the expense of these searches.
-	 */
-	spin_lock_irqsave(&nc->lock, flags);
-	pl = __find_path(&nc->valid_paths, p);
-	if (!pl)
-		pl = __find_path(&nc->invalid_paths, p);
-
-	if (!pl)
-		DMWARN("asked to change the state of an unknown path");
-
-	else
-		list_move_tail(&pl->list, valid ?
-			       &nc->valid_paths : &nc->invalid_paths);
-
-	spin_unlock_irqrestore(&nc->lock, flags);
-}
-
-/* Path selector */
-static struct path *null_select_path(struct path_selector *ps,
-				     struct bio *bio,
-				     union map_info *map_context)
-{
-	unsigned long flags;
-	struct null_c *nc = (struct null_c *) ps->context;
-	struct list_head *list = &nc->valid_paths;
-	struct path_list *pl = NULL;
-
-	spin_lock_irqsave(&nc->lock, flags);
-	if (!list_empty(list))
-		pl = list_entry(list->next, struct path_list, list);
-	spin_unlock_irqrestore(&nc->lock, flags);
-
-	return pl ? pl->path : NULL;
-}
-
-static struct path_selector_type null_ps = {
-	.name = "null",
-	.ctr = null_ctr,
-	.dtr = null_dtr,
-	.add_path = null_add_path,
-	.set_path_state = null_set_path_state,
-	.select_path = null_select_path,
-	.endio = NULL,
-	.status = NULL,
-};
-
-int dm_register_null_ps(void) {
-	return dm_register_path_selector(&null_ps);
-}
-
-void dm_unregister_null_ps(void) {
-	dm_unregister_path_selector(&null_ps);
-}