diff mbox

[v4,1/2] dm unstriped: the target

Message ID 02ebee64c38f09a0709f66aa2489adc78ba547a7.1513618685.git.heinzm@redhat.com (mailing list archive)
State Superseded, archived
Delegated to: Mike Snitzer
Headers show

Commit Message

Heinz Mauelshagen Dec. 18, 2017, 6:08 p.m. UTC
Signed-off-by: Heinz Mauelshagen <heinzm@redhat.com>
---
 drivers/md/Kconfig       |  11 +++
 drivers/md/Makefile      |   1 +
 drivers/md/dm-unstripe.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 245 insertions(+)
 create mode 100644 drivers/md/dm-unstripe.c
diff mbox

Patch

diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 83b9362be09c..7f8099f53ad6 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -269,6 +269,17 @@  config DM_BIO_PRISON
 
 source "drivers/md/persistent-data/Kconfig"
 
+config DM_UNSTRIPED
+       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 controllers
+	 on the stripes.
+
+	 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..63255f3ebd97 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_UNSTRIPED)	+= 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..a590e1544dcf
--- /dev/null
+++ b/drivers/md/dm-unstripe.c
@@ -0,0 +1,233 @@ 
+/*
+ * Copyright © 2017 Intel Corporation
+ *
+ * Authors:
+ *    Scott  Bauer      <scott.bauer@intel.com>
+ *
+ * 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;
+	sector_t cur_drive_chunk_sectors;
+	unsigned int chunk_size;
+	u8 chunk_shift;
+	u8 cur_drive;
+	u8 total_drives;
+};
+
+#define DM_MSG_PREFIX "dm-unstriped"
+
+static void unstripe_dtr(struct dm_target *ti)
+{
+	struct unstripe *ut = ti->private;
+
+	if (ut->ddisk)
+		dm_put_device(ti, ut->ddisk);
+	kfree(ut);
+}
+
+/*
+ * Construct an "unstriped" mapping:
+ *
+ * Args:
+ *	<unstriped_dev> <extract_dev#> <#total_devs> <chunk_size>
+ *
+ * <unstriped_dev> device path of the raid0 device to unstripe (e.g. /dev/nvme0)
+ * <extract_dev#>  index starting at 0 of device to extract
+ * <#total_devs>   number of total, extractable devices
+ */
+static int unstripe_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int r = -EINVAL;
+	sector_t sectors;
+	struct unstripe *ut;
+
+	if (argc != 4) {
+		ti->error = "Invalid numver of arguments!";
+		return -EINVAL;
+	}
+
+	/* FIXME: interesting to allow multi-segment mappings for some testing? */
+	if (ti->begin) {
+		ti->error = "Non-zero target begin!";
+		return -EINVAL;
+	}
+
+	ut = kzalloc(sizeof(*ut), GFP_KERNEL);
+	if (!ut) {
+		ti->error = "Failed to allocate space for target!";
+		return -ENOMEM;
+	}
+
+	if (sscanf(argv[1], "%hhu", &ut->cur_drive) != 1 ||
+	    sscanf(argv[2], "%hhu", &ut->total_drives) != 1 ||
+	    sscanf(argv[3], "%u", &ut->chunk_size) != 1) {
+		ti->error = "Error parsing drive/chunk arguments!";
+		goto err;
+	}
+
+	if (!ut->total_drives || (ut->cur_drive > ut->total_drives && ut->total_drives > 1)) {
+		ti->error = "Please provide drive between [0,total_devices]";
+		goto err;
+	}
+
+	if (ut->chunk_size && !is_power_of_2(ut->chunk_size)) {
+		ti->error = "Non power of 2 chunk size!";
+		goto err;
+	}
+
+	r = dm_get_device(ti, argv[0], dm_table_get_mode(ti->table), &ut->ddisk);
+	if (r) {
+		ti->error = "dm-unstripe dev lookup failure!";
+		goto err;
+	}
+
+	/* FIXME: uspace should provide proper chunk size! */
+	/* Given chunk sectors or max hw sectors if 0 */
+	ut->chunk_sectors = ut->chunk_size ?: queue_max_hw_sectors(bdev_get_queue(ut->ddisk->bdev));
+	ut->cur_drive_chunk_sectors = ut->cur_drive * ut->chunk_sectors;
+	ut->stripe_sectors = (ut->total_drives - 1) * ut->chunk_sectors;
+	ut->chunk_shift = fls(ut->chunk_sectors) - 1;
+
+	/* Check uspace provided proper device length divisible by total drives */
+	sectors = i_size_read(ut->ddisk->bdev->bd_inode) >> SECTOR_SHIFT;
+	if (do_div(sectors, ut->total_drives)) {
+		ti->error = "Device length not divisible by total number of drives!";
+		goto err;
+	}
+
+	/* Check uspace provided proper target length! */
+	if (sectors != ti->len) {
+		ti->error = "Drive length not equal to target length!";
+		goto err;
+	}
+
+	/* Check uspace provided proper drive length divisible by chunk size */
+	if (do_div(sectors, ut->chunk_sectors)) {
+		ti->error = "Drive length not divisible by chunk size";
+		goto err;
+	}
+
+	r = dm_set_target_max_io_len(ti, ut->chunk_sectors);
+	if (r) {
+		ti->error = "Failed to set max io len!";
+		goto err;
+	}
+
+	ti->private = ut;
+	return 0;
+err:
+	unstripe_dtr(ti);
+	return r;
+}
+
+static sector_t map_to_core(struct dm_target *ti, struct bio *bio)
+{
+	struct unstripe *ut = ti->private;
+	sector_t sec = bio->bi_iter.bi_sector;
+
+	/* Account for what drive we're operating on */
+	sec += ut->cur_drive_chunk_sectors;
+
+	/* Shift us up to the right "row" on the drive*/
+	sec += ut->stripe_sectors * (sec >> ut->chunk_shift);
+	return sec;
+}
+
+static int unstripe_map(struct dm_target *ti, struct bio *bio)
+{
+	struct unstripe *ut = ti->private;
+
+	bio_set_dev(bio, ut->ddisk->bdev);
+	bio->bi_iter.bi_sector = map_to_core(ti, bio);
+	return DM_MAPIO_REMAPPED;
+}
+
+static void unstripe_status(struct dm_target *ti, status_type_t type,
+			    unsigned int status_flags, char *result, unsigned int maxlen)
+{
+	struct unstripe *ut = ti->private;
+	unsigned int sz = 0;
+
+	switch (type) {
+	case STATUSTYPE_INFO:
+		break;
+
+	case STATUSTYPE_TABLE:
+		DMEMIT("%s %hhu %hhu %u",
+		       ut->ddisk->name, ut->cur_drive, ut->total_drives, ut->chunk_size);
+	}
+}
+
+static void unstripe_iohints(struct dm_target *ti,
+			struct queue_limits *limits)
+{
+	struct unstripe *ut = ti->private;
+	struct queue_limits *lim = &bdev_get_queue(ut->ddisk->bdev)->limits;
+
+	blk_limits_io_min(limits, lim->io_min);
+	blk_limits_io_opt(limits, lim->io_opt);
+	limits->chunk_sectors = ut->chunk_sectors;
+}
+
+static int unstripe_iterate(struct dm_target *ti, iterate_devices_callout_fn fn,
+		       void *data)
+{
+	struct unstripe *ut = ti->private;
+
+	return fn(ti, ut->ddisk, 0, ti->len, data);
+}
+
+static struct target_type unstripe_target = {
+	.name = "unstriped",
+	.version = {1, 0, 0},
+	.module = THIS_MODULE,
+	.ctr = unstripe_ctr,
+	.dtr = unstripe_dtr,
+	.map = unstripe_map,
+	.status = unstripe_status,
+	.iterate_devices = unstripe_iterate,
+	.io_hints = unstripe_iohints,
+};
+
+static int __init dm_unstripe_init(void)
+{
+	int r = dm_register_target(&unstripe_target);
+
+	if (r < 0)
+		DMERR("register failed %d", r);
+
+	return r;
+}
+
+static void __exit dm_unstripe_exit(void)
+{
+	dm_unregister_target(&unstripe_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@intel.com>");
+MODULE_LICENSE("GPL");