dm-flakey.c 11.8 KB
Newer Older
Josef Bacik's avatar
Josef Bacik committed
1 2
/*
 * Copyright (C) 2003 Sistina Software (UK) Limited.
3
 * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
Josef Bacik's avatar
Josef Bacik committed
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)	\
19
	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
20

Josef Bacik's avatar
Josef Bacik committed
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;
31
	unsigned long flags;
32 33 34 35
	unsigned corrupt_bio_byte;
	unsigned corrupt_bio_rw;
	unsigned corrupt_bio_value;
	unsigned corrupt_bio_flags;
Josef Bacik's avatar
Josef Bacik committed
36 37
};

38
enum feature_flag_bits {
39 40
	DROP_WRITES,
	ERROR_WRITES
41 42
};

43 44 45 46
struct per_bio_data {
	bool bio_submitted;
};

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

54
	static const 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"},
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;
68

69
	while (argc) {
70 71 72
		arg_name = dm_shift_arg(as);
		argc--;

73 74 75 76 77
		if (!arg_name) {
			ti->error = "Insufficient feature arguments";
			return -EINVAL;
		}

78 79 80 81 82 83 84
		/*
		 * 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;
85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103
			} 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;
104 105 106 107 108
			}

			continue;
		}

109 110 111 112
		/*
		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
		 */
		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
113
			if (!argc) {
114
				ti->error = "Feature corrupt_bio_byte requires parameters";
115 116
				return -EINVAL;
			}
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 151 152 153 154 155

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

156
		ti->error = "Unrecognised flakey feature requested";
157
		return -EINVAL;
158 159
	}

160 161 162
	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;
163 164 165 166

	} 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;
167 168 169
	}

	return 0;
170 171
}

Josef Bacik's avatar
Josef Bacik committed
172
/*
173 174
 * Construct a flakey mapping:
 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
175 176 177
 *
 *   Feature args:
 *     [drop_writes]
178 179 180 181 182
 *     [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.
Josef Bacik's avatar
Josef Bacik committed
183 184 185
 */
static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
{
186
	static const struct dm_arg _args[] = {
187 188 189 190 191
		{0, UINT_MAX, "Invalid up interval"},
		{0, UINT_MAX, "Invalid down interval"},
	};

	int r;
Josef Bacik's avatar
Josef Bacik committed
192
	struct flakey_c *fc;
193 194 195
	unsigned long long tmpll;
	struct dm_arg_set as;
	const char *devname;
196
	char dummy;
Josef Bacik's avatar
Josef Bacik committed
197

198 199 200 201 202
	as.argc = argc;
	as.argv = argv;

	if (argc < 4) {
		ti->error = "Invalid argument count";
Josef Bacik's avatar
Josef Bacik committed
203 204 205
		return -EINVAL;
	}

206
	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
Josef Bacik's avatar
Josef Bacik committed
207
	if (!fc) {
208
		ti->error = "Cannot allocate context";
Josef Bacik's avatar
Josef Bacik committed
209 210 211 212
		return -ENOMEM;
	}
	fc->start_time = jiffies;

213 214
	devname = dm_shift_arg(&as);

215
	r = -EINVAL;
216
	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1) {
217
		ti->error = "Invalid device sector";
Josef Bacik's avatar
Josef Bacik committed
218 219
		goto bad;
	}
220
	fc->start = tmpll;
Josef Bacik's avatar
Josef Bacik committed
221

222 223
	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
	if (r)
Josef Bacik's avatar
Josef Bacik committed
224 225
		goto bad;

226 227
	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
	if (r)
Josef Bacik's avatar
Josef Bacik committed
228 229 230
		goto bad;

	if (!(fc->up_interval + fc->down_interval)) {
231
		ti->error = "Total (up + down) interval is zero";
232
		r = -EINVAL;
Josef Bacik's avatar
Josef Bacik committed
233 234 235 236
		goto bad;
	}

	if (fc->up_interval + fc->down_interval < fc->up_interval) {
237
		ti->error = "Interval overflow";
238
		r = -EINVAL;
Josef Bacik's avatar
Josef Bacik committed
239 240 241
		goto bad;
	}

