cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

dm-flakey.c (12197B)


      1/*
      2 * Copyright (C) 2003 Sistina Software (UK) Limited.
      3 * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.
      4 *
      5 * This file is released under the GPL.
      6 */
      7
      8#include <linux/device-mapper.h>
      9
     10#include <linux/module.h>
     11#include <linux/init.h>
     12#include <linux/blkdev.h>
     13#include <linux/bio.h>
     14#include <linux/slab.h>
     15
     16#define DM_MSG_PREFIX "flakey"
     17
     18#define all_corrupt_bio_flags_match(bio, fc)	\
     19	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)
     20
     21/*
     22 * Flakey: Used for testing only, simulates intermittent,
     23 * catastrophic device failure.
     24 */
     25struct flakey_c {
     26	struct dm_dev *dev;
     27	unsigned long start_time;
     28	sector_t start;
     29	unsigned up_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
     38enum feature_flag_bits {
     39	DROP_WRITES,
     40	ERROR_WRITES
     41};
     42
     43struct per_bio_data {
     44	bool bio_submitted;
     45};
     46
     47static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,
     48			  struct dm_target *ti)
     49{
     50	int r;
     51	unsigned argc;
     52	const char *arg_name;
     53
     54	static const struct dm_arg _args[] = {
     55		{0, 6, "Invalid number of feature args"},
     56		{1, UINT_MAX, "Invalid corrupt bio byte"},
     57		{0, 255, "Invalid corrupt value to write into bio byte (0-255)"},
     58		{0, UINT_MAX, "Invalid corrupt bio flags mask"},
     59	};
     60
     61	/* No feature arguments supplied. */
     62	if (!as->argc)
     63		return 0;
     64
     65	r = dm_read_arg_group(_args, as, &argc, &ti->error);
     66	if (r)
     67		return r;
     68
     69	while (argc) {
     70		arg_name = dm_shift_arg(as);
     71		argc--;
     72
     73		if (!arg_name) {
     74			ti->error = "Insufficient feature arguments";
     75			return -EINVAL;
     76		}
     77
     78		/*
     79		 * drop_writes
     80		 */
     81		if (!strcasecmp(arg_name, "drop_writes")) {
     82			if (test_and_set_bit(DROP_WRITES, &fc->flags)) {
     83				ti->error = "Feature drop_writes duplicated";
     84				return -EINVAL;
     85			} else if (test_bit(ERROR_WRITES, &fc->flags)) {
     86				ti->error = "Feature drop_writes conflicts with feature error_writes";
     87				return -EINVAL;
     88			}
     89
     90			continue;
     91		}
     92
     93		/*
     94		 * error_writes
     95		 */
     96		if (!strcasecmp(arg_name, "error_writes")) {
     97			if (test_and_set_bit(ERROR_WRITES, &fc->flags)) {
     98				ti->error = "Feature error_writes duplicated";
     99				return -EINVAL;
    100
    101			} else if (test_bit(DROP_WRITES, &fc->flags)) {
    102				ti->error = "Feature error_writes conflicts with feature drop_writes";
    103				return -EINVAL;
    104			}
    105
    106			continue;
    107		}
    108
    109		/*
    110		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>
    111		 */
    112		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {
    113			if (!argc) {
    114				ti->error = "Feature corrupt_bio_byte requires parameters";
    115				return -EINVAL;
    116			}
    117
    118			r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);
    119			if (r)
    120				return r;
    121			argc--;
    122
    123			/*
    124			 * Direction r or w?
    125			 */
    126			arg_name = dm_shift_arg(as);
    127			if (!strcasecmp(arg_name, "w"))
    128				fc->corrupt_bio_rw = WRITE;
    129			else if (!strcasecmp(arg_name, "r"))
    130				fc->corrupt_bio_rw = READ;
    131			else {
    132				ti->error = "Invalid corrupt bio direction (r or w)";
    133				return -EINVAL;
    134			}
    135			argc--;
    136
    137			/*
    138			 * Value of byte (0-255) to write in place of correct one.
    139			 */
    140			r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);
    141			if (r)
    142				return r;
    143			argc--;
    144
    145			/*
    146			 * Only corrupt bios with these flags set.
    147			 */
    148			r = dm_read_arg(_args + 3, as, &fc->corrupt_bio_flags, &ti->error);
    149			if (r)
    150				return r;
    151			argc--;
    152
    153			continue;
    154		}
    155
    156		ti->error = "Unrecognised flakey feature requested";
    157		return -EINVAL;
    158	}
    159
    160	if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
    161		ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
    162		return -EINVAL;
    163
    164	} else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {
    165		ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set";
    166		return -EINVAL;
    167	}
    168
    169	return 0;
    170}
    171
    172/*
    173 * Construct a flakey mapping:
    174 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]
    175 *
    176 *   Feature args:
    177 *     [drop_writes]
    178 *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]
    179 *
    180 *   Nth_byte starts from 1 for the first byte.
    181 *   Direction is r for READ or w for WRITE.
    182 *   bio_flags is ignored if 0.
    183 */
    184static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)
    185{
    186	static const struct dm_arg _args[] = {
    187		{0, UINT_MAX, "Invalid up interval"},
    188		{0, UINT_MAX, "Invalid down interval"},
    189	};
    190
    191	int r;
    192	struct flakey_c *fc;
    193	unsigned long long tmpll;
    194	struct dm_arg_set as;
    195	const char *devname;
    196	char dummy;
    197
    198	as.argc = argc;
    199	as.argv = argv;
    200
    201	if (argc < 4) {
    202		ti->error = "Invalid argument count";
    203		return -EINVAL;
    204	}
    205
    206	fc = kzalloc(sizeof(*fc), GFP_KERNEL);
    207	if (!fc) {
    208		ti->error = "Cannot allocate context";
    209		return -ENOMEM;
    210	}
    211	fc->start_time = jiffies;
    212
    213	devname = dm_shift_arg(&as);
    214
    215	r = -EINVAL;
    216	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) {
    217		ti->error = "Invalid device sector";
    218		goto bad;
    219	}
    220	fc->start = tmpll;
    221
    222	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);
    223	if (r)
    224		goto bad;
    225
    226	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);
    227	if (r)
    228		goto bad;
    229
    230	if (!(fc->up_interval + fc->down_interval)) {
    231		ti->error = "Total (up + down) interval is zero";
    232		r = -EINVAL;
    233		goto bad;
    234	}
    235
    236	if (fc->up_interval + fc->down_interval < fc->up_interval) {
    237		ti->error = "Interval overflow";
    238		r = -EINVAL;
    239		goto bad;
    240	}
    241
    242	r = parse_features(&as, fc, ti);
    243	if (r)
    244		goto bad;
    245
    246	r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);
    247	if (r) {
    248		ti->error = "Device lookup failed";
    249		goto bad;
    250	}
    251
    252	ti->num_flush_bios = 1;
    253	ti->num_discard_bios = 1;
    254	ti->per_io_data_size = sizeof(struct per_bio_data);
    255	ti->private = fc;
    256	return 0;
    257
    258bad:
    259	kfree(fc);
    260	return r;
    261}
    262
    263static void flakey_dtr(struct dm_target *ti)
    264{
    265	struct flakey_c *fc = ti->private;
    266
    267	dm_put_device(ti, fc->dev);
    268	kfree(fc);
    269}
    270
    271static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)
    272{
    273	struct flakey_c *fc = ti->private;
    274
    275	return fc->start + dm_target_offset(ti, bi_sector);
    276}
    277
    278static void flakey_map_bio(struct dm_target *ti, struct bio *bio)
    279{
    280	struct flakey_c *fc = ti->private;
    281
    282	bio_set_dev(bio, fc->dev->bdev);
    283	bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);
    284}
    285
    286static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)
    287{
    288	unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;
    289
    290	struct bvec_iter iter;
    291	struct bio_vec bvec;
    292
    293	if (!bio_has_data(bio))
    294		return;
    295
    296	/*
    297	 * Overwrite the Nth byte of the bio's data, on whichever page
    298	 * it falls.
    299	 */
    300	bio_for_each_segment(bvec, bio, iter) {
    301		if (bio_iter_len(bio, iter) > corrupt_bio_byte) {
    302			char *segment = (page_address(bio_iter_page(bio, iter))
    303					 + bio_iter_offset(bio, iter));
    304			segment[corrupt_bio_byte] = fc->corrupt_bio_value;
    305			DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "
    306				"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",
    307				bio, fc->corrupt_bio_value, fc->corrupt_bio_byte,
    308				(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,
    309				(unsigned long long)bio->bi_iter.bi_sector, bio->bi_iter.bi_size);
    310			break;
    311		}
    312		corrupt_bio_byte -= bio_iter_len(bio, iter);
    313	}
    314}
    315
    316static int flakey_map(struct dm_target *ti, struct bio *bio)
    317{
    318	struct flakey_c *fc = ti->private;
    319	unsigned elapsed;
    320	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
    321	pb->bio_submitted = false;
    322
    323	if (op_is_zone_mgmt(bio_op(bio)))
    324		goto map_bio;
    325
    326	/* Are we alive ? */
    327	elapsed = (jiffies - fc->start_time) / HZ;
    328	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {
    329		/*
    330		 * Flag this bio as submitted while down.
    331		 */
    332		pb->bio_submitted = true;
    333
    334		/*
    335		 * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.
    336		 * Otherwise, flakey_end_io() will decide if the reads should be modified.
    337		 */
    338		if (bio_data_dir(bio) == READ) {
    339			if (!fc->corrupt_bio_byte && !test_bit(DROP_WRITES, &fc->flags) &&
    340			    !test_bit(ERROR_WRITES, &fc->flags))
    341				return DM_MAPIO_KILL;
    342			goto map_bio;
    343		}
    344
    345		/*
    346		 * Drop or error writes?
    347		 */
    348		if (test_bit(DROP_WRITES, &fc->flags)) {
    349			bio_endio(bio);
    350			return DM_MAPIO_SUBMITTED;
    351		}
    352		else if (test_bit(ERROR_WRITES, &fc->flags)) {
    353			bio_io_error(bio);
    354			return DM_MAPIO_SUBMITTED;
    355		}
    356
    357		/*
    358		 * Corrupt matching writes.
    359		 */
    360		if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == WRITE)) {
    361			if (all_corrupt_bio_flags_match(bio, fc))
    362				corrupt_bio_data(bio, fc);
    363			goto map_bio;
    364		}
    365
    366		/*
    367		 * By default, error all I/O.
    368		 */
    369		return DM_MAPIO_KILL;
    370	}
    371
    372map_bio:
    373	flakey_map_bio(ti, bio);
    374
    375	return DM_MAPIO_REMAPPED;
    376}
    377
    378static int flakey_end_io(struct dm_target *ti, struct bio *bio,
    379			 blk_status_t *error)
    380{
    381	struct flakey_c *fc = ti->private;
    382	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));
    383
    384	if (op_is_zone_mgmt(bio_op(bio)))
    385		return DM_ENDIO_DONE;
    386
    387	if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {
    388		if (fc->corrupt_bio_byte && (fc->corrupt_bio_rw == READ) &&
    389		    all_corrupt_bio_flags_match(bio, fc)) {
    390			/*
    391			 * Corrupt successful matching READs while in down state.
    392			 */
    393			corrupt_bio_data(bio, fc);
    394
    395		} else if (!test_bit(DROP_WRITES, &fc->flags) &&
    396			   !test_bit(ERROR_WRITES, &fc->flags)) {
    397			/*
    398			 * Error read during the down_interval if drop_writes
    399			 * and error_writes were not configured.
    400			 */
    401			*error = BLK_STS_IOERR;
    402		}
    403	}
    404
    405	return DM_ENDIO_DONE;
    406}
    407
    408static void flakey_status(struct dm_target *ti, status_type_t type,
    409			  unsigned status_flags, char *result, unsigned maxlen)
    410{
    411	unsigned sz = 0;
    412	struct flakey_c *fc = ti->private;
    413	unsigned drop_writes, error_writes;
    414
    415	switch (type) {
    416	case STATUSTYPE_INFO:
    417		result[0] = '\0';
    418		break;
    419
    420	case STATUSTYPE_TABLE:
    421		DMEMIT("%s %llu %u %u ", fc->dev->name,
    422		       (unsigned long long)fc->start, fc->up_interval,
    423		       fc->down_interval);
    424
    425		drop_writes = test_bit(DROP_WRITES, &fc->flags);
    426		error_writes = test_bit(ERROR_WRITES, &fc->flags);
    427		DMEMIT("%u ", drop_writes + error_writes + (fc->corrupt_bio_byte > 0) * 5);
    428
    429		if (drop_writes)
    430			DMEMIT("drop_writes ");
    431		else if (error_writes)
    432			DMEMIT("error_writes ");
    433
    434		if (fc->corrupt_bio_byte)
    435			DMEMIT("corrupt_bio_byte %u %c %u %u ",
    436			       fc->corrupt_bio_byte,
    437			       (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',
    438			       fc->corrupt_bio_value, fc->corrupt_bio_flags);
    439
    440		break;
    441
    442	case STATUSTYPE_IMA:
    443		result[0] = '\0';
    444		break;
    445	}
    446}
    447
    448static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)
    449{
    450	struct flakey_c *fc = ti->private;
    451
    452	*bdev = fc->dev->bdev;
    453
    454	/*
    455	 * Only pass ioctls through if the device sizes match exactly.
    456	 */
    457	if (fc->start || ti->len != bdev_nr_sectors((*bdev)))
    458		return 1;
    459	return 0;
    460}
    461
    462#ifdef CONFIG_BLK_DEV_ZONED
    463static int flakey_report_zones(struct dm_target *ti,
    464		struct dm_report_zones_args *args, unsigned int nr_zones)
    465{
    466	struct flakey_c *fc = ti->private;
    467
    468	return dm_report_zones(fc->dev->bdev, fc->start,
    469			       flakey_map_sector(ti, args->next_sector),
    470			       args, nr_zones);
    471}
    472#else
    473#define flakey_report_zones NULL
    474#endif
    475
    476static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)
    477{
    478	struct flakey_c *fc = ti->private;
    479
    480	return fn(ti, fc->dev, fc->start, ti->len, data);
    481}
    482
    483static struct target_type flakey_target = {
    484	.name   = "flakey",
    485	.version = {1, 5, 0},
    486	.features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,
    487	.report_zones = flakey_report_zones,
    488	.module = THIS_MODULE,
    489	.ctr    = flakey_ctr,
    490	.dtr    = flakey_dtr,
    491	.map    = flakey_map,
    492	.end_io = flakey_end_io,
    493	.status = flakey_status,
    494	.prepare_ioctl = flakey_prepare_ioctl,
    495	.iterate_devices = flakey_iterate_devices,
    496};
    497
    498static int __init dm_flakey_init(void)
    499{
    500	int r = dm_register_target(&flakey_target);
    501
    502	if (r < 0)
    503		DMERR("register failed %d", r);
    504
    505	return r;
    506}
    507
    508static void __exit dm_flakey_exit(void)
    509{
    510	dm_unregister_target(&flakey_target);
    511}
    512
    513/* Module hooks */
    514module_init(dm_flakey_init);
    515module_exit(dm_flakey_exit);
    516
    517MODULE_DESCRIPTION(DM_NAME " flakey target");
    518MODULE_AUTHOR("Joe Thornber <dm-devel@redhat.com>");
    519MODULE_LICENSE("GPL");