The random_read_corrupt and random_write_corrupt options corrupt random byte in a bio with a given probability. The corruptions only happen in the "down" interval. Signed-off-by: Mikulas Patocka <mpatocka@xxxxxxxxxx> --- Documentation/admin-guide/device-mapper/dm-flakey.rst | 10 + drivers/md/dm-flakey.c | 118 ++++++++++++++---- 2 files changed, 108 insertions(+), 20 deletions(-) Index: linux-2.6/drivers/md/dm-flakey.c =================================================================== --- linux-2.6.orig/drivers/md/dm-flakey.c +++ linux-2.6/drivers/md/dm-flakey.c @@ -16,6 +16,8 @@ #define DM_MSG_PREFIX "flakey" +#define PROBABILITY_BASE 1000000000 + #define all_corrupt_bio_flags_match(bio, fc) \ (((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags) @@ -34,6 +36,8 @@ struct flakey_c { unsigned int corrupt_bio_rw; unsigned int corrupt_bio_value; blk_opf_t corrupt_bio_flags; + unsigned int random_read_corrupt; + unsigned int random_write_corrupt; }; enum feature_flag_bits { @@ -54,10 +58,11 @@ static int parse_features(struct dm_arg_ const char *arg_name; static const struct dm_arg _args[] = { - {0, 7, "Invalid number of feature args"}, + {0, 11, "Invalid number of feature args"}, {1, UINT_MAX, "Invalid corrupt bio byte"}, {0, 255, "Invalid corrupt value to write into bio byte (0-255)"}, {0, UINT_MAX, "Invalid corrupt bio flags mask"}, + {0, PROBABILITY_BASE, "Invalid random corrupt argument"}, }; /* No feature arguments supplied. */ @@ -170,6 +175,32 @@ static int parse_features(struct dm_arg_ continue; } + if (!strcasecmp(arg_name, "random_read_corrupt")) { + if (!argc) { + ti->error = "Feature random_read_corrupt requires a parameter"; + return -EINVAL; + } + r = dm_read_arg(_args + 4, as, &fc->random_read_corrupt, &ti->error); + if (r) + return r; + argc--; + + continue; + } + + if (!strcasecmp(arg_name, "random_write_corrupt")) { + if (!argc) { + ti->error = "Feature random_write_corrupt requires a parameter"; + return -EINVAL; + } + r = dm_read_arg(_args + 4, as, &fc->random_write_corrupt, &ti->error); + if (r) + return r; + argc--; + + continue; + } + ti->error = "Unrecognised flakey feature requested"; return -EINVAL; } @@ -184,7 +215,8 @@ static int parse_features(struct dm_arg_ } if (!fc->corrupt_bio_byte && !test_bit(ERROR_READS, &fc->flags) && - !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags)) { + !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags) && + !fc->random_read_corrupt && !fc->random_write_corrupt) { set_bit(ERROR_WRITES, &fc->flags); set_bit(ERROR_READS, &fc->flags); } @@ -306,28 +338,23 @@ static void flakey_map_bio(struct dm_tar bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector); } -static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) +static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte, unsigned char corrupt_bio_value) { - unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1; - struct bvec_iter iter; struct bio_vec bvec; - if (!bio_has_data(bio)) - return; - /* * Overwrite the Nth byte of the bio's data, on whichever page * it falls. */ bio_for_each_segment(bvec, bio, iter) { if (bio_iter_len(bio, iter) > corrupt_bio_byte) { - char *segment = bvec_kmap_local(&bvec); - segment[corrupt_bio_byte] = fc->corrupt_bio_value; + unsigned char *segment = bvec_kmap_local(&bvec); + segment[corrupt_bio_byte] = corrupt_bio_value; kunmap_local(segment); DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " "(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n", - bio, fc->corrupt_bio_value, fc->corrupt_bio_byte, + bio, corrupt_bio_value, corrupt_bio_byte, (bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf, (unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size); break; @@ -336,6 +363,30 @@ static void corrupt_bio_data(struct bio } } +static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) +{ + unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1; + + if (!bio_has_data(bio)) + return; + + corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value); +} + +static void corrupt_bio_random(struct bio *bio) +{ + unsigned int corrupt_byte; + unsigned char corrupt_value; + + if (!bio_has_data(bio)) + return; + + corrupt_byte = get_random_u32() % bio->bi_iter.bi_size; + corrupt_value = get_random_u8(); + + corrupt_bio_common(bio, corrupt_byte, corrupt_value); +} + static void clone_free(struct bio *clone) { struct folio_iter fi; @@ -434,6 +485,7 @@ static int flakey_map(struct dm_target * /* Are we alive ? */ elapsed = (jiffies - fc->start_time) / HZ; if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { + bool corrupt_fixed, corrupt_random; /* * Flag this bio as submitted while down. */ @@ -463,16 +515,28 @@ static int flakey_map(struct dm_target * /* * Corrupt matching writes. */ + corrupt_fixed = false; + corrupt_random = false; if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) { - if (all_corrupt_bio_flags_match(bio, fc)) { - struct bio *clone = clone_bio(ti, fc, bio); - if (clone) { + if (all_corrupt_bio_flags_match(bio, fc)) + corrupt_fixed = true; + } + if (fc->random_write_corrupt) { + u64 rnd = get_random_u64(); + u32 rem = do_div(rnd, PROBABILITY_BASE); + if (rem < fc->random_write_corrupt) + corrupt_random = true; + } + if (corrupt_fixed || corrupt_random) { + struct bio *clone = clone_bio(ti, fc, bio); + if (clone) { + if (corrupt_fixed) corrupt_bio_data(clone, fc); - submit_bio(clone); - return DM_MAPIO_SUBMITTED; - } - } - goto map_bio; + if (corrupt_random) + corrupt_bio_random(clone); + submit_bio(clone); + return DM_MAPIO_SUBMITTED; + } } } @@ -501,6 +565,12 @@ static int flakey_end_io(struct dm_targe corrupt_bio_data(bio, fc); } } + if (fc->random_read_corrupt) { + u64 rnd = get_random_u64(); + u32 rem = do_div(rnd, PROBABILITY_BASE); + if (rem < fc->random_read_corrupt) + corrupt_bio_random(bio); + } if (test_bit(ERROR_READS, &fc->flags)) { /* * Error read during the down_interval if drop_writes @@ -533,7 +603,10 @@ static void flakey_status(struct dm_targ error_reads = test_bit(ERROR_READS, &fc->flags); drop_writes = test_bit(DROP_WRITES, &fc->flags); error_writes = test_bit(ERROR_WRITES, &fc->flags); - DMEMIT(" %u", error_reads + drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5); + DMEMIT(" %u", error_reads + drop_writes + error_writes + + (fc->corrupt_bio_byte > 0) * 5 + + (fc->random_read_corrupt > 0) * 2 + + (fc->random_write_corrupt > 0) * 2); if (error_reads) DMEMIT(" error_reads"); @@ -548,6 +621,11 @@ static void flakey_status(struct dm_targ (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r', fc->corrupt_bio_value, fc->corrupt_bio_flags); + if (fc->random_read_corrupt > 0) + DMEMIT(" %u", fc->random_read_corrupt); + if (fc->random_write_corrupt > 0) + DMEMIT(" %u", fc->random_write_corrupt); + break; case STATUSTYPE_IMA: Index: linux-2.6/Documentation/admin-guide/device-mapper/dm-flakey.rst =================================================================== --- linux-2.6.orig/Documentation/admin-guide/device-mapper/dm-flakey.rst +++ linux-2.6/Documentation/admin-guide/device-mapper/dm-flakey.rst @@ -67,6 +67,16 @@ Optional feature parameters: Perform the replacement only if bio->bi_opf has all the selected flags set. + random_read_corrupt <probability> + During <down interval>, replace random byte in a read bio + with a random value. probability is an integer between + 0 and 1000000000 meaning 0% to 100% probability of corruption. + + random_write_corrupt <probability> + During <down interval>, replace random byte in a write bio + with a random value. probability is an integer between + 0 and 1000000000 meaning 0% to 100% probability of corruption. + Examples: Replaces the 32nd byte of READ bios with the value 1:: -- dm-devel mailing list dm-devel@xxxxxxxxxx https://listman.redhat.com/mailman/listinfo/dm-devel