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:
parent
1d9a943898
commit
4c2c845bdc
|
@ -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::
|
||||
|
|
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue