dm-flakey.c 10.9 KB
Newer Older
J
Josef Bacik 已提交
1 2
/*
 * Copyright (C) 2003 Sistina Software (UK) Limited.
3
 * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
J
Josef Bacik 已提交
4 5 6 7 8 9 10 11 12 13 14 15 16 17
 *
 * 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"

18
#define all_corrupt_bio_flags_match(bio, fc)	\
J
Jens Axboe 已提交
19
	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
20

J
Josef Bacik 已提交
21 22 23 24 25 26 27 28 29 30
/*
 * 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;
M
Mike Snitzer 已提交
31
	unsigned long flags;
32 33 34 35
	unsigned corrupt_bio_byte;
	unsigned corrupt_bio_rw;
	unsigned corrupt_bio_value;
	unsigned corrupt_bio_flags;
J
Josef Bacik 已提交
36 37
};

M
Mike Snitzer 已提交
38
enum feature_flag_bits {
39 40
	DROP_WRITES,
	ERROR_WRITES
M
Mike Snitzer 已提交
41 42
};

M
Mikulas Patocka 已提交
43 44 45 46
struct per_bio_data {
	bool bio_submitted;
};

M
Mike Snitzer 已提交
47 48
static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
			  struct dm_target *ti)
M
Mike Snitzer 已提交
49 50 51 52 53 54
{
	int r;
	unsigned argc;
	const char *arg_name;

	static struct dm_arg _args[] = {
55 56 57 58
		{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"},
M
Mike Snitzer 已提交
59 60 61 62 63 64 65 66
	};

	/* No feature arguments supplied. */
	if (!as->argc)
		return 0;

	r = dm_read_arg_group(_args, as, &argc, &ti->error);
	if (r)
67
		return r;
M
Mike Snitzer 已提交
68

69
	while (argc) {
M
Mike Snitzer 已提交
70 71 72
		arg_name = dm_shift_arg(as);
		argc--;

M
Mike Snitzer 已提交
73 74 75 76 77 78 79
		/*
		 * 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;
80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98
			} else if (test_bit(ERROR_WRITES, &fc->flags)) {
				ti->error = "Feature drop_writes conflicts with feature error_writes";
				return -EINVAL;
			}

			continue;
		}

		/*
		 * error_writes
		 */
		if (!strcasecmp(arg_name, "error_writes")) {
			if (test_and_set_bit(ERROR_WRITES, &fc->flags)) {
				ti->error = "Feature error_writes duplicated";
				return -EINVAL;

			} else if (test_bit(DROP_WRITES, &fc->flags)) {
				ti->error = "Feature error_writes conflicts with feature drop_writes";
				return -EINVAL;
M
Mike Snitzer 已提交
99 100 101 102 103
			}

			continue;
		}

104 105 106 107
		/*
		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
		 */
		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
108
			if (!argc) {
109
				ti->error = "Feature corrupt_bio_byte requires parameters";
110 111
				return -EINVAL;
			}
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150

			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;
		}

M
Mike Snitzer 已提交
151
		ti->error = "Unrecognised flakey feature requested";
152
		return -EINVAL;
M
Mike Snitzer 已提交
153 154
	}

155 156 157
	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;
158 159 160 161

	} else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
		ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
		return -EINVAL;
162 163 164
	}

	return 0;
M
Mike Snitzer 已提交
165 166
}

J
Josef Bacik 已提交
167
/*
M
Mike Snitzer 已提交
168 169
 * Construct a flakey mapping:
 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
M
Mike Snitzer 已提交
170 171 172
 *
 *   Feature args:
 *     [drop_writes]
173 174 175 176 177
 *     [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.
J
Josef Bacik 已提交
178 179 180
 */
static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
M
Mike Snitzer 已提交
181 182 183 184 185 186
	static struct dm_arg _args[] = {
		{0, UINT_MAX, "Invalid up interval"},
		{0, UINT_MAX, "Invalid down interval"},
	};

	int r;
J
Josef Bacik 已提交
187
	struct flakey_c *fc;
M
Mike Snitzer 已提交
188 189 190
	unsigned long long tmpll;
	struct dm_arg_set as;
	const char *devname;
191
	char dummy;
J
Josef Bacik 已提交
192

