162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * faulty.c : Multiple Devices driver for Linux
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2004 Neil Brown
662306a36Sopenharmony_ci *
762306a36Sopenharmony_ci * fautly-device-simulator personality for md
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci/*
1262306a36Sopenharmony_ci * The "faulty" personality causes some requests to fail.
1362306a36Sopenharmony_ci *
1462306a36Sopenharmony_ci * Possible failure modes are:
1562306a36Sopenharmony_ci *   reads fail "randomly" but succeed on retry
1662306a36Sopenharmony_ci *   writes fail "randomly" but succeed on retry
1762306a36Sopenharmony_ci *   reads for some address fail and then persist until a write
1862306a36Sopenharmony_ci *   reads for some address fail and then persist irrespective of write
1962306a36Sopenharmony_ci *   writes for some address fail and persist
2062306a36Sopenharmony_ci *   all writes fail
2162306a36Sopenharmony_ci *
2262306a36Sopenharmony_ci * Different modes can be active at a time, but only
2362306a36Sopenharmony_ci * one can be set at array creation.  Others can be added later.
2462306a36Sopenharmony_ci * A mode can be one-shot or recurrent with the recurrence being
2562306a36Sopenharmony_ci * once in every N requests.
2662306a36Sopenharmony_ci * The bottom 5 bits of the "layout" indicate the mode.  The
2762306a36Sopenharmony_ci * remainder indicate a period, or 0 for one-shot.
2862306a36Sopenharmony_ci *
2962306a36Sopenharmony_ci * There is an implementation limit on the number of concurrently
3062306a36Sopenharmony_ci * persisting-faulty blocks. When a new fault is requested that would
3162306a36Sopenharmony_ci * exceed the limit, it is ignored.
3262306a36Sopenharmony_ci * All current faults can be clear using a layout of "0".
3362306a36Sopenharmony_ci *
3462306a36Sopenharmony_ci * Requests are always sent to the device.  If they are to fail,
3562306a36Sopenharmony_ci * we clone the bio and insert a new b_end_io into the chain.
3662306a36Sopenharmony_ci */
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci#define	WriteTransient	0
3962306a36Sopenharmony_ci#define	ReadTransient	1
4062306a36Sopenharmony_ci#define	WritePersistent	2
4162306a36Sopenharmony_ci#define	ReadPersistent	3
4262306a36Sopenharmony_ci#define	WriteAll	4 /* doesn't go to device */
4362306a36Sopenharmony_ci#define	ReadFixable	5
4462306a36Sopenharmony_ci#define	Modes	6
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci#define	ClearErrors	31
4762306a36Sopenharmony_ci#define	ClearFaults	30
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci#define AllPersist	100 /* internal use only */
5062306a36Sopenharmony_ci#define	NoPersist	101
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci#define	ModeMask	0x1f
5362306a36Sopenharmony_ci#define	ModeShift	5
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci#define MaxFault	50
5662306a36Sopenharmony_ci#include <linux/blkdev.h>
5762306a36Sopenharmony_ci#include <linux/module.h>
5862306a36Sopenharmony_ci#include <linux/raid/md_u.h>
5962306a36Sopenharmony_ci#include <linux/slab.h>
6062306a36Sopenharmony_ci#include "md.h"
6162306a36Sopenharmony_ci#include <linux/seq_file.h>
6262306a36Sopenharmony_ci
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_cistatic void faulty_fail(struct bio *bio)
6562306a36Sopenharmony_ci{
6662306a36Sopenharmony_ci	struct bio *b = bio->bi_private;
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_ci	b->bi_iter.bi_size = bio->bi_iter.bi_size;
6962306a36Sopenharmony_ci	b->bi_iter.bi_sector = bio->bi_iter.bi_sector;
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	bio_put(bio);
7262306a36Sopenharmony_ci
7362306a36Sopenharmony_ci	bio_io_error(b);
7462306a36Sopenharmony_ci}
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_cistruct faulty_conf {
7762306a36Sopenharmony_ci	int period[Modes];
7862306a36Sopenharmony_ci	atomic_t counters[Modes];
7962306a36Sopenharmony_ci	sector_t faults[MaxFault];
8062306a36Sopenharmony_ci	int	modes[MaxFault];
8162306a36Sopenharmony_ci	int nfaults;
8262306a36Sopenharmony_ci	struct md_rdev *rdev;
8362306a36Sopenharmony_ci};
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_cistatic int check_mode(struct faulty_conf *conf, int mode)
8662306a36Sopenharmony_ci{
8762306a36Sopenharmony_ci	if (conf->period[mode] == 0 &&
8862306a36Sopenharmony_ci	    atomic_read(&conf->counters[mode]) <= 0)
8962306a36Sopenharmony_ci		return 0; /* no failure, no decrement */
9062306a36Sopenharmony_ci
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	if (atomic_dec_and_test(&conf->counters[mode])) {
9362306a36Sopenharmony_ci		if (conf->period[mode])
9462306a36Sopenharmony_ci			atomic_set(&conf->counters[mode], conf->period[mode]);
9562306a36Sopenharmony_ci		return 1;
9662306a36Sopenharmony_ci	}
9762306a36Sopenharmony_ci	return 0;
9862306a36Sopenharmony_ci}
9962306a36Sopenharmony_ci
10062306a36Sopenharmony_cistatic int check_sector(struct faulty_conf *conf, sector_t start, sector_t end, int dir)
10162306a36Sopenharmony_ci{
10262306a36Sopenharmony_ci	/* If we find a ReadFixable sector, we fix it ... */
10362306a36Sopenharmony_ci	int i;
10462306a36Sopenharmony_ci	for (i=0; i<conf->nfaults; i++)
10562306a36Sopenharmony_ci		if (conf->faults[i] >= start &&
10662306a36Sopenharmony_ci		    conf->faults[i] < end) {
10762306a36Sopenharmony_ci			/* found it ... */
10862306a36Sopenharmony_ci			switch (conf->modes[i] * 2 + dir) {
10962306a36Sopenharmony_ci			case WritePersistent*2+WRITE: return 1;
11062306a36Sopenharmony_ci			case ReadPersistent*2+READ: return 1;
11162306a36Sopenharmony_ci			case ReadFixable*2+READ: return 1;
11262306a36Sopenharmony_ci			case ReadFixable*2+WRITE:
11362306a36Sopenharmony_ci				conf->modes[i] = NoPersist;
11462306a36Sopenharmony_ci				return 0;
11562306a36Sopenharmony_ci			case AllPersist*2+READ:
11662306a36Sopenharmony_ci			case AllPersist*2+WRITE: return 1;
11762306a36Sopenharmony_ci			default:
11862306a36Sopenharmony_ci				return 0;
11962306a36Sopenharmony_ci			}
12062306a36Sopenharmony_ci		}
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic void add_sector(struct faulty_conf *conf, sector_t start, int mode)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	int i;
12762306a36Sopenharmony_ci	int n = conf->nfaults;
12862306a36Sopenharmony_ci	for (i=0; i<conf->nfaults; i++)
12962306a36Sopenharmony_ci		if (conf->faults[i] == start) {
13062306a36Sopenharmony_ci			switch(mode) {
13162306a36Sopenharmony_ci			case NoPersist: conf->modes[i] = mode; return;
13262306a36Sopenharmony_ci			case WritePersistent:
13362306a36Sopenharmony_ci				if (conf->modes[i] == ReadPersistent ||
13462306a36Sopenharmony_ci				    conf->modes[i] == ReadFixable)
13562306a36Sopenharmony_ci					conf->modes[i] = AllPersist;
13662306a36Sopenharmony_ci				else
13762306a36Sopenharmony_ci					conf->modes[i] = WritePersistent;
13862306a36Sopenharmony_ci				return;
13962306a36Sopenharmony_ci			case ReadPersistent:
14062306a36Sopenharmony_ci				if (conf->modes[i] == WritePersistent)
14162306a36Sopenharmony_ci					conf->modes[i] = AllPersist;
14262306a36Sopenharmony_ci				else
14362306a36Sopenharmony_ci					conf->modes[i] = ReadPersistent;
14462306a36Sopenharmony_ci				return;
14562306a36Sopenharmony_ci			case ReadFixable:
14662306a36Sopenharmony_ci				if (conf->modes[i] == WritePersistent ||
14762306a36Sopenharmony_ci				    conf->modes[i] == ReadPersistent)
14862306a36Sopenharmony_ci					conf->modes[i] = AllPersist;
14962306a36Sopenharmony_ci				else
15062306a36Sopenharmony_ci					conf->modes[i] = ReadFixable;
15162306a36Sopenharmony_ci				return;
15262306a36Sopenharmony_ci			}
15362306a36Sopenharmony_ci		} else if (conf->modes[i] == NoPersist)
15462306a36Sopenharmony_ci			n = i;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	if (n >= MaxFault)
15762306a36Sopenharmony_ci		return;
15862306a36Sopenharmony_ci	conf->faults[n] = start;
15962306a36Sopenharmony_ci	conf->modes[n] = mode;
16062306a36Sopenharmony_ci	if (conf->nfaults == n)
16162306a36Sopenharmony_ci		conf->nfaults = n+1;
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_cistatic bool faulty_make_request(struct mddev *mddev, struct bio *bio)
16562306a36Sopenharmony_ci{
16662306a36Sopenharmony_ci	struct faulty_conf *conf = mddev->private;
16762306a36Sopenharmony_ci	int failit = 0;
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	if (bio_data_dir(bio) == WRITE) {
17062306a36Sopenharmony_ci		/* write request */
17162306a36Sopenharmony_ci		if (atomic_read(&conf->counters[WriteAll])) {
17262306a36Sopenharmony_ci			/* special case - don't decrement, don't submit_bio_noacct,
17362306a36Sopenharmony_ci			 * just fail immediately
17462306a36Sopenharmony_ci			 */
17562306a36Sopenharmony_ci			bio_io_error(bio);
17662306a36Sopenharmony_ci			return true;
17762306a36Sopenharmony_ci		}
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci		if (check_sector(conf, bio->bi_iter.bi_sector,
18062306a36Sopenharmony_ci				 bio_end_sector(bio), WRITE))
18162306a36Sopenharmony_ci			failit = 1;
18262306a36Sopenharmony_ci		if (check_mode(conf, WritePersistent)) {
18362306a36Sopenharmony_ci			add_sector(conf, bio->bi_iter.bi_sector,
18462306a36Sopenharmony_ci				   WritePersistent);
18562306a36Sopenharmony_ci			failit = 1;
18662306a36Sopenharmony_ci		}
18762306a36Sopenharmony_ci		if (check_mode(conf, WriteTransient))
18862306a36Sopenharmony_ci			failit = 1;
18962306a36Sopenharmony_ci	} else {
19062306a36Sopenharmony_ci		/* read request */
19162306a36Sopenharmony_ci		if (check_sector(conf, bio->bi_iter.bi_sector,
19262306a36Sopenharmony_ci				 bio_end_sector(bio), READ))
19362306a36Sopenharmony_ci			failit = 1;
19462306a36Sopenharmony_ci		if (check_mode(conf, ReadTransient))
19562306a36Sopenharmony_ci			failit = 1;
19662306a36Sopenharmony_ci		if (check_mode(conf, ReadPersistent)) {
19762306a36Sopenharmony_ci			add_sector(conf, bio->bi_iter.bi_sector,
19862306a36Sopenharmony_ci				   ReadPersistent);
19962306a36Sopenharmony_ci			failit = 1;
20062306a36Sopenharmony_ci		}
20162306a36Sopenharmony_ci		if (check_mode(conf, ReadFixable)) {
20262306a36Sopenharmony_ci			add_sector(conf, bio->bi_iter.bi_sector,
20362306a36Sopenharmony_ci				   ReadFixable);
20462306a36Sopenharmony_ci			failit = 1;
20562306a36Sopenharmony_ci		}
20662306a36Sopenharmony_ci	}
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	md_account_bio(mddev, &bio);
20962306a36Sopenharmony_ci	if (failit) {
21062306a36Sopenharmony_ci		struct bio *b = bio_alloc_clone(conf->rdev->bdev, bio, GFP_NOIO,
21162306a36Sopenharmony_ci						&mddev->bio_set);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci		b->bi_private = bio;
21462306a36Sopenharmony_ci		b->bi_end_io = faulty_fail;
21562306a36Sopenharmony_ci		bio = b;
21662306a36Sopenharmony_ci	} else
21762306a36Sopenharmony_ci		bio_set_dev(bio, conf->rdev->bdev);
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	submit_bio_noacct(bio);
22062306a36Sopenharmony_ci	return true;
22162306a36Sopenharmony_ci}
22262306a36Sopenharmony_ci
22362306a36Sopenharmony_cistatic void faulty_status(struct seq_file *seq, struct mddev *mddev)
22462306a36Sopenharmony_ci{
22562306a36Sopenharmony_ci	struct faulty_conf *conf = mddev->private;
22662306a36Sopenharmony_ci	int n;
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[WriteTransient])) != 0)
22962306a36Sopenharmony_ci		seq_printf(seq, " WriteTransient=%d(%d)",
23062306a36Sopenharmony_ci			   n, conf->period[WriteTransient]);
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[ReadTransient])) != 0)
23362306a36Sopenharmony_ci		seq_printf(seq, " ReadTransient=%d(%d)",
23462306a36Sopenharmony_ci			   n, conf->period[ReadTransient]);
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[WritePersistent])) != 0)
23762306a36Sopenharmony_ci		seq_printf(seq, " WritePersistent=%d(%d)",
23862306a36Sopenharmony_ci			   n, conf->period[WritePersistent]);
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[ReadPersistent])) != 0)
24162306a36Sopenharmony_ci		seq_printf(seq, " ReadPersistent=%d(%d)",
24262306a36Sopenharmony_ci			   n, conf->period[ReadPersistent]);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci
24562306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[ReadFixable])) != 0)
24662306a36Sopenharmony_ci		seq_printf(seq, " ReadFixable=%d(%d)",
24762306a36Sopenharmony_ci			   n, conf->period[ReadFixable]);
24862306a36Sopenharmony_ci
24962306a36Sopenharmony_ci	if ((n=atomic_read(&conf->counters[WriteAll])) != 0)
25062306a36Sopenharmony_ci		seq_printf(seq, " WriteAll");
25162306a36Sopenharmony_ci
25262306a36Sopenharmony_ci	seq_printf(seq, " nfaults=%d", conf->nfaults);
25362306a36Sopenharmony_ci}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_cistatic int faulty_reshape(struct mddev *mddev)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	int mode = mddev->new_layout & ModeMask;
25962306a36Sopenharmony_ci	int count = mddev->new_layout >> ModeShift;
26062306a36Sopenharmony_ci	struct faulty_conf *conf = mddev->private;
26162306a36Sopenharmony_ci
26262306a36Sopenharmony_ci	if (mddev->new_layout < 0)
26362306a36Sopenharmony_ci		return 0;
26462306a36Sopenharmony_ci
26562306a36Sopenharmony_ci	/* new layout */
26662306a36Sopenharmony_ci	if (mode == ClearFaults)
26762306a36Sopenharmony_ci		conf->nfaults = 0;
26862306a36Sopenharmony_ci	else if (mode == ClearErrors) {
26962306a36Sopenharmony_ci		int i;
27062306a36Sopenharmony_ci		for (i=0 ; i < Modes ; i++) {
27162306a36Sopenharmony_ci			conf->period[i] = 0;
27262306a36Sopenharmony_ci			atomic_set(&conf->counters[i], 0);
27362306a36Sopenharmony_ci		}
27462306a36Sopenharmony_ci	} else if (mode < Modes) {
27562306a36Sopenharmony_ci		conf->period[mode] = count;
27662306a36Sopenharmony_ci		if (!count) count++;
27762306a36Sopenharmony_ci		atomic_set(&conf->counters[mode], count);
27862306a36Sopenharmony_ci	} else
27962306a36Sopenharmony_ci		return -EINVAL;
28062306a36Sopenharmony_ci	mddev->new_layout = -1;
28162306a36Sopenharmony_ci	mddev->layout = -1; /* makes sure further changes come through */
28262306a36Sopenharmony_ci	return 0;
28362306a36Sopenharmony_ci}
28462306a36Sopenharmony_ci
28562306a36Sopenharmony_cistatic sector_t faulty_size(struct mddev *mddev, sector_t sectors, int raid_disks)
28662306a36Sopenharmony_ci{
28762306a36Sopenharmony_ci	WARN_ONCE(raid_disks,
28862306a36Sopenharmony_ci		  "%s does not support generic reshape\n", __func__);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	if (sectors == 0)
29162306a36Sopenharmony_ci		return mddev->dev_sectors;
29262306a36Sopenharmony_ci
29362306a36Sopenharmony_ci	return sectors;
29462306a36Sopenharmony_ci}
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_cistatic int faulty_run(struct mddev *mddev)
29762306a36Sopenharmony_ci{
29862306a36Sopenharmony_ci	struct md_rdev *rdev;
29962306a36Sopenharmony_ci	int i;
30062306a36Sopenharmony_ci	struct faulty_conf *conf;
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci	if (md_check_no_bitmap(mddev))
30362306a36Sopenharmony_ci		return -EINVAL;
30462306a36Sopenharmony_ci
30562306a36Sopenharmony_ci	conf = kmalloc(sizeof(*conf), GFP_KERNEL);
30662306a36Sopenharmony_ci	if (!conf)
30762306a36Sopenharmony_ci		return -ENOMEM;
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	for (i=0; i<Modes; i++) {
31062306a36Sopenharmony_ci		atomic_set(&conf->counters[i], 0);
31162306a36Sopenharmony_ci		conf->period[i] = 0;
31262306a36Sopenharmony_ci	}
31362306a36Sopenharmony_ci	conf->nfaults = 0;
31462306a36Sopenharmony_ci
31562306a36Sopenharmony_ci	rdev_for_each(rdev, mddev) {
31662306a36Sopenharmony_ci		conf->rdev = rdev;
31762306a36Sopenharmony_ci		disk_stack_limits(mddev->gendisk, rdev->bdev,
31862306a36Sopenharmony_ci				  rdev->data_offset << 9);
31962306a36Sopenharmony_ci	}
32062306a36Sopenharmony_ci
32162306a36Sopenharmony_ci	md_set_array_sectors(mddev, faulty_size(mddev, 0, 0));
32262306a36Sopenharmony_ci	mddev->private = conf;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	faulty_reshape(mddev);
32562306a36Sopenharmony_ci
32662306a36Sopenharmony_ci	return 0;
32762306a36Sopenharmony_ci}
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_cistatic void faulty_free(struct mddev *mddev, void *priv)
33062306a36Sopenharmony_ci{
33162306a36Sopenharmony_ci	struct faulty_conf *conf = priv;
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci	kfree(conf);
33462306a36Sopenharmony_ci}
33562306a36Sopenharmony_ci
33662306a36Sopenharmony_cistatic struct md_personality faulty_personality =
33762306a36Sopenharmony_ci{
33862306a36Sopenharmony_ci	.name		= "faulty",
33962306a36Sopenharmony_ci	.level		= LEVEL_FAULTY,
34062306a36Sopenharmony_ci	.owner		= THIS_MODULE,
34162306a36Sopenharmony_ci	.make_request	= faulty_make_request,
34262306a36Sopenharmony_ci	.run		= faulty_run,
34362306a36Sopenharmony_ci	.free		= faulty_free,
34462306a36Sopenharmony_ci	.status		= faulty_status,
34562306a36Sopenharmony_ci	.check_reshape	= faulty_reshape,
34662306a36Sopenharmony_ci	.size		= faulty_size,
34762306a36Sopenharmony_ci};
34862306a36Sopenharmony_ci
34962306a36Sopenharmony_cistatic int __init raid_init(void)
35062306a36Sopenharmony_ci{
35162306a36Sopenharmony_ci	return register_md_personality(&faulty_personality);
35262306a36Sopenharmony_ci}
35362306a36Sopenharmony_ci
35462306a36Sopenharmony_cistatic void raid_exit(void)
35562306a36Sopenharmony_ci{
35662306a36Sopenharmony_ci	unregister_md_personality(&faulty_personality);
35762306a36Sopenharmony_ci}
35862306a36Sopenharmony_ci
35962306a36Sopenharmony_cimodule_init(raid_init);
36062306a36Sopenharmony_cimodule_exit(raid_exit);
36162306a36Sopenharmony_ciMODULE_LICENSE("GPL");
36262306a36Sopenharmony_ciMODULE_DESCRIPTION("Fault injection personality for MD (deprecated)");
36362306a36Sopenharmony_ciMODULE_ALIAS("md-personality-10"); /* faulty */
36462306a36Sopenharmony_ciMODULE_ALIAS("md-faulty");
36562306a36Sopenharmony_ciMODULE_ALIAS("md-level--5");
366