xref: /openbmc/linux/drivers/md/md-faulty.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
1af1a8899SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
2935fe098SMike Snitzer /*
3935fe098SMike Snitzer  * faulty.c : Multiple Devices driver for Linux
4935fe098SMike Snitzer  *
5935fe098SMike Snitzer  * Copyright (C) 2004 Neil Brown
6935fe098SMike Snitzer  *
7935fe098SMike Snitzer  * fautly-device-simulator personality for md
8935fe098SMike Snitzer  */
9935fe098SMike Snitzer 
10935fe098SMike Snitzer 
11935fe098SMike Snitzer /*
12935fe098SMike Snitzer  * The "faulty" personality causes some requests to fail.
13935fe098SMike Snitzer  *
14935fe098SMike Snitzer  * Possible failure modes are:
15935fe098SMike Snitzer  *   reads fail "randomly" but succeed on retry
16935fe098SMike Snitzer  *   writes fail "randomly" but succeed on retry
17935fe098SMike Snitzer  *   reads for some address fail and then persist until a write
18935fe098SMike Snitzer  *   reads for some address fail and then persist irrespective of write
19935fe098SMike Snitzer  *   writes for some address fail and persist
20935fe098SMike Snitzer  *   all writes fail
21935fe098SMike Snitzer  *
22935fe098SMike Snitzer  * Different modes can be active at a time, but only
23935fe098SMike Snitzer  * one can be set at array creation.  Others can be added later.
24935fe098SMike Snitzer  * A mode can be one-shot or recurrent with the recurrence being
25935fe098SMike Snitzer  * once in every N requests.
26935fe098SMike Snitzer  * The bottom 5 bits of the "layout" indicate the mode.  The
27935fe098SMike Snitzer  * remainder indicate a period, or 0 for one-shot.
28935fe098SMike Snitzer  *
29935fe098SMike Snitzer  * There is an implementation limit on the number of concurrently
30935fe098SMike Snitzer  * persisting-faulty blocks. When a new fault is requested that would
31935fe098SMike Snitzer  * exceed the limit, it is ignored.
32935fe098SMike Snitzer  * All current faults can be clear using a layout of "0".
33935fe098SMike Snitzer  *
34935fe098SMike Snitzer  * Requests are always sent to the device.  If they are to fail,
35935fe098SMike Snitzer  * we clone the bio and insert a new b_end_io into the chain.
36935fe098SMike Snitzer  */
37935fe098SMike Snitzer 
38935fe098SMike Snitzer #define	WriteTransient	0
39935fe098SMike Snitzer #define	ReadTransient	1
40935fe098SMike Snitzer #define	WritePersistent	2
41935fe098SMike Snitzer #define	ReadPersistent	3
42935fe098SMike Snitzer #define	WriteAll	4 /* doesn't go to device */
43935fe098SMike Snitzer #define	ReadFixable	5
44935fe098SMike Snitzer #define	Modes	6
45935fe098SMike Snitzer 
46935fe098SMike Snitzer #define	ClearErrors	31
47935fe098SMike Snitzer #define	ClearFaults	30
48935fe098SMike Snitzer 
49935fe098SMike Snitzer #define AllPersist	100 /* internal use only */
50935fe098SMike Snitzer #define	NoPersist	101
51935fe098SMike Snitzer 
52935fe098SMike Snitzer #define	ModeMask	0x1f
53935fe098SMike Snitzer #define	ModeShift	5
54935fe098SMike Snitzer 
55935fe098SMike Snitzer #define MaxFault	50
56935fe098SMike Snitzer #include <linux/blkdev.h>
57935fe098SMike Snitzer #include <linux/module.h>
58935fe098SMike Snitzer #include <linux/raid/md_u.h>
59935fe098SMike Snitzer #include <linux/slab.h>
60935fe098SMike Snitzer #include "md.h"
61935fe098SMike Snitzer #include <linux/seq_file.h>
62935fe098SMike Snitzer 
63935fe098SMike Snitzer 
faulty_fail(struct bio * bio)64935fe098SMike Snitzer static void faulty_fail(struct bio *bio)
65935fe098SMike Snitzer {
66935fe098SMike Snitzer 	struct bio *b = bio->bi_private;
67935fe098SMike Snitzer 
68935fe098SMike Snitzer 	b->bi_iter.bi_size = bio->bi_iter.bi_size;
69935fe098SMike Snitzer 	b->bi_iter.bi_sector = bio->bi_iter.bi_sector;
70935fe098SMike Snitzer 
71935fe098SMike Snitzer 	bio_put(bio);
72935fe098SMike Snitzer 
73935fe098SMike Snitzer 	bio_io_error(b);
74935fe098SMike Snitzer }
75935fe098SMike Snitzer 
76935fe098SMike Snitzer struct faulty_conf {
77935fe098SMike Snitzer 	int period[Modes];
78935fe098SMike Snitzer 	atomic_t counters[Modes];
79935fe098SMike Snitzer 	sector_t faults[MaxFault];
80935fe098SMike Snitzer 	int	modes[MaxFault];
81935fe098SMike Snitzer 	int nfaults;
82935fe098SMike Snitzer 	struct md_rdev *rdev;
83935fe098SMike Snitzer };
84935fe098SMike Snitzer 
check_mode(struct faulty_conf * conf,int mode)85935fe098SMike Snitzer static int check_mode(struct faulty_conf *conf, int mode)
86935fe098SMike Snitzer {
87935fe098SMike Snitzer 	if (conf->period[mode] == 0 &&
88935fe098SMike Snitzer 	    atomic_read(&conf->counters[mode]) <= 0)
89935fe098SMike Snitzer 		return 0; /* no failure, no decrement */
90935fe098SMike Snitzer 
91935fe098SMike Snitzer 
92935fe098SMike Snitzer 	if (atomic_dec_and_test(&conf->counters[mode])) {
93935fe098SMike Snitzer 		if (conf->period[mode])
94935fe098SMike Snitzer 			atomic_set(&conf->counters[mode], conf->period[mode]);
95935fe098SMike Snitzer 		return 1;
96935fe098SMike Snitzer 	}
97935fe098SMike Snitzer 	return 0;
98935fe098SMike Snitzer }
99935fe098SMike Snitzer 
check_sector(struct faulty_conf * conf,sector_t start,sector_t end,int dir)100935fe098SMike Snitzer static int check_sector(struct faulty_conf *conf, sector_t start, sector_t end, int dir)
101935fe098SMike Snitzer {
102935fe098SMike Snitzer 	/* If we find a ReadFixable sector, we fix it ... */
103935fe098SMike Snitzer 	int i;
104935fe098SMike Snitzer 	for (i=0; i<conf->nfaults; i++)
105935fe098SMike Snitzer 		if (conf->faults[i] >= start &&
106935fe098SMike Snitzer 		    conf->faults[i] < end) {
107935fe098SMike Snitzer 			/* found it ... */
108935fe098SMike Snitzer 			switch (conf->modes[i] * 2 + dir) {
109935fe098SMike Snitzer 			case WritePersistent*2+WRITE: return 1;
110935fe098SMike Snitzer 			case ReadPersistent*2+READ: return 1;
111935fe098SMike Snitzer 			case ReadFixable*2+READ: return 1;
112935fe098SMike Snitzer 			case ReadFixable*2+WRITE:
113935fe098SMike Snitzer 				conf->modes[i] = NoPersist;
114935fe098SMike Snitzer 				return 0;
115935fe098SMike Snitzer 			case AllPersist*2+READ:
116935fe098SMike Snitzer 			case AllPersist*2+WRITE: return 1;
117935fe098SMike Snitzer 			default:
118935fe098SMike Snitzer 				return 0;
119935fe098SMike Snitzer 			}
120935fe098SMike Snitzer 		}
121935fe098SMike Snitzer 	return 0;
122935fe098SMike Snitzer }
123935fe098SMike Snitzer 
add_sector(struct faulty_conf * conf,sector_t start,int mode)124935fe098SMike Snitzer static void add_sector(struct faulty_conf *conf, sector_t start, int mode)
125935fe098SMike Snitzer {
126935fe098SMike Snitzer 	int i;
127935fe098SMike Snitzer 	int n = conf->nfaults;
128935fe098SMike Snitzer 	for (i=0; i<conf->nfaults; i++)
129935fe098SMike Snitzer 		if (conf->faults[i] == start) {
130935fe098SMike Snitzer 			switch(mode) {
131935fe098SMike Snitzer 			case NoPersist: conf->modes[i] = mode; return;
132935fe098SMike Snitzer 			case WritePersistent:
133935fe098SMike Snitzer 				if (conf->modes[i] == ReadPersistent ||
134935fe098SMike Snitzer 				    conf->modes[i] == ReadFixable)
135935fe098SMike Snitzer 					conf->modes[i] = AllPersist;
136935fe098SMike Snitzer 				else
137935fe098SMike Snitzer 					conf->modes[i] = WritePersistent;
138935fe098SMike Snitzer 				return;
139935fe098SMike Snitzer 			case ReadPersistent:
140935fe098SMike Snitzer 				if (conf->modes[i] == WritePersistent)
141935fe098SMike Snitzer 					conf->modes[i] = AllPersist;
142935fe098SMike Snitzer 				else
143935fe098SMike Snitzer 					conf->modes[i] = ReadPersistent;
144935fe098SMike Snitzer 				return;
145935fe098SMike Snitzer 			case ReadFixable:
146935fe098SMike Snitzer 				if (conf->modes[i] == WritePersistent ||
147935fe098SMike Snitzer 				    conf->modes[i] == ReadPersistent)
148935fe098SMike Snitzer 					conf->modes[i] = AllPersist;
149935fe098SMike Snitzer 				else
150935fe098SMike Snitzer 					conf->modes[i] = ReadFixable;
151935fe098SMike Snitzer 				return;
152935fe098SMike Snitzer 			}
153935fe098SMike Snitzer 		} else if (conf->modes[i] == NoPersist)
154935fe098SMike Snitzer 			n = i;
155935fe098SMike Snitzer 
156935fe098SMike Snitzer 	if (n >= MaxFault)
157935fe098SMike Snitzer 		return;
158935fe098SMike Snitzer 	conf->faults[n] = start;
159935fe098SMike Snitzer 	conf->modes[n] = mode;
160935fe098SMike Snitzer 	if (conf->nfaults == n)
161935fe098SMike Snitzer 		conf->nfaults = n+1;
162935fe098SMike Snitzer }
163935fe098SMike Snitzer 
faulty_make_request(struct mddev * mddev,struct bio * bio)164935fe098SMike Snitzer static bool faulty_make_request(struct mddev *mddev, struct bio *bio)
165935fe098SMike Snitzer {
166935fe098SMike Snitzer 	struct faulty_conf *conf = mddev->private;
167935fe098SMike Snitzer 	int failit = 0;
168935fe098SMike Snitzer 
169935fe098SMike Snitzer 	if (bio_data_dir(bio) == WRITE) {
170935fe098SMike Snitzer 		/* write request */
171935fe098SMike Snitzer 		if (atomic_read(&conf->counters[WriteAll])) {
172ed00aabdSChristoph Hellwig 			/* special case - don't decrement, don't submit_bio_noacct,
173935fe098SMike Snitzer 			 * just fail immediately
174935fe098SMike Snitzer 			 */
175935fe098SMike Snitzer 			bio_io_error(bio);
176935fe098SMike Snitzer 			return true;
177935fe098SMike Snitzer 		}
178935fe098SMike Snitzer 
179935fe098SMike Snitzer 		if (check_sector(conf, bio->bi_iter.bi_sector,
180935fe098SMike Snitzer 				 bio_end_sector(bio), WRITE))
181935fe098SMike Snitzer 			failit = 1;
182935fe098SMike Snitzer 		if (check_mode(conf, WritePersistent)) {
183935fe098SMike Snitzer 			add_sector(conf, bio->bi_iter.bi_sector,
184935fe098SMike Snitzer 				   WritePersistent);
185935fe098SMike Snitzer 			failit = 1;
186935fe098SMike Snitzer 		}
187935fe098SMike Snitzer 		if (check_mode(conf, WriteTransient))
188935fe098SMike Snitzer 			failit = 1;
189935fe098SMike Snitzer 	} else {
190935fe098SMike Snitzer 		/* read request */
191935fe098SMike Snitzer 		if (check_sector(conf, bio->bi_iter.bi_sector,
192935fe098SMike Snitzer 				 bio_end_sector(bio), READ))
193935fe098SMike Snitzer 			failit = 1;
194935fe098SMike Snitzer 		if (check_mode(conf, ReadTransient))
195935fe098SMike Snitzer 			failit = 1;
196935fe098SMike Snitzer 		if (check_mode(conf, ReadPersistent)) {
197935fe098SMike Snitzer 			add_sector(conf, bio->bi_iter.bi_sector,
198935fe098SMike Snitzer 				   ReadPersistent);
199935fe098SMike Snitzer 			failit = 1;
200935fe098SMike Snitzer 		}
201935fe098SMike Snitzer 		if (check_mode(conf, ReadFixable)) {
202935fe098SMike Snitzer 			add_sector(conf, bio->bi_iter.bi_sector,
203935fe098SMike Snitzer 				   ReadFixable);
204935fe098SMike Snitzer 			failit = 1;
205935fe098SMike Snitzer 		}
206935fe098SMike Snitzer 	}
207*dd9a6860SYu Kuai 
208*dd9a6860SYu Kuai 	md_account_bio(mddev, &bio);
209935fe098SMike Snitzer 	if (failit) {
210abfc426dSChristoph Hellwig 		struct bio *b = bio_alloc_clone(conf->rdev->bdev, bio, GFP_NOIO,
211abfc426dSChristoph Hellwig 						&mddev->bio_set);
212935fe098SMike Snitzer 
213935fe098SMike Snitzer 		b->bi_private = bio;
214935fe098SMike Snitzer 		b->bi_end_io = faulty_fail;
215935fe098SMike Snitzer 		bio = b;
216935fe098SMike Snitzer 	} else
217935fe098SMike Snitzer 		bio_set_dev(bio, conf->rdev->bdev);
218935fe098SMike Snitzer 
219ed00aabdSChristoph Hellwig 	submit_bio_noacct(bio);
220935fe098SMike Snitzer 	return true;
221935fe098SMike Snitzer }
222935fe098SMike Snitzer 
faulty_status(struct seq_file * seq,struct mddev * mddev)223935fe098SMike Snitzer static void faulty_status(struct seq_file *seq, struct mddev *mddev)
224935fe098SMike Snitzer {
225935fe098SMike Snitzer 	struct faulty_conf *conf = mddev->private;
226935fe098SMike Snitzer 	int n;
227935fe098SMike Snitzer 
228935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[WriteTransient])) != 0)
229935fe098SMike Snitzer 		seq_printf(seq, " WriteTransient=%d(%d)",
230935fe098SMike Snitzer 			   n, conf->period[WriteTransient]);
231935fe098SMike Snitzer 
232935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[ReadTransient])) != 0)
233935fe098SMike Snitzer 		seq_printf(seq, " ReadTransient=%d(%d)",
234935fe098SMike Snitzer 			   n, conf->period[ReadTransient]);
235935fe098SMike Snitzer 
236935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[WritePersistent])) != 0)
237935fe098SMike Snitzer 		seq_printf(seq, " WritePersistent=%d(%d)",
238935fe098SMike Snitzer 			   n, conf->period[WritePersistent]);
239935fe098SMike Snitzer 
240935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[ReadPersistent])) != 0)
241935fe098SMike Snitzer 		seq_printf(seq, " ReadPersistent=%d(%d)",
242935fe098SMike Snitzer 			   n, conf->period[ReadPersistent]);
243935fe098SMike Snitzer 
244935fe098SMike Snitzer 
245935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[ReadFixable])) != 0)
246935fe098SMike Snitzer 		seq_printf(seq, " ReadFixable=%d(%d)",
247935fe098SMike Snitzer 			   n, conf->period[ReadFixable]);
248935fe098SMike Snitzer 
249935fe098SMike Snitzer 	if ((n=atomic_read(&conf->counters[WriteAll])) != 0)
250935fe098SMike Snitzer 		seq_printf(seq, " WriteAll");
251935fe098SMike Snitzer 
252935fe098SMike Snitzer 	seq_printf(seq, " nfaults=%d", conf->nfaults);
253935fe098SMike Snitzer }
254935fe098SMike Snitzer 
255935fe098SMike Snitzer 
faulty_reshape(struct mddev * mddev)256935fe098SMike Snitzer static int faulty_reshape(struct mddev *mddev)
257935fe098SMike Snitzer {
258935fe098SMike Snitzer 	int mode = mddev->new_layout & ModeMask;
259935fe098SMike Snitzer 	int count = mddev->new_layout >> ModeShift;
260935fe098SMike Snitzer 	struct faulty_conf *conf = mddev->private;
261935fe098SMike Snitzer 
262935fe098SMike Snitzer 	if (mddev->new_layout < 0)
263935fe098SMike Snitzer 		return 0;
264935fe098SMike Snitzer 
265935fe098SMike Snitzer 	/* new layout */
266935fe098SMike Snitzer 	if (mode == ClearFaults)
267935fe098SMike Snitzer 		conf->nfaults = 0;
268935fe098SMike Snitzer 	else if (mode == ClearErrors) {
269935fe098SMike Snitzer 		int i;
270935fe098SMike Snitzer 		for (i=0 ; i < Modes ; i++) {
271935fe098SMike Snitzer 			conf->period[i] = 0;
272935fe098SMike Snitzer 			atomic_set(&conf->counters[i], 0);
273935fe098SMike Snitzer 		}
274935fe098SMike Snitzer 	} else if (mode < Modes) {
275935fe098SMike Snitzer 		conf->period[mode] = count;
276935fe098SMike Snitzer 		if (!count) count++;
277935fe098SMike Snitzer 		atomic_set(&conf->counters[mode], count);
278935fe098SMike Snitzer 	} else
279935fe098SMike Snitzer 		return -EINVAL;
280935fe098SMike Snitzer 	mddev->new_layout = -1;
281935fe098SMike Snitzer 	mddev->layout = -1; /* makes sure further changes come through */
282935fe098SMike Snitzer 	return 0;
283935fe098SMike Snitzer }
284935fe098SMike Snitzer 
faulty_size(struct mddev * mddev,sector_t sectors,int raid_disks)285935fe098SMike Snitzer static sector_t faulty_size(struct mddev *mddev, sector_t sectors, int raid_disks)
286935fe098SMike Snitzer {
287935fe098SMike Snitzer 	WARN_ONCE(raid_disks,
288935fe098SMike Snitzer 		  "%s does not support generic reshape\n", __func__);
289935fe098SMike Snitzer 
290935fe098SMike Snitzer 	if (sectors == 0)
291935fe098SMike Snitzer 		return mddev->dev_sectors;
292935fe098SMike Snitzer 
293935fe098SMike Snitzer 	return sectors;
294935fe098SMike Snitzer }
295935fe098SMike Snitzer 
faulty_run(struct mddev * mddev)296935fe098SMike Snitzer static int faulty_run(struct mddev *mddev)
297935fe098SMike Snitzer {
298935fe098SMike Snitzer 	struct md_rdev *rdev;
299935fe098SMike Snitzer 	int i;
300935fe098SMike Snitzer 	struct faulty_conf *conf;
301935fe098SMike Snitzer 
302935fe098SMike Snitzer 	if (md_check_no_bitmap(mddev))
303935fe098SMike Snitzer 		return -EINVAL;
304935fe098SMike Snitzer 
305935fe098SMike Snitzer 	conf = kmalloc(sizeof(*conf), GFP_KERNEL);
306935fe098SMike Snitzer 	if (!conf)
307935fe098SMike Snitzer 		return -ENOMEM;
308935fe098SMike Snitzer 
309935fe098SMike Snitzer 	for (i=0; i<Modes; i++) {
310935fe098SMike Snitzer 		atomic_set(&conf->counters[i], 0);
311935fe098SMike Snitzer 		conf->period[i] = 0;
312935fe098SMike Snitzer 	}
313935fe098SMike Snitzer 	conf->nfaults = 0;
314935fe098SMike Snitzer 
315935fe098SMike Snitzer 	rdev_for_each(rdev, mddev) {
316935fe098SMike Snitzer 		conf->rdev = rdev;
317935fe098SMike Snitzer 		disk_stack_limits(mddev->gendisk, rdev->bdev,
318935fe098SMike Snitzer 				  rdev->data_offset << 9);
319935fe098SMike Snitzer 	}
320935fe098SMike Snitzer 
321935fe098SMike Snitzer 	md_set_array_sectors(mddev, faulty_size(mddev, 0, 0));
322935fe098SMike Snitzer 	mddev->private = conf;
323935fe098SMike Snitzer 
324935fe098SMike Snitzer 	faulty_reshape(mddev);
325935fe098SMike Snitzer 
326935fe098SMike Snitzer 	return 0;
327935fe098SMike Snitzer }
328935fe098SMike Snitzer 
faulty_free(struct mddev * mddev,void * priv)329935fe098SMike Snitzer static void faulty_free(struct mddev *mddev, void *priv)
330935fe098SMike Snitzer {
331935fe098SMike Snitzer 	struct faulty_conf *conf = priv;
332935fe098SMike Snitzer 
333935fe098SMike Snitzer 	kfree(conf);
334935fe098SMike Snitzer }
335935fe098SMike Snitzer 
336935fe098SMike Snitzer static struct md_personality faulty_personality =
337935fe098SMike Snitzer {
338935fe098SMike Snitzer 	.name		= "faulty",
339935fe098SMike Snitzer 	.level		= LEVEL_FAULTY,
340935fe098SMike Snitzer 	.owner		= THIS_MODULE,
341935fe098SMike Snitzer 	.make_request	= faulty_make_request,
342935fe098SMike Snitzer 	.run		= faulty_run,
343935fe098SMike Snitzer 	.free		= faulty_free,
344935fe098SMike Snitzer 	.status		= faulty_status,
345935fe098SMike Snitzer 	.check_reshape	= faulty_reshape,
346935fe098SMike Snitzer 	.size		= faulty_size,
347935fe098SMike Snitzer };
348935fe098SMike Snitzer 
raid_init(void)349935fe098SMike Snitzer static int __init raid_init(void)
350935fe098SMike Snitzer {
351935fe098SMike Snitzer 	return register_md_personality(&faulty_personality);
352935fe098SMike Snitzer }
353935fe098SMike Snitzer 
raid_exit(void)354935fe098SMike Snitzer static void raid_exit(void)
355935fe098SMike Snitzer {
356935fe098SMike Snitzer 	unregister_md_personality(&faulty_personality);
357935fe098SMike Snitzer }
358935fe098SMike Snitzer 
359935fe098SMike Snitzer module_init(raid_init);
360935fe098SMike Snitzer module_exit(raid_exit);
361935fe098SMike Snitzer MODULE_LICENSE("GPL");
362608f52e3SGuoqing Jiang MODULE_DESCRIPTION("Fault injection personality for MD (deprecated)");
363935fe098SMike Snitzer MODULE_ALIAS("md-personality-10"); /* faulty */
364935fe098SMike Snitzer MODULE_ALIAS("md-faulty");
365935fe098SMike Snitzer MODULE_ALIAS("md-level--5");
366