Merge tag 'pci-v6.4-fixes-1' of git://git.kernel.org/pub/scm/linux/kernel/git/pci/pci
[linux.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         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;
326                         struct page *page = bio_iter_page(bio, iter);
327                         if (unlikely(page == ZERO_PAGE(0)))
328                                 break;
329                         segment = bvec_kmap_local(&bvec);
330                         segment[corrupt_bio_byte] = fc->corrupt_bio_value;
331                         kunmap_local(segment);
332                         DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
333                                 "(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
334                                 bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
335                                 (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
336                                 (unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size);
337                         break;
338                 }
339                 corrupt_bio_byte -= bio_iter_len(bio, iter);
340         }
341 }
342
343 static int flakey_map(struct dm_target *ti, struct bio *bio)
344 {
345         struct flakey_c *fc = ti->private;
346         unsigned int elapsed;
347         struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
348
349         pb->bio_submitted = false;
350
351         if (op_is_zone_mgmt(bio_op(bio)))
352                 goto map_bio;
353
354         /* Are we alive ? */
355         elapsed = (jiffies - fc->start_time) / HZ;
356         if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
357                 /*
358                  * Flag this bio as submitted while down.
359                  */
360                 pb->bio_submitted = true;
361
362                 /*
363                  * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
364                  * Otherwise, flakey_end_io() will decide if the reads should be modified.
365                  */
366                 if (bio_data_dir(bio) == READ) {
367                         if (test_bit(ERROR_READS, &fc->flags))
368                                 return DM_MAPIO_KILL;
369                         goto map_bio;
370                 }
371
372                 /*
373                  * Drop or error writes?
374                  */
375                 if (test_bit(DROP_WRITES, &fc->flags)) {
376                         bio_endio(bio);
377                         return DM_MAPIO_SUBMITTED;
378                 } else if (test_bit(ERROR_WRITES, &fc->flags)) {
379                         bio_io_error(bio);
380                         return DM_MAPIO_SUBMITTED;
381                 }
382
383                 /*
384                  * Corrupt matching writes.
385                  */
386                 if (fc->corrupt_bio_byte) {
387                         if (fc->corrupt_bio_rw == WRITE) {
388                                 if (all_corrupt_bio_flags_match(bio, fc))
389                                         corrupt_bio_data(bio, fc);
390                         }
391                         goto map_bio;
392                 }
393         }
394
395 map_bio:
396         flakey_map_bio(ti, bio);
397
398         return DM_MAPIO_REMAPPED;
399 }
400
401 static int flakey_end_io(struct dm_target *ti, struct bio *bio,
402                          blk_status_t *error)
403 {
404         struct flakey_c *fc = ti->private;
405         struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
406
407         if (op_is_zone_mgmt(bio_op(bio)))
408                 return DM_ENDIO_DONE;
409
410         if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
411                 if (fc->corrupt_bio_byte) {
412                         if ((fc->corrupt_bio_rw == READ) &&
413                             all_corrupt_bio_flags_match(bio, fc)) {
414                                 /*
415                                  * Corrupt successful matching READs while in down state.
416                                  */
417                                 corrupt_bio_data(bio, fc);
418                         }
419                 }
420                 if (test_bit(ERROR_READS, &fc->flags)) {
421                         /*
422                          * Error read during the down_interval if drop_writes
423                          * and error_writes were not configured.
424                          */
425                         *error = BLK_STS_IOERR;
426                 }
427         }
428
429         return DM_ENDIO_DONE;
430 }
431
432 static void flakey_status(struct dm_target *ti, status_type_t type,
433                           unsigned int status_flags, char *result, unsigned int maxlen)
434 {
435         unsigned int sz = 0;
436         struct flakey_c *fc = ti->private;
437         unsigned int error_reads, drop_writes, error_writes;
438
439         switch (type) {
440         case STATUSTYPE_INFO:
441                 result[0] = '\0';
442                 break;
443
444         case STATUSTYPE_TABLE:
445                 DMEMIT("%s %llu %u %u", fc->dev->name,
446                        (unsigned long long)fc->start, fc->up_interval,
447                        fc->down_interval);
448
449                 error_reads = test_bit(ERROR_READS, &fc->flags);
450                 drop_writes = test_bit(DROP_WRITES, &fc->flags);
451                 error_writes = test_bit(ERROR_WRITES, &fc->flags);
452                 DMEMIT(" %u", error_reads + drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
453
454                 if (error_reads)
455                         DMEMIT(" error_reads");
456                 if (drop_writes)
457                         DMEMIT(" drop_writes");
458                 else if (error_writes)
459                         DMEMIT(" error_writes");
460
461                 if (fc->corrupt_bio_byte)
462                         DMEMIT(" corrupt_bio_byte %u %c %u %u",
463                                fc->corrupt_bio_byte,
464                                (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
465                                fc->corrupt_bio_value, fc->corrupt_bio_flags);
466
467                 break;
468
469         case STATUSTYPE_IMA:
470                 result[0] = '\0';
471                 break;
472         }
473 }
474
475 static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
476 {
477         struct flakey_c *fc = ti->private;
478
479         *bdev = fc->dev->bdev;
480
481         /*
482          * Only pass ioctls through if the device sizes match exactly.
483          */
484         if (fc->start || ti->len != bdev_nr_sectors((*bdev)))
485                 return 1;
486         return 0;
487 }
488
489 #ifdef CONFIG_BLK_DEV_ZONED
490 static int flakey_report_zones(struct dm_target *ti,
491                 struct dm_report_zones_args *args, unsigned int nr_zones)
492 {
493         struct flakey_c *fc = ti->private;
494
495         return dm_report_zones(fc->dev->bdev, fc->start,
496                                flakey_map_sector(ti, args->next_sector),
497                                args, nr_zones);
498 }
499 #else
500 #define flakey_report_zones NULL
501 #endif
502
503 static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
504 {
505         struct flakey_c *fc = ti->private;
506
507         return fn(ti, fc->dev, fc->start, ti->len, data);
508 }
509
510 static struct target_type flakey_target = {
511         .name   = "flakey",
512         .version = {1, 5, 0},
513         .features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,
514         .report_zones = flakey_report_zones,
515         .module = THIS_MODULE,
516         .ctr    = flakey_ctr,
517         .dtr    = flakey_dtr,
518         .map    = flakey_map,
519         .end_io = flakey_end_io,
520         .status = flakey_status,
521         .prepare_ioctl = flakey_prepare_ioctl,
522         .iterate_devices = flakey_iterate_devices,
523 };
524 module_dm(flakey);
525
526 MODULE_DESCRIPTION(DM_NAME " flakey target");
527 MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
528 MODULE_LICENSE("GPL");