]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
dm-flakey: make corrupting read bios work
authorBenjamin Marzinski <bmarzins@redhat.com>
Tue, 22 Apr 2025 23:47:38 +0000 (19:47 -0400)
committerMikulas Patocka <mpatocka@redhat.com>
Sun, 4 May 2025 09:35:05 +0000 (11:35 +0200)
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: a3998799fb4df ("dm flakey: add corrupt_bio_byte feature")
Signed-off-by: Benjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: Mikulas Patocka <mpatocka@redhat.com>
drivers/md/dm-flakey.c

index 0421f933668019547a58d178c40f337da7427d44..a8ee3df32d5f5f34289d25d3c82a77aa38d02c69 100644 (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,
@@ -354,7 +355,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;
@@ -363,7 +365,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;
@@ -372,36 +374,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)
@@ -496,7 +493,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;
@@ -505,10 +502,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;
+               }
 
                /*
                 * If ERROR_READS isn't set flakey_end_io() will decide if the
@@ -531,6 +529,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.
                 */
@@ -550,9 +550,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;
                        }
@@ -574,21 +576,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);
                }
        }