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 { 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)) {
/* /*