242
	r = parse_features(&as, fc, ti);
243 244 245
	if (r)
		goto bad;

246 247
	r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);
	if (r) {
248
		ti->error = "Device lookup failed";
Josef Bacik's avatar
Josef Bacik committed
249 250 251
		goto bad;
	}

252 253
	ti->num_flush_bios = 1;
	ti->num_discard_bios = 1;
254
	ti->per_io_data_size = sizeof(struct per_bio_data);
Josef Bacik's avatar
Josef Bacik committed
255 256 257 258 259
	ti->private = fc;
	return 0;

bad:
	kfree(fc);
260
	return r;
Josef Bacik's avatar
Josef Bacik committed
261 262 263 264 265 266 267 268 269 270 271 272 273 274
}

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;

275
	return fc->start + dm_target_offset(ti, bi_sector);
Josef Bacik's avatar
Josef Bacik committed
276 277 278 279 280 281
}

static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
{
	struct flakey_c *fc = ti->private;

282
	bio_set_dev(bio, fc->dev->bdev);
283
	if (bio_sectors(bio) || bio_op(bio) == REQ_OP_ZONE_RESET)
284 285
		bio->bi_iter.bi_sector =
			flakey_map_sector(ti, bio->bi_iter.bi_sector);
Josef Bacik's avatar
Josef Bacik committed
286 287
}

288 289 290 291 292 293 294 295 296 297 298 299
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 "
300
			"(rw=%c bi_opf=%u bi_sector=%llu cur_bytes=%u)\n",
301
			bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
302
			(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
303
			(unsigned long long)bio->bi_iter.bi_sector, bio_bytes);
304 305 306
	}
}

Mikulas Patocka's avatar
Mikulas Patocka committed
307
static int flakey_map(struct dm_target *ti, struct bio *bio)
Josef Bacik's avatar
Josef Bacik committed
308 309 310
{
	struct flakey_c *fc = ti->private;
	unsigned elapsed;
311 312
	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
	pb->bio_submitted = false;
Josef Bacik's avatar
Josef Bacik committed
313

314 315 316 317
	/* Do not fail reset zone */
	if (bio_op(bio) == REQ_OP_ZONE_RESET)
		goto map_bio;

Josef Bacik's avatar
Josef Bacik committed
318 319
	/* Are we alive ? */
	elapsed = (jiffies - fc->start_time) / HZ;
320
	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
321 322 323
		/*
		 * Flag this bio as submitted while down.
		 */
324
		pb->bio_submitted = true;
325 326

		/*
327
		 * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
328
		 * Otherwise, flakey_end_io() will decide if the reads should be modified.
329
		 */
330
		if (bio_data_dir(bio) == READ) {
331 332
			if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags) &&
			    !test_bit(ERROR_WRITES, &fc->flags))
333
				return DM_MAPIO_KILL;
334
			goto map_bio;
335
		}
336 337

		/*
338
		 * Drop or error writes?
339 340
		 */
		if (test_bit(DROP_WRITES, &fc->flags)) {
341
			bio_endio(bio);
342 343
			return DM_MAPIO_SUBMITTED;
		}
344 345 346 347
		else if (test_bit(ERROR_WRITES, &fc->flags)) {
			bio_io_error(bio);
			return DM_MAPIO_SUBMITTED;
		}
348 349 350 351 352 353 354

		/*
		 * 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);
355 356 357 358
			goto map_bio;
		}

		/*
359
		 * By default, error all I/O.
360
		 */
361
		return DM_MAPIO_KILL;
362
	}
Josef Bacik's avatar
Josef Bacik committed
363

364
map_bio:
Josef Bacik's avatar
Josef Bacik committed
365 366 367 368 369
	flakey_map_bio(ti, bio);

	return DM_MAPIO_REMAPPED;
}

