linux/drivers/md/faulty.c
<<
>>
Prefs
   1/*
   2 * faulty.c : Multiple Devices driver for Linux
   3 *
   4 * Copyright (C) 2004 Neil Brown
   5 *
   6 * fautly-device-simulator personality for md
   7 *
   8 *
   9 * This program is free software; you can redistribute it and/or modify
  10 * it under the terms of the GNU General Public License as published by
  11 * the Free Software Foundation; either version 2, or (at your option)
  12 * any later version.
  13 *
  14 * You should have received a copy of the GNU General Public License
  15 * (for example /usr/src/linux/COPYING); if not, write to the Free
  16 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  17 */
  18
  19
  20/*
  21 * The "faulty" personality causes some requests to fail.
  22 *
  23 * Possible failure modes are:
  24 *   reads fail "randomly" but succeed on retry
  25 *   writes fail "randomly" but succeed on retry
  26 *   reads for some address fail and then persist until a write
  27 *   reads for some address fail and then persist irrespective of write
  28 *   writes for some address fail and persist
  29 *   all writes fail
  30 *
  31 * Different modes can be active at a time, but only
  32 * one can be set at array creation.  Others can be added later.
  33 * A mode can be one-shot or recurrent with the recurrance being
  34 * once in every N requests.
  35 * The bottom 5 bits of the "layout" indicate the mode.  The
  36 * remainder indicate a period, or 0 for one-shot.
  37 *
  38 * There is an implementation limit on the number of concurrently
  39 * persisting-faulty blocks. When a new fault is requested that would
  40 * exceed the limit, it is ignored.
  41 * All current faults can be clear using a layout of "0".
  42 *
  43 * Requests are always sent to the device.  If they are to fail,
  44 * we clone the bio and insert a new b_end_io into the chain.
  45 */
  46
  47#define WriteTransient  0
  48#define ReadTransient   1
  49#define WritePersistent 2
  50#define ReadPersistent  3
  51#define WriteAll        4 /* doesn't go to device */
  52#define ReadFixable     5
  53#define Modes   6
  54
  55#define ClearErrors     31
  56#define ClearFaults     30
  57
  58#define AllPersist      100 /* internal use only */
  59#define NoPersist       101
  60
  61#define ModeMask        0x1f
  62#define ModeShift       5
  63
  64#define MaxFault        50
  65#include <linux/raid/md.h>
  66
  67
  68static void faulty_fail(struct bio *bio, int error)
  69{
  70        struct bio *b = bio->bi_private;
  71
  72        b->bi_size = bio->bi_size;
  73        b->bi_sector = bio->bi_sector;
  74
  75        bio_put(bio);
  76
  77        bio_io_error(b);
  78}
  79
  80typedef struct faulty_conf {
  81        int period[Modes];
  82        atomic_t counters[Modes];
  83        sector_t faults[MaxFault];
  84        int     modes[MaxFault];
  85        int nfaults;
  86        mdk_rdev_t *rdev;
  87} conf_t;
  88
  89static int check_mode(conf_t *conf, int mode)
  90{
  91        if (conf->period[mode] == 0 &&
  92            atomic_read(&conf->counters[mode]) <= 0)
  93                return 0; /* no failure, no decrement */
  94
  95
  96        if (atomic_dec_and_test(&conf->counters[mode])) {
  97                if (conf->period[mode])
  98                        atomic_set(&conf->counters[mode], conf->period[mode]);
  99                return 1;
 100        }
 101        return 0;
 102}
 103
 104static int check_sector(conf_t *conf, sector_t start, sector_t end, int dir)
 105{
 106        /* If we find a ReadFixable sector, we fix it ... */
 107        int i;
 108        for (i=0; i<conf->nfaults; i++)
 109                if (conf->faults[i] >= start &&
 110                    conf->faults[i] < end) {
 111                        /* found it ... */
 112                        switch (conf->modes[i] * 2 + dir) {
 113                        case WritePersistent*2+WRITE: return 1;
 114                        case ReadPersistent*2+READ: return 1;
 115                        case ReadFixable*2+READ: return 1;
 116                        case ReadFixable*2+WRITE:
 117                                conf->modes[i] = NoPersist;
 118                                return 0;
 119                        case AllPersist*2+READ:
 120                        case AllPersist*2+WRITE: return 1;
 121                        default:
 122                                return 0;
 123                        }
 124                }
 125        return 0;
 126}
 127
 128static void add_sector(conf_t *conf, sector_t start, int mode)
 129{
 130        int i;
 131        int n = conf->nfaults;
 132        for (i=0; i<conf->nfaults; i++)
 133                if (conf->faults[i] == start) {
 134                        switch(mode) {
 135                        case NoPersist: conf->modes[i] = mode; return;
 136                        case WritePersistent:
 137                                if (conf->modes[i] == ReadPersistent ||
 138                                    conf->modes[i] == ReadFixable)
 139                                        conf->modes[i] = AllPersist;
 140                                else
 141                                        conf->modes[i] = WritePersistent;
 142                                return;
 143                        case ReadPersistent:
 144                                if (conf->modes[i] == WritePersistent)
 145                                        conf->modes[i] = AllPersist;
 146                                else
 147                                        conf->modes[i] = ReadPersistent;
 148                                return;
 149                        case ReadFixable:
 150                                if (conf->modes[i] == WritePersistent ||
 151                                    conf->modes[i] == ReadPersistent)
 152                                        conf->modes[i] = AllPersist;
 153                                else
 154                                        conf->modes[i] = ReadFixable;
 155                                return;
 156                        }
 157                } else if (conf->modes[i] == NoPersist)
 158                        n = i;
 159
 160        if (n >= MaxFault)
 161                return;
 162        conf->faults[n] = start;
 163        conf->modes[n] = mode;
 164        if (conf->nfaults == n)
 165                conf->nfaults = n+1;
 166}
 167
 168static int make_request(struct request_queue *q, struct bio *bio)
 169{
 170        mddev_t *mddev = q->queuedata;
 171        conf_t *conf = (conf_t*)mddev->private;
 172        int failit = 0;
 173
 174        if (bio_data_dir(bio) == WRITE) {
 175                /* write request */
 176                if (atomic_read(&conf->counters[WriteAll])) {
 177                        /* special case - don't decrement, don't generic_make_request,
 178                         * just fail immediately
 179                         */
 180                        bio_endio(bio, -EIO);
 181                        return 0;
 182                }
 183
 184                if (check_sector(conf, bio->bi_sector, bio->bi_sector+(bio->bi_size>>9),
 185                                 WRITE))
 186                        failit = 1;
 187                if (check_mode(conf, WritePersistent)) {
 188                        add_sector(conf, bio->bi_sector, WritePersistent);
 189                        failit = 1;
 190                }
 191                if (check_mode(conf, WriteTransient))
 192                        failit = 1;
 193        } else {
 194                /* read request */
 195                if (check_sector(conf, bio->bi_sector, bio->bi_sector + (bio->bi_size>>9),
 196                                 READ))
 197                        failit = 1;
 198                if (check_mode(conf, ReadTransient))
 199                        failit = 1;
 200                if (check_mode(conf, ReadPersistent)) {
 201                        add_sector(conf, bio->bi_sector, ReadPersistent);
 202                        failit = 1;
 203                }
 204                if (check_mode(conf, ReadFixable)) {
 205                        add_sector(conf, bio->bi_sector, ReadFixable);
 206                        failit = 1;
 207                }
 208        }
 209        if (failit) {
 210                struct bio *b = bio_clone(bio, GFP_NOIO);
 211                b->bi_bdev = conf->rdev->bdev;
 212                b->bi_private = bio;
 213                b->bi_end_io = faulty_fail;
 214                generic_make_request(b);
 215                return 0;
 216        } else {
 217                bio->bi_bdev = conf->rdev->bdev;
 218                return 1;
 219        }
 220}
 221
 222static void status(struct seq_file *seq, mddev_t *mddev)
 223{
 224        conf_t *conf = (conf_t*)mddev->private;
 225        int n;
 226
 227        if ((n=atomic_read(&conf->counters[WriteTransient])) != 0)
 228                seq_printf(seq, " WriteTransient=%d(%d)",
 229                           n, conf->period[WriteTransient]);
 230
 231        if ((n=atomic_read(&conf->counters[ReadTransient])) != 0)
 232                seq_printf(seq, " ReadTransient=%d(%d)",
 233                           n, conf->period[ReadTransient]);
 234
 235        if ((n=atomic_read(&conf->counters[WritePersistent])) != 0)
 236                seq_printf(seq, " WritePersistent=%d(%d)",
 237                           n, conf->period[WritePersistent]);
 238
 239        if ((n=atomic_read(&conf->counters[ReadPersistent])) != 0)
 240                seq_printf(seq, " ReadPersistent=%d(%d)",
 241                           n, conf->period[ReadPersistent]);
 242
 243
 244        if ((n=atomic_read(&conf->counters[ReadFixable])) != 0)
 245                seq_printf(seq, " ReadFixable=%d(%d)",
 246                           n, conf->period[ReadFixable]);
 247
 248        if ((n=atomic_read(&conf->counters[WriteAll])) != 0)
 249                seq_printf(seq, " WriteAll");
 250
 251        seq_printf(seq, " nfaults=%d", conf->nfaults);
 252}
 253
 254
 255static int reconfig(mddev_t *mddev, int layout, int chunk_size)
 256{
 257        int mode = layout & ModeMask;
 258        int count = layout >> ModeShift;
 259        conf_t *conf = mddev->private;
 260
 261        if (chunk_size != -1)
 262                return -EINVAL;
 263
 264        /* new layout */
 265        if (mode == ClearFaults)
 266                conf->nfaults = 0;
 267        else if (mode == ClearErrors) {
 268                int i;
 269                for (i=0 ; i < Modes ; i++) {
 270                        conf->period[i] = 0;
 271                        atomic_set(&conf->counters[i], 0);
 272                }
 273        } else if (mode < Modes) {
 274                conf->period[mode] = count;
 275                if (!count) count++;
 276                atomic_set(&conf->counters[mode], count);
 277        } else
 278                return -EINVAL;
 279        mddev->layout = -1; /* makes sure further changes come through */
 280        return 0;
 281}
 282
 283static int run(mddev_t *mddev)
 284{
 285        mdk_rdev_t *rdev;
 286        struct list_head *tmp;
 287        int i;
 288
 289        conf_t *conf = kmalloc(sizeof(*conf), GFP_KERNEL);
 290
 291        for (i=0; i<Modes; i++) {
 292                atomic_set(&conf->counters[i], 0);
 293                conf->period[i] = 0;
 294        }
 295        conf->nfaults = 0;
 296
 297        ITERATE_RDEV(mddev, rdev, tmp)
 298                conf->rdev = rdev;
 299
 300        mddev->array_size = mddev->size;
 301        mddev->private = conf;
 302
 303        reconfig(mddev, mddev->layout, -1);
 304
 305        return 0;
 306}
 307
 308static int stop(mddev_t *mddev)
 309{
 310        conf_t *conf = (conf_t *)mddev->private;
 311
 312        kfree(conf);
 313        mddev->private = NULL;
 314        return 0;
 315}
 316
 317static struct mdk_personality faulty_personality =
 318{
 319        .name           = "faulty",
 320        .level          = LEVEL_FAULTY,
 321        .owner          = THIS_MODULE,
 322        .make_request   = make_request,
 323        .run            = run,
 324        .stop           = stop,
 325        .status         = status,
 326        .reconfig       = reconfig,
 327};
 328
 329static int __init raid_init(void)
 330{
 331        return register_md_personality(&faulty_personality);
 332}
 333
 334static void raid_exit(void)
 335{
 336        unregister_md_personality(&faulty_personality);
 337}
 338
 339module_init(raid_init);
 340module_exit(raid_exit);
 341MODULE_LICENSE("GPL");
 342MODULE_ALIAS("md-personality-10"); /* faulty */
 343MODULE_ALIAS("md-faulty");
 344MODULE_ALIAS("md-level--5");
 345