From: Mike Snitzer 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 Signed-off-by: Alasdair G Kergon --- drivers/md/dm-flakey.c | 35 +++++++++++++++++++++++++++++++++-- Documentation/device-mapper/dm-flakey.txt | 19 +++ drivers/md/dm-flakey.c | 155 +++++++++++++++++++++++++++--- 2 files changed, 159 insertions(+), 15 deletions(-) Index: linux-3.0/Documentation/device-mapper/dm-flakey.txt =================================================================== --- linux-3.0.orig/Documentation/device-mapper/dm-flakey.txt +++ linux-3.0/Documentation/device-mapper/dm-flakey.txt @@ -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 : + During , replace of the data of + each matching bio with . + + : The offset of the byte to replace. + Counting starts at 1, to replace the first byte. + : Either 'r' to corrupt reads or 'w' to corrupt writes. + 'w' is incompatible with drop_writes. + : The value (from 0-255) to write. + : 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 Index: linux-3.0/drivers/md/dm-flakey.c =================================================================== --- linux-3.0.orig/drivers/md/dm-flakey.c +++ linux-3.0/drivers/md/dm-flakey.c @@ -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_ 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_ 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_ continue; } + /* + * corrupt_bio_byte + */ + 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_ * * Feature args: * [drop_writes] + * [corrupt_bio_byte ] + * + * 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_tar 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 @@ map_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_targe 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,