Blame view

drivers/md/dm-flakey.c 9.94 KB
3407ef526   Josef Bacik   dm: add flakey ta...
1
2
  /*
   * Copyright (C) 2003 Sistina Software (UK) Limited.
a3998799f   Mike Snitzer   dm flakey: add co...
3
   * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
3407ef526   Josef Bacik   dm: add flakey ta...
4
5
6
7
8
9
10
11
12
13
14
15
16
   *
   * This file is released under the GPL.
   */
  
  #include <linux/device-mapper.h>
  
  #include <linux/module.h>
  #include <linux/init.h>
  #include <linux/blkdev.h>
  #include <linux/bio.h>
  #include <linux/slab.h>
  
  #define DM_MSG_PREFIX "flakey"
a3998799f   Mike Snitzer   dm flakey: add co...
17
18
  #define all_corrupt_bio_flags_match(bio, fc)	\
  	(((bio)->bi_rw & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
3407ef526   Josef Bacik   dm: add flakey ta...
19
20
21
22
23
24
25
26
27
28
  /*
   * Flakey: Used for testing only, simulates intermittent,
   * catastrophic device failure.
   */
  struct flakey_c {
  	struct dm_dev *dev;
  	unsigned long start_time;
  	sector_t start;
  	unsigned up_interval;
  	unsigned down_interval;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
29
  	unsigned long flags;
a3998799f   Mike Snitzer   dm flakey: add co...
30
31
32
33
  	unsigned corrupt_bio_byte;
  	unsigned corrupt_bio_rw;
  	unsigned corrupt_bio_value;
  	unsigned corrupt_bio_flags;
3407ef526   Josef Bacik   dm: add flakey ta...
34
  };
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
35
36
37
  enum feature_flag_bits {
  	DROP_WRITES
  };
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
38
39
40
  struct per_bio_data {
  	bool bio_submitted;
  };
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
41
42
  static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
  			  struct dm_target *ti)
dfd068b01   Mike Snitzer   dm flakey: suppor...
43
44
45
46
47
48
  {
  	int r;
  	unsigned argc;
  	const char *arg_name;
  
  	static struct dm_arg _args[] = {
a3998799f   Mike Snitzer   dm flakey: add co...
49
50
51
52
  		{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"},
dfd068b01   Mike Snitzer   dm flakey: suppor...
53
54
55
56
57
58
59
60
  	};
  
  	/* No feature arguments supplied. */
  	if (!as->argc)
  		return 0;
  
  	r = dm_read_arg_group(_args, as, &argc, &ti->error);
  	if (r)
a3998799f   Mike Snitzer   dm flakey: add co...
61
  		return r;
dfd068b01   Mike Snitzer   dm flakey: suppor...
62

a3998799f   Mike Snitzer   dm flakey: add co...
63
  	while (argc) {
dfd068b01   Mike Snitzer   dm flakey: suppor...
64
65
  		arg_name = dm_shift_arg(as);
  		argc--;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
66
67
68
69
70
71
72
73
74
75
76
  		/*
  		 * drop_writes
  		 */
  		if (!strcasecmp(arg_name, "drop_writes")) {
  			if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
  				ti->error = "Feature drop_writes duplicated";
  				return -EINVAL;
  			}
  
  			continue;
  		}
a3998799f   Mike Snitzer   dm flakey: add co...
77
78
79
80
  		/*
  		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
  		 */
  		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
68e58a294   Mike Snitzer   dm: flakey fix co...
81
  			if (!argc) {
a3998799f   Mike Snitzer   dm flakey: add co...
82
  				ti->error = "Feature corrupt_bio_byte requires parameters";
68e58a294   Mike Snitzer   dm: flakey fix co...
83
84
  				return -EINVAL;
  			}
a3998799f   Mike Snitzer   dm flakey: add co...
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
  
  			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;
  		}
dfd068b01   Mike Snitzer   dm flakey: suppor...
123
  		ti->error = "Unrecognised flakey feature requested";
a3998799f   Mike Snitzer   dm flakey: add co...
124
  		return -EINVAL;
dfd068b01   Mike Snitzer   dm flakey: suppor...
125
  	}
a3998799f   Mike Snitzer   dm flakey: add co...
126
127
128
129
130
131
  	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;
dfd068b01   Mike Snitzer   dm flakey: suppor...
132
  }
3407ef526   Josef Bacik   dm: add flakey ta...
133
  /*
dfd068b01   Mike Snitzer   dm flakey: suppor...
134
135
   * Construct a flakey mapping:
   * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
136
137
138
   *
   *   Feature args:
   *     [drop_writes]
a3998799f   Mike Snitzer   dm flakey: add co...
139
140
141
142
143
   *     [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.
3407ef526   Josef Bacik   dm: add flakey ta...
144
145
146
   */
  static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
  {
dfd068b01   Mike Snitzer   dm flakey: suppor...
147
148
149
150
151
152
  	static struct dm_arg _args[] = {
  		{0, UINT_MAX, "Invalid up interval"},
  		{0, UINT_MAX, "Invalid down interval"},
  	};
  
  	int r;
3407ef526   Josef Bacik   dm: add flakey ta...
153
  	struct flakey_c *fc;
dfd068b01   Mike Snitzer   dm flakey: suppor...
154
155
156
  	unsigned long long tmpll;
  	struct dm_arg_set as;
  	const char *devname;
31998ef19   Mikulas Patocka   dm: reject traili...
157
  	char dummy;
3407ef526   Josef Bacik   dm: add flakey ta...
158

dfd068b01   Mike Snitzer   dm flakey: suppor...
159
160
161
162
163
  	as.argc = argc;
  	as.argv = argv;
  
  	if (argc < 4) {
  		ti->error = "Invalid argument count";
3407ef526   Josef Bacik   dm: add flakey ta...
164
165
  		return -EINVAL;
  	}
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
166
  	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
3407ef526   Josef Bacik   dm: add flakey ta...
167
  	if (!fc) {
75e3a0f55   Alasdair G Kergon   dm flakey: correc...
168
  		ti->error = "Cannot allocate context";
3407ef526   Josef Bacik   dm: add flakey ta...
169
170
171
  		return -ENOMEM;
  	}
  	fc->start_time = jiffies;
dfd068b01   Mike Snitzer   dm flakey: suppor...
172
  	devname = dm_shift_arg(&as);
31998ef19   Mikulas Patocka   dm: reject traili...
173
  	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1) {
dfd068b01   Mike Snitzer   dm flakey: suppor...
174
  		ti->error = "Invalid device sector";
3407ef526   Josef Bacik   dm: add flakey ta...
175
176
  		goto bad;
  	}
dfd068b01   Mike Snitzer   dm flakey: suppor...
177
  	fc->start = tmpll;
3407ef526   Josef Bacik   dm: add flakey ta...
178

dfd068b01   Mike Snitzer   dm flakey: suppor...
179
180
  	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
  	if (r)
3407ef526   Josef Bacik   dm: add flakey ta...
181
  		goto bad;
3407ef526   Josef Bacik   dm: add flakey ta...
182

dfd068b01   Mike Snitzer   dm flakey: suppor...
183
184
  	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
  	if (r)
3407ef526   Josef Bacik   dm: add flakey ta...
185
  		goto bad;
3407ef526   Josef Bacik   dm: add flakey ta...
186
187
  
  	if (!(fc->up_interval + fc->down_interval)) {
dfd068b01   Mike Snitzer   dm flakey: suppor...
188
  		ti->error = "Total (up + down) interval is zero";
3407ef526   Josef Bacik   dm: add flakey ta...
189
190
191
192
  		goto bad;
  	}
  
  	if (fc->up_interval + fc->down_interval < fc->up_interval) {
dfd068b01   Mike Snitzer   dm flakey: suppor...
193
  		ti->error = "Interval overflow";
3407ef526   Josef Bacik   dm: add flakey ta...
194
195
  		goto bad;
  	}
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
196
  	r = parse_features(&as, fc, ti);
dfd068b01   Mike Snitzer   dm flakey: suppor...
197
198
199
200
201
  	if (r)
  		goto bad;
  
  	if (dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev)) {
  		ti->error = "Device lookup failed";
3407ef526   Josef Bacik   dm: add flakey ta...
202
203
  		goto bad;
  	}
55a62eef8   Alasdair G Kergon   dm: rename reques...
204
205
  	ti->num_flush_bios = 1;
  	ti->num_discard_bios = 1;
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
206
  	ti->per_bio_data_size = sizeof(struct per_bio_data);
