dm flakey: introduce random_read_corrupt and random_write_corrupt options

The random_read_corrupt and random_write_corrupt options corrupt a
random byte in a bio with the provided probability. The corruption
only happens in the "down" interval.

Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Mike Snitzer <snitzer@kernel.org>
This commit is contained in:
Mikulas Patocka 2023-05-01 09:20:08 -04:00 committed by Mike Snitzer
parent 1d9a943898
commit 4c2c845bdc
2 changed files with 110 additions and 20 deletions

View File

@ -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::

View File

@ -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_set *as, struct flakey_c *fc,
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_set *as, struct flakey_c *fc,
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_set *as, struct flakey_c *fc,
}
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,36 +338,57 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
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);
(unsigned long long)bio->bi_iter.bi_sector,
bio->bi_iter.bi_size);
break;
}
corrupt_bio_byte -= bio_iter_len(bio, iter);
}
}
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;
@ -436,6 +489,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
/* 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.
*/
@ -465,16 +519,28 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
/*
* 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;
}
if (corrupt_random)
corrupt_bio_random(clone);
submit_bio(clone);
return DM_MAPIO_SUBMITTED;
}
goto map_bio;
}
}
@ -503,6 +569,12 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
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
@ -535,7 +607,10 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
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");
@ -550,6 +625,11 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
(fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
fc->corrupt_bio_value, fc->corrupt_bio_flags);
if (fc->random_read_corrupt > 0)
DMEMIT(" random_read_corrupt %u", fc->random_read_corrupt);
if (fc->random_write_corrupt > 0)
DMEMIT(" random_write_corrupt %u", fc->random_write_corrupt);
break;
case STATUSTYPE_IMA: