brintos

brintos / linux-shallow public Read only

0
0
Text · 16.6 KiB · 731467d Raw
695 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (C) 2003 Sistina Software (UK) Limited.4 * Copyright (C) 2004, 2010-2011 Red Hat, Inc. All rights reserved.5 *6 * This file is released under the GPL.7 */8 9#include <linux/device-mapper.h>10 11#include <linux/module.h>12#include <linux/init.h>13#include <linux/blkdev.h>14#include <linux/bio.h>15#include <linux/slab.h>16 17#define DM_MSG_PREFIX "flakey"18 19#define PROBABILITY_BASE	100000000020 21#define all_corrupt_bio_flags_match(bio, fc)	\22	(((bio)->bi_opf & (fc)->corrupt_bio_flags) == (fc)->corrupt_bio_flags)23 24/*25 * Flakey: Used for testing only, simulates intermittent,26 * catastrophic device failure.27 */28struct flakey_c {29	struct dm_dev *dev;30	unsigned long start_time;31	sector_t start;32	unsigned int up_interval;33	unsigned int down_interval;34	unsigned long flags;35	unsigned int corrupt_bio_byte;36	unsigned int corrupt_bio_rw;37	unsigned int corrupt_bio_value;38	blk_opf_t corrupt_bio_flags;39	unsigned int random_read_corrupt;40	unsigned int random_write_corrupt;41};42 43enum feature_flag_bits {44	ERROR_READS,45	DROP_WRITES,46	ERROR_WRITES47};48 49struct per_bio_data {50	bool bio_submitted;51};52 53static int parse_features(struct dm_arg_set *as, struct flakey_c *fc,54			  struct dm_target *ti)55{56	int r;57	unsigned int argc;58	const char *arg_name;59 60	static const struct dm_arg _args[] = {61		{0, 11, "Invalid number of feature args"},62		{1, UINT_MAX, "Invalid corrupt bio byte"},63		{0, 255, "Invalid corrupt value to write into bio byte (0-255)"},64		{0, UINT_MAX, "Invalid corrupt bio flags mask"},65		{0, PROBABILITY_BASE, "Invalid random corrupt argument"},66	};67 68	/* No feature arguments supplied. */69	if (!as->argc)70		return 0;71 72	r = dm_read_arg_group(_args, as, &argc, &ti->error);73	if (r)74		return r;75 76	while (argc) {77		arg_name = dm_shift_arg(as);78		argc--;79 80		if (!arg_name) {81			ti->error = "Insufficient feature arguments";82			return -EINVAL;83		}84 85		/*86		 * error_reads87		 */88		if (!strcasecmp(arg_name, "error_reads")) {89			if (test_and_set_bit(ERROR_READS, &fc->flags)) {90				ti->error = "Feature error_reads duplicated";91				return -EINVAL;92			}93			continue;94		}95 96		/*97		 * drop_writes98		 */99		if (!strcasecmp(arg_name, "drop_writes")) {100			if (test_and_set_bit(DROP_WRITES, &fc->flags)) {101				ti->error = "Feature drop_writes duplicated";102				return -EINVAL;103			} else if (test_bit(ERROR_WRITES, &fc->flags)) {104				ti->error = "Feature drop_writes conflicts with feature error_writes";105				return -EINVAL;106			}107 108			continue;109		}110 111		/*112		 * error_writes113		 */114		if (!strcasecmp(arg_name, "error_writes")) {115			if (test_and_set_bit(ERROR_WRITES, &fc->flags)) {116				ti->error = "Feature error_writes duplicated";117				return -EINVAL;118 119			} else if (test_bit(DROP_WRITES, &fc->flags)) {120				ti->error = "Feature error_writes conflicts with feature drop_writes";121				return -EINVAL;122			}123 124			continue;125		}126 127		/*128		 * corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>129		 */130		if (!strcasecmp(arg_name, "corrupt_bio_byte")) {131			if (!argc) {132				ti->error = "Feature corrupt_bio_byte requires parameters";133				return -EINVAL;134			}135 136			r = dm_read_arg(_args + 1, as, &fc->corrupt_bio_byte, &ti->error);137			if (r)138				return r;139			argc--;140 141			/*142			 * Direction r or w?143			 */144			arg_name = dm_shift_arg(as);145			if (arg_name && !strcasecmp(arg_name, "w"))146				fc->corrupt_bio_rw = WRITE;147			else if (arg_name && !strcasecmp(arg_name, "r"))148				fc->corrupt_bio_rw = READ;149			else {150				ti->error = "Invalid corrupt bio direction (r or w)";151				return -EINVAL;152			}153			argc--;154 155			/*156			 * Value of byte (0-255) to write in place of correct one.157			 */158			r = dm_read_arg(_args + 2, as, &fc->corrupt_bio_value, &ti->error);159			if (r)160				return r;161			argc--;162 163			/*164			 * Only corrupt bios with these flags set.165			 */166			BUILD_BUG_ON(sizeof(fc->corrupt_bio_flags) !=167				     sizeof(unsigned int));168			r = dm_read_arg(_args + 3, as,169				(__force unsigned int *)&fc->corrupt_bio_flags,170				&ti->error);171			if (r)172				return r;173			argc--;174 175			continue;176		}177 178		if (!strcasecmp(arg_name, "random_read_corrupt")) {179			if (!argc) {180				ti->error = "Feature random_read_corrupt requires a parameter";181				return -EINVAL;182			}183			r = dm_read_arg(_args + 4, as, &fc->random_read_corrupt, &ti->error);184			if (r)185				return r;186			argc--;187 188			continue;189		}190 191		if (!strcasecmp(arg_name, "random_write_corrupt")) {192			if (!argc) {193				ti->error = "Feature random_write_corrupt requires a parameter";194				return -EINVAL;195			}196			r = dm_read_arg(_args + 4, as, &fc->random_write_corrupt, &ti->error);197			if (r)198				return r;199			argc--;200 201			continue;202		}203 204		ti->error = "Unrecognised flakey feature requested";205		return -EINVAL;206	}207 208	if (test_bit(DROP_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {209		ti->error = "drop_writes is incompatible with corrupt_bio_byte with the WRITE flag set";210		return -EINVAL;211 212	} else if (test_bit(ERROR_WRITES, &fc->flags) && (fc->corrupt_bio_rw == WRITE)) {213		ti->error = "error_writes is incompatible with corrupt_bio_byte with the WRITE flag set";214		return -EINVAL;215	}216 217	if (!fc->corrupt_bio_byte && !test_bit(ERROR_READS, &fc->flags) &&218	    !test_bit(DROP_WRITES, &fc->flags) && !test_bit(ERROR_WRITES, &fc->flags) &&219	    !fc->random_read_corrupt && !fc->random_write_corrupt) {220		set_bit(ERROR_WRITES, &fc->flags);221		set_bit(ERROR_READS, &fc->flags);222	}223 224	return 0;225}226 227/*228 * Construct a flakey mapping:229 * <dev_path> <offset> <up interval> <down interval> [<#feature args> [<arg>]*]230 *231 *   Feature args:232 *     [drop_writes]233 *     [corrupt_bio_byte <Nth_byte> <direction> <value> <bio_flags>]234 *235 *   Nth_byte starts from 1 for the first byte.236 *   Direction is r for READ or w for WRITE.237 *   bio_flags is ignored if 0.238 */239static int flakey_ctr(struct dm_target *ti, unsigned int argc, char **argv)240{241	static const struct dm_arg _args[] = {242		{0, UINT_MAX, "Invalid up interval"},243		{0, UINT_MAX, "Invalid down interval"},244	};245 246	int r;247	struct flakey_c *fc;248	unsigned long long tmpll;249	struct dm_arg_set as;250	const char *devname;251	char dummy;252 253	as.argc = argc;254	as.argv = argv;255 256	if (argc < 4) {257		ti->error = "Invalid argument count";258		return -EINVAL;259	}260 261	fc = kzalloc(sizeof(*fc), GFP_KERNEL);262	if (!fc) {263		ti->error = "Cannot allocate context";264		return -ENOMEM;265	}266	fc->start_time = jiffies;267 268	devname = dm_shift_arg(&as);269 270	r = -EINVAL;271	if (sscanf(dm_shift_arg(&as), "%llu%c", &tmpll, &dummy) != 1 || tmpll != (sector_t)tmpll) {272		ti->error = "Invalid device sector";273		goto bad;274	}275	fc->start = tmpll;276 277	r = dm_read_arg(_args, &as, &fc->up_interval, &ti->error);278	if (r)279		goto bad;280 281	r = dm_read_arg(_args, &as, &fc->down_interval, &ti->error);282	if (r)283		goto bad;284 285	if (!(fc->up_interval + fc->down_interval)) {286		ti->error = "Total (up + down) interval is zero";287		r = -EINVAL;288		goto bad;289	}290 291	if (fc->up_interval + fc->down_interval < fc->up_interval) {292		ti->error = "Interval overflow";293		r = -EINVAL;294		goto bad;295	}296 297	r = parse_features(&as, fc, ti);298	if (r)299		goto bad;300 301	r = dm_get_device(ti, devname, dm_table_get_mode(ti->table), &fc->dev);302	if (r) {303		ti->error = "Device lookup failed";304		goto bad;305	}306 307	ti->num_flush_bios = 1;308	ti->num_discard_bios = 1;309	ti->per_io_data_size = sizeof(struct per_bio_data);310	ti->private = fc;311	return 0;312 313bad:314	kfree(fc);315	return r;316}317 318static void flakey_dtr(struct dm_target *ti)319{320	struct flakey_c *fc = ti->private;321 322	dm_put_device(ti, fc->dev);323	kfree(fc);324}325 326static sector_t flakey_map_sector(struct dm_target *ti, sector_t bi_sector)327{328	struct flakey_c *fc = ti->private;329 330	return fc->start + dm_target_offset(ti, bi_sector);331}332 333static void flakey_map_bio(struct dm_target *ti, struct bio *bio)334{335	struct flakey_c *fc = ti->private;336 337	bio_set_dev(bio, fc->dev->bdev);338	bio->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);339}340 341static void corrupt_bio_common(struct bio *bio, unsigned int corrupt_bio_byte,342			       unsigned char corrupt_bio_value)343{344	struct bvec_iter iter;345	struct bio_vec bvec;346 347	/*348	 * Overwrite the Nth byte of the bio's data, on whichever page349	 * it falls.350	 */351	bio_for_each_segment(bvec, bio, iter) {352		if (bio_iter_len(bio, iter) > corrupt_bio_byte) {353			unsigned char *segment = bvec_kmap_local(&bvec);354			segment[corrupt_bio_byte] = corrupt_bio_value;355			kunmap_local(segment);356			DMDEBUG("Corrupting data bio=%p by writing %u to byte %u "357				"(rw=%c bi_opf=%u bi_sector=%llu size=%u)\n",358				bio, corrupt_bio_value, corrupt_bio_byte,359				(bio_data_dir(bio) == WRITE) ? 'w' : 'r', bio->bi_opf,360				(unsigned long long)bio->bi_iter.bi_sector,361				bio->bi_iter.bi_size);362			break;363		}364		corrupt_bio_byte -= bio_iter_len(bio, iter);365	}366}367 368static void corrupt_bio_data(struct bio *bio, struct flakey_c *fc)369{370	unsigned int corrupt_bio_byte = fc->corrupt_bio_byte - 1;371 372	if (!bio_has_data(bio))373		return;374 375	corrupt_bio_common(bio, corrupt_bio_byte, fc->corrupt_bio_value);376}377 378static void corrupt_bio_random(struct bio *bio)379{380	unsigned int corrupt_byte;381	unsigned char corrupt_value;382 383	if (!bio_has_data(bio))384		return;385 386	corrupt_byte = get_random_u32() % bio->bi_iter.bi_size;387	corrupt_value = get_random_u8();388 389	corrupt_bio_common(bio, corrupt_byte, corrupt_value);390}391 392static void clone_free(struct bio *clone)393{394	struct folio_iter fi;395 396	if (clone->bi_vcnt > 0) { /* bio_for_each_folio_all crashes with an empty bio */397		bio_for_each_folio_all(fi, clone)398			folio_put(fi.folio);399	}400 401	bio_uninit(clone);402	kfree(clone);403}404 405static void clone_endio(struct bio *clone)406{407	struct bio *bio = clone->bi_private;408	bio->bi_status = clone->bi_status;409	clone_free(clone);410	bio_endio(bio);411}412 413static struct bio *clone_bio(struct dm_target *ti, struct flakey_c *fc, struct bio *bio)414{415	struct bio *clone;416	unsigned size, remaining_size, nr_iovecs, order;417	struct bvec_iter iter = bio->bi_iter;418 419	if (unlikely(bio->bi_iter.bi_size > UIO_MAXIOV << PAGE_SHIFT))420		dm_accept_partial_bio(bio, UIO_MAXIOV << PAGE_SHIFT >> SECTOR_SHIFT);421 422	size = bio->bi_iter.bi_size;423	nr_iovecs = (size + PAGE_SIZE - 1) >> PAGE_SHIFT;424 425	clone = bio_kmalloc(nr_iovecs, GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN);426	if (!clone)427		return NULL;428 429	bio_init(clone, fc->dev->bdev, bio->bi_inline_vecs, nr_iovecs, bio->bi_opf);430 431	clone->bi_iter.bi_sector = flakey_map_sector(ti, bio->bi_iter.bi_sector);432	clone->bi_private = bio;433	clone->bi_end_io = clone_endio;434 435	remaining_size = size;436 437	order = MAX_PAGE_ORDER;438	while (remaining_size) {439		struct page *pages;440		unsigned size_to_add, to_copy;441		unsigned char *virt;442		unsigned remaining_order = __fls((remaining_size + PAGE_SIZE - 1) >> PAGE_SHIFT);443		order = min(order, remaining_order);444 445retry_alloc_pages:446		pages = alloc_pages(GFP_NOIO | __GFP_NORETRY | __GFP_NOWARN | __GFP_COMP, order);447		if (unlikely(!pages)) {448			if (order) {449				order--;450				goto retry_alloc_pages;451			}452			clone_free(clone);453			return NULL;454		}455		size_to_add = min((unsigned)PAGE_SIZE << order, remaining_size);456 457		virt = page_to_virt(pages);458		to_copy = size_to_add;459		do {460			struct bio_vec bvec = bvec_iter_bvec(bio->bi_io_vec, iter);461			unsigned this_step = min(bvec.bv_len, to_copy);462			void *map = bvec_kmap_local(&bvec);463			memcpy(virt, map, this_step);464			kunmap_local(map);465 466			bvec_iter_advance(bio->bi_io_vec, &iter, this_step);467			to_copy -= this_step;468			virt += this_step;469		} while (to_copy);470 471		__bio_add_page(clone, pages, size_to_add, 0);472		remaining_size -= size_to_add;473	}474 475	return clone;476}477 478static int flakey_map(struct dm_target *ti, struct bio *bio)479{480	struct flakey_c *fc = ti->private;481	unsigned int elapsed;482	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));483 484	pb->bio_submitted = false;485 486	if (op_is_zone_mgmt(bio_op(bio)))487		goto map_bio;488 489	/* Are we alive ? */490	elapsed = (jiffies - fc->start_time) / HZ;491	if (elapsed % (fc->up_interval + fc->down_interval) >= fc->up_interval) {492		bool corrupt_fixed, corrupt_random;493		/*494		 * Flag this bio as submitted while down.495		 */496		pb->bio_submitted = true;497 498		/*499		 * Error reads if neither corrupt_bio_byte or drop_writes or error_writes are set.500		 * Otherwise, flakey_end_io() will decide if the reads should be modified.501		 */502		if (bio_data_dir(bio) == READ) {503			if (test_bit(ERROR_READS, &fc->flags))504				return DM_MAPIO_KILL;505			goto map_bio;506		}507 508		/*509		 * Drop or error writes?510		 */511		if (test_bit(DROP_WRITES, &fc->flags)) {512			bio_endio(bio);513			return DM_MAPIO_SUBMITTED;514		} else if (test_bit(ERROR_WRITES, &fc->flags)) {515			bio_io_error(bio);516			return DM_MAPIO_SUBMITTED;517		}518 519		/*520		 * Corrupt matching writes.521		 */522		corrupt_fixed = false;523		corrupt_random = false;524		if (fc->corrupt_bio_byte && fc->corrupt_bio_rw == WRITE) {525			if (all_corrupt_bio_flags_match(bio, fc))526				corrupt_fixed = true;527		}528		if (fc->random_write_corrupt) {529			u64 rnd = get_random_u64();530			u32 rem = do_div(rnd, PROBABILITY_BASE);531			if (rem < fc->random_write_corrupt)532				corrupt_random = true;533		}534		if (corrupt_fixed || corrupt_random) {535			struct bio *clone = clone_bio(ti, fc, bio);536			if (clone) {537				if (corrupt_fixed)538					corrupt_bio_data(clone, fc);539				if (corrupt_random)540					corrupt_bio_random(clone);541				submit_bio(clone);542				return DM_MAPIO_SUBMITTED;543			}544		}545	}546 547map_bio:548	flakey_map_bio(ti, bio);549 550	return DM_MAPIO_REMAPPED;551}552 553static int flakey_end_io(struct dm_target *ti, struct bio *bio,554			 blk_status_t *error)555{556	struct flakey_c *fc = ti->private;557	struct per_bio_data *pb = dm_per_bio_data(bio, sizeof(struct per_bio_data));558 559	if (op_is_zone_mgmt(bio_op(bio)))560		return DM_ENDIO_DONE;561 562	if (!*error && pb->bio_submitted && (bio_data_dir(bio) == READ)) {563		if (fc->corrupt_bio_byte) {564			if ((fc->corrupt_bio_rw == READ) &&565			    all_corrupt_bio_flags_match(bio, fc)) {566				/*567				 * Corrupt successful matching READs while in down state.568				 */569				corrupt_bio_data(bio, fc);570			}571		}572		if (fc->random_read_corrupt) {573			u64 rnd = get_random_u64();574			u32 rem = do_div(rnd, PROBABILITY_BASE);575			if (rem < fc->random_read_corrupt)576				corrupt_bio_random(bio);577		}578		if (test_bit(ERROR_READS, &fc->flags)) {579			/*580			 * Error read during the down_interval if drop_writes581			 * and error_writes were not configured.582			 */583			*error = BLK_STS_IOERR;584		}585	}586 587	return DM_ENDIO_DONE;588}589 590static void flakey_status(struct dm_target *ti, status_type_t type,591			  unsigned int status_flags, char *result, unsigned int maxlen)592{593	unsigned int sz = 0;594	struct flakey_c *fc = ti->private;595	unsigned int error_reads, drop_writes, error_writes;596 597	switch (type) {598	case STATUSTYPE_INFO:599		result[0] = '\0';600		break;601 602	case STATUSTYPE_TABLE:603		DMEMIT("%s %llu %u %u", fc->dev->name,604		       (unsigned long long)fc->start, fc->up_interval,605		       fc->down_interval);606 607		error_reads = test_bit(ERROR_READS, &fc->flags);608		drop_writes = test_bit(DROP_WRITES, &fc->flags);609		error_writes = test_bit(ERROR_WRITES, &fc->flags);610		DMEMIT(" %u", error_reads + drop_writes + error_writes +611			(fc->corrupt_bio_byte > 0) * 5 +612			(fc->random_read_corrupt > 0) * 2 +613			(fc->random_write_corrupt > 0) * 2);614 615		if (error_reads)616			DMEMIT(" error_reads");617		if (drop_writes)618			DMEMIT(" drop_writes");619		else if (error_writes)620			DMEMIT(" error_writes");621 622		if (fc->corrupt_bio_byte)623			DMEMIT(" corrupt_bio_byte %u %c %u %u",624			       fc->corrupt_bio_byte,625			       (fc->corrupt_bio_rw == WRITE) ? 'w' : 'r',626			       fc->corrupt_bio_value, fc->corrupt_bio_flags);627 628		if (fc->random_read_corrupt > 0)629			DMEMIT(" random_read_corrupt %u", fc->random_read_corrupt);630		if (fc->random_write_corrupt > 0)631			DMEMIT(" random_write_corrupt %u", fc->random_write_corrupt);632 633		break;634 635	case STATUSTYPE_IMA:636		result[0] = '\0';637		break;638	}639}640 641static int flakey_prepare_ioctl(struct dm_target *ti, struct block_device **bdev)642{643	struct flakey_c *fc = ti->private;644 645	*bdev = fc->dev->bdev;646 647	/*648	 * Only pass ioctls through if the device sizes match exactly.649	 */650	if (fc->start || ti->len != bdev_nr_sectors((*bdev)))651		return 1;652	return 0;653}654 655#ifdef CONFIG_BLK_DEV_ZONED656static int flakey_report_zones(struct dm_target *ti,657		struct dm_report_zones_args *args, unsigned int nr_zones)658{659	struct flakey_c *fc = ti->private;660 661	return dm_report_zones(fc->dev->bdev, fc->start,662			       flakey_map_sector(ti, args->next_sector),663			       args, nr_zones);664}665#else666#define flakey_report_zones NULL667#endif668 669static int flakey_iterate_devices(struct dm_target *ti, iterate_devices_callout_fn fn, void *data)670{671	struct flakey_c *fc = ti->private;672 673	return fn(ti, fc->dev, fc->start, ti->len, data);674}675 676static struct target_type flakey_target = {677	.name   = "flakey",678	.version = {1, 5, 0},679	.features = DM_TARGET_ZONED_HM | DM_TARGET_PASSES_CRYPTO,680	.report_zones = flakey_report_zones,681	.module = THIS_MODULE,682	.ctr    = flakey_ctr,683	.dtr    = flakey_dtr,684	.map    = flakey_map,685	.end_io = flakey_end_io,686	.status = flakey_status,687	.prepare_ioctl = flakey_prepare_ioctl,688	.iterate_devices = flakey_iterate_devices,689};690module_dm(flakey);691 692MODULE_DESCRIPTION(DM_NAME " flakey target");693MODULE_AUTHOR("Joe Thornber <dm-devel@lists.linux.dev>");694MODULE_LICENSE("GPL");695