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