author | Alberto Bertogli
<albertito@blitiri.com.ar> 2008-10-13 21:16:53 UTC |
committer | Alberto Bertogli
<albertito@blitiri.com.ar> 2009-10-31 20:13:49 UTC |
parent | ad5ae4ede02b38b8b395ab242544a16a68f0125d |
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, ®ion, 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"); +