M
Mike Snitzer 已提交
193 194 195 196 197
	as.argc = argc;
	as.argv = argv;

	if (argc < 4) {
		ti->error = "Invalid argument count";
J
Josef Bacik 已提交
198 199 200
		return -EINVAL;
	}

M
Mike Snitzer 已提交
201
	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
J
Josef Bacik 已提交
202
	if (!fc) {
203
		ti->error = "Cannot allocate context";
J
Josef Bacik 已提交
204 205 206 207
		return -ENOMEM;
	}
	fc->start_time = jiffies;

M
Mike Snitzer 已提交
208 209
	devname = dm_shift_arg(&as);

210
	r = -EINVAL;
211
	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1) {
M
Mike Snitzer 已提交
212
		ti->error = "Invalid device sector";
J
Josef Bacik 已提交
213 214
		goto bad;
	}
M
Mike Snitzer 已提交
215
	fc->start = tmpll;
J
Josef Bacik 已提交
216

M
Mike Snitzer 已提交
217 218
	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
	if (r)
J
Josef Bacik 已提交
219 220
		goto bad;

M
Mike Snitzer 已提交
221 222
	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
	if (r)
J
Josef Bacik 已提交
223 224 225
		goto bad;

	if (!(fc->up_interval + fc->down_interval)) {
M
Mike Snitzer 已提交
226
		ti->error = "Total (up + down) interval is zero";
227
		r = -EINVAL;
J
Josef Bacik 已提交
228 229 230 231
		goto bad;
	}

	if (fc->up_interval + fc->down_interval < fc->up_interval) {
M
Mike Snitzer 已提交
232
		ti->error = "Interval overflow";
233
		r = -EINVAL;
J
Josef Bacik 已提交
234 235 236
		goto bad;
	}

M
Mike Snitzer 已提交
237
	r = parse_features(&as, fc, ti);
M
Mike Snitzer 已提交
238 239 240
	if (r)
		goto bad;

241 242
	r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);
	if (r) {
M
Mike Snitzer 已提交
243
		ti->error = "Device lookup failed";
J
Josef Bacik 已提交
244 245 246
		goto bad;
	}

247 248
	ti->num_flush_bios = 1;
	ti->num_discard_bios = 1;
249
	ti->per_io_data_size = sizeof(struct per_bio_data);
J
Josef Bacik 已提交
250 251 252 253 254
	ti->private = fc;
	return 0;

bad:
	kfree(fc);
255
	return r;
J
Josef Bacik 已提交
256 257 258 259 260 261 262 263 264 265 266 267 268 269
}

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;

270
	return fc->start + dm_target_offset(ti, bi_sector);
J
Josef Bacik 已提交
271 272 273 274 275 276 277 278
}

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))
279 280
		bio->bi_iter.bi_sector =
			flakey_map_sector(ti, bio->bi_iter.bi_sector);
J
Josef Bacik 已提交
281 282
}

283 284 285 286 287 288 289 290 291 292 293 294
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 "
J
Jens Axboe 已提交
295
			"(rw=%c bi_opf=%u bi_sector=%llu cur_bytes=%u)\n",
296
			bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
J
Jens Axboe 已提交
297
			(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
298
			(unsigned long long)bio->bi_iter.bi_sector, bio_bytes);
299 300 301
	}
}

M
Mikulas Patocka 已提交
302
static int flakey_map(struct dm_target *ti, struct bio *bio)
J
Josef Bacik 已提交
303 304 305
{
	struct flakey_c *fc = ti->private;
	unsigned elapsed;
M
Mikulas Patocka 已提交
306 307
	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
	pb->bio_submitted = false;
J
Josef Bacik 已提交
308 309 310

	/* Are we alive ? */
	elapsed = (jiffies - fc->start_time) / HZ;
M
Mike Snitzer 已提交
311
	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
312 313 314
		/*
		 * Flag this bio as submitted while down.
		 */
M
Mikulas Patocka 已提交
315
		pb->bio_submitted = true;
316 317

		/*
318
		 * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
319
		 * Otherwise, flakey_end_io() will decide if the reads should be modified.
320
		 */
321
		if (bio_data_dir(bio) == READ) {
322 323
			if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags) &&
			    !test_bit(ERROR_WRITES, &fc->flags))
324
				return -EIO;
325
			goto map_bio;
326
		}
