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(arg: _args, arg_set: as, num_args: &argc, error: &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(s1: arg_name, s2: "error_reads" )) { |
89 | if (test_and_set_bit(nr: ERROR_READS, addr: &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(s1: arg_name, s2: "drop_writes" )) { |
100 | if (test_and_set_bit(nr: DROP_WRITES, addr: &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(s1: arg_name, s2: "error_writes" )) { |
115 | if (test_and_set_bit(nr: ERROR_WRITES, addr: &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(s1: arg_name, s2: "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(arg: _args + 1, arg_set: as, value: &fc->corrupt_bio_byte, error: &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(s1: arg_name, s2: "w" )) |
146 | fc->corrupt_bio_rw = WRITE; |
147 | else if (arg_name && !strcasecmp(s1: arg_name, s2: "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(arg: _args + 2, arg_set: as, value: &fc->corrupt_bio_value, error: &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(arg: _args + 3, arg_set: as, |
169 | value: (__force unsigned int *)&fc->corrupt_bio_flags, |
170 | error: &ti->error); |
171 | if (r) |
172 | return r; |
173 | argc--; |
174 | |
175 | continue; |
176 | } |
177 | |
178 | if (!strcasecmp(s1: arg_name, s2: "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(arg: _args + 4, arg_set: as, value: &fc->random_read_corrupt, error: &ti->error); |
184 | if (r) |
185 | return r; |
186 | argc--; |
187 | |
188 | continue; |
189 | } |
190 | |
191 | if (!strcasecmp(s1: arg_name, s2: "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(arg: _args + 4, arg_set: as, value: &fc->random_write_corrupt, error: &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(nr: ERROR_WRITES, addr: &fc->flags); |
221 | set_bit(nr: ERROR_READS, addr: &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(size: 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: &as); |
269 | |
270 | r = -EINVAL; |
271 | if (sscanf(dm_shift_arg(as: &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(arg: _args, arg_set: &as, value: &fc->up_interval, error: &ti->error); |
278 | if (r) |
279 | goto bad; |
280 | |
281 | r = dm_read_arg(arg: _args, arg_set: &as, value: &fc->down_interval, error: &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: &as, fc, ti); |
298 | if (r) |
299 | goto bad; |
300 | |
301 | r = dm_get_device(ti, path: devname, mode: dm_table_get_mode(t: ti->table), result: &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(objp: 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, d: fc->dev); |
323 | kfree(objp: 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, bdev: fc->dev->bdev); |
338 | bio->bi_iter.bi_sector = flakey_map_sector(ti, bi_sector: 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: &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, corrupt_bio_value: 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_bio_byte: corrupt_byte, corrupt_bio_value: 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(folio: fi.folio); |
399 | } |
400 | |
401 | bio_uninit(clone); |
402 | kfree(objp: 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_vecs: nr_iovecs, GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN); |
426 | if (!clone) |
427 | return NULL; |
428 | |
429 | bio_init(bio: clone, bdev: fc->dev->bdev, table: bio->bi_inline_vecs, max_vecs: nr_iovecs, opf: bio->bi_opf); |
430 | |
431 | clone->bi_iter.bi_sector = flakey_map_sector(ti, bi_sector: 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_ORDER - 1; |
438 | while (remaining_size) { |
439 | struct page *pages; |
440 | unsigned size_to_add, to_copy; |
441 | unsigned char *virt; |
442 | unsigned remaining_order = __fls(word: (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: &bvec); |
463 | memcpy(virt, map, this_step); |
464 | kunmap_local(map); |
465 | |
466 | bvec_iter_advance(bv: bio->bi_io_vec, iter: &iter, bytes: this_step); |
467 | to_copy -= this_step; |
468 | virt += this_step; |
469 | } while (to_copy); |
470 | |
471 | __bio_add_page(bio: clone, page: pages, len: size_to_add, off: 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, data_size: sizeof(struct per_bio_data)); |
483 | |
484 | pb->bio_submitted = false; |
485 | |
486 | if (op_is_zone_mgmt(op: 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(bio: clone, fc); |
539 | if (corrupt_random) |
540 | corrupt_bio_random(bio: clone); |
541 | submit_bio(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, data_size: sizeof(struct per_bio_data)); |
558 | |
559 | if (op_is_zone_mgmt(op: 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: (*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(bdev: fc->dev->bdev, start: fc->start, |
662 | sector: flakey_map_sector(ti, bi_sector: 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@redhat.com>" ); |
694 | MODULE_LICENSE("GPL" ); |
695 | |