diff options
author | Mike Snitzer <snitzer@redhat.com> | 2011-08-02 07:32:06 -0400 |
---|---|---|
committer | Alasdair G Kergon <agk@redhat.com> | 2011-08-02 07:32:06 -0400 |
commit | a3998799fb4df0b0af8271a7d50c4269032397aa (patch) | |
tree | 2a0dc0a082519afd70cc991da36f620bc21272fe /drivers/md | |
parent | b26f5e3d7127487e934758c1fbe05d683b082cb0 (diff) |
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>
Diffstat (limited to 'drivers/md')
-rw-r--r-- | drivers/md/dm-flakey.c | 155 |
1 files changed, 140 insertions, 15 deletions
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index e7c4c2a64f4b..89f73ca22cfa 100644 --- a/drivers/md/dm-flakey.c +++ b/drivers/md/dm-flakey.c | |||
@@ -1,6 +1,6 @@ | |||
1 | /* | 1 | /* |
2 | * Copyright (C) 2003 Sistina Software (UK) Limited. | 2 | * Copyright (C) 2003 Sistina Software (UK) Limited. |
3 | * Copyright (C) 2004, 2010 Red Hat, Inc. All rights reserved. | 3 | * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved. |
4 | * | 4 | * |
5 | * This file is released under the GPL. | 5 | * This file is released under the GPL. |
6 | */ | 6 | */ |
@@ -15,6 +15,9 @@ | |||
15 | 15 | ||
16 | #define DM_MSG_PREFIX "flakey" | 16 | #define DM_MSG_PREFIX "flakey" |
17 | 17 | ||
18 | #define all_corrupt_bio_flags_match(bio, fc) \ | ||
19 | (((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags) | ||
20 | |||
18 | /* | 21 | /* |
19 | * Flakey: Used for testing only, simulates intermittent, | 22 | * Flakey: Used for testing only, simulates intermittent, |
20 | * catastrophic device failure. | 23 | * catastrophic device failure. |
@@ -26,6 +29,10 @@ struct flakey_c { | |||
26 | unsigned up_interval; | 29 | unsigned up_interval; |
27 | unsigned down_interval; | 30 | unsigned down_interval; |
28 | unsigned long flags; | 31 | unsigned long flags; |
32 | unsigned corrupt_bio_byte; | ||
33 | unsigned corrupt_bio_rw; | ||
34 | unsigned corrupt_bio_value; | ||
35 | unsigned corrupt_bio_flags; | ||
29 | }; | 36 | }; |
30 | 37 | ||
31 | enum feature_flag_bits { | 38 | enum feature_flag_bits { |
@@ -40,7 +47,10 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, | |||
40 | const char *arg_name; | 47 | const char *arg_name; |
41 | 48 | ||
42 | static struct dm_arg _args[] = { | 49 | static struct dm_arg _args[] = { |
43 | {0, 1, "Invalid number of feature args"}, | 50 | {0, 6, "Invalid number of feature args"}, |
51 | {1, UINT_MAX, "Invalid corrupt bio byte"}, | ||
52 | {0, 255, "Invalid corrupt value to write into bio byte (0-255)"}, | ||
53 | {0, UINT_MAX, "Invalid corrupt bio flags mask"}, | ||
44 | }; | 54 | }; |
45 | 55 | ||
46 | /* No feature arguments supplied. */ | 56 | /* No feature arguments supplied. */ |
@@ -49,9 +59,9 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, | |||
49 | 59 | ||
50 | r = dm_read_arg_group(_args, as, &argc, &ti->error); | 60 | r = dm_read_arg_group(_args, as, &argc, &ti->error); |
51 | if (r) | 61 | if (r) |
52 | return -EINVAL; | 62 | return r; |
53 | 63 | ||
54 | while (argc && !r) { | 64 | while (argc) { |
55 | arg_name = dm_shift_arg(as); | 65 | arg_name = dm_shift_arg(as); |
56 | argc--; | 66 | argc--; |
57 | 67 | ||
@@ -67,11 +77,61 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, | |||
67 | continue; | 77 | continue; |
68 | } | 78 | } |
69 | 79 | ||
80 | /* | ||
81 | * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags> | ||
82 | */ | ||
83 | if (!strcasecmp(arg_name, "corrupt_bio_byte")) { | ||
84 | if (!argc) | ||
85 | ti->error = "Feature corrupt_bio_byte requires parameters"; | ||
86 | |||
87 | r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error); | ||
88 | if (r) | ||
89 | return r; | ||
90 | argc--; | ||
91 | |||
92 | /* | ||
93 | * Direction r or w? | ||
94 | */ | ||
95 | arg_name = dm_shift_arg(as); | ||
96 | if (!strcasecmp(arg_name, "w")) | ||
97 | fc->corrupt_bio_rw = WRITE; | ||
98 | else if (!strcasecmp(arg_name, "r")) | ||
99 | fc->corrupt_bio_rw = READ; | ||
100 | else { | ||
101 | ti->error = "Invalid corrupt bio direction (r or w)"; | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | argc--; | ||
105 | |||
106 | /* | ||
107 | * Value of byte (0-255) to write in place of correct one. | ||
108 | */ | ||
109 | r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error); | ||
110 | if (r) | ||
111 | return r; | ||
112 | argc--; | ||
113 | |||
114 | /* | ||
115 | * Only corrupt bios with these flags set. | ||
116 | */ | ||
117 | r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error); | ||
118 | if (r) | ||
119 | return r; | ||
120 | argc--; | ||
121 | |||
122 | continue; | ||
123 | } | ||
124 | |||
70 | ti->error = "Unrecognised flakey feature requested"; | 125 | ti->error = "Unrecognised flakey feature requested"; |
71 | r = -EINVAL; | 126 | return -EINVAL; |
72 | } | 127 | } |
73 | 128 | ||
74 | return r; | 129 | if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) { |
130 | ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set"; | ||
131 | return -EINVAL; | ||
132 | } | ||
133 | |||
134 | return 0; | ||
75 | } | 135 | } |
76 | 136 | ||
77 | /* | 137 | /* |
@@ -80,6 +140,11 @@ static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, | |||
80 | * | 140 | * |
81 | * Feature args: | 141 | * Feature args: |
82 | * [drop_writes] | 142 | * [drop_writes] |
143 | * [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>] | ||
144 | * | ||
145 | * Nth_byte starts from 1 for the first byte. | ||
146 | * Direction is r for READ or w for WRITE. | ||
147 | * bio_flags is ignored if 0. | ||
83 | */ | 148 | */ |
84 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 149 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
85 | { | 150 | { |
@@ -178,31 +243,64 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |||
178 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | 243 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); |
179 | } | 244 | } |
180 | 245 | ||
246 | static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) | ||
247 | { | ||
248 | unsigned bio_bytes = bio_cur_bytes(bio); | ||
249 | char *data = bio_data(bio); | ||
250 | |||
251 | /* | ||
252 | * Overwrite the Nth byte of the data returned. | ||
253 | */ | ||
254 | if (data && bio_bytes >= fc->corrupt_bio_byte) { | ||
255 | data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value; | ||
256 | |||
257 | DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " | ||
258 | "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n", | ||
259 | bio, fc->corrupt_bio_value, fc->corrupt_bio_byte, | ||
260 | (bio_data_dir(bio) == WRITE) ? 'w' : 'r', | ||
261 | bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes); | ||
262 | } | ||
263 | } | ||
264 | |||
181 | static int flakey_map(struct dm_target *ti, struct bio *bio, | 265 | static int flakey_map(struct dm_target *ti, struct bio *bio, |
182 | union map_info *map_context) | 266 | union map_info *map_context) |
183 | { | 267 | { |
184 | struct flakey_c *fc = ti->private; | 268 | struct flakey_c *fc = ti->private; |
185 | unsigned elapsed; | 269 | unsigned elapsed; |
186 | unsigned rw; | ||
187 | 270 | ||
188 | /* Are we alive ? */ | 271 | /* Are we alive ? */ |
189 | elapsed = (jiffies - fc->start_time) / HZ; | 272 | elapsed = (jiffies - fc->start_time) / HZ; |
190 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { | 273 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { |
191 | rw = bio_data_dir(bio); | 274 | /* |
275 | * Flag this bio as submitted while down. | ||
276 | */ | ||
277 | map_context->ll = 1; | ||
278 | |||
279 | /* | ||
280 | * Map reads as normal. | ||
281 | */ | ||
282 | if (bio_data_dir(bio) == READ) | ||
283 | goto map_bio; | ||
192 | 284 | ||
193 | /* | 285 | /* |
194 | * Drop writes. Map reads as normal. | 286 | * Drop writes? |
195 | */ | 287 | */ |
196 | if (test_bit(DROP_WRITES, &fc->flags)) { | 288 | if (test_bit(DROP_WRITES, &fc->flags)) { |
197 | if (rw == WRITE) { | 289 | bio_endio(bio, 0); |
198 | bio_endio(bio, 0); | 290 | return DM_MAPIO_SUBMITTED; |
199 | return DM_MAPIO_SUBMITTED; | 291 | } |
200 | } | 292 | |
293 | /* | ||
294 | * Corrupt matching writes. | ||
295 | */ | ||
296 | if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) { | ||
297 | if (all_corrupt_bio_flags_match(bio, fc)) | ||
298 | corrupt_bio_data(bio, fc); | ||
201 | goto map_bio; | 299 | goto map_bio; |
202 | } | 300 | } |
203 | 301 | ||
204 | /* | 302 | /* |
205 | * Default setting errors all I/O. | 303 | * By default, error all I/O. |
206 | */ | 304 | */ |
207 | return -EIO; | 305 | return -EIO; |
208 | } | 306 | } |
@@ -213,6 +311,24 @@ map_bio: | |||
213 | return DM_MAPIO_REMAPPED; | 311 | return DM_MAPIO_REMAPPED; |
214 | } | 312 | } |
215 | 313 | ||
314 | static int flakey_end_io(struct dm_target *ti, struct bio *bio, | ||
315 | int error, union map_info *map_context) | ||
316 | { | ||
317 | struct flakey_c *fc = ti->private; | ||
318 | unsigned bio_submitted_while_down = map_context->ll; | ||
319 | |||
320 | /* | ||
321 | * Corrupt successful READs while in down state. | ||
322 | * If flags were specified, only corrupt those that match. | ||
323 | */ | ||
324 | if (!error && bio_submitted_while_down && | ||
325 | (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) && | ||
326 | all_corrupt_bio_flags_match(bio, fc)) | ||
327 | corrupt_bio_data(bio, fc); | ||
328 | |||
329 | return error; | ||
330 | } | ||
331 | |||
216 | static int flakey_status(struct dm_target *ti, status_type_t type, | 332 | static int flakey_status(struct dm_target *ti, status_type_t type, |
217 | char *result, unsigned int maxlen) | 333 | char *result, unsigned int maxlen) |
218 | { | 334 | { |
@@ -231,9 +347,17 @@ static int flakey_status(struct dm_target *ti, status_type_t type, | |||
231 | fc->down_interval); | 347 | fc->down_interval); |
232 | 348 | ||
233 | drop_writes = test_bit(DROP_WRITES, &fc->flags); | 349 | drop_writes = test_bit(DROP_WRITES, &fc->flags); |
234 | DMEMIT("%u ", drop_writes); | 350 | DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5); |
351 | |||
235 | if (drop_writes) | 352 | if (drop_writes) |
236 | DMEMIT("drop_writes "); | 353 | DMEMIT("drop_writes "); |
354 | |||
355 | if (fc->corrupt_bio_byte) | ||
356 | DMEMIT("corrupt_bio_byte %u %c %u %u ", | ||
357 | fc->corrupt_bio_byte, | ||
358 | (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r', | ||
359 | fc->corrupt_bio_value, fc->corrupt_bio_flags); | ||
360 | |||
237 | break; | 361 | break; |
238 | } | 362 | } |
239 | return 0; | 363 | return 0; |
@@ -275,6 +399,7 @@ static struct target_type flakey_target = { | |||
275 | .ctr = flakey_ctr, | 399 | .ctr = flakey_ctr, |
276 | .dtr = flakey_dtr, | 400 | .dtr = flakey_dtr, |
277 | .map = flakey_map, | 401 | .map = flakey_map, |
402 | .end_io = flakey_end_io, | ||
278 | .status = flakey_status, | 403 | .status = flakey_status, |
279 | .ioctl = flakey_ioctl, | 404 | .ioctl = flakey_ioctl, |
280 | .merge = flakey_merge, | 405 | .merge = flakey_merge, |