M
Mike Snitzer 已提交
327 328

		/*
329
		 * Drop or error writes?
M
Mike Snitzer 已提交
330 331
		 */
		if (test_bit(DROP_WRITES, &fc->flags)) {
332
			bio_endio(bio);
333 334
			return DM_MAPIO_SUBMITTED;
		}
335 336 337 338
		else if (test_bit(ERROR_WRITES, &fc->flags)) {
			bio_io_error(bio);
			return DM_MAPIO_SUBMITTED;
		}
339 340 341 342 343 344 345

		/*
		 * 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);
M
Mike Snitzer 已提交
346 347 348 349
			goto map_bio;
		}

		/*
350
		 * By default, error all I/O.
M
Mike Snitzer 已提交
351
		 */
J
Josef Bacik 已提交
352
		return -EIO;
M
Mike Snitzer 已提交
353
	}
J
Josef Bacik 已提交
354

M
Mike Snitzer 已提交
355
map_bio:
J
Josef Bacik 已提交
356 357 358 359 360
	flakey_map_bio(ti, bio);

	return DM_MAPIO_REMAPPED;
}

M
Mikulas Patocka 已提交
361
static int flakey_end_io(struct dm_target *ti, struct bio *bio, int error)
362 363
{
	struct flakey_c *fc = ti->private;
M
Mikulas Patocka 已提交
364
	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
365

366
	if (!error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
367 368 369 370 371
		if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
		    all_corrupt_bio_flags_match(bio, fc)) {
			/*
			 * Corrupt successful matching READs while in down state.
			 */
372
			corrupt_bio_data(bio, fc);
373

374 375
		} else if (!test_bit(DROP_WRITES, &fc->flags) &&
			   !test_bit(ERROR_WRITES, &fc->flags)) {
376 377
			/*
			 * Error read during the down_interval if drop_writes
378
			 * and error_writes were not configured.
379
			 */
380
			return -EIO;
381
		}
382
	}
383 384 385 386

	return error;
}

387 388
static void flakey_status(struct dm_target *ti, status_type_t type,
			  unsigned status_flags, char *result, unsigned maxlen)
J
Josef Bacik 已提交
389
{
M
Mike Snitzer 已提交
390
	unsigned sz = 0;
J
Josef Bacik 已提交
391
	struct flakey_c *fc = ti->private;
392
	unsigned drop_writes, error_writes;
J
Josef Bacik 已提交
393 394 395 396 397 398 399

	switch (type) {
	case STATUSTYPE_INFO:
		result[0] = '\0';
		break;

	case STATUSTYPE_TABLE:
M
Mike Snitzer 已提交
400 401 402 403 404
		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);
405 406
		error_writes = test_bit(ERROR_WRITES, &fc->flags);
		DMEMIT("%u ", drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
407

M
Mike Snitzer 已提交
408 409
		if (drop_writes)
			DMEMIT("drop_writes ");
410 411
		else if (error_writes)
			DMEMIT("error_writes ");
412 413 414 415 416 417 418

		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);

J
Josef Bacik 已提交
419 420 421 422
		break;
	}
}

C
Christoph Hellwig 已提交
423 424
static int flakey_prepare_ioctl(struct dm_target *ti,
		struct block_device **bdev, fmode_t *mode)
J
Josef Bacik 已提交
425 426
{
	struct flakey_c *fc = ti->private;
C
Christoph Hellwig 已提交
427 428

	*bdev = fc->dev->bdev;
J
Josef Bacik 已提交
429

430 431 432 433
	/*
	 * Only pass ioctls through if the device sizes match exactly.
	 */
	if (fc->start ||
C
Christoph Hellwig 已提交
434 435 436
	    ti->len != i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT)
		return 1;
	return 0;
J
Josef Bacik 已提交
437 438 439 440 441 442 443 444 445 446 447
}

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",
448
	.version = {1, 4, 0},
J
Josef Bacik 已提交
449 450 451 452
	.module = THIS_MODULE,
	.ctr    = flakey_ctr,
	.dtr    = flakey_dtr,
	.map    = flakey_map,
453
	.end_io = flakey_end_io,
J
Josef Bacik 已提交
454
	.status = flakey_status,
C
Christoph Hellwig 已提交
455
	.prepare_ioctl = flakey_prepare_ioctl,
J
Josef Bacik 已提交
456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480
	.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");