This device mapper module remaps and unstripes IO so it lands solely on a single drive in a RAID 0/dm-stripe target. In a 4 drive RAID 0 the mapper exposes 1/4th of the LBA range as a virtual drive. Each IO to that virtual drive will land on only one of the 4 drives, selected by the user. Signed-off-by: Scott Bauer <scott.bauer@xxxxxxxxx> Acked-by: Keith Busch <keith.busch@xxxxxxxxx> --- drivers/md/Kconfig | 10 +++ drivers/md/Makefile | 1 + drivers/md/dm-unstripe.c | 204 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 215 insertions(+) create mode 100644 drivers/md/dm-unstripe.c diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig index 83b9362be09c..e1c48a7f98f1 100644 --- a/drivers/md/Kconfig +++ b/drivers/md/Kconfig @@ -269,6 +269,16 @@ config DM_BIO_PRISON source "drivers/md/persistent-data/Kconfig" +config DM_UN_STRIPE + tristate "Transpose IO to individual drives on a raid device" + depends on BLK_DEV_DM + ---help--- + Enable this feature if you with to unstripe I/O on a RAID 0 + device to the respective drive. If your hardware has physical + RAID 0 this module can unstripe the I/O to respective sides. + + If unsure say N. + config DM_CRYPT tristate "Crypt target support" depends on BLK_DEV_DM diff --git a/drivers/md/Makefile b/drivers/md/Makefile index f701bb211783..2cc380b71319 100644 --- a/drivers/md/Makefile +++ b/drivers/md/Makefile @@ -43,6 +43,7 @@ obj-$(CONFIG_BCACHE) += bcache/ obj-$(CONFIG_BLK_DEV_MD) += md-mod.o obj-$(CONFIG_BLK_DEV_DM) += dm-mod.o obj-$(CONFIG_BLK_DEV_DM_BUILTIN) += dm-builtin.o +obj-$(CONFIG_DM_UN_STRIPE) += dm-unstripe.o obj-$(CONFIG_DM_BUFIO) += dm-bufio.o obj-$(CONFIG_DM_BIO_PRISON) += dm-bio-prison.o obj-$(CONFIG_DM_CRYPT) += dm-crypt.o diff --git a/drivers/md/dm-unstripe.c b/drivers/md/dm-unstripe.c new file mode 100644 index 000000000000..086689fb11e3 --- /dev/null +++ b/drivers/md/dm-unstripe.c @@ -0,0 +1,204 @@ +/* + * Copyright © 2017 Intel Corporation + * + * Authors: + * Scott Bauer <scott.bauer@xxxxxxxxx> + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + */ + +#include "dm.h" +#include <linux/module.h> +#include <linux/init.h> +#include <linux/blkdev.h> +#include <linux/bio.h> +#include <linux/slab.h> +#include <linux/bitops.h> +#include <linux/device-mapper.h> + + +struct unstripe { + struct dm_dev *ddisk; + sector_t chunk_sectors; + sector_t stripe_sectors; + u8 chunk_shift; + u8 cur_drive; +}; + + +#define DM_MSG_PREFIX "dm-unstripe" +static const char *parse_err = "Please provide the necessary information:" + "<drive> <device (0 indexed)> <total_devices>" + " <chunk size in 512B sectors || 0 to use max hw sector size>"; + +/* + * Argument layout: + * <drive> <stripe/drive to extract (0 indexed)> + * <total_devices> <chunk size in 512B sect> + */ +static int set_ctr(struct dm_target *ti, unsigned int argc, char **argv) +{ + struct block_device *bbdev; + struct unstripe *target; + unsigned int chunk_size; + u64 tot_sec, mod; + u8 cur_drive, tot_drives; + char dummy; + int ret; + + if (argc != 4) { + DMERR("%s", parse_err); + return -EINVAL; + } + + if (sscanf(argv[1], "%hhu%c", &cur_drive, &dummy) != 1 || + sscanf(argv[2], "%hhu%c", &tot_drives, &dummy) != 1 || + sscanf(argv[3], "%u%c", &chunk_size, &dummy) != 1) { + DMERR("%s", parse_err); + return -EINVAL; + } + + if (tot_drives == 0 || (cur_drive >= tot_drives && tot_drives > 1)) { + DMERR("Please provide a drive between [0,%hhu)", tot_drives); + return -EINVAL; + } + + target = kzalloc(sizeof(*target), GFP_KERNEL); + + if (!target) { + DMERR("Failed to allocate space for DM unstripe!"); + return -ENOMEM; + } + + ret = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), + &target->ddisk); + if (ret) { + kfree(target); + DMERR("dm-unstripe dev lookup failure! for drive %s", argv[0]); + return ret; + } + + bbdev = target->ddisk->bdev; + + target->cur_drive = cur_drive; + if (chunk_size) + target->chunk_sectors = chunk_size; + else + target->chunk_sectors = + queue_max_hw_sectors(bdev_get_queue(bbdev)); + + target->stripe_sectors = (tot_drives - 1) * target->chunk_sectors; + target->chunk_shift = fls(target->chunk_sectors) - 1; + + ret = dm_set_target_max_io_len(ti, target->chunk_sectors); + if (ret) { + dm_put_device(ti, target->ddisk); + kfree(target); + DMERR("Failed to set max io len!"); + return ret; + } + ti->private = target; + + tot_sec = i_size_read(bbdev->bd_inode) >> SECTOR_SHIFT; + mod = tot_sec % target->chunk_sectors; + + if (ti->len == 1) + ti->len = (tot_sec / tot_drives) - mod; + ti->begin = 0; + return 0; +} + +static void set_dtr(struct dm_target *ti) +{ + struct unstripe *target = ti->private; + + dm_put_device(ti, target->ddisk); + kfree(target); +} + + +static sector_t map_to_core(struct dm_target *ti, struct bio *bio) +{ + struct unstripe *target = ti->private; + unsigned long long sec = bio->bi_iter.bi_sector; + unsigned long long group; + + group = (sec >> target->chunk_shift); + /* Account for what drive we're operating on */ + sec += (target->cur_drive * target->chunk_sectors); + /* Shift us up to the right "row" on the drive*/ + sec += target->stripe_sectors * group; + return sec; +} + +static int set_map_bio(struct dm_target *ti, struct bio *bio) +{ + struct unstripe *target = ti->private; + + if (bio_sectors(bio)) + bio->bi_iter.bi_sector = map_to_core(ti, bio); + + bio_set_dev(bio, target->ddisk->bdev); + submit_bio(bio); + return DM_MAPIO_SUBMITTED; +} + +static void set_iohints(struct dm_target *ti, + struct queue_limits *limits) +{ + struct unstripe *target = ti->private; + struct queue_limits *lim = &bdev_get_queue(target->ddisk->bdev)->limits; + + blk_limits_io_min(limits, lim->io_min); + blk_limits_io_opt(limits, lim->io_opt); + limits->chunk_sectors = target->chunk_sectors; +} + +static int set_iterate(struct dm_target *ti, iterate_devices_callout_fn fn, + void *data) +{ + struct unstripe *target = ti->private; + + return fn(ti, target->ddisk, 0, ti->len, data); +} + +static struct target_type iset_target = { + .name = "unstripe", + .version = {1, 0, 0}, + .module = THIS_MODULE, + .ctr = set_ctr, + .dtr = set_dtr, + .map = set_map_bio, + .iterate_devices = set_iterate, + .io_hints = set_iohints, +}; + +static int __init dm_unstripe_init(void) +{ + int r = dm_register_target(&iset_target); + + if (r < 0) + DMERR("register failed %d", r); + + return r; +} + +static void __exit dm_unstripe_exit(void) +{ + dm_unregister_target(&iset_target); +} + +module_init(dm_unstripe_init); +module_exit(dm_unstripe_exit); + +MODULE_DESCRIPTION(DM_NAME " DM unstripe"); +MODULE_ALIAS("dm-unstripe"); +MODULE_AUTHOR("Scott Bauer <scott.bauer@xxxxxxxxx>"); +MODULE_LICENSE("GPL"); -- 2.11.0 -- dm-devel mailing list dm-devel@xxxxxxxxxx https://www.redhat.com/mailman/listinfo/dm-devel