Merge tag 'for-6.3-rc1-tag' of git://git.kernel.org/pub/scm/linux/kernel/git/kdave...
[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 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         DROP_WRITES,
41         ERROR_WRITES
42 };
43
44 struct per_bio_data {
45         bool bio_submitted;
46 };
47
48 static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
49                           struct dm_target *ti)
50 {
51         int r;
52         unsigned int argc;
53         const char *arg_name;
54
55         static const struct dm_arg _args[] = {
56                 {0, 6, "Invalid number of feature args"},
57                 {1, UINT_MAX, "Invalid corrupt bio byte"},
58                 {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
59                 {0, UINT_MAX, "Invalid corrupt bio flags mask"},
60         };
61
62         /* No feature arguments supplied. */
63         if (!as->argc)
64                 return 0;
65
66         r = dm_read_arg_group(_args, as, &argc, &ti->error);
67         if (r)
68                 return r;
69
70         while (argc) {
71                 arg_name = dm_shift_arg(as);
72                 argc--;
73
74                 if (!arg_name) {
75                         ti->error = "Insufficient feature arguments";
76                         return -EINVAL;
77                 }
78
79                 /*
80                  * drop_writes
81                  */
82                 if (!strcasecmp(arg_name, "drop_writes")) {
83                         if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
84                                 ti->error = "Feature drop_writes duplicated";
85                                 return -EINVAL;
86                         } else if (test_bit(ERROR_WRITES, &fc->flags)) {
87                                 ti->error = "Feature drop_writes conflicts with feature error_writes";
88                                 return -EINVAL;
89                         }
90
91                         continue;
92                 }
93
94                 /*
95                  * error_writes
96                  */
97                 if (!strcasecmp(arg_name, "error_writes")) {
98                         if (test_and_set_bit(ERROR_WRITES, &fc->flags)) {
99                                 ti->error = "Feature error_writes duplicated";
100                                 return -EINVAL;
101
102                         } else if (test_bit(DROP_WRITES, &fc->flags)) {
103                                 ti->error = "Feature error_writes conflicts with feature drop_writes";
104                                 return -EINVAL;
105                         }
106
107                         continue;
108                 }
109
110                 /*
111                  * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
112                  */
113                 if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
114                         if (!argc) {
115                                 ti->error = "Feature corrupt_bio_byte requires parameters";
116                                 return -EINVAL;
117                         }
118
119                         r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
120                         if (r)
121                                 return r;
122                         argc--;
123
124                         /*
125                          * Direction r or w?
126                          */
127                         arg_name = dm_shift_arg(as);
128                         if (!strcasecmp(arg_name, "w"))
129                                 fc->corrupt_bio_rw = WRITE;
130                         else if (!strcasecmp(arg_name, "r"))
131                                 fc->corrupt_bio_rw = READ;
132                         else {
133                                 ti->error = "Invalid corrupt bio direction (r or w)";
134                                 return -EINVAL;
135                         }
136                         argc--;
137
138                         /*
139                          * Value of byte (0-255) to write in place of correct one.
140                          */
141                         r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
142                         if (r)
143                                 return r;
144                         argc--;
145
146                         /*
147                          * Only corrupt bios with these flags set.
148                          */
149                         BUILD_BUG_ON(sizeof(fc->corrupt_bio_flags) !=
150                                      sizeof(unsigned int));
151                         r = dm_read_arg(_args + 3, as,
152                                 (__force unsigned int *)&fc->corrupt_bio_flags,
153                                 &ti->error);
154                         if (r)
155                                 return r;
156                         argc--;
157
158                         continue;
159                 }
160
161                 ti->error = "Unrecognised flakey feature requested";
162                 return -EINVAL;
163         }
164
165         if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
166                 ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
167                 return -EINVAL;
168
169         } else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
170                 ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
171                 return -EINVAL;
172         }
173
174         return 0;
175 }
176
177 /*
178  * Construct a flakey mapping:
179  * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
180  *
181  *   Feature args:
182  *     [drop_writes]
183  *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
184  *
185  *   Nth_byte starts from 1 for the first byte.
186  *   Direction is r for READ or w for WRITE.
187  *   bio_flags is ignored if 0.
188  */
189 static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
190 {
191         static const struct dm_arg _args[] = {
192                 {0, UINT_MAX, "Invalid up interval"},
193                 {0, UINT_MAX, "Invalid down interval"},
194         };
195
196         int r;
197         struct flakey_c *fc;
198         unsigned long long tmpll;
199         struct dm_arg_set as;
200         const char *devname;
201         char dummy;
202
203         as.argc = argc;
204         as.argv = argv;
205
206         if (argc < 4) {
207                 ti->error = "Invalid argument count";
208                 return -EINVAL;
209         }
210
211         fc = kzalloc(sizeof(*fc), GFP_KERNEL);
212         if (!fc) {
213                 ti->error = "Cannot allocate context";
214                 return -ENOMEM;
215         }
216         fc->start_time = jiffies;
217
218         devname = dm_shift_arg(&as);
219
220         r = -EINVAL;
221         if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) {
222                 ti->error = "Invalid device sector";
223                 goto bad;
224         }
225         fc->start = tmpll;
226
227         r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
228         if (r)
229                 goto bad;
230
231         r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
232         if (r)
233                 goto bad;
234
235         if (!(fc->up_interval + fc->down_interval)) {
236                 ti->error = "Total (up + down) interval is zero";
237                 r = -EINVAL;
238                 goto bad;
239         }
240
241         if (fc->up_interval + fc->down_interval < fc->up_interval) {
242                 ti->error = "Interval overflow";
243                 r = -EINVAL;
244                 goto bad;
245         }
246
247         r = parse_features(&as, fc, ti);
248         if (r)
249                 goto bad;
250
251         r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);
252         if (r) {
253                 ti->error = "Device lookup failed";
254                 goto bad;
255         }
256
257         ti->num_flush_bios = 1;
258         ti->num_discard_bios = 1;
259         ti->per_io_data_size = sizeof(struct per_bio_data);
260         ti->private = fc;
261         return 0;
262
263 bad:
264         kfree(fc);
265         return r;
266 }
267
268 static void flakey_dtr(struct dm_target *ti)
269 {
270         struct flakey_c *fc = ti->private;
271
272         dm_put_device(ti, fc->dev);
273         kfree(fc);
274 }
275
276 static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
277 {
278         struct flakey_c *fc = ti->private;
279
280         return fc->start + dm_target_offset(ti, bi_sector);
281 }
282
283 static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
284 {
285         struct flakey_c *fc = ti->private;
286
287         bio_set_dev(bio, fc->dev->bdev);
288         bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
289 }
290
291 static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
292 {
293         unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
294
295         struct bvec_iter iter;
296         struct bio_vec bvec;
297
298         if (!bio_has_data(bio))
299                 return;
300
301         /*
302          * Overwrite the Nth byte of the bio's data, on whichever page
303          * it falls.
304          */
305         bio_for_each_segment(bvec, bio, iter) {
306                 if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
307                         char *segment;
308                         struct page *page = bio_iter_page(bio, iter);
309                         if (unlikely(page == ZERO_PAGE(0)))
310                                 break;
311                         segment = bvec_kmap_local(&bvec);
312                         segment[corrupt_bio_byte] = fc->corrupt_bio_value;
313                         kunmap_local(segment);
314                         DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
315                                 "(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
316                                 bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
317                                 (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
318                                 (unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size);
319                         break;
320                 }
321                 corrupt_bio_byte -= bio_iter_len(bio, iter);
322         }
323 }
324
325 static int flakey_map(struct dm_target *ti, struct bio *bio)
326 {
327         struct flakey_c *fc = ti->private;
328         unsigned int elapsed;
329         struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
330
331         pb->bio_submitted = false;
332
333         if (op_is_zone_mgmt(bio_op(bio)))
334                 goto map_bio;
335
336         /* Are we alive ? */
337         elapsed = (jiffies - fc->start_time) / HZ;
338         if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
339                 /*
340                  * Flag this bio as submitted while down.
341                  */
342                 pb->bio_submitted = true;
343
344                 /*
345                  * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
346                  * Otherwise, flakey_end_io() will decide if the reads should be modified.
347                  */
348                 if (bio_data_dir(bio) == READ) {
349                         if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags) &&
350                             !test_bit(ERROR_WRITES, &fc->flags))
351                                 return DM_MAPIO_KILL;
352                         goto map_bio;
353                 }
354
355                 /*
356                  * Drop or error writes?
357                  */
358                 if (test_bit(DROP_WRITES, &fc->flags)) {
359                         bio_endio(bio);
360                         return DM_MAPIO_SUBMITTED;
361                 } else if (test_bit(ERROR_WRITES, &fc->flags)) {
362                         bio_io_error(bio);
363                         return DM_MAPIO_SUBMITTED;
364                 }
365
366                 /*
367                  * Corrupt matching writes.
368                  */
369                 if (fc->corrupt_bio_byte) {
370                         if (fc->corrupt_bio_rw == WRITE) {
371                                 if (all_corrupt_bio_flags_match(bio, fc))
372                                         corrupt_bio_data(bio, fc);
373                         }
374                         goto map_bio;
375                 }
376
377                 /*
378                  * By default, error all I/O.
379                  */
380                 return DM_MAPIO_KILL;
381         }
382
383 map_bio:
384         flakey_map_bio(ti, bio);
385
386         return DM_MAPIO_REMAPPED;
387 }
388
389 static int flakey_end_io(struct dm_target *ti, struct bio *bio,
390                          blk_status_t *error)
391 {
392         struct flakey_c *fc = ti->private;
393         struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
394
395         if (op_is_zone_mgmt(bio_op(bio)))
396                 return DM_ENDIO_DONE;
397
398         if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
399                 if (fc->corrupt_bio_byte) {
400                         if ((fc->corrupt_bio_rw == READ) &&
401                             all_corrupt_bio_flags_match(bio, fc)) {
402                                 /*
403                                  * Corrupt successful matching READs while in down state.
404                                  */
405                                 corrupt_bio_data(bio, fc);
406                         }
407                 } else if (!test_bit(DROP_WRITES, &fc->flags) &&
408                            !test_bit(ERROR_WRITES, &fc->flags)) {
409                         /*
410                          * Error read during the down_interval if drop_writes
411                          * and error_writes were not configured.
412                          */
413                         *error = BLK_STS_IOERR;
414                 }
415         }
416
417         return DM_ENDIO_DONE;
418 }
419
420 static void flakey_status(struct dm_target *ti, status_type_t type,
421                           unsigned int status_flags, char *result, unsigned int maxlen)
422 {
423         unsigned int sz = 0;
424         struct flakey_c *fc = ti->private;
425         unsigned int drop_writes, error_writes;
426
427         switch (type) {
428         case STATUSTYPE_INFO:
429                 result[0] = '\0';
430                 break;
431
432         case STATUSTYPE_TABLE:
433                 DMEMIT("%s %llu %u %u ", fc->dev->name,
434                        (unsigned long long)fc->start, fc->up_interval,
435                        fc->down_interval);
436
437                 drop_writes = test_bit(DROP_WRITES, &fc->flags);
438                 error_writes = test_bit(ERROR_WRITES, &fc->flags);
439                 DMEMIT("%u ", drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
440
441                 if (drop_writes)
442                         DMEMIT("drop_writes ");
443                 else if (error_writes)
444                         DMEMIT("error_writes ");
445
446                 if (fc->corrupt_bio_byte)
447                         DMEMIT("corrupt_bio_byte %u %c %u %u ",
448                                fc->corrupt_bio_byte,
449                                (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
450                                fc->corrupt_bio_value, fc->corrupt_bio_flags);
451
452                 break;
453
454         case STATUSTYPE_IMA:
455                 result[0] = '\0';
456                 break;
457         }
458 }
459
460 static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
461 {
462         struct flakey_c *fc = ti->private;
463
464         *bdev = fc->dev->bdev;
465
466         /*
467          * Only pass ioctls through if the device sizes match exactly.
468          */
469         if (fc->start || ti->len != bdev_nr_sectors((*bdev)))
470                 return 1;
471         return 0;
472 }
473
474 #ifdef CONFIG_BLK_DEV_ZONED
475 static int flakey_report_zones(struct dm_target *ti,
476                 struct dm_report_zones_args *args, unsigned int nr_zones)
477 {
478         struct flakey_c *fc = ti->private;
479
480         return dm_report_zones(fc->dev->bdev, fc->start,
481                                flakey_map_sector(ti, args->next_sector),
482                                args, nr_zones);
483 }
484 #else
485 #define flakey_report_zones NULL
486 #endif
487
488 static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
489 {
490         struct flakey_c *fc = ti->private;
491
492         return fn(ti, fc->dev, fc->start, ti->len, data);
493 }
494
495 static struct target_type flakey_target = {
496         .name   = "flakey",
497         .version = {1, 5, 0},
498         .features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,
499         .report_zones = flakey_report_zones,
500         .module = THIS_MODULE,
501         .ctr    = flakey_ctr,
502         .dtr    = flakey_dtr,
503         .map    = flakey_map,
504         .end_io = flakey_end_io,
505         .status = flakey_status,
506         .prepare_ioctl = flakey_prepare_ioctl,
507         .iterate_devices = flakey_iterate_devices,
508 };
509
510 static int __init dm_flakey_init(void)
511 {
512         int r = dm_register_target(&flakey_target);
513
514         if (r < 0)
515                 DMERR("register failed %d", r);
516
517         return r;
518 }
519
520 static void __exit dm_flakey_exit(void)
521 {
522         dm_unregister_target(&flakey_target);
523 }
524
525 /* Module hooks */
526 module_init(dm_flakey_init);
527 module_exit(dm_flakey_exit);
528
529 MODULE_DESCRIPTION(DM_NAME " flakey target");
530 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
531 MODULE_LICENSE("GPL");