dm-flakey: make corrupting read bios work
[ Upstream commit 13e79076c89f6e96a6cca8f6df38b40d025907b4 ]
dm-flakey corrupts the read bios in the endio function. However, the
corrupt_bio_* functions checked bio_has_data() to see if there was data
to corrupt. Since this was the endio function, there was no data left to
complete, so bio_has_data() was always false. Fix this by saving a copy
of the bio's bi_iter in flakey_map(), and using this to initialize the
iter for corrupting the read bios. This patch also skips cloning the bio
for write bios with no data.
Reported-by: Kent Overstreet <kent.overstreet@linux.dev>
Fixes: a3998799fb
("dm flakey: add corrupt_bio_byte feature")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
Signed-off-by: Sasha Levin <sashal@kernel.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
1aef0e1083
commit
8412696035
@@ -47,7 +47,8 @@ enum feature_flag_bits {
|
|||||||
};
|
};
|
||||||
|
|
||||||
struct per_bio_data {
|
struct per_bio_data {
|
||||||
bool bio_submitted;
|
bool bio_can_corrupt;
|
||||||
|
struct bvec_iter saved_iter;
|
||||||
};
|
};
|
||||||
|
|
||||||
static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
|
static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
|
||||||
@@ -339,7 +340,8 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,
|
static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,
|
||||||
unsigned char corrupt_bio_value)
|
unsigned char corrupt_bio_value,
|
||||||
|
struct bvec_iter start)
|
||||||
{
|
{
|
||||||
struct bvec_iter iter;
|
struct bvec_iter iter;
|
||||||
struct bio_vec bvec;
|
struct bio_vec bvec;
|
||||||
@@ -348,7 +350,7 @@ static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,
|
|||||||
* 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, start) {
|
||||||
if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
|
if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
|
||||||
unsigned char *segment = bvec_kmap_local(&bvec);
|
unsigned char *segment = bvec_kmap_local(&bvec);
|
||||||
segment[corrupt_bio_byte] = corrupt_bio_value;
|
segment[corrupt_bio_byte] = corrupt_bio_value;
|
||||||
@@ -357,36 +359,31 @@ static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,
|
|||||||
"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
|
"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
|
||||||
bio, corrupt_bio_value, 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,
|
(unsigned long long)start.bi_sector,
|
||||||
bio->bi_iter.bi_size);
|
start.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)
|
static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc,
|
||||||
|
struct bvec_iter start)
|
||||||
{
|
{
|
||||||
unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
|
unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
|
||||||
|
|
||||||
if (!bio_has_data(bio))
|
corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value, start);
|
||||||
return;
|
|
||||||
|
|
||||||
corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void corrupt_bio_random(struct bio *bio)
|
static void corrupt_bio_random(struct bio *bio, struct bvec_iter start)
|
||||||
{
|
{
|
||||||
unsigned int corrupt_byte;
|
unsigned int corrupt_byte;
|
||||||
unsigned char corrupt_value;
|
unsigned char corrupt_value;
|
||||||
|
|
||||||
if (!bio_has_data(bio))
|
corrupt_byte = get_random_u32() % start.bi_size;
|
||||||
return;
|
|
||||||
|
|
||||||
corrupt_byte = get_random_u32() % bio->bi_iter.bi_size;
|
|
||||||
corrupt_value = get_random_u8();
|
corrupt_value = get_random_u8();
|
||||||
|
|
||||||
corrupt_bio_common(bio, corrupt_byte, corrupt_value);
|
corrupt_bio_common(bio, corrupt_byte, corrupt_value, start);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void clone_free(struct bio *clone)
|
static void clone_free(struct bio *clone)
|
||||||
@@ -481,7 +478,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
|
|||||||
unsigned int elapsed;
|
unsigned int elapsed;
|
||||||
struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
|
struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
|
||||||
|
|
||||||
pb->bio_submitted = false;
|
pb->bio_can_corrupt = false;
|
||||||
|
|
||||||
if (op_is_zone_mgmt(bio_op(bio)))
|
if (op_is_zone_mgmt(bio_op(bio)))
|
||||||
goto map_bio;
|
goto map_bio;
|
||||||
@@ -490,10 +487,11 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
|
|||||||
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;
|
bool corrupt_fixed, corrupt_random;
|
||||||
/*
|
|
||||||
* Flag this bio as submitted while down.
|
if (bio_has_data(bio)) {
|
||||||
*/
|
pb->bio_can_corrupt = true;
|
||||||
pb->bio_submitted = true;
|
pb->saved_iter = bio->bi_iter;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
|
* Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
|
||||||
@@ -516,6 +514,8 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
|
|||||||
return DM_MAPIO_SUBMITTED;
|
return DM_MAPIO_SUBMITTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!pb->bio_can_corrupt)
|
||||||
|
goto map_bio;
|
||||||
/*
|
/*
|
||||||
* Corrupt matching writes.
|
* Corrupt matching writes.
|
||||||
*/
|
*/
|
||||||
@@ -535,9 +535,11 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
|
|||||||
struct bio *clone = clone_bio(ti, fc, bio);
|
struct bio *clone = clone_bio(ti, fc, bio);
|
||||||
if (clone) {
|
if (clone) {
|
||||||
if (corrupt_fixed)
|
if (corrupt_fixed)
|
||||||
corrupt_bio_data(clone, fc);
|
corrupt_bio_data(clone, fc,
|
||||||
|
clone->bi_iter);
|
||||||
if (corrupt_random)
|
if (corrupt_random)
|
||||||
corrupt_bio_random(clone);
|
corrupt_bio_random(clone,
|
||||||
|
clone->bi_iter);
|
||||||
submit_bio(clone);
|
submit_bio(clone);
|
||||||
return DM_MAPIO_SUBMITTED;
|
return DM_MAPIO_SUBMITTED;
|
||||||
}
|
}
|
||||||
@@ -559,21 +561,21 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
|
|||||||
if (op_is_zone_mgmt(bio_op(bio)))
|
if (op_is_zone_mgmt(bio_op(bio)))
|
||||||
return DM_ENDIO_DONE;
|
return DM_ENDIO_DONE;
|
||||||
|
|
||||||
if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
|
if (!*error && pb->bio_can_corrupt && (bio_data_dir(bio) == READ)) {
|
||||||
if (fc->corrupt_bio_byte) {
|
if (fc->corrupt_bio_byte) {
|
||||||
if ((fc->corrupt_bio_rw == READ) &&
|
if ((fc->corrupt_bio_rw == READ) &&
|
||||||
all_corrupt_bio_flags_match(bio, fc)) {
|
all_corrupt_bio_flags_match(bio, fc)) {
|
||||||
/*
|
/*
|
||||||
* Corrupt successful matching READs while in down state.
|
* Corrupt successful matching READs while in down state.
|
||||||
*/
|
*/
|
||||||
corrupt_bio_data(bio, fc);
|
corrupt_bio_data(bio, fc, pb->saved_iter);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (fc->random_read_corrupt) {
|
if (fc->random_read_corrupt) {
|
||||||
u64 rnd = get_random_u64();
|
u64 rnd = get_random_u64();
|
||||||
u32 rem = do_div(rnd, PROBABILITY_BASE);
|
u32 rem = do_div(rnd, PROBABILITY_BASE);
|
||||||
if (rem < fc->random_read_corrupt)
|
if (rem < fc->random_read_corrupt)
|
||||||
corrupt_bio_random(bio);
|
corrupt_bio_random(bio, pb->saved_iter);
|
||||||
}
|
}
|
||||||
if (test_bit(ERROR_READS, &fc->flags)) {
|
if (test_bit(ERROR_READS, &fc->flags)) {
|
||||||
/*
|
/*
|
||||||
|
Reference in New Issue
Block a user