xref: /openbmc/linux/drivers/md/dm-flakey.c (revision 1d9a943898533e83f20370c0e1448d606627522e)
1 // SPDX-License-Identifier: GPL-2.0-only
2 /*
3  * Copyright (C) 2003 Sistina Software (UK) Limited.
4  * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
5  *
6  * This file is released under the GPL.
7  */
8 
9 #include <linux/device-mapper.h>
10 
11 #include <linux/module.h>
12 #include <linux/init.h>
13 #include <linux/blkdev.h>
14 #include <linux/bio.h>
15 #include <linux/slab.h>
16 
17 #define DM_MSG_PREFIX "flakey"
18 
19 #define all_corrupt_bio_flags_match(bio, fc)	\
20 	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
21 
22 /*
23  * Flakey: Used for testing only, simulates intermittent,
24  * catastrophic device failure.
25  */
26 struct flakey_c {
27 	struct dm_dev *dev;
28 	unsigned long start_time;
29 	sector_t start;
30 	unsigned int up_interval;
31 	unsigned int down_interval;
32 	unsigned long flags;
33 	unsigned int corrupt_bio_byte;
34 	unsigned int corrupt_bio_rw;
35 	unsigned int corrupt_bio_value;
36 	blk_opf_t corrupt_bio_flags;
37 };
38 
39 enum feature_flag_bits {
40 	ERROR_READS,
41 	DROP_WRITES,
42 	ERROR_WRITES
43 };
44 
45 struct per_bio_data {
46 	bool bio_submitted;
47 };
48 
49 static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
50 			  struct dm_target *ti)
51 {
52 	int r;
53 	unsigned int argc;
54 	const char *arg_name;
55 
56 	static const struct dm_arg _args[] = {
57 		{0, 7, "Invalid number of feature args"},
58 		{1, UINT_MAX, "Invalid corrupt bio byte"},
59 		{0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
60 		{0, UINT_MAX, "Invalid corrupt bio flags mask"},
61 	};
62 
63 	/* No feature arguments supplied. */
64 	if (!as->argc)
65 		return 0;
66 
67 	r = dm_read_arg_group(_args, as, &argc, &ti->error);
68 	if (r)
69 		return r;
70 
71 	while (argc) {
72 		arg_name = dm_shift_arg(as);
73 		argc--;
74 
75 		if (!arg_name) {
76 			ti->error = "Insufficient feature arguments";
77 			return -EINVAL;
78 		}
79 
80 		/*
81 		 * error_reads
82 		 */
83 		if (!strcasecmp(arg_name, "error_reads")) {
84 			if (test_and_set_bit(ERROR_READS, &fc->flags)) {
85 				ti->error = "Feature error_reads duplicated";
86 				return -EINVAL;
87 			}
88 			continue;
89 		}
90 
91 		/*
92 		 * drop_writes
93 		 */
94 		if (!strcasecmp(arg_name, "drop_writes")) {
95 			if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
96 				ti->error = "Feature drop_writes duplicated";
97 				return -EINVAL;
98 			} else if (test_bit(ERROR_WRITES, &fc->flags)) {
99 				ti->error = "Feature drop_writes conflicts with feature error_writes";
100 				return -EINVAL;
101 			}
102 
103 			continue;
104 		}
105 
106 		/*
107 		 * error_writes
108 		 */
109 		if (!strcasecmp(arg_name, "error_writes")) {
110 			if (test_and_set_bit(ERROR_WRITES, &fc->flags)) {
111 				ti->error = "Feature error_writes duplicated";
112 				return -EINVAL;
113 
114 			} else if (test_bit(DROP_WRITES, &fc->flags)) {
115 				ti->error = "Feature error_writes conflicts with feature drop_writes";
116 				return -EINVAL;
117 			}
118 
119 			continue;
120 		}
121 
122 		/*
123 		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
124 		 */
125 		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
126 			if (!argc) {
127 				ti->error = "Feature corrupt_bio_byte requires parameters";
128 				return -EINVAL;
129 			}
130 
131 			r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
132 			if (r)
133 				return r;
134 			argc--;
135 
136 			/*
137 			 * Direction r or w?
138 			 */
139 			arg_name = dm_shift_arg(as);
140 			if (arg_name && !strcasecmp(arg_name, "w"))
141 				fc->corrupt_bio_rw = WRITE;
142 			else if (arg_name && !strcasecmp(arg_name, "r"))
143 				fc->corrupt_bio_rw = READ;
144 			else {
145 				ti->error = "Invalid corrupt bio direction (r or w)";
146 				return -EINVAL;
147 			}
148 			argc--;
149 
150 			/*
151 			 * Value of byte (0-255) to write in place of correct one.
152 			 */
153 			r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
154 			if (r)
155 				return r;
156 			argc--;
157 
158 			/*
159 			 * Only corrupt bios with these flags set.
160 			 */
161 			BUILD_BUG_ON(sizeof(fc->corrupt_bio_flags) !=
162 				     sizeof(unsigned int));
163 			r = dm_read_arg(_args + 3, as,
164 				(__force unsigned int *)&fc->corrupt_bio_flags,
165 				&ti->error);
166 			if (r)
167 				return r;
168 			argc--;
169 
170 			continue;
171 		}
172 
173 		ti->error = "Unrecognised flakey feature requested";
174 		return -EINVAL;
175 	}
176 
177 	if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
178 		ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
179 		return -EINVAL;
180 
181 	} else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
182 		ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
183 		return -EINVAL;
184 	}
185 
186 	if (!fc->corrupt_bio_byte && !test_bit(ERROR_READS, &fc->flags) &&
187 	    !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags)) {
188 		set_bit(ERROR_WRITES, &fc->flags);
189 		set_bit(ERROR_READS, &fc->flags);
190 	}
191 
192 	return 0;
193 }
194 
195 /*
196  * Construct a flakey mapping:
197  * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
198  *
199  *   Feature args:
200  *     [drop_writes]
201  *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
202  *
203  *   Nth_byte starts from 1 for the first byte.
204  *   Direction is r for READ or w for WRITE.
205  *   bio_flags is ignored if 0.
206  */
207 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
208 {
209 	static const struct dm_arg _args[] = {
210 		{0, UINT_MAX, "Invalid up interval"},
211 		{0, UINT_MAX, "Invalid down interval"},
212 	};
213 
214 	int r;
215 	struct flakey_c *fc;
216 	unsigned long long tmpll;
217 	struct dm_arg_set as;
218 	const char *devname;
219 	char dummy;
220 
221 	as.argc = argc;
222 	as.argv = argv;
223 
224 	if (argc < 4) {
225 		ti->error = "Invalid argument count";
226 		return -EINVAL;
227 	}
228 
229 	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
230 	if (!fc) {
231 		ti->error = "Cannot allocate context";
232 		return -ENOMEM;
233 	}
234 	fc->start_time = jiffies;
235 
236 	devname = dm_shift_arg(&as);
237 
238 	r = -EINVAL;
239 	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) {
240 		ti->error = "Invalid device sector";
241 		goto bad;
242 	}
243 	fc->start = tmpll;
244 
245 	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
246 	if (r)
247 		goto bad;
248 
249 	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
250 	if (r)
251 		goto bad;
252 
253 	if (!(fc->up_interval + fc->down_interval)) {
254 		ti->error = "Total (up + down) interval is zero";
255 		r = -EINVAL;
256 		goto bad;
257 	}
258 
259 	if (fc->up_interval + fc->down_interval < fc->up_interval) {
260 		ti->error = "Interval overflow";
261 		r = -EINVAL;
262 		goto bad;
263 	}
264 
265 	r = parse_features(&as, fc, ti);
266 	if (r)
267 		goto bad;
268 
269 	r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);
270 	if (r) {
271 		ti->error = "Device lookup failed";
272 		goto bad;
273 	}
274 
275 	ti->num_flush_bios = 1;
276 	ti->num_discard_bios = 1;
277 	ti->per_io_data_size = sizeof(struct per_bio_data);
278 	ti->private = fc;
279 	return 0;
280 
281 bad:
282 	kfree(fc);
283 	return r;
284 }
285 
286 static void flakey_dtr(struct dm_target *ti)
287 {
288 	struct flakey_c *fc = ti->private;
289 
290 	dm_put_device(ti, fc->dev);
291 	kfree(fc);
292 }
293 
294 static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
295 {
296 	struct flakey_c *fc = ti->private;
297 
298 	return fc->start + dm_target_offset(ti, bi_sector);
299 }
300 
301 static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
302 {
303 	struct flakey_c *fc = ti->private;
304 
305 	bio_set_dev(bio, fc->dev->bdev);
306 	bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
307 }
308 
309 static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
310 {
311 	unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
312 
313 	struct bvec_iter iter;
314 	struct bio_vec bvec;
315 
316 	if (!bio_has_data(bio))
317 		return;
318 
319 	/*
320 	 * Overwrite the Nth byte of the bio's data, on whichever page
321 	 * it falls.
322 	 */
323 	bio_for_each_segment(bvec, bio, iter) {
324 		if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
325 			char *segment = bvec_kmap_local(&bvec);
326 			segment[corrupt_bio_byte] = fc->corrupt_bio_value;
327 			kunmap_local(segment);
328 			DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
329 				"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
330 				bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
331 				(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
332 				(unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size);
333 			break;
334 		}
335 		corrupt_bio_byte -= bio_iter_len(bio, iter);
336 	}
337 }
338 
339 static void clone_free(struct bio *clone)
340 {
341 	struct folio_iter fi;
342 
343 	if (clone->bi_vcnt > 0) { /* bio_for_each_folio_all crashes with an empty bio */
344 		bio_for_each_folio_all(fi, clone)
345 			folio_put(fi.folio);
346 	}
347 
348 	bio_uninit(clone);
349 	kfree(clone);
350 }
351 
352 static void clone_endio(struct bio *clone)
353 {
354 	struct bio *bio = clone->bi_private;
355 	bio->bi_status = clone->bi_status;
356 	clone_free(clone);
357 	bio_endio(bio);
358 }
359 
360 static struct bio *clone_bio(struct dm_target *ti, struct flakey_c *fc, struct bio *bio)
361 {
362 	struct bio *clone;
363 	unsigned size, remaining_size, nr_iovecs, order;
364 	struct bvec_iter iter = bio->bi_iter;
365 
366 	if (unlikely(bio->bi_iter.bi_size > UIO_MAXIOV << PAGE_SHIFT))
367 		dm_accept_partial_bio(bio, UIO_MAXIOV << PAGE_SHIFT >> SECTOR_SHIFT);
368 
369 	size = bio->bi_iter.bi_size;
370 	nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;
371 
372 	clone = bio_kmalloc(nr_iovecs, GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);
373 	if (!clone)
374 		return NULL;
375 
376 	bio_init(clone, fc->dev->bdev, bio->bi_inline_vecs, nr_iovecs, bio->bi_opf);
377 
378 	clone->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
379 	clone->bi_private = bio;
380 	clone->bi_end_io = clone_endio;
381 
382 	remaining_size = size;
383 
384 	order = MAX_ORDER - 1;
385 	while (remaining_size) {
386 		struct page *pages;
387 		unsigned size_to_add, to_copy;
388 		unsigned char *virt;
389 		unsigned remaining_order = __fls((remaining_size + PAGE_SIZE - 1) >> PAGE_SHIFT);
390 		order = min(order, remaining_order);
391 
392 retry_alloc_pages:
393 		pages = alloc_pages(GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | __GFP_COMP, order);
394 		if (unlikely(!pages)) {
395 			if (order) {
396 				order--;
397 				goto retry_alloc_pages;
398 			}
399 			clone_free(clone);
400 			return NULL;
401 		}
402 		size_to_add = min((unsigned)PAGE_SIZE << order, remaining_size);
403 
404 		virt = page_to_virt(pages);
405 		to_copy = size_to_add;
406 		do {
407 			struct bio_vec bvec = bvec_iter_bvec(bio->bi_io_vec, iter);
408 			unsigned this_step = min(bvec.bv_len, to_copy);
409 			void *map = bvec_kmap_local(&bvec);
410 			memcpy(virt, map, this_step);
411 			kunmap_local(map);
412 
413 			bvec_iter_advance(bio->bi_io_vec, &iter, this_step);
414 			to_copy -= this_step;
415 			virt += this_step;
416 		} while (to_copy);
417 
418 		__bio_add_page(clone, pages, size_to_add, 0);
419 		remaining_size -= size_to_add;
420 	}
421 
422 	return clone;
423 }
424 
425 static int flakey_map(struct dm_target *ti, struct bio *bio)
426 {
427 	struct flakey_c *fc = ti->private;
428 	unsigned int elapsed;
429 	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
430 
431 	pb->bio_submitted = false;
432 
433 	if (op_is_zone_mgmt(bio_op(bio)))
434 		goto map_bio;
435 
436 	/* Are we alive ? */
437 	elapsed = (jiffies - fc->start_time) / HZ;
438 	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
439 		/*
440 		 * Flag this bio as submitted while down.
441 		 */
442 		pb->bio_submitted = true;
443 
444 		/*
445 		 * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
446 		 * Otherwise, flakey_end_io() will decide if the reads should be modified.
447 		 */
448 		if (bio_data_dir(bio) == READ) {
449 			if (test_bit(ERROR_READS, &fc->flags))
450 				return DM_MAPIO_KILL;
451 			goto map_bio;
452 		}
453 
454 		/*
455 		 * Drop or error writes?
456 		 */
457 		if (test_bit(DROP_WRITES, &fc->flags)) {
458 			bio_endio(bio);
459 			return DM_MAPIO_SUBMITTED;
460 		} else if (test_bit(ERROR_WRITES, &fc->flags)) {
461 			bio_io_error(bio);
462 			return DM_MAPIO_SUBMITTED;
463 		}
464 
465 		/*
466 		 * Corrupt matching writes.
467 		 */
468 		if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) {
469 			if (all_corrupt_bio_flags_match(bio, fc)) {
470 				struct bio *clone = clone_bio(ti, fc, bio);
471 				if (clone) {
472 					corrupt_bio_data(clone, fc);
473 					submit_bio(clone);
474 					return DM_MAPIO_SUBMITTED;
475 				}
476 			}
477 			goto map_bio;
478 		}
479 	}
480 
481 map_bio:
482 	flakey_map_bio(ti, bio);
483 
484 	return DM_MAPIO_REMAPPED;
485 }
486 
487 static int flakey_end_io(struct dm_target *ti, struct bio *bio,
488 			 blk_status_t *error)
489 {
490 	struct flakey_c *fc = ti->private;
491 	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
492 
493 	if (op_is_zone_mgmt(bio_op(bio)))
494 		return DM_ENDIO_DONE;
495 
496 	if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
497 		if (fc->corrupt_bio_byte) {
498 			if ((fc->corrupt_bio_rw == READ) &&
499 			    all_corrupt_bio_flags_match(bio, fc)) {
500 				/*
501 				 * Corrupt successful matching READs while in down state.
502 				 */
503 				corrupt_bio_data(bio, fc);
504 			}
505 		}
506 		if (test_bit(ERROR_READS, &fc->flags)) {
507 			/*
508 			 * Error read during the down_interval if drop_writes
509 			 * and error_writes were not configured.
510 			 */
511 			*error = BLK_STS_IOERR;
512 		}
513 	}
514 
515 	return DM_ENDIO_DONE;
516 }
517 
518 static void flakey_status(struct dm_target *ti, status_type_t type,
519 			  unsigned int status_flags, char *result, unsigned int maxlen)
520 {
521 	unsigned int sz = 0;
522 	struct flakey_c *fc = ti->private;
523 	unsigned int error_reads, drop_writes, error_writes;
524 
525 	switch (type) {
526 	case STATUSTYPE_INFO:
527 		result[0] = '\0';
528 		break;
529 
530 	case STATUSTYPE_TABLE:
531 		DMEMIT("%s %llu %u %u", fc->dev->name,
532 		       (unsigned long long)fc->start, fc->up_interval,
533 		       fc->down_interval);
534 
535 		error_reads = test_bit(ERROR_READS, &fc->flags);
536 		drop_writes = test_bit(DROP_WRITES, &fc->flags);
537 		error_writes = test_bit(ERROR_WRITES, &fc->flags);
538 		DMEMIT(" %u", error_reads + drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
539 
540 		if (error_reads)
541 			DMEMIT(" error_reads");
542 		if (drop_writes)
543 			DMEMIT(" drop_writes");
544 		else if (error_writes)
545 			DMEMIT(" error_writes");
546 
547 		if (fc->corrupt_bio_byte)
548 			DMEMIT(" corrupt_bio_byte %u %c %u %u",
549 			       fc->corrupt_bio_byte,
550 			       (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
551 			       fc->corrupt_bio_value, fc->corrupt_bio_flags);
552 
553 		break;
554 
555 	case STATUSTYPE_IMA:
556 		result[0] = '\0';
557 		break;
558 	}
559 }
560 
561 static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
562 {
563 	struct flakey_c *fc = ti->private;
564 
565 	*bdev = fc->dev->bdev;
566 
567 	/*
568 	 * Only pass ioctls through if the device sizes match exactly.
569 	 */
570 	if (fc->start || ti->len != bdev_nr_sectors((*bdev)))
571 		return 1;
572 	return 0;
573 }
574 
575 #ifdef CONFIG_BLK_DEV_ZONED
576 static int flakey_report_zones(struct dm_target *ti,
577 		struct dm_report_zones_args *args, unsigned int nr_zones)
578 {
579 	struct flakey_c *fc = ti->private;
580 
581 	return dm_report_zones(fc->dev->bdev, fc->start,
582 			       flakey_map_sector(ti, args->next_sector),
583 			       args, nr_zones);
584 }
585 #else
586 #define flakey_report_zones NULL
587 #endif
588 
589 static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
590 {
591 	struct flakey_c *fc = ti->private;
592 
593 	return fn(ti, fc->dev, fc->start, ti->len, data);
594 }
595 
596 static struct target_type flakey_target = {
597 	.name   = "flakey",
598 	.version = {1, 5, 0},
599 	.features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,
600 	.report_zones = flakey_report_zones,
601 	.module = THIS_MODULE,
602 	.ctr    = flakey_ctr,
603 	.dtr    = flakey_dtr,
604 	.map    = flakey_map,
605 	.end_io = flakey_end_io,
606 	.status = flakey_status,
607 	.prepare_ioctl = flakey_prepare_ioctl,
608 	.iterate_devices = flakey_iterate_devices,
609 };
610 module_dm(flakey);
611 
612 MODULE_DESCRIPTION(DM_NAME " flakey target");
613 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
614 MODULE_LICENSE("GPL");
615