git » linux-kernel » commit abb284b

Device-mapper asynchronous linear target

author Alberto Bertogli
2008-10-13 21:16:53 UTC
committer Alberto Bertogli
2009-10-31 20:13:49 UTC
parent ad5ae4ede02b38b8b395ab242544a16a68f0125d

Device-mapper asynchronous linear target

This patch adds a new device-mapper module, called ""async-linear".

It behaves just like the regular "linear" target, except it uses the dm-io
infraestructure to do the writing in an asynchronous way.

It is only useful for testing and learning purposes.

Signed-off-by: Alberto Bertogli <albertito@blitiri.com.ar>

drivers/md/Kconfig +12 -0
drivers/md/Makefile +1 -0
drivers/md/dm-async-linear.c +311 -0

diff --git a/drivers/md/Kconfig b/drivers/md/Kconfig
index 07d92c11b5d..a466fa37082 100644
--- a/drivers/md/Kconfig
+++ b/drivers/md/Kconfig
@@ -269,6 +269,18 @@ config DM_DELAY
 
 	If unsure, say N.
 
+config DM_ASYNC_LINEAR
+	tristate "Async linear target"
+	depends on BLK_DEV_DM
+	---help---
+	  This module behaves just like the regular "linear" target, except it
+	  uses the dm-io infraestructure to do the writing in an asynchronous
+	  way.
+
+	  It is only useful for testing and learning purposes.
+
+	  If unsure, say N.
+
 config DM_UEVENT
 	bool "DM uevents (EXPERIMENTAL)"
 	depends on BLK_DEV_DM && EXPERIMENTAL
diff --git a/drivers/md/Makefile b/drivers/md/Makefile
index f1ef33dfd8c..86997c72fc7 100644
--- a/drivers/md/Makefile
+++ b/drivers/md/Makefile
@@ -36,6 +36,7 @@ obj-$(CONFIG_DM_MULTIPATH)	+= dm-multipath.o dm-round-robin.o
 obj-$(CONFIG_DM_SNAPSHOT)	+= dm-snapshot.o
 obj-$(CONFIG_DM_MIRROR)		+= dm-mirror.o dm-log.o
 obj-$(CONFIG_DM_ZERO)		+= dm-zero.o
+obj-$(CONFIG_DM_ASYNC_LINEAR)	+= dm-async-linear.o
 
 quiet_cmd_unroll = UNROLL  $@
       cmd_unroll = $(PERL) $(srctree)/$(src)/unroll.pl $(UNROLL) \
