Commit 4c2c845b authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Mike Snitzer

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: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent 1d9a9438
...@@ -67,6 +67,16 @@ Optional feature parameters: ...@@ -67,6 +67,16 @@ Optional feature parameters:
Perform the replacement only if bio->bi_opf has all the Perform the replacement only if bio->bi_opf has all the
selected flags set. 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: Examples:
Replaces the 32nd byte of READ bios with the value 1:: Replaces the 32nd byte of READ bios with the value 1::
......
...@@ -16,6 +16,8 @@ ...@@ -16,6 +16,8 @@
#define DM_MSG_PREFIX "flakey" #define DM_MSG_PREFIX "flakey"
#define PROBABILITY_BASE 1000000000
#define all_corrupt_bio_flags_match(bio, fc) \ #define all_corrupt_bio_flags_match(bio, fc) \
(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags) (((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
...@@ -34,6 +36,8 @@ struct flakey_c { ...@@ -34,6 +36,8 @@ struct flakey_c {
unsigned int corrupt_bio_rw; unsigned int corrupt_bio_rw;
unsigned int corrupt_bio_value; unsigned int corrupt_bio_value;
blk_opf_t corrupt_bio_flags; blk_opf_t corrupt_bio_flags;
unsigned int random_read_corrupt;
unsigned int random_write_corrupt;
}; };
enum feature_flag_bits { enum feature_flag_bits {
...@@ -54,10 +58,11 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, ...@@ -54,10 +58,11 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
const char *arg_name; const char *arg_name;
static const struct dm_arg _args[] = { 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"}, {1, UINT_MAX, "Invalid corrupt bio byte"},
{0, 255, "Invalid corrupt value to write into bio byte (0-255)"}, {0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
{0, UINT_MAX, "Invalid corrupt bio flags mask"}, {0, UINT_MAX, "Invalid corrupt bio flags mask"},
{0, PROBABILITY_BASE, "Invalid random corrupt argument"},
}; };
/* No feature arguments supplied. */ /* No feature arguments supplied. */
...@@ -170,6 +175,32 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, ...@@ -170,6 +175,32 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
continue; 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"; ti->error = "Unrecognised flakey feature requested";
return -EINVAL; return -EINVAL;
} }
...@@ -184,7 +215,8 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, ...@@ -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) && 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_WRITES, &fc->flags);
set_bit(ERROR_READS, &fc->flags); set_bit(ERROR_READS, &fc->flags);
} }
...@@ -306,36 +338,57 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio) ...@@ -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); 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 bvec_iter iter;
struct bio_vec bvec; struct bio_vec bvec;
if (!bio_has_data(bio))
return;
/* /*
* Overwrite the Nth byte of the bio's data, on whichever page * Overwrite the Nth byte of the bio's data, on whichever page
* it falls. * it falls.
*/ */
bio_for_each_segment(bvec, bio, iter) { bio_for_each_segment(bvec, bio, iter) {
if (bio_iter_len(bio, iter) > corrupt_bio_byte) { if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
char *segment = bvec_kmap_local(&bvec); unsigned char *segment = bvec_kmap_local(&bvec);
segment[corrupt_bio_byte] = fc->corrupt_bio_value; segment[corrupt_bio_byte] = corrupt_bio_value;
kunmap_local(segment); kunmap_local(segment);
DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n", "(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, (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; break;
} }
corrupt_bio_byte -= bio_iter_len(bio, iter); 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) static void clone_free(struct bio *clone)
{ {
struct folio_iter fi; struct folio_iter fi;
...@@ -436,6 +489,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio) ...@@ -436,6 +489,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
/* Are we alive ? */ /* Are we alive ? */
elapsed = (jiffies - fc->start_time) / HZ; elapsed = (jiffies - fc->start_time) / HZ;
if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
bool corrupt_fixed, corrupt_random;
/* /*
* Flag this bio as submitted while down. * Flag this bio as submitted while down.
*/ */
...@@ -465,17 +519,29 @@ static int flakey_map(struct dm_target *ti, struct bio *bio) ...@@ -465,17 +519,29 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
/* /*
* Corrupt matching writes. * Corrupt matching writes.
*/ */
corrupt_fixed = false;
corrupt_random = false;
if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) { if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) {
if (all_corrupt_bio_flags_match(bio, fc)) { 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); struct bio *clone = clone_bio(ti, fc, bio);
if (clone) { if (clone) {
if (corrupt_fixed)
corrupt_bio_data(clone, fc); corrupt_bio_data(clone, fc);
if (corrupt_random)
corrupt_bio_random(clone);
submit_bio(clone); submit_bio(clone);
return DM_MAPIO_SUBMITTED; return DM_MAPIO_SUBMITTED;
} }
} }
goto map_bio;
}
} }
map_bio: map_bio:
...@@ -503,6 +569,12 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio, ...@@ -503,6 +569,12 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
corrupt_bio_data(bio, fc); 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)) { if (test_bit(ERROR_READS, &fc->flags)) {
/* /*
* Error read during the down_interval if drop_writes * 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, ...@@ -535,7 +607,10 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
error_reads = test_bit(ERROR_READS, &fc->flags); error_reads = test_bit(ERROR_READS, &fc->flags);
drop_writes = test_bit(DROP_WRITES, &fc->flags); drop_writes = test_bit(DROP_WRITES, &fc->flags);
error_writes = test_bit(ERROR_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) if (error_reads)
DMEMIT(" error_reads"); DMEMIT(" error_reads");
...@@ -550,6 +625,11 @@ static void flakey_status(struct dm_target *ti, status_type_t type, ...@@ -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_rw == WRITE) ? 'w' : 'r',
fc->corrupt_bio_value, fc->corrupt_bio_flags); 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; break;
case STATUSTYPE_IMA: case STATUSTYPE_IMA:
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment