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