diff options
Diffstat (limited to 'drivers/md/dm-flakey.c')
-rw-r--r-- | drivers/md/dm-flakey.c | 272 |
1 files changed, 246 insertions, 26 deletions
diff --git a/drivers/md/dm-flakey.c b/drivers/md/dm-flakey.c index ea790623c30..f84c08029b2 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. |
@@ -25,60 +28,191 @@ struct flakey_c { | |||
25 | sector_t start; | 28 | sector_t start; |
26 | unsigned up_interval; | 29 | unsigned up_interval; |
27 | unsigned down_interval; | 30 | unsigned down_interval; |
31 | unsigned long flags; | ||
32 | unsigned corrupt_bio_byte; | ||
33 | unsigned corrupt_bio_rw; | ||
34 | unsigned corrupt_bio_value; | ||
35 | unsigned corrupt_bio_flags; | ||
36 | }; | ||
37 | |||
38 | enum feature_flag_bits { | ||
39 | DROP_WRITES | ||
28 | }; | 40 | }; |
29 | 41 | ||
42 | static int parse_features(struct dm_arg_set *as, struct flakey_c *fc, | ||
43 | struct dm_target *ti) | ||
44 | { | ||
45 | int r; | ||
46 | unsigned argc; | ||
47 | const char *arg_name; | ||
48 | |||
49 | static struct dm_arg _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"}, | ||
54 | }; | ||
55 | |||
56 | /* No feature arguments supplied. */ | ||
57 | if (!as->argc) | ||
58 | return 0; | ||
59 | |||
60 | r = dm_read_arg_group(_args, as, &argc, &ti->error); | ||
61 | if (r) | ||
62 | return r; | ||
63 | |||
64 | while (argc) { | ||
65 | arg_name = dm_shift_arg(as); | ||
66 | argc--; | ||
67 | |||
68 | /* | ||
69 | * drop_writes | ||
70 | */ | ||
71 | if (!strcasecmp(arg_name, "drop_writes")) { | ||
72 | if (test_and_set_bit(DROP_WRITES, &fc->flags)) { | ||
73 | ti->error = "Feature drop_writes duplicated"; | ||
74 | return -EINVAL; | ||
75 | } | ||
76 | |||
77 | continue; | ||
78 | } | ||
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 | return -EINVAL; | ||
87 | } | ||
88 | |||
89 | r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error); | ||
90 | if (r) | ||
91 | return r; | ||
92 | argc--; | ||
93 | |||
94 | /* | ||
95 | * Direction r or w? | ||
96 | */ | ||
97 | arg_name = dm_shift_arg(as); | ||
98 | if (!strcasecmp(arg_name, "w")) | ||
99 | fc->corrupt_bio_rw = WRITE; | ||
100 | else if (!strcasecmp(arg_name, "r")) | ||
101 | fc->corrupt_bio_rw = READ; | ||
102 | else { | ||
103 | ti->error = "Invalid corrupt bio direction (r or w)"; | ||
104 | return -EINVAL; | ||
105 | } | ||
106 | argc--; | ||
107 | |||
108 | /* | ||
109 | * Value of byte (0-255) to write in place of correct one. | ||
110 | */ | ||
111 | r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error); | ||
112 | if (r) | ||
113 | return r; | ||
114 | argc--; | ||
115 | |||
116 | /* | ||
117 | * Only corrupt bios with these flags set. | ||
118 | */ | ||
119 | r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error); | ||
120 | if (r) | ||
121 | return r; | ||
122 | argc--; | ||
123 | |||
124 | continue; | ||
125 | } | ||
126 | |||
127 | ti->error = "Unrecognised flakey feature requested"; | ||
128 | return -EINVAL; | ||
129 | } | ||
130 | |||
131 | if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) { | ||
132 | ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set"; | ||
133 | return -EINVAL; | ||
134 | } | ||
135 | |||
136 | return 0; | ||
137 | } | ||
138 | |||
30 | /* | 139 | /* |
31 | * Construct a flakey mapping: <dev_path> <offset> <up interval> <down interval> | 140 | * Construct a flakey mapping: |
141 | * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*] | ||
142 | * | ||
143 | * Feature args: | ||
144 | * [drop_writes] | ||
145 | * [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>] | ||
146 | * | ||
147 | * Nth_byte starts from 1 for the first byte. | ||
148 | * Direction is r for READ or w for WRITE. | ||
149 | * bio_flags is ignored if 0. | ||
32 | */ | 150 | */ |
33 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) | 151 | static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv) |
34 | { | 152 | { |
153 | static struct dm_arg _args[] = { | ||
154 | {0, UINT_MAX, "Invalid up interval"}, | ||
155 | {0, UINT_MAX, "Invalid down interval"}, | ||
156 | }; | ||
157 | |||
158 | int r; | ||
35 | struct flakey_c *fc; | 159 | struct flakey_c *fc; |
36 | unsigned long long tmp; | 160 | unsigned long long tmpll; |
161 | struct dm_arg_set as; | ||
162 | const char *devname; | ||
37 | 163 | ||
38 | if (argc != 4) { | 164 | as.argc = argc; |
39 | ti->error = "dm-flakey: Invalid argument count"; | 165 | as.argv = argv; |
166 | |||
167 | if (argc < 4) { | ||
168 | ti->error = "Invalid argument count"; | ||
40 | return -EINVAL; | 169 | return -EINVAL; |
41 | } | 170 | } |
42 | 171 | ||
43 | fc = kmalloc(sizeof(*fc), GFP_KERNEL); | 172 | fc = kzalloc(sizeof(*fc), GFP_KERNEL); |
44 | if (!fc) { | 173 | if (!fc) { |
45 | ti->error = "dm-flakey: Cannot allocate linear context"; | 174 | ti->error = "Cannot allocate linear context"; |
46 | return -ENOMEM; | 175 | return -ENOMEM; |
47 | } | 176 | } |
48 | fc->start_time = jiffies; | 177 | fc->start_time = jiffies; |
49 | 178 | ||
50 | if (sscanf(argv[1], "%llu", &tmp) != 1) { | 179 | devname = dm_shift_arg(&as); |
51 | ti->error = "dm-flakey: Invalid device sector"; | 180 | |
181 | if (sscanf(dm_shift_arg(&as), "%llu", &tmpll) != 1) { | ||
182 | ti->error = "Invalid device sector"; | ||
52 | goto bad; | 183 | goto bad; |
53 | } | 184 | } |
54 | fc->start = tmp; | 185 | fc->start = tmpll; |
55 | 186 | ||
56 | if (sscanf(argv[2], "%u", &fc->up_interval) != 1) { | 187 | r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error); |
57 | ti->error = "dm-flakey: Invalid up interval"; | 188 | if (r) |
58 | goto bad; | 189 | goto bad; |
59 | } | ||
60 | 190 | ||
61 | if (sscanf(argv[3], "%u", &fc->down_interval) != 1) { | 191 | r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error); |
62 | ti->error = "dm-flakey: Invalid down interval"; | 192 | if (r) |
63 | goto bad; | 193 | goto bad; |
64 | } | ||
65 | 194 | ||
66 | if (!(fc->up_interval + fc->down_interval)) { | 195 | if (!(fc->up_interval + fc->down_interval)) { |
67 | ti->error = "dm-flakey: Total (up + down) interval is zero"; | 196 | ti->error = "Total (up + down) interval is zero"; |
68 | goto bad; | 197 | goto bad; |
69 | } | 198 | } |
70 | 199 | ||
71 | if (fc->up_interval + fc->down_interval < fc->up_interval) { | 200 | if (fc->up_interval + fc->down_interval < fc->up_interval) { |
72 | ti->error = "dm-flakey: Interval overflow"; | 201 | ti->error = "Interval overflow"; |
73 | goto bad; | 202 | goto bad; |
74 | } | 203 | } |
75 | 204 | ||
76 | if (dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &fc->dev)) { | 205 | r = parse_features(&as, fc, ti); |
77 | ti->error = "dm-flakey: Device lookup failed"; | 206 | if (r) |
207 | goto bad; | ||
208 | |||
209 | if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) { | ||
210 | ti->error = "Device lookup failed"; | ||
78 | goto bad; | 211 | goto bad; |
79 | } | 212 | } |
80 | 213 | ||
81 | ti->num_flush_requests = 1; | 214 | ti->num_flush_requests = 1; |
215 | ti->num_discard_requests = 1; | ||
82 | ti->private = fc; | 216 | ti->private = fc; |
83 | return 0; | 217 | return 0; |
84 | 218 | ||
@@ -99,7 +233,7 @@ static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector) | |||
99 | { | 233 | { |
100 | struct flakey_c *fc = ti->private; | 234 | struct flakey_c *fc = ti->private; |
101 | 235 | ||
102 | return fc->start + (bi_sector - ti->begin); | 236 | return fc->start + dm_target_offset(ti, bi_sector); |
103 | } | 237 | } |
104 | 238 | ||
105 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | 239 | static void flakey_map_bio(struct dm_target *ti, struct bio *bio) |
@@ -111,6 +245,25 @@ static void flakey_map_bio(struct dm_target *ti, struct bio *bio) | |||
111 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); | 245 | bio->bi_sector = flakey_map_sector(ti, bio->bi_sector); |
112 | } | 246 | } |
113 | 247 | ||
248 | static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc) | ||
249 | { | ||
250 | unsigned bio_bytes = bio_cur_bytes(bio); | ||
251 | char *data = bio_data(bio); | ||
252 | |||
253 | /* | ||
254 | * Overwrite the Nth byte of the data returned. | ||
255 | */ | ||
256 | if (data && bio_bytes >= fc->corrupt_bio_byte) { | ||
257 | data[fc->corrupt_bio_byte - 1] = fc->corrupt_bio_value; | ||
258 | |||
259 | DMDEBUG("Corrupting data bio=%p by writing %u to byte %u " | ||
260 | "(rw=%c bi_rw=%lu bi_sector=%llu cur_bytes=%u)\n", | ||
261 | bio, fc->corrupt_bio_value, fc->corrupt_bio_byte, | ||
262 | (bio_data_dir(bio) == WRITE) ? 'w' : 'r', | ||
263 | bio->bi_rw, (unsigned long long)bio->bi_sector, bio_bytes); | ||
264 | } | ||
265 | } | ||
266 | |||
114 | static int flakey_map(struct dm_target *ti, struct bio *bio, | 267 | static int flakey_map(struct dm_target *ti, struct bio *bio, |
115 | union map_info *map_context) | 268 | union map_info *map_context) |
116 | { | 269 | { |
@@ -119,18 +272,71 @@ static int flakey_map(struct dm_target *ti, struct bio *bio, | |||
119 | 272 | ||
120 | /* Are we alive ? */ | 273 | /* Are we alive ? */ |
121 | elapsed = (jiffies - fc->start_time) / HZ; | 274 | elapsed = (jiffies - fc->start_time) / HZ; |
122 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) | 275 | if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) { |
276 | /* | ||
277 | * Flag this bio as submitted while down. | ||
278 | */ | ||
279 | map_context->ll = 1; | ||
280 | |||
281 | /* | ||
282 | * Map reads as normal. | ||
283 | */ | ||
284 | if (bio_data_dir(bio) == READ) | ||
285 | goto map_bio; | ||
286 | |||
287 | /* | ||
288 | * Drop writes? | ||
289 | */ | ||
290 | if (test_bit(DROP_WRITES, &fc->flags)) { | ||
291 | bio_endio(bio, 0); | ||
292 | return DM_MAPIO_SUBMITTED; | ||
293 | } | ||
294 | |||
295 | /* | ||
296 | * Corrupt matching writes. | ||
297 | */ | ||
298 | if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) { | ||
299 | if (all_corrupt_bio_flags_match(bio, fc)) | ||
300 | corrupt_bio_data(bio, fc); | ||
301 | goto map_bio; | ||
302 | } | ||
303 | |||
304 | /* | ||
305 | * By default, error all I/O. | ||
306 | */ | ||
123 | return -EIO; | 307 | return -EIO; |
308 | } | ||
124 | 309 | ||
310 | map_bio: | ||
125 | flakey_map_bio(ti, bio); | 311 | flakey_map_bio(ti, bio); |
126 | 312 | ||
127 | return DM_MAPIO_REMAPPED; | 313 | return DM_MAPIO_REMAPPED; |
128 | } | 314 | } |
129 | 315 | ||
316 | static int flakey_end_io(struct dm_target *ti, struct bio *bio, | ||
317 | int error, union map_info *map_context) | ||
318 | { | ||
319 | struct flakey_c *fc = ti->private; | ||
320 | unsigned bio_submitted_while_down = map_context->ll; | ||
321 | |||
322 | /* | ||
323 | * Corrupt successful READs while in down state. | ||
324 | * If flags were specified, only corrupt those that match. | ||
325 | */ | ||
326 | if (!error && bio_submitted_while_down && | ||
327 | (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) && | ||
328 | all_corrupt_bio_flags_match(bio, fc)) | ||
329 | corrupt_bio_data(bio, fc); | ||
330 | |||
331 | return error; | ||
332 | } | ||
333 | |||
130 | static int flakey_status(struct dm_target *ti, status_type_t type, | 334 | static int flakey_status(struct dm_target *ti, status_type_t type, |
131 | char *result, unsigned int maxlen) | 335 | char *result, unsigned int maxlen) |
132 | { | 336 | { |
337 | unsigned sz = 0; | ||
133 | struct flakey_c *fc = ti->private; | 338 | struct flakey_c *fc = ti->private; |
339 | unsigned drop_writes; | ||
134 | 340 | ||
135 | switch (type) { | 341 | switch (type) { |
136 | case STATUSTYPE_INFO: | 342 | case STATUSTYPE_INFO: |
@@ -138,9 +344,22 @@ static int flakey_status(struct dm_target *ti, status_type_t type, | |||
138 | break; | 344 | break; |
139 | 345 | ||
140 | case STATUSTYPE_TABLE: | 346 | case STATUSTYPE_TABLE: |
141 | snprintf(result, maxlen, "%s %llu %u %u", fc->dev->name, | 347 | DMEMIT("%s %llu %u %u ", fc->dev->name, |
142 | (unsigned long long)fc->start, fc->up_interval, | 348 | (unsigned long long)fc->start, fc->up_interval, |
143 | fc->down_interval); | 349 | fc->down_interval); |
350 | |||
351 | drop_writes = test_bit(DROP_WRITES, &fc->flags); | ||
352 | DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5); | ||
353 | |||
354 | if (drop_writes) | ||
355 | DMEMIT("drop_writes "); | ||
356 | |||
357 | if (fc->corrupt_bio_byte) | ||
358 | DMEMIT("corrupt_bio_byte %u %c %u %u ", | ||
359 | fc->corrupt_bio_byte, | ||
360 | (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r', | ||
361 | fc->corrupt_bio_value, fc->corrupt_bio_flags); | ||
362 | |||
144 | break; | 363 | break; |
145 | } | 364 | } |
146 | return 0; | 365 | return 0; |
@@ -177,11 +396,12 @@ static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_ | |||
177 | 396 | ||
178 | static struct target_type flakey_target = { | 397 | static struct target_type flakey_target = { |
179 | .name = "flakey", | 398 | .name = "flakey", |
180 | .version = {1, 1, 0}, | 399 | .version = {1, 2, 0}, |
181 | .module = THIS_MODULE, | 400 | .module = THIS_MODULE, |
182 | .ctr = flakey_ctr, | 401 | .ctr = flakey_ctr, |
183 | .dtr = flakey_dtr, | 402 | .dtr = flakey_dtr, |
184 | .map = flakey_map, | 403 | .map = flakey_map, |
404 | .end_io = flakey_end_io, | ||
185 | .status = flakey_status, | 405 | .status = flakey_status, |
186 | .ioctl = flakey_ioctl, | 406 | .ioctl = flakey_ioctl, |
187 | .merge = flakey_merge, | 407 | .merge = flakey_merge, |