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:
Benjamin Marzinski
2025-04-22 19:47:38 -04:00
committed by Greg Kroah-Hartman
parent 1aef0e1083
commit 8412696035

View File

@@ -47,7 +47,8 @@ enum feature_flag_bits {
};
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,
@@ -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,
unsigned char corrupt_bio_value)
unsigned char corrupt_bio_value,
struct bvec_iter start)
{
struct bvec_iter iter;
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
* 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) {
unsigned char *segment = bvec_kmap_local(&bvec);
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",
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)start.bi_sector,
start.bi_size);
break;
}
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;
if (!bio_has_data(bio))
return;
corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value);
corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value, start);
}
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 char corrupt_value;
if (!bio_has_data(bio))
return;
corrupt_byte = get_random_u32() % bio->bi_iter.bi_size;
corrupt_byte = get_random_u32() % start.bi_size;
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)
@@ -481,7 +478,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
unsigned int elapsed;
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)))
goto map_bio;
@@ -490,10 +487,11 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
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.
*/
pb->bio_submitted = true;
if (bio_has_data(bio)) {
pb->bio_can_corrupt = true;
pb->saved_iter = bio->bi_iter;
}
/*
* 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;
}
if (!pb->bio_can_corrupt)
goto map_bio;
/*
* 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);
if (clone) {
if (corrupt_fixed)
corrupt_bio_data(clone, fc);
corrupt_bio_data(clone, fc,
clone->bi_iter);
if (corrupt_random)
corrupt_bio_random(clone);
corrupt_bio_random(clone,
clone->bi_iter);
submit_bio(clone);
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)))
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_rw == READ) &&
all_corrupt_bio_flags_match(bio, fc)) {
/*
* 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) {
u64 rnd = get_random_u64();
u32 rem = do_div(rnd, PROBABILITY_BASE);
if (rem < fc->random_read_corrupt)
corrupt_bio_random(bio);
corrupt_bio_random(bio, pb->saved_iter);
}
if (test_bit(ERROR_READS, &fc->flags)) {
/*