370
static int flakey_end_io(struct dm_target *ti, struct bio *bio,
371
			 blk_status_t *error)
372 373
{
	struct flakey_c *fc = ti->private;
374
	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
375

376 377 378
	if (bio_op(bio) == REQ_OP_ZONE_RESET)
		return DM_ENDIO_DONE;

379
	if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
380 381 382 383 384
		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.
			 */
385
			corrupt_bio_data(bio, fc);
386

387 388
		} else if (!test_bit(DROP_WRITES, &fc->flags) &&
			   !test_bit(ERROR_WRITES, &fc->flags)) {
389 390
			/*
			 * Error read during the down_interval if drop_writes
391
			 * and error_writes were not configured.
392
			 */
393
			*error = BLK_STS_IOERR;
394
		}
395
	}
396

397
	return DM_ENDIO_DONE;
398 399
}

400 401
static void flakey_status(struct dm_target *ti, status_type_t type,
			  unsigned status_flags, char *result, unsigned maxlen)
Josef Bacik's avatar
Josef Bacik committed
402
{
403
	unsigned sz = 0;
Josef Bacik's avatar
Josef Bacik committed
404
	struct flakey_c *fc = ti->private;
405
	unsigned drop_writes, error_writes;
Josef Bacik's avatar
Josef Bacik committed
406 407 408 409 410 411 412

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

	case STATUSTYPE_TABLE:
413 414 415 416 417
		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);
418 419
		error_writes = test_bit(ERROR_WRITES, &fc->flags);
		DMEMIT("%u ", drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
420

421 422
		if (drop_writes)
			DMEMIT("drop_writes ");
423 424
		else if (error_writes)
			DMEMIT("error_writes ");
425 426 427 428 429 430 431

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

Josef Bacik's avatar
Josef Bacik committed
432 433 434 435
		break;
	}
}

436
static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
Josef Bacik's avatar
Josef Bacik committed
437 438
{
	struct flakey_c *fc = ti->private;
439 440

	*bdev = fc->dev->bdev;
Josef Bacik's avatar
Josef Bacik committed
441

442 443 444 445
	/*
	 * Only pass ioctls through if the device sizes match exactly.
	 */
	if (fc->start ||
446 447 448
	    ti->len != i_size_read((*bdev)->bd_inode) >> SECTOR_SHIFT)
		return 1;
	return 0;
Josef Bacik's avatar
Josef Bacik committed
449 450
}

451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470
#ifdef CONFIG_BLK_DEV_ZONED
static int flakey_report_zones(struct dm_target *ti, sector_t sector,
			       struct blk_zone *zones, unsigned int *nr_zones,
			       gfp_t gfp_mask)
{
	struct flakey_c *fc = ti->private;
	int ret;

	/* Do report and remap it */
	ret = blkdev_report_zones(fc->dev->bdev, flakey_map_sector(ti, sector),
				  zones, nr_zones, gfp_mask);
	if (ret != 0)
		return ret;

	if (*nr_zones)
		dm_remap_zone_report(ti, fc->start, zones, nr_zones);
	return 0;
}
#endif

Josef Bacik's avatar
Josef Bacik committed
471 472 473 474 475 476 477 478 479
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",
480
	.version = {1, 5, 0},
481
#ifdef CONFIG_BLK_DEV_ZONED
482
	.features = DM_TARGET_ZONED_HM,
483
	.report_zones = flakey_report_zones,
484
#endif
Josef Bacik's avatar
Josef Bacik committed
485 486 487 488
	.module = THIS_MODULE,
	.ctr    = flakey_ctr,
	.dtr    = flakey_dtr,
	.map    = flakey_map,
489
	.end_io = flakey_end_io,
Josef Bacik's avatar
Josef Bacik committed
490
	.status = flakey_status,
491
	.prepare_ioctl = flakey_prepare_ioctl,
Josef Bacik's avatar
Josef Bacik committed
492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516
	.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");