Commit 4c2c845b authored by Mikulas Patocka's avatar Mikulas Patocka Committed by Mike Snitzer
Browse files

dm flakey: introduce random_read_corrupt and random_write_corrupt options



The random_read_corrupt and random_write_corrupt options corrupt a
random byte in a bio with the provided probability. The corruption
only happens in the "down" interval.

Signed-off-by: default avatarMikulas Patocka <mpatocka@redhat.com>
Signed-off-by: default avatarMike Snitzer <snitzer@kernel.org>
parent 1d9a9438
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -67,6 +67,16 @@ Optional feature parameters:
	Perform the replacement only if bio->bi_opf has all the
	selected flags set.

  random_read_corrupt <probability>
	During <down interval>, replace random byte in a read bio
	with a random value. probability is an integer between
	0 and 1000000000 meaning 0% to 100% probability of corruption.

  random_write_corrupt <probability>
	During <down interval>, replace random byte in a write bio
	with a random value. probability is an integer between
	0 and 1000000000 meaning 0% to 100% probability of corruption.

Examples:

Replaces the 32nd byte of READ bios with the value 1::
+100 −20
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

#define DM_MSG_PREFIX "flakey"

#define PROBABILITY_BASE	1000000000

#define all_corrupt_bio_flags_match(bio, fc)	\
	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)

@@ -34,6 +36,8 @@ struct flakey_c {
	unsigned int corrupt_bio_rw;
	unsigned int corrupt_bio_value;
	blk_opf_t corrupt_bio_flags;
	unsigned int random_read_corrupt;
	unsigned int random_write_corrupt;
};

enum feature_flag_bits {
@@ -54,10 +58,11 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
	const char *arg_name;

	static const struct dm_arg _args[] = {
		{0, 7, "Invalid number of feature args"},
		{0, 11, "Invalid number of feature args"},
		{1, UINT_MAX, "Invalid corrupt bio byte"},
		{0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
		{0, UINT_MAX, "Invalid corrupt bio flags mask"},
		{0, PROBABILITY_BASE, "Invalid random corrupt argument"},
	};

	/* No feature arguments supplied. */
@@ -170,6 +175,32 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
			continue;
		}

		if (!strcasecmp(arg_name, "random_read_corrupt")) {
			if (!argc) {
				ti->error = "Feature random_read_corrupt requires a parameter";
				return -EINVAL;
			}
			r = dm_read_arg(_args + 4, as, &fc->random_read_corrupt, &ti->error);
			if (r)
				return r;
			argc--;

			continue;
		}

		if (!strcasecmp(arg_name, "random_write_corrupt")) {
			if (!argc) {
				ti->error = "Feature random_write_corrupt requires a parameter";
				return -EINVAL;
			}
			r = dm_read_arg(_args + 4, as, &fc->random_write_corrupt, &ti->error);
			if (r)
				return r;
			argc--;

			continue;
		}

		ti->error = "Unrecognised flakey feature requested";
		return -EINVAL;
	}
@@ -184,7 +215,8 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
	}

	if (!fc->corrupt_bio_byte && !test_bit(ERROR_READS, &fc->flags) &&
	    !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags)) {
	    !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags) &&
	    !fc->random_read_corrupt && !fc->random_write_corrupt) {
		set_bit(ERROR_WRITES, &fc->flags);
		set_bit(ERROR_READS, &fc->flags);
	}
@@ -306,36 +338,57 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
	bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
}

static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,
			       unsigned char corrupt_bio_value)
{
	unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;

	struct bvec_iter iter;
	struct bio_vec bvec;

	if (!bio_has_data(bio))
		return;

	/*
	 * Overwrite the Nth byte of the bio's data, on whichever page
	 * it falls.
	 */
	bio_for_each_segment(bvec, bio, iter) {
		if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
			char *segment = bvec_kmap_local(&bvec);
			segment[corrupt_bio_byte] = fc->corrupt_bio_value;
			unsigned char *segment = bvec_kmap_local(&bvec);
			segment[corrupt_bio_byte] = corrupt_bio_value;
			kunmap_local(segment);
			DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
				"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
				bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
				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)bio->bi_iter.bi_sector,
				bio->bi_iter.bi_size);
			break;
		}
		corrupt_bio_byte -= bio_iter_len(bio, iter);
	}
}

static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
{
	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);
}

static void corrupt_bio_random(struct bio *bio)
{
	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_value = get_random_u8();

	corrupt_bio_common(bio, corrupt_byte, corrupt_value);
}

static void clone_free(struct bio *clone)
{
	struct folio_iter fi;
@@ -436,6 +489,7 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
	/* Are we alive ? */
	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.
		 */
@@ -465,17 +519,29 @@ static int flakey_map(struct dm_target *ti, struct bio *bio)
		/*
		 * Corrupt matching writes.
		 */
		corrupt_fixed = false;
		corrupt_random = false;
		if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) {
			if (all_corrupt_bio_flags_match(bio, fc)) {
			if (all_corrupt_bio_flags_match(bio, fc))
				corrupt_fixed = true;
		}
		if (fc->random_write_corrupt) {
			u64 rnd = get_random_u64();
			u32 rem = do_div(rnd, PROBABILITY_BASE);
			if (rem < fc->random_write_corrupt)
				corrupt_random = true;
		}
		if (corrupt_fixed || corrupt_random) {
			struct bio *clone = clone_bio(ti, fc, bio);
			if (clone) {
				if (corrupt_fixed)
					corrupt_bio_data(clone, fc);
				if (corrupt_random)
					corrupt_bio_random(clone);
				submit_bio(clone);
				return DM_MAPIO_SUBMITTED;
			}
		}
			goto map_bio;
		}
	}

map_bio:
@@ -503,6 +569,12 @@ static int flakey_end_io(struct dm_target *ti, struct bio *bio,
				corrupt_bio_data(bio, fc);
			}
		}
		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);
		}
		if (test_bit(ERROR_READS, &fc->flags)) {
			/*
			 * Error read during the down_interval if drop_writes
@@ -535,7 +607,10 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
		error_reads = test_bit(ERROR_READS, &fc->flags);
		drop_writes = test_bit(DROP_WRITES, &fc->flags);
		error_writes = test_bit(ERROR_WRITES, &fc->flags);
		DMEMIT(" %u", error_reads + drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
		DMEMIT(" %u", error_reads + drop_writes + error_writes +
			(fc->corrupt_bio_byte > 0) * 5 +
			(fc->random_read_corrupt > 0) * 2 +
			(fc->random_write_corrupt > 0) * 2);

		if (error_reads)
			DMEMIT(" error_reads");
@@ -550,6 +625,11 @@ static void flakey_status(struct dm_target *ti, status_type_t type,
			       (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
			       fc->corrupt_bio_value, fc->corrupt_bio_flags);

		if (fc->random_read_corrupt > 0)
			DMEMIT(" random_read_corrupt %u", fc->random_read_corrupt);
		if (fc->random_write_corrupt > 0)
			DMEMIT(" random_write_corrupt %u", fc->random_write_corrupt);

		break;

	case STATUSTYPE_IMA: