From: Mike Snitzer Use .end_io hook to corrupt_bio_data() after reads complete. User may specify a series of bio->bi_rw flags that must be seen in order to trigger corruption (e.g. 33 = REQ_WRITE|REQ_META). Signed-off-by: Mike Snitzer --- drivers/md/dm-flakey.c | 40 ++++++++++++++++++++++++++++++++++------ drivers/md/dm-flakey.c | 51 ++++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 44 insertions(+), 7 deletions(-) Index: linux-2.6.39/drivers/md/dm-flakey.c =================================================================== --- linux-2.6.39.orig/drivers/md/dm-flakey.c +++ linux-2.6.39/drivers/md/dm-flakey.c @@ -28,6 +28,7 @@ struct flakey_c { unsigned down_interval; unsigned long flags; unsigned corrupt_bio_byte; + unsigned corrupt_bio_flags; }; enum feature_flag_bits { @@ -42,8 +43,9 @@ static int parse_features(struct dm_arg_ const char *arg_name; static struct dm_arg _args[] = { - {0, 3, "invalid number of feature args"}, + {0, 4, "invalid number of feature args"}, {1, UINT_MAX, "invalid corrupt bio byte value"}, + {0, UINT_MAX, "invalid corrupt bio flags mask"}, }; /* No feature arguments supplied. */ @@ -58,12 +60,20 @@ static int parse_features(struct dm_arg_ arg_name = dm_shift_arg(as); argc--; - /* corrupt_bio_byte */ + /* corrupt_bio_byte */ if (!strnicmp(arg_name, MESG_STR("corrupt_bio_byte")) && (argc >= 1)) { r = dm_read_arg(_args + 1, dm_shift_arg(as), &fc->corrupt_bio_byte, &ti->error); argc--; + + /* + * Only corrupt bios that have specific bi_rw flag(s), + * e.g.: READ=0 or REQ_WRITE=1|REQ_META=32 + */ + r = dm_read_arg(_args + 2, dm_shift_arg(as), + &fc->corrupt_bio_flags, &ti->error); + argc--; continue; } @@ -180,14 +190,20 @@ static void flakey_map_bio(struct dm_tar bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); } +#define corrupt_bio_data_dir(fc) ((fc)->corrupt_bio_flags & 1) +#define all_corrupt_bio_flags_match(fc, bio) \ + (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags) + static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) { unsigned bio_bytes = bio_cur_bytes(bio); char *data = bio_data(bio); /* write 0 to the specified Nth byte of the bio */ - if (data && bio_bytes >= fc->corrupt_bio_byte) + if (data && bio_bytes >= fc->corrupt_bio_byte) { data[fc->corrupt_bio_byte - 1] = 0; + DMDEBUG("corrupting data rw=%lu\n", bio_data_dir(bio)); + } } static int flakey_map(struct dm_target *ti, struct bio *bio, @@ -203,9 +219,13 @@ static int flakey_map(struct dm_target * rw = bio_data_dir(bio); if (fc->corrupt_bio_byte) { - /* corrupt writes but don't touch reads */ - if (rw == WRITE) + /* corrupt matching writes, defer reads until end_io */ + if (rw == WRITE && rw == corrupt_bio_data_dir(fc) && + all_corrupt_bio_flags_match(fc, bio)) corrupt_bio_data(bio, fc); + + /* flag this bio as submitted while down */ + map_context->ll = 1; goto map_bio; } if (test_bit(DROP_WRITES, &fc->flags)) { @@ -227,6 +247,21 @@ 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; + unsigned rw = bio_data_dir(bio); + + if (!error && bio_submitted_while_down) + if (rw == READ && rw == corrupt_bio_data_dir(fc) && + all_corrupt_bio_flags_match(fc, bio)) + corrupt_bio_data(bio, fc); + + return error; +} + static int flakey_status(struct dm_target *ti, status_type_t type, char *result, unsigned int maxlen) { @@ -245,10 +280,11 @@ static int flakey_status(struct dm_targe fc->down_interval); drop_writes = test_bit(DROP_WRITES, &fc->flags); - DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 2); + DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 3); if (fc->corrupt_bio_byte) - DMEMIT("corrupt_bio_byte %u ", fc->corrupt_bio_byte); + DMEMIT("corrupt_bio_byte %u %u ", fc->corrupt_bio_byte, + fc->corrupt_bio_flags); if (drop_writes) DMEMIT("drop_writes "); break; @@ -292,6 +328,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,