3407ef526   Josef Bacik   dm: add flakey ta...
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
  	ti->private = fc;
  	return 0;
  
  bad:
  	kfree(fc);
  	return -EINVAL;
  }
  
  static void flakey_dtr(struct dm_target *ti)
  {
  	struct flakey_c *fc = ti->private;
  
  	dm_put_device(ti, fc->dev);
  	kfree(fc);
  }
  
  static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
  {
  	struct flakey_c *fc = ti->private;
30e4171bf   Mike Snitzer   dm flakey: use dm...
226
  	return fc->start + dm_target_offset(ti, bi_sector);
3407ef526   Josef Bacik   dm: add flakey ta...
227
228
229
230
231
232
233
234
  }
  
  static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
  {
  	struct flakey_c *fc = ti->private;
  
  	bio->bi_bdev = fc->dev->bdev;
  	if (bio_sectors(bio))
4f024f379   Kent Overstreet   block: Abstract o...
235
236
  		bio->bi_iter.bi_sector =
  			flakey_map_sector(ti, bio->bi_iter.bi_sector);
3407ef526   Josef Bacik   dm: add flakey ta...
237
  }
a3998799f   Mike Snitzer   dm flakey: add co...
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
  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)
  ",
  			bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
4f024f379   Kent Overstreet   block: Abstract o...
253
254
  			(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_rw,
  			(unsigned long long)bio->bi_iter.bi_sector, bio_bytes);
a3998799f   Mike Snitzer   dm flakey: add co...
255
256
  	}
  }
7de3ee57d   Mikulas Patocka   dm: remove map_info
257
  static int flakey_map(struct dm_target *ti, struct bio *bio)
3407ef526   Josef Bacik   dm: add flakey ta...
258
259
260
  {
  	struct flakey_c *fc = ti->private;
  	unsigned elapsed;
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
261
262
  	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
  	pb->bio_submitted = false;
3407ef526   Josef Bacik   dm: add flakey ta...
263
264
265
  
  	/* Are we alive ? */
  	elapsed = (jiffies - fc->start_time) / HZ;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
266
  	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
a3998799f   Mike Snitzer   dm flakey: add co...
267
268
269
  		/*
  		 * Flag this bio as submitted while down.
  		 */
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
270
  		pb->bio_submitted = true;
a3998799f   Mike Snitzer   dm flakey: add co...
271
272
273
274
275
276
  
  		/*
  		 * Map reads as normal.
  		 */
  		if (bio_data_dir(bio) == READ)
  			goto map_bio;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
277
278
  
  		/*
a3998799f   Mike Snitzer   dm flakey: add co...
279
  		 * Drop writes?
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
280
281
  		 */
  		if (test_bit(DROP_WRITES, &fc->flags)) {
a3998799f   Mike Snitzer   dm flakey: add co...
282
283
284
285
286
287
288
289
290
291
  			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);
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
292
293
294
295
  			goto map_bio;
  		}
  
  		/*
a3998799f   Mike Snitzer   dm flakey: add co...
296
  		 * By default, error all I/O.
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
297
  		 */
3407ef526   Josef Bacik   dm: add flakey ta...
298
  		return -EIO;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
299
  	}
3407ef526   Josef Bacik   dm: add flakey ta...
300

b26f5e3d7   Mike Snitzer   dm flakey: add dr...
301
  map_bio:
3407ef526   Josef Bacik   dm: add flakey ta...
302
303
304
305
  	flakey_map_bio(ti, bio);
  
  	return DM_MAPIO_REMAPPED;
  }
7de3ee57d   Mikulas Patocka   dm: remove map_info
306
  static int flakey_end_io(struct dm_target *ti, struct bio *bio, int error)
a3998799f   Mike Snitzer   dm flakey: add co...
307
308
  {
  	struct flakey_c *fc = ti->private;
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
309
  	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
a3998799f   Mike Snitzer   dm flakey: add co...
310
311
312
313
314
  
  	/*
  	 * Corrupt successful READs while in down state.
  	 * If flags were specified, only corrupt those that match.
  	 */
c7cfdf597   Mikulas Patocka   dm flakey: dont u...
315
  	if (fc->corrupt_bio_byte && !error && pb->bio_submitted &&
a3998799f   Mike Snitzer   dm flakey: add co...
316
317
318
319
320
321
  	    (bio_data_dir(bio) == READ) && (fc->corrupt_bio_rw == READ) &&
  	    all_corrupt_bio_flags_match(bio, fc))
  		corrupt_bio_data(bio, fc);
  
  	return error;
  }
fd7c092e7   Mikulas Patocka   dm: fix truncated...
322
323
  static void flakey_status(struct dm_target *ti, status_type_t type,
  			  unsigned status_flags, char *result, unsigned maxlen)