diff --git a/drivers/md/dm-async-linear.c b/drivers/md/dm-async-linear.c
new file mode 100644
index 00000000000..aed29b0b92c
--- /dev/null
+++ b/drivers/md/dm-async-linear.c
@@ -0,0 +1,311 @@
+/*
+ * A target that stores checksums on writes, and verifies them on reads.
+ * Alberto Bertogli <albertito@blitiri.com.ar>, 2008
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/bio.h>
+#include <linux/slab.h>
+#include <linux/dm-io.h>
+
+#include "dm.h"
+
+#define DM_MSG_PREFIX "async-linear"
+
+
+struct linear_c {
+	struct dm_dev *data_dev;
+	sector_t data_start;
+
+	struct dm_io_client *dm_io_client;
+};
+
+
+/* Given a dm device sector, returns the corresponding data device sector to
+ * find it from. */
+static sector_t map_data_sector(struct dm_target *ti, sector_t data)
+{
+	struct linear_c *lc = ti->private;
+
+	return lc->data_start + (data - ti->begin);
+}
+
+
+/* Constructor: <data dev path> <data dev offset> */
+static int linear_ctr(struct dm_target *ti, unsigned int argc, char **argv)
+{
+	int mode, err;
+	unsigned long long data_offset;
+	sector_t data_dev_len;
+	struct linear_c *lc;
+
+	if (argc != 2) {
+		ti->error = "Incorrect number of arguments";
+		return -EINVAL;
+	}
+
+	lc = kmalloc(sizeof(*lc), GFP_KERNEL);
+	if (lc == NULL) {
+		ti->error = "Cannot allocate context information";
+		return -ENOMEM;
+	}
+	lc->data_dev = NULL;
+	lc->data_start = 0;
+
+	err = -EINVAL;
+
+	if (sscanf(argv[1], "%llu", &data_offset) != 1) {
+		ti->error = "Invalid data dev offset";
+		goto error;
+	}
+	lc->data_start = data_offset;
+	data_dev_len = ti->len;
+
+	mode = dm_table_get_mode(ti->table);
+	if (dm_get_device(ti, argv[0], lc->data_start, data_dev_len, mode,
+			&(lc->data_dev))) {
+		ti->error = "data device lookup failed";
+		goto error;
+	}
+
+	/* TODO: ver el numero de paginas */
+	lc->dm_io_client = dm_io_client_create(128);
+	if (lc->dm_io_client == NULL) {
+		ti->error = "Cannot create dm-io client";
+		err = -ENOMEM;
+		goto error;
+	}
+
+	ti->private = lc;
+
+	return 0;
+
+error:
+	if (lc->data_dev)
+		dm_put_device(ti, lc->data_dev);
+	kfree(lc);
+	return err;
+}
+
+
+/* Destructor, undoes what was done in the constructor */
+static void linear_dtr(struct dm_target *ti)
+{
+	struct linear_c *lc = ti->private;
+
+	dm_io_client_destroy(lc->dm_io_client);
+	dm_put_device(ti, lc->data_dev);
+	kfree(lc);
+}
+
+
+/* Operation mapping */
+struct pending_read {
+	/* XXX: lock this? */
+	struct dm_target *ti;
+	struct linear_c *lc;
+	struct bio *orig_bio;
+
+	/* XXX: use these? */
+	unsigned long data_error_bits;
+
+	/* number of operations pending */
+	unsigned int nr_pending;
+};
+
+static void read_complete_cb(unsigned long error, void *context)
+{
+	struct pending_read *pr = context;
+
+	printk(KERN_DEBUG "read complete: %p %u %lu\n", pr->orig_bio, pr->nr_pending, error);
+	pr->nr_pending--;
+
+	if (pr->nr_pending == 0) {
+		printk(KERN_DEBUG "bio cnt pre  %u\n", atomic_read(&pr->orig_bio->bi_cnt));
+		bio_endio(pr->orig_bio, 0);
+		printk(KERN_DEBUG "bio cnt post %u\n", atomic_read(&pr->orig_bio->bi_cnt));
+
+		kfree(pr);
+		printk(KERN_DEBUG "free pr\n");
+	} else {
+		printk(KERN_DEBUG "!!! PENDING %u", pr->nr_pending);
+	}
+}
+
+static int read_data_async(struct pending_read *pr)
+{
+	struct linear_c *lc = pr->lc;
+	struct dm_target *ti = pr->ti;
+	struct bio *bio = pr->orig_bio;
+	struct dm_io_request req;
+	struct dm_io_region region;
+	sector_t mapped_first, mapped_last;
+	int r;
+
+	printk(KERN_DEBUG "async read\n");
+	printk(KERN_DEBUG "  bio bvec: %p + %d = %p, %lu\n", bio->bi_io_vec,
+			bio->bi_idx, bio->bi_io_vec + bio->bi_idx,
+			bio->bi_vcnt);
+	printk(KERN_DEBUG "  bio s:%llu e:%llu n:%llu cn:%llu\n",
+			bio->bi_sector, bio->bi_sector + bio_sectors(bio) - 1,
+			bio_sectors(bio), bio_cur_sectors(bio));
+	printk(KERN_DEBUG "  bio bvec %p %u %u\n", bio->bi_io_vec->bv_page,
+			bio->bi_io_vec->bv_len, bio->bi_io_vec->bv_offset);
+
+	req.bi_rw = READ;
+	req.notify.fn = read_complete_cb;
+	req.notify.context = pr;
+	req.client = lc->dm_io_client;
+
+	/* If the bio crosses over an imd sector, we need to split it up. To
+	 * detect that situation, we map the first and last sectors of the
+	 * bio, and then check if mapped_last - mapped_first != bio_sectors */
+	mapped_first = map_data_sector(ti, bio->bi_sector);
+	mapped_last = map_data_sector(ti,
+			bio->bi_sector + bio_sectors(bio) - 1);
+
+	/* simple mapping */
+
+	pr->nr_pending += 1;
+	req.mem.type = DM_IO_BVEC;
+	req.mem.ptr.bvec = bio->bi_io_vec + bio->bi_idx;
+
+	region.bdev = lc->data_dev->bdev;
+	region.sector = mapped_first;
+	region.count = bio_sectors(bio);
+
+
+	printk(KERN_DEBUG "async read about to queue\n");
+
+	r = dm_io(&req, 1, &region, NULL);
+	//msleep_interruptible(200);
+	printk(KERN_DEBUG "async read queued, returning %d\n", r);
+	return r;
+}
+
+
+static int do_read(struct dm_target *ti, struct bio *bio,
+		union map_info *map_context)
+{
+	int r;
+	struct linear_c *lc = ti->private;
+	struct pending_read *pr;
+
+	printk(KERN_DEBUG "reading %p\n", bio);
+
+	pr = kmalloc(sizeof(*pr), GFP_KERNEL);
+	if (pr == NULL) {
+		return -ENOMEM;
+	}
+
+	pr->ti = ti;
+	pr->lc = lc;
+	pr->orig_bio = bio;
+	pr->nr_pending = 0;
+	/* pr->nr_pending will be increased by read_*_async(), when we know
+	 * how may operations we need */
+
+	/* We will make one async request to read the imd and another one for
+	 * the metadata. We could merge them if we only use one device, but
+	 * for simplicty we will let the I/O scheduler do it at the moment. */
+	r = read_data_async(pr);
+	if (r != 0)
+		return r;
+
+	/* return SUBMITTED to indicate we took care of this bio */
+	printk(KERN_DEBUG "read returned submitted %u\n", atomic_read(&bio->bi_cnt));
+	return DM_MAPIO_SUBMITTED;
+}
+
+static int do_write(struct dm_target *ti, struct bio *bio,
+		union map_info *map_context)
+{
+	struct linear_c *lc = ti->private;
+	sector_t mapped_start, mapped_end;
+
+	printk(KERN_DEBUG "viene el write\n");
+
+	/* If the bio crosses over an imd sector, we need to split it up. To
+	 * detect that situation, we map the first and last sectors of the
+	 * bio, and then check if mapped_end - mapped_start != bio_sectors */
+	mapped_start = map_data_sector(ti, bio->bi_sector);
+	mapped_end = map_data_sector(ti, bio->bi_sector + bio_sectors(bio));
+
+	bio->bi_bdev = lc->data_dev->bdev;
+	bio->bi_sector = mapped_start;
+
+	return DM_MAPIO_REMAPPED;
+}
+
+
+static int linear_map(struct dm_target *ti, struct bio *bio,
+		      union map_info *map_context)
+{
+	printk(KERN_DEBUG "---- mapping bio %p c:%d\n", bio,
+			atomic_read(&bio->bi_cnt));
+
+	switch(bio_rw(bio)) {
+	case READ:
+	case READA:
+		return do_read(ti, bio, map_context);
+	case WRITE:
+		return do_write(ti, bio, map_context);
+	}
+
+	/* we should never reach here; if so, end the bio with error and spit
+	 * out a bug */
+	printk(KERN_WARNING "dm-linear: unknown bio_rw() value %lu\n",
+			bio_rw(bio));
+	bio_io_error(bio);
+	//BUG_ON(1);
+	return -1;
+}
+
+static int linear_end_io(struct dm_target *ti, struct bio *bio, int error,
+		union map_info *map_context)
+{
+	printk(KERN_DEBUG "end_io: %p %llu %d\n", bio, bio->bi_sector, error);
+	printk(KERN_DEBUG "bio cnt pre  %u\n", atomic_read(&bio->bi_cnt));
+	//bio_endio(bio, 0);
+	//printk(KERN_DEBUG "bio cnt post %u\n", atomic_read(&bio->bi_cnt));
+	return 0;
+}
+
+/* Target registration and module stuff */
+static struct target_type linear_target = {
+	.name   = "async-linear",
+	.version = {1, 0, 0},
+	.module = THIS_MODULE,
+	.ctr    = linear_ctr,
+	.dtr	= linear_dtr,
+	.map    = linear_map,
+	.end_io = linear_end_io,
+};
+
+int __init dm_async_linear_init(void)
+{
+	int rv = dm_register_target(&linear_target);
+
+	if (rv < 0)
+		DMERR("register failed: %d", rv);
+
+	return rv;
+}
+
+void __exit dm_async_linear_exit(void)
+{
+	int rv = dm_unregister_target(&linear_target);
+
+	if (rv < 0)
+		DMERR("unregister failed: %d", rv);
+}
+
+module_init(dm_async_linear_init)
+module_exit(dm_async_linear_exit)
+
+MODULE_AUTHOR("Alberto Bertogli <albertito@blitiri.com.ar>");
+MODULE_DESCRIPTION(DM_NAME " asynchronous linear target");
+MODULE_LICENSE("GPL");
+