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 +#include +#include +#include +#include +#include + +#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 + * + * + * + * + * + * + * + * + * + * + * + * 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 "); +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 +#include +#include +#include +#include + +/* + * 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: + */ +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 "); +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 - -/* 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); -}