3407ef526   Josef Bacik   dm: add flakey ta...
324
  {
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
325
  	unsigned sz = 0;
3407ef526   Josef Bacik   dm: add flakey ta...
326
  	struct flakey_c *fc = ti->private;
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
327
  	unsigned drop_writes;
3407ef526   Josef Bacik   dm: add flakey ta...
328
329
330
331
332
333
334
  
  	switch (type) {
  	case STATUSTYPE_INFO:
  		result[0] = '\0';
  		break;
  
  	case STATUSTYPE_TABLE:
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
335
336
337
338
339
  		DMEMIT("%s %llu %u %u ", fc->dev->name,
  		       (unsigned long long)fc->start, fc->up_interval,
  		       fc->down_interval);
  
  		drop_writes = test_bit(DROP_WRITES, &fc->flags);
a3998799f   Mike Snitzer   dm flakey: add co...
340
  		DMEMIT("%u ", drop_writes + (fc->corrupt_bio_byte > 0) * 5);
b26f5e3d7   Mike Snitzer   dm flakey: add dr...
341
342
  		if (drop_writes)
  			DMEMIT("drop_writes ");
a3998799f   Mike Snitzer   dm flakey: add co...
343
344
345
346
347
348
  
  		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);
3407ef526   Josef Bacik   dm: add flakey ta...
349
350
  		break;
  	}
3407ef526   Josef Bacik   dm: add flakey ta...
351
352
353
354
355
  }
  
  static int flakey_ioctl(struct dm_target *ti, unsigned int cmd, unsigned long arg)
  {
  	struct flakey_c *fc = ti->private;
ec8013bed   Paolo Bonzini   dm: do not forwar...
356
357
  	struct dm_dev *dev = fc->dev;
  	int r = 0;
3407ef526   Josef Bacik   dm: add flakey ta...
358

ec8013bed   Paolo Bonzini   dm: do not forwar...
359
360
361
362
363
364
365
366
  	/*
  	 * Only pass ioctls through if the device sizes match exactly.
  	 */
  	if (fc->start ||
  	    ti->len != i_size_read(dev->bdev->bd_inode) >> SECTOR_SHIFT)
  		r = scsi_verify_blk_ioctl(NULL, cmd);
  
  	return r ? : __blkdev_driver_ioctl(dev->bdev, dev->mode, cmd, arg);
3407ef526   Josef Bacik   dm: add flakey ta...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
  }
  
  static int flakey_merge(struct dm_target *ti, struct bvec_merge_data *bvm,
  			struct bio_vec *biovec, int max_size)
  {
  	struct flakey_c *fc = ti->private;
  	struct request_queue *q = bdev_get_queue(fc->dev->bdev);
  
  	if (!q->merge_bvec_fn)
  		return max_size;
  
  	bvm->bi_bdev = fc->dev->bdev;
  	bvm->bi_sector = flakey_map_sector(ti, bvm->bi_sector);
  
  	return min(max_size, q->merge_bvec_fn(q, bvm, biovec));
  }
  
  static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
  {
  	struct flakey_c *fc = ti->private;
  
  	return fn(ti, fc->dev, fc->start, ti->len, data);
  }
  
  static struct target_type flakey_target = {
  	.name   = "flakey",
fd7c092e7   Mikulas Patocka   dm: fix truncated...
393
  	.version = {1, 3, 1},
3407ef526   Josef Bacik   dm: add flakey ta...
394
395
396
397
  	.module = THIS_MODULE,
  	.ctr    = flakey_ctr,
  	.dtr    = flakey_dtr,
  	.map    = flakey_map,
a3998799f   Mike Snitzer   dm flakey: add co...
398
  	.end_io = flakey_end_io,
3407ef526   Josef Bacik   dm: add flakey ta...
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
  	.status = flakey_status,
  	.ioctl	= flakey_ioctl,
  	.merge	= flakey_merge,
  	.iterate_devices = flakey_iterate_devices,
  };
  
  static int __init dm_flakey_init(void)
  {
  	int r = dm_register_target(&flakey_target);
  
  	if (r < 0)
  		DMERR("register failed %d", r);
  
  	return r;
  }
  
  static void __exit dm_flakey_exit(void)
  {
  	dm_unregister_target(&flakey_target);
  }
  
  /* Module hooks */
  module_init(dm_flakey_init);
  module_exit(dm_flakey_exit);
  
  MODULE_DESCRIPTION(DM_NAME " flakey target");
  MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
  MODULE_LICENSE("GPL");