mirror of
				https://github.com/torvalds/linux.git
				synced 2025-11-04 10:40:15 +02:00 
			
		
		
		
	dm flakey: add corrupt_bio_byte feature
Add corrupt_bio_byte feature to simulate corruption by overwriting a byte at a specified position with a specified value during intervals when the device is "down". Signed-off-by: Mike Snitzer <snitzer@redhat.com> Signed-off-by: Alasdair G Kergon <agk@redhat.com>
This commit is contained in:
		
							parent
							
								
									b26f5e3d71
								
							
						
					
					
						commit
						a3998799fb
					
				
					 2 changed files with 159 additions and 15 deletions
				
			
		| 
						 | 
				
			
			@ -32,3 +32,22 @@ Optional feature parameters:
 | 
			
		|||
  drop_writes:
 | 
			
		||||
	All write I/O is silently ignored.
 | 
			
		||||
	Read I/O is handled correctly.
 | 
			
		||||
 | 
			
		||||
  corrupt_bio_byte <Nth_byte> <direction> <value> <flags>:
 | 
			
		||||
	During <down interval>, replace <Nth_byte> of the data of
 | 
			
		||||
	each matching bio with <value>.
 | 
			
		||||
 | 
			
		||||
    <Nth_byte>: The offset of the byte to replace.
 | 
			
		||||
		Counting starts at 1, to replace the first byte.
 | 
			
		||||
    <direction>: Either 'r' to corrupt reads or 'w' to corrupt writes.
 | 
			
		||||
		 'w' is incompatible with drop_writes.
 | 
			
		||||
    <value>: The value (from 0-255) to write.
 | 
			
		||||
    <flags>: Perform the replacement only if bio->bi_rw has all the
 | 
			
		||||
	     selected flags set.
 | 
			
		||||
 | 
			
		||||
Examples:
 | 
			
		||||
  corrupt_bio_byte 32 r 1 0
 | 
			
		||||
	- replaces the 32nd byte of READ bios with the value 1
 | 
			
		||||
 | 
			
		||||
  corrupt_bio_byte 224 w 0 32
 | 
			
		||||
	- replaces the 224th byte of REQ_META (=32) bios with the value 0
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,6 @@
 | 
			
		|||
/*
 | 
			
		||||
 * Copyright (C) 2003 Sistina Software (UK) Limited.
 | 
			
		||||
 * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
 | 
			
		||||
 *
 | 
			
		||||
 * This file is released under the GPL.
 | 
			
		||||
 */
 | 
			
		||||
| 
						 | 
				
			
			@ -15,6 +15,9 @@
 | 
			
		|||
 | 
			
		||||
#define DM_MSG_PREFIX "flakey"
 | 
			
		||||
 | 
			
		||||
#define all_corrupt_bio_flags_match(bio, fc)	\
 | 
			
		||||
	(((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Flakey: Used for testing only, simulates intermittent,
 | 
			
		||||
 * catastrophic device failure.
 | 
			
		||||
| 
						 | 
				
			
			@ -26,6 +29,10 @@ struct flakey_c {
 | 
			
		|||
	unsigned up_interval;
 | 
			
		||||
	unsigned down_interval;
 | 
			
		||||
	unsigned long flags;
 | 
			
		||||
	unsigned corrupt_bio_byte;
 | 
			
		||||
	unsigned corrupt_bio_rw;
 | 
			
		||||
	unsigned corrupt_bio_value;
 | 
			
		||||
	unsigned corrupt_bio_flags;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum feature_flag_bits {
 | 
			
		||||
| 
						 | 
				
			
			@ -40,7 +47,10 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
 | 
			
		|||
	const char *arg_name;
 | 
			
		||||
 | 
			
		||||
	static struct dm_arg _args[] = {
 | 
			
		||||
		{0, 1, "Invalid number of feature args"},
 | 
			
		||||
		{0, 6, "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"},
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
	/* No feature arguments supplied. */
 | 
			
		||||
| 
						 | 
				
			
			@ -49,9 +59,9 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
 | 
			
		|||
 | 
			
		||||
	r = dm_read_arg_group(_args, as, &argc, &ti->error);
 | 
			
		||||
	if (r)
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
		return r;
 | 
			
		||||
 | 
			
		||||
	while (argc && !r) {
 | 
			
		||||
	while (argc) {
 | 
			
		||||
		arg_name = dm_shift_arg(as);
 | 
			
		||||
		argc--;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -67,11 +77,61 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
 | 
			
		|||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
 | 
			
		||||
		 */
 | 
			
		||||
		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
 | 
			
		||||
			if (!argc)
 | 
			
		||||
				ti->error = "Feature corrupt_bio_byte requires parameters";
 | 
			
		||||
 | 
			
		||||
			r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
 | 
			
		||||
			if (r)
 | 
			
		||||
				return r;
 | 
			
		||||
			argc--;
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Direction r or w?
 | 
			
		||||
			 */
 | 
			
		||||
			arg_name = dm_shift_arg(as);
 | 
			
		||||
			if (!strcasecmp(arg_name, "w"))
 | 
			
		||||
				fc->corrupt_bio_rw = WRITE;
 | 
			
		||||
			else if (!strcasecmp(arg_name, "r"))
 | 
			
		||||
				fc->corrupt_bio_rw = READ;
 | 
			
		||||
			else {
 | 
			
		||||
				ti->error = "Invalid corrupt bio direction (r or w)";
 | 
			
		||||
				return -EINVAL;
 | 
			
		||||
			}
 | 
			
		||||
			argc--;
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Value of byte (0-255) to write in place of correct one.
 | 
			
		||||
			 */
 | 
			
		||||
			r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
 | 
			
		||||
			if (r)
 | 
			
		||||
				return r;
 | 
			
		||||
			argc--;
 | 
			
		||||
 | 
			
		||||
			/*
 | 
			
		||||
			 * Only corrupt bios with these flags set.
 | 
			
		||||
			 */
 | 
			
		||||
			r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
 | 
			
		||||
			if (r)
 | 
			
		||||
				return r;
 | 
			
		||||
			argc--;
 | 
			
		||||
 | 
			
		||||
			continue;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		ti->error = "Unrecognised flakey feature requested";
 | 
			
		||||
		r = -EINVAL;
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r;
 | 
			
		||||
	if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
 | 
			
		||||
		ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +140,11 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
 | 
			
		|||
 *
 | 
			
		||||
 *   Feature args:
 | 
			
		||||
 *     [drop_writes]
 | 
			
		||||
 *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
 | 
			
		||||
 *
 | 
			
		||||
 *   Nth_byte starts from 1 for the first byte.
 | 
			
		||||
 *   Direction is r for READ or w for WRITE.
 | 
			
		||||
 *   bio_flags is ignored if 0.
 | 
			
		||||
 */
 | 
			
		||||
static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -178,31 +243,64 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
 | 
			
		|||
		bio->bi_sector = flakey_map_sector(ti, bio->bi_sector);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
 | 
			
		||||
{
 | 
			
		||||
	unsigned bio_bytes = bio_cur_bytes(bio);
 | 
			
		||||
	char *data = bio_data(bio);
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Overwrite the Nth byte of the data returned.
 | 
			
		||||
	 */
 | 
			
		||||
	if (data && bio_bytes >= fc->corrupt_bio_byte) {
 | 
			
		||||
		data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value;
 | 
			
		||||
 | 
			
		||||
		DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
 | 
			
		||||
			"(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n",
 | 
			
		||||
			bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
 | 
			
		||||
			(bio_data_dir(bio) == WRITE) ? 'w' : 'r',
 | 
			
		||||
			bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int flakey_map(struct dm_target *ti, struct bio *bio,
 | 
			
		||||
		      union map_info *map_context)
 | 
			
		||||
{
 | 
			
		||||
	struct flakey_c *fc = ti->private;
 | 
			
		||||
	unsigned elapsed;
 | 
			
		||||
	unsigned rw;
 | 
			
		||||
 | 
			
		||||
	/* Are we alive ? */
 | 
			
		||||
	elapsed = (jiffies - fc->start_time) / HZ;
 | 
			
		||||
	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
 | 
			
		||||
		rw = bio_data_dir(bio);
 | 
			
		||||
		/*
 | 
			
		||||
		 * Flag this bio as submitted while down.
 | 
			
		||||
		 */
 | 
			
		||||
		map_context->ll = 1;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Drop writes.  Map reads as normal.
 | 
			
		||||
		 * Map reads as normal.
 | 
			
		||||
		 */
 | 
			
		||||
		if (bio_data_dir(bio) == READ)
 | 
			
		||||
			goto map_bio;
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Drop writes?
 | 
			
		||||
		 */
 | 
			
		||||
		if (test_bit(DROP_WRITES, &fc->flags)) {
 | 
			
		||||
			if (rw == WRITE) {
 | 
			
		||||
				bio_endio(bio, 0);
 | 
			
		||||
				return DM_MAPIO_SUBMITTED;
 | 
			
		||||
			}
 | 
			
		||||
			bio_endio(bio, 0);
 | 
			
		||||
			return DM_MAPIO_SUBMITTED;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Corrupt matching writes.
 | 
			
		||||
		 */
 | 
			
		||||
		if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
 | 
			
		||||
			if (all_corrupt_bio_flags_match(bio, fc))
 | 
			
		||||
				corrupt_bio_data(bio, fc);
 | 
			
		||||
			goto map_bio;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		 * Default setting errors all I/O.
 | 
			
		||||
		 * By default, error all I/O.
 | 
			
		||||
		 */
 | 
			
		||||
		return -EIO;
 | 
			
		||||
	}
 | 
			
		||||
| 
						 | 
				
			
			@ -213,6 +311,24 @@ static int flakey_map(struct dm_target *ti, struct bio *bio,
 | 
			
		|||
	return DM_MAPIO_REMAPPED;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int flakey_end_io(struct dm_target *ti, struct bio *bio,
 | 
			
		||||
			 int error, union map_info *map_context)
 | 
			
		||||
{
 | 
			
		||||
	struct flakey_c *fc = ti->private;
 | 
			
		||||
	unsigned bio_submitted_while_down = map_context->ll;
 | 
			
		||||
 | 
			
		||||
	/*
 | 
			
		||||
	 * Corrupt successful READs while in down state.
 | 
			
		||||
	 * If flags were specified, only corrupt those that match.
 | 
			
		||||
	 */
 | 
			
		||||
	if (!error && bio_submitted_while_down &&
 | 
			
		||||
	    (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
 | 
			
		||||
	    all_corrupt_bio_flags_match(bio, fc))
 | 
			
		||||
		corrupt_bio_data(bio, fc);
 | 
			
		||||
 | 
			
		||||
	return error;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int flakey_status(struct dm_target *ti, status_type_t type,
 | 
			
		||||
			 char *result, unsigned int maxlen)
 | 
			
		||||
{
 | 
			
		||||
| 
						 | 
				
			
			@ -231,9 +347,17 @@ static int flakey_status(struct dm_target *ti, status_type_t type,
 | 
			
		|||
		       fc->down_interval);
 | 
			
		||||
 | 
			
		||||
		drop_writes = test_bit(DROP_WRITES, &fc->flags);
 | 
			
		||||
		DMEMIT("%u ", drop_writes);
 | 
			
		||||
		DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
 | 
			
		||||
 | 
			
		||||
		if (drop_writes)
 | 
			
		||||
			DMEMIT("drop_writes ");
 | 
			
		||||
 | 
			
		||||
		if (fc->corrupt_bio_byte)
 | 
			
		||||
			DMEMIT("corrupt_bio_byte %u %c %u %u ",
 | 
			
		||||
			       fc->corrupt_bio_byte,
 | 
			
		||||
			       (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
 | 
			
		||||
			       fc->corrupt_bio_value, fc->corrupt_bio_flags);
 | 
			
		||||
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return 0;
 | 
			
		||||
| 
						 | 
				
			
			@ -275,6 +399,7 @@ static struct target_type flakey_target = {
 | 
			
		|||
	.ctr    = flakey_ctr,
 | 
			
		||||
	.dtr    = flakey_dtr,
 | 
			
		||||
	.map    = flakey_map,
 | 
			
		||||
	.end_io = flakey_end_io,
 | 
			
		||||
	.status = flakey_status,
 | 
			
		||||
	.ioctl	= flakey_ioctl,
 | 
			
		||||
	.merge	= flakey_merge,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue