Commit 13e79076 authored by Benjamin Marzinski's avatar Benjamin Marzinski Committed by Mikulas Patocka
Browse files

dm-flakey: make corrupting read bios work



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: default avatarKent Overstreet <kent.overstreet@linux.dev>
Fixes: a3998799 ("dm flakey: add corrupt_bio_byte feature")
Signed-off-by: default avatarBenjamin Marzinski <bmarzins@redhat.com>
Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
parent 4319f0aa
Loading
Loading
Loading
Loading
+28 −26
Original line number Diff line number Diff line
@@ -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);
		}
	}