[PATCH][RFC] Use bio markers for request callback

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi all,

this is a proposal for a different implementation of request callbacks. The existing ->endio callback of a request is actually a destructor function, to be called to terminate a request and free all structures.

However, on certain occasions (like request-based multipathing) it is desirable to have a callback function for a request which is called right after the request is finished, ie in end_that_request_first() before any bio->bi_endio callback is called.

So a simple solution for this is to clone the request and add a new 'marker' bio in front of the bio list of the request. This callback will be attached a structure in bi_private which keeps a pointer to the cloned and the original request, thus serving as a callback for the request itself.

Proposed patch attached. As usual comments are welcome.

Cheers,

Hannes
diff --git a/block/ll_rw_blk.c b/block/ll_rw_blk.c
index a15845c..c286f05 100644
--- a/block/ll_rw_blk.c
+++ b/block/ll_rw_blk.c
@@ -40,7 +40,6 @@ static void blk_unplug_work(struct work_struct *work);
 static void blk_unplug_timeout(unsigned long data);
 static void drive_stat_acct(struct request *rq, int nr_sectors, int new_io);
 static void init_request_from_bio(struct request *req, struct bio *bio);
-static int __make_request(struct request_queue *q, struct bio *bio);
 static struct io_context *current_io_context(gfp_t gfp_flags, int node);
 
 /*
@@ -2912,7 +2911,7 @@ static void init_request_from_bio(struct request *req, struct bio *bio)
 	req->start_time = jiffies;
 }
 
-static int __make_request(struct request_queue *q, struct bio *bio)
+int __make_request(struct request_queue *q, struct bio *bio)
 {
 	struct request *req;
 	int el_ret, nr_sectors, barrier, err;
@@ -3030,6 +3029,7 @@ end_io:
 	bio_endio(bio, nr_sectors << 9, err);
 	return 0;
 }
+EXPORT_SYMBOL(__make_request);
 
 /*
  * If bio->bi_dev is a partition, remap the location
diff --git a/drivers/md/dm-mpath.c b/drivers/md/dm-mpath.c
index d6ca9d0..76acda2 100644
--- a/drivers/md/dm-mpath.c
+++ b/drivers/md/dm-mpath.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2003 Sistina Software Limited.
  * Copyright (C) 2004-2005 Red Hat, Inc. All rights reserved.
+ * Copyright (C) 2007 NEC Corporation.
  *
  * This file is released under the GPL.
  */
@@ -8,8 +9,7 @@
 #include "dm.h"
 #include "dm-path-selector.h"
 #include "dm-hw-handler.h"
-#include "dm-bio-list.h"
-#include "dm-bio-record.h"
+#include "dm-rq-record.h"
 
 #include <linux/ctype.h>
 #include <linux/init.h>
@@ -77,7 +77,7 @@ struct multipath {
 	unsigned saved_queue_if_no_path;/* Saved state during suspension */
 
 	struct work_struct process_queued_ios;
-	struct bio_list queued_ios;
+	struct list_head queued_ios;
 	unsigned queue_size;
 
 	struct work_struct trigger_event;
@@ -94,7 +94,7 @@ struct multipath {
  */
 struct dm_mpath_io {
 	struct pgpath *pgpath;
-	struct dm_bio_details details;
+	struct dm_rq_details details;
 };
 
 typedef int (*action_fn) (struct pgpath *pgpath);
@@ -171,6 +171,7 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
 	m = kzalloc(sizeof(*m), GFP_KERNEL);
 	if (m) {
 		INIT_LIST_HEAD(&m->priority_groups);
+		INIT_LIST_HEAD(&m->queued_ios);
 		spin_lock_init(&m->lock);
 		m->queue_io = 1;
 		INIT_WORK(&m->process_queued_ios, process_queued_ios);
@@ -180,6 +181,9 @@ static struct multipath *alloc_multipath(struct dm_target *ti)
 			kfree(m);
 			return NULL;
 		}
+
+		dm_table_enable_elevator(ti->table);
+
 		m->ti = ti;
 		ti->private = m;
 	}
@@ -202,6 +206,8 @@ static void free_multipath(struct multipath *m)
 		dm_put_hw_handler(hwh->type);
 	}
 
+	dm_table_disable_elevator(m->ti->table);
+
 	mempool_destroy(m->mpio_pool);
 	kfree(m);
 }
@@ -299,7 +305,7 @@ static int __must_push_back(struct multipath *m)
 		dm_noflush_suspending(m->ti));
 }
 
-static int map_io(struct multipath *m, struct bio *bio,
+static int map_io(struct multipath *m, struct request *clone,
 		  struct dm_mpath_io *mpio, unsigned was_queued)
 {
 	int r = DM_MAPIO_REMAPPED;
@@ -321,22 +327,30 @@ static int map_io(struct multipath *m, struct bio *bio,
 	if ((pgpath && m->queue_io) ||
 	    (!pgpath && m->queue_if_no_path)) {
 		/* Queue for the daemon to resubmit */
-		bio_list_add(&m->queued_ios, bio);
+		list_add_tail(&clone->queuelist, &m->queued_ios);
 		m->queue_size++;
 		if ((m->pg_init_required && !m->pg_init_in_progress) ||
 		    !m->queue_io)
 			queue_work(kmultipathd, &m->process_queued_ios);
 		pgpath = NULL;
+		clone->q = NULL;
+		clone->rq_disk = NULL;
 		r = DM_MAPIO_SUBMITTED;
-	} else if (pgpath)
-		bio->bi_bdev = pgpath->path.dev->bdev;
-	else if (__must_push_back(m))
-		r = DM_MAPIO_REQUEUE;
-	else
+	} else if (pgpath) {
+		clone->q = bdev_get_queue(pgpath->path.dev->bdev);
+		clone->rq_disk = pgpath->path.dev->bdev->bd_disk;
+	} else {
+		printk(KERN_INFO "request %p: failed to map\n", clone);
+		clone->q = NULL;
+		clone->rq_disk = NULL;
 		r = -EIO;	/* Failed */
+	}
 
 	mpio->pgpath = pgpath;
 
+	if (r == DM_MAPIO_REMAPPED && m->current_pg->ps.type->start_io)
+		m->current_pg->ps.type->start_io(&m->current_pg->ps,
+						 &pgpath->path, clone);
 	spin_unlock_irqrestore(&m->lock, flags);
 
 	return r;
@@ -373,30 +387,38 @@ static void dispatch_queued_ios(struct multipath *m)
 {
 	int r;
 	unsigned long flags;
-	struct bio *bio = NULL, *next;
 	struct dm_mpath_io *mpio;
 	union map_info *info;
+	struct request *clone, *n;
+	LIST_HEAD(cl);
 
 	spin_lock_irqsave(&m->lock, flags);
-	bio = bio_list_get(&m->queued_ios);
+	list_splice_init(&m->queued_ios, &cl);
 	spin_unlock_irqrestore(&m->lock, flags);
 
-	while (bio) {
-		next = bio->bi_next;
-		bio->bi_next = NULL;
+	list_for_each_entry_safe(clone, n, &cl, queuelist) {
+		struct bio *bio = clone->bio;
+		list_del(&clone->queuelist);
+
+		BUG_ON(!bio);
 
-		info = dm_get_mapinfo(bio);
+		info = dm_get_mapinfo(clone->bio);
 		mpio = info->ptr;
 
-		r = map_io(m, bio, mpio, 1);
-		if (r < 0)
+		r = map_io(m, clone, mpio, 1);
+		if (r < 0) {
+			printk(KERN_INFO "%s: map clone %p failed\n",
+			       __FUNCTION__, clone);
+			/* FIXME */
 			bio_endio(bio, bio->bi_size, r);
-		else if (r == DM_MAPIO_REMAPPED)
-			generic_make_request(bio);
-		else if (r == DM_MAPIO_REQUEUE)
+		} else if (r == DM_MAPIO_REMAPPED)
+			dm_dispatch_request(clone->q, clone);
+		else if (r == DM_MAPIO_REQUEUE) {
+			printk(KERN_INFO "%s: requeue map clone %p\n",
+			       __FUNCTION__, clone);
+			/* FIXME */
 			bio_endio(bio, bio->bi_size, -EIO);
-
-		bio = next;
+		}
 	}
 }
 
@@ -789,23 +811,29 @@ static void multipath_dtr(struct dm_target *ti)
 }
 
 /*
- * Map bios, recording original fields for later in case we have to resubmit
+ * Map cloned requests
  */
-static int multipath_map(struct dm_target *ti, struct bio *bio,
+static int multipath_map(struct dm_target *ti, struct request *clone,
 			 union map_info *map_context)
 {
 	int r;
 	struct dm_mpath_io *mpio;
 	struct multipath *m = (struct multipath *) ti->private;
 
+	if (blk_barrier_rq(clone))
+		return -EOPNOTSUPP;
+
 	mpio = mempool_alloc(m->mpio_pool, GFP_NOIO);
-	dm_bio_record(&mpio->details, bio);
+	memset(mpio, 0, sizeof(mpio));
 
+	clone->cmd_flags |= REQ_FAILFAST;
+	dm_rq_record(&mpio->details, clone);
 	map_context->ptr = mpio;
-	bio->bi_rw |= (1 << BIO_RW_FAILFAST);
-	r = map_io(m, bio, mpio, 0);
-	if (r < 0 || r == DM_MAPIO_REQUEUE)
+	r = map_io(m, clone, mpio, 0);
+	if (r < 0 || r == DM_MAPIO_REQUEUE) {
 		mempool_free(mpio, m->mpio_pool);
+		map_context->ptr = NULL;
+	}
 
 	return r;
 }
@@ -1007,25 +1035,37 @@ void dm_pg_init_complete(struct dm_path *path, unsigned err_flags)
 	spin_unlock_irqrestore(&m->lock, flags);
 }
 
+static void multipath_queue_in_tgt(struct dm_target *ti, struct request *clone,
+				   union map_info *map_context)
+{
+	struct multipath *m = (struct multipath *) ti->private;
+	struct dm_mpath_io *mpio = (struct dm_mpath_io *) map_context->ptr;
+	unsigned long flags = 0UL;
+
+	dm_rq_restore(&mpio->details, clone);
+
+	/* queue for the daemon to resubmit or fail */
+	spin_lock_irqsave(&m->lock, flags);
+	list_add_tail(&clone->queuelist, &m->queued_ios);
+	m->queue_size++;
+	if (!m->queue_io)
+		queue_work(kmultipathd, &m->process_queued_ios);
+	spin_unlock_irqrestore(&m->lock, flags);
+}
+
 /*
  * end_io handling
  */
-static int do_end_io(struct multipath *m, struct bio *bio,
+static int do_end_io(struct multipath *m, struct request *clone,
 		     int error, struct dm_mpath_io *mpio)
 {
 	struct hw_handler *hwh = &m->hw_handler;
 	unsigned err_flags = MP_FAIL_PATH;	/* Default behavior */
-	unsigned long flags;
+	unsigned long flags = 0UL;
 
-	if (!error)
+	if (!clone->errors && !error)
 		return 0;	/* I/O complete */
 
-	if ((error == -EWOULDBLOCK) && bio_rw_ahead(bio))
-		return error;
-
-	if (error == -EOPNOTSUPP)
-		return error;
-
 	spin_lock_irqsave(&m->lock, flags);
 	if (!m->nr_valid_paths) {
 		if (__must_push_back(m)) {
@@ -1041,9 +1081,6 @@ static int do_end_io(struct multipath *m, struct bio *bio,
 	}
 	spin_unlock_irqrestore(&m->lock, flags);
 
-	if (hwh->type && hwh->type->error)
-		err_flags = hwh->type->error(hwh, bio);
-
 	if (mpio->pgpath) {
 		if (err_flags & MP_FAIL_PATH)
 			fail_path(mpio->pgpath);
@@ -1056,21 +1093,14 @@ static int do_end_io(struct multipath *m, struct bio *bio,
 		return -EIO;
 
       requeue:
-	dm_bio_restore(&mpio->details, bio);
-
-	/* queue for the daemon to resubmit or fail */
-	spin_lock_irqsave(&m->lock, flags);
-	bio_list_add(&m->queued_ios, bio);
-	m->queue_size++;
-	if (!m->queue_io)
-		queue_work(kmultipathd, &m->process_queued_ios);
-	spin_unlock_irqrestore(&m->lock, flags);
-
 	return DM_ENDIO_INCOMPLETE;	/* io not complete */
 }
 
-static int multipath_end_io(struct dm_target *ti, struct bio *bio,
-			    int error, union map_info *map_context)
+/*
+ * clone->q's lock must be held
+ */
+static int multipath_end_io_first(struct dm_target *ti, struct request *clone,
+				  int error, union map_info *map_context)
 {
 	struct multipath *m = ti->private;
 	struct dm_mpath_io *mpio = map_context->ptr;
@@ -1078,18 +1108,34 @@ static int multipath_end_io(struct dm_target *ti, struct bio *bio,
 	struct path_selector *ps;
 	int r;
 
-	r  = do_end_io(m, bio, error, mpio);
+	r  = do_end_io(m, clone, error, mpio);
 	if (pgpath) {
 		ps = &pgpath->pg->ps;
 		if (ps->type->end_io)
-			ps->type->end_io(ps, &pgpath->path);
+			ps->type->end_io(ps, &pgpath->path, clone);
 	}
-	if (r != DM_ENDIO_INCOMPLETE)
+	if (r != DM_ENDIO_INCOMPLETE && mpio) {
 		mempool_free(mpio, m->mpio_pool);
+		map_context->ptr = NULL;
+	}
 
 	return r;
 }
 
+static int multipath_end_io(struct dm_target *ti, struct request *clone,
+			    int error, union map_info *map_context)
+{
+	struct multipath *m = (struct multipath *) ti->private;
+	struct dm_mpath_io *mpio = (struct dm_mpath_io *) map_context->ptr;
+
+	if (mpio) {
+		mempool_free(mpio, m->mpio_pool);
+		map_context->ptr = NULL;
+	}
+
+	return 0;
+}
+
 /*
  * Suspend can't complete until all the I/O is processed so if
  * the last path fails we must error any remaining I/O.
@@ -1329,8 +1375,10 @@ static struct target_type multipath_target = {
 	.module = THIS_MODULE,
 	.ctr = multipath_ctr,
 	.dtr = multipath_dtr,
-	.map = multipath_map,
-	.end_io = multipath_end_io,
+	.map_rq = multipath_map,
+	.rq_end_io_first = multipath_end_io_first,
+	.rq_end_io = multipath_end_io,
+	.queue_in_tgt = multipath_queue_in_tgt,
 	.presuspend = multipath_presuspend,
 	.resume = multipath_resume,
 	.status = multipath_status,
diff --git a/drivers/md/dm-path-selector.h b/drivers/md/dm-path-selector.h
index 27357b8..089a979 100644
--- a/drivers/md/dm-path-selector.h
+++ b/drivers/md/dm-path-selector.h
@@ -75,7 +75,10 @@ struct path_selector_type {
 	int (*status) (struct path_selector *ps, struct dm_path *path,
 		       status_type_t type, char *result, unsigned int maxlen);
 
-	int (*end_io) (struct path_selector *ps, struct dm_path *path);
+	int (*start_io) (struct path_selector *ps, struct dm_path *path,
+			struct request *clone);
+	int (*end_io) (struct path_selector *ps, struct dm_path *path,
+			struct request *clone);
 };
 
 /* Register a path selector */
diff --git a/drivers/md/dm-rq-record.h b/drivers/md/dm-rq-record.h
new file mode 100644
index 0000000..4e09598
--- /dev/null
+++ b/drivers/md/dm-rq-record.h
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2007 NEC Corporation.
+ *
+ * This file is released under the GPL.
+ */
+
+#ifndef DM_RQ_RECORD_H
+#define DM_RQ_RECORD_H
+
+#include <linux/blkdev.h>
+
+/*
+ * There are lots of mutable fields in the request struct that get
+ * changed by the lower levels of the block layer.  Some targets,
+ * such as multipath, may wish to resubmit a request on error.  The
+ * functions in this file help the target record and restore the
+ * original request state.
+ */
+struct dm_rq_details {
+	unsigned int cmd_flags;
+	int ref_count;
+};
+
+static inline void dm_rq_record(struct dm_rq_details *rd, struct request *rq)
+{
+	rd->cmd_flags = rq->cmd_flags;
+	rd->ref_count = rq->ref_count;
+}
+
+static inline void dm_rq_restore(struct dm_rq_details *rd, struct request *rq)
+{
+	rq->cmd_flags = rd->cmd_flags;
+	rq->ref_count = rd->ref_count;
+}
+
+#endif
diff --git a/drivers/md/dm-table.c b/drivers/md/dm-table.c
index 2bcde57..ad0c85e 100644
--- a/drivers/md/dm-table.c
+++ b/drivers/md/dm-table.c
@@ -1033,6 +1033,28 @@ struct mapped_device *dm_table_get_md(struct dm_table *t)
 	return t->md;
 }
 
+int dm_table_enable_elevator(struct dm_table *t)
+{
+	struct mapped_device *md = dm_table_get_md(t);
+
+	if (!md)
+		return 1;
+
+	if (dm_enable_elevator(md)) {
+		dm_put(md);
+		return 1;
+	}
+	dm_put(md);
+
+	return 0;
+}
+
+void dm_table_disable_elevator(struct dm_table *t)
+{
+	/* No reference taken */
+	dm_disable_elevator(t->md);
+}
+
 EXPORT_SYMBOL(dm_vcalloc);
 EXPORT_SYMBOL(dm_get_device);
 EXPORT_SYMBOL(dm_put_device);
@@ -1044,3 +1066,5 @@ EXPORT_SYMBOL(dm_table_put);
 EXPORT_SYMBOL(dm_table_get);
 EXPORT_SYMBOL(dm_table_unplug_all);
 EXPORT_SYMBOL(dm_table_flush_all);
+EXPORT_SYMBOL(dm_table_enable_elevator);
+EXPORT_SYMBOL(dm_table_disable_elevator);
diff --git a/drivers/md/dm.c b/drivers/md/dm.c
index 2120155..7ac9527 100644
--- a/drivers/md/dm.c
+++ b/drivers/md/dm.c
@@ -24,6 +24,8 @@
 
 #define DM_MSG_PREFIX "core"
 
+#define DM_DEBUG
+
 static const char *_name = DM_NAME;
 
 static unsigned int major = 0;
@@ -31,13 +33,19 @@ static unsigned int _major = 0;
 
 static DEFINE_SPINLOCK(_minor_lock);
 /*
- * One of these is allocated per bio.
+ * One of these is allocated per bio / request.
  */
 struct dm_io {
 	struct mapped_device *md;
 	int error;
-	struct bio *bio;
-	atomic_t io_count;
+	union {
+		struct bio *bio;
+		struct request *req;
+	} data;
+	union {
+		atomic_t io_count;
+		struct request *orig;
+	} parm;
 	unsigned long start_time;
 };
 
@@ -48,6 +56,7 @@ struct dm_io {
 struct dm_target_io {
 	struct dm_io *io;
 	struct dm_target *ti;
+	struct dm_table *map;
 	union map_info info;
 };
 
@@ -103,6 +112,7 @@ struct mapped_device {
 	 * io objects are allocated from here.
 	 */
 	mempool_t *io_pool;
+	mempool_t *rq_pool;
 	mempool_t *tio_pool;
 
 	struct bio_set *bs;
@@ -125,6 +135,7 @@ struct mapped_device {
 
 #define MIN_IOS 256
 static struct kmem_cache *_io_cache;
+static struct kmem_cache *_rq_cache;
 static struct kmem_cache *_tio_cache;
 
 static int __init local_init(void)
@@ -143,6 +154,14 @@ static int __init local_init(void)
 		return -ENOMEM;
 	}
 
+	_rq_cache = kmem_cache_create("dm_rq", sizeof(struct request),
+				      0, 0, NULL);
+	if (!_rq_cache) {
+		kmem_cache_destroy(_tio_cache);
+		kmem_cache_destroy(_io_cache);
+		return -ENOMEM;
+	}
+
 	_major = major;
 	r = register_blkdev(_major, _name);
 	if (r < 0) {
@@ -160,6 +179,7 @@ static int __init local_init(void)
 static void local_exit(void)
 {
 	kmem_cache_destroy(_tio_cache);
+	kmem_cache_destroy(_rq_cache);
 	kmem_cache_destroy(_io_cache);
 	unregister_blkdev(_major, _name);
 
@@ -341,6 +361,16 @@ static void free_tio(struct mapped_device *md, struct dm_target_io *tio)
 	mempool_free(tio, md->tio_pool);
 }
 
+static inline struct request *alloc_rq(struct mapped_device *md)
+{
+	return mempool_alloc(md->rq_pool, GFP_NOIO);
+}
+
+static inline void free_rq(struct mapped_device *md, struct request *rq)
+{
+	mempool_free(rq, md->rq_pool);
+}
+
 static void start_io_acct(struct dm_io *io)
 {
 	struct mapped_device *md = io->md;
@@ -356,7 +386,7 @@ static void start_io_acct(struct dm_io *io)
 static int end_io_acct(struct dm_io *io)
 {
 	struct mapped_device *md = io->md;
-	struct bio *bio = io->bio;
+	struct bio *bio = io->data.bio;
 	unsigned long duration = jiffies - io->start_time;
 	int pending;
 	int rw = bio_data_dir(bio);
@@ -434,6 +464,94 @@ int dm_set_geometry(struct mapped_device *md, struct hd_geometry *geo)
 	return 0;
 }
 
+/*
+ * The queue is only valid as long as you have a reference
+ * count on 'md'.
+ */
+struct request_queue *dm_get_queue(struct mapped_device *md)
+{
+	if (blk_get_queue(md->queue))
+		return NULL;
+
+	return md->queue;
+}
+EXPORT_SYMBOL_GPL(dm_get_queue);
+
+void dm_put_queue(struct request_queue *q)
+{
+	blk_put_queue(q);
+}
+EXPORT_SYMBOL_GPL(dm_put_queue);
+
+void dm_stop_queue(struct request_queue *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	blk_stop_queue(q);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dm_stop_queue);
+
+void dm_start_queue(struct request_queue *q)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	if (blk_queue_stopped(q))
+		blk_start_queue(q);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dm_start_queue);
+
+void __dm_requeue_request(struct request_queue *q, struct request *rq)
+{
+	if (elv_queue_empty(q))
+		blk_plug_device(q);
+	blk_requeue_request(q, rq);
+}
+EXPORT_SYMBOL_GPL(__dm_requeue_request);
+
+void dm_requeue_request(struct request_queue *q, struct request *rq)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+	__dm_requeue_request(q, rq);
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dm_requeue_request);
+
+void dm_dispatch_request(struct request_queue *q, struct request *rq)
+{
+	int where = ELEVATOR_INSERT_BACK;
+	unsigned long flags;
+
+	spin_lock_irqsave(q->queue_lock, flags);
+
+	/* Initialize statistic stuff */
+	disk_round_stats(rq->rq_disk);
+	rq->start_time = jiffies;
+	rq->rq_disk->in_flight++;
+
+	__elv_add_request(q, rq, where, 0);
+
+	spin_unlock_irqrestore(q->queue_lock, flags);
+}
+EXPORT_SYMBOL_GPL(dm_dispatch_request);
+
+static void kill_request(struct request *rq)
+{
+	int nr_bytes = rq->hard_nr_sectors << 9;
+
+	if (!nr_bytes)
+		nr_bytes = rq->data_len;
+
+	rq->cmd_flags |= REQ_QUIET;
+	end_that_request_first(rq, 0, nr_bytes);
+	end_that_request_last(rq, 0);
+}
+
 /*-----------------------------------------------------------------
  * CRUD START:
  *   A more elegant soln is in the works that uses the queue
@@ -460,7 +578,7 @@ static void dec_pending(struct dm_io *io, int error)
 	if (error && !(io->error > 0 && __noflush_suspending(io->md)))
 		io->error = error;
 
-	if (atomic_dec_and_test(&io->io_count)) {
+	if (atomic_dec_and_test(&io->parm.io_count)) {
 		if (io->error == DM_ENDIO_REQUEUE) {
 			/*
 			 * Target requested pushing back the I/O.
@@ -469,7 +587,7 @@ static void dec_pending(struct dm_io *io, int error)
 			 */
 			spin_lock_irqsave(&io->md->pushback_lock, flags);
 			if (__noflush_suspending(io->md))
-				bio_list_add(&io->md->pushback, io->bio);
+				bio_list_add(&io->md->pushback, io->data.bio);
 			else
 				/* noflush suspend was interrupted. */
 				io->error = -EIO;
@@ -481,10 +599,10 @@ static void dec_pending(struct dm_io *io, int error)
 			wake_up(&io->md->wait);
 
 		if (io->error != DM_ENDIO_REQUEUE) {
-			blk_add_trace_bio(io->md->queue, io->bio,
+			blk_add_trace_bio(io->md->queue, io->data.bio,
 					  BLK_TA_COMPLETE);
 
-			bio_endio(io->bio, io->bio->bi_size, io->error);
+			bio_endio(io->data.bio, io->data.bio->bi_size, io->error);
 		}
 
 		free_io(io->md, io);
@@ -533,6 +651,30 @@ static int clone_endio(struct bio *bio, unsigned int done, int error)
 	return r;
 }
 
+static void blk_update_cloned_rq(struct request *rq, struct request *clone)
+{
+	clone->nr_phys_segments = rq->nr_phys_segments;
+	clone->nr_hw_segments = rq->nr_hw_segments;
+	clone->current_nr_sectors = rq->current_nr_sectors;
+	clone->hard_cur_sectors = rq->hard_cur_sectors;
+	clone->hard_nr_sectors = rq->hard_nr_sectors;
+	clone->nr_sectors = rq->nr_sectors;
+	clone->hard_sector = rq->hard_sector;
+	clone->sector = rq->sector;
+	clone->data_len = rq->data_len;
+	clone->buffer = rq->buffer;
+	clone->data = rq->data;
+	clone->bio = rq->bio;
+	clone->biotail = rq->biotail;
+}
+
+static void dec_rq_pending(struct dm_target_io *tio)
+{
+	if (!atomic_dec_return(&tio->io->md->pending))
+		/* nudge anyone waiting on suspend queue */
+		wake_up(&tio->io->md->wait);
+}
+
 static sector_t max_io_len(struct mapped_device *md,
 			   sector_t sector, struct dm_target *ti)
 {
@@ -573,14 +715,14 @@ static void __map_bio(struct dm_target *ti, struct bio *clone,
 	 * anything, the target has assumed ownership of
 	 * this io.
 	 */
-	atomic_inc(&tio->io->io_count);
+	atomic_inc(&tio->io->parm.io_count);
 	sector = clone->bi_sector;
 	r = ti->type->map(ti, clone, &tio->info);
 	if (r == DM_MAPIO_REMAPPED) {
 		/* the bio has been remapped so dispatch it */
 
 		blk_add_trace_remap(bdev_get_queue(clone->bi_bdev), clone,
-				    tio->io->bio->bi_bdev->bd_dev,
+				    tio->io->data.bio->bi_bdev->bd_dev,
 				    clone->bi_sector, sector);
 
 		generic_make_request(clone);
@@ -613,7 +755,10 @@ struct clone_info {
 static void dm_bio_destructor(struct bio *bio)
 {
 	struct bio_set *bs = bio->bi_private;
-
+#ifdef DM_DEBUG
+	if (bio_rq_callback(bio))
+		printk(KERN_INFO "%s: bio %p\n", __FUNCTION__, bio);
+#endif
 	bio_free(bio, bs);
 }
 
@@ -686,6 +831,7 @@ static void __clone_and_map(struct clone_info *ci)
 		clone = clone_bio(bio, ci->sector, ci->idx,
 				  bio->bi_vcnt - ci->idx, ci->sector_count,
 				  ci->md->bs);
+
 		__map_bio(ti, clone, tio);
 		ci->sector_count = 0;
 
@@ -740,7 +886,6 @@ static void __clone_and_map(struct clone_info *ci)
 			clone = split_bvec(bio, ci->sector, ci->idx,
 					   bv->bv_offset + offset, len,
 					   ci->md->bs);
-
 			__map_bio(ti, clone, tio);
 
 			ci->sector += len;
@@ -769,8 +914,8 @@ static void __split_bio(struct mapped_device *md, struct bio *bio)
 	ci.bio = bio;
 	ci.io = alloc_io(md);
 	ci.io->error = 0;
-	atomic_set(&ci.io->io_count, 1);
-	ci.io->bio = bio;
+	atomic_set(&ci.io->parm.io_count, 1);
+	ci.io->data.bio = bio;
 	ci.io->md = md;
 	ci.sector = bio->bi_sector;
 	ci.sector_count = bio_sectors(bio);
@@ -784,6 +929,272 @@ static void __split_bio(struct mapped_device *md, struct bio *bio)
 	dec_pending(ci.io, 0);
 	dm_table_put(ci.map);
 }
+
+/*
+ * request cloning
+ */
+static void clone_end_request(struct request *rq, int error)
+{
+	struct dm_target_io *tio = (struct dm_target_io *)rq->end_io_data;
+	dm_request_endio_fn endio = tio->ti->type->rq_end_io;
+	struct request_queue *q = rq->q;
+	struct request *orig = tio->io->parm.orig;
+	struct mapped_device *md = tio->io->md;
+	int uptodate;
+
+	/* Safeguard */
+	rq->end_io = NULL;
+	rq->end_io_data = NULL;
+
+	if (endio)
+		endio(tio->ti, rq, error, &tio->info);
+
+	__blk_put_request(q, rq);
+
+	if (orig) {
+		spin_unlock_irq(q->queue_lock);
+		uptodate = (error < 0)? error: !error;
+		end_that_request_last(orig, uptodate);
+		spin_lock_irq(q->queue_lock);
+	}
+
+	dec_rq_pending(tio);
+	free_io(md, tio->io);
+	free_tio(md, tio);
+	free_rq(md, rq);
+}
+
+static int clone_rq_endio(struct bio *bio, unsigned int bytes, int error)
+{
+	struct dm_target_io *tio = (struct dm_target_io *)bio->bi_private;
+	struct request *clone = tio->io->data.req;
+	struct request *orig = tio->io->parm.orig;
+	struct mapped_device *md = tio->io->md;
+	int r = 0, uptodate;
+	dm_request_endio_first_fn endio = tio->ti->type->rq_end_io_first;
+	dm_request_queue_in_tgt_fn queue_in_tgt = tio->ti->type->queue_in_tgt;
+	struct request_queue *q = clone->q, *q_orig = orig->q;
+
+	if (bio->bi_size)
+		return 1;
+
+	if (endio) {
+		r = endio(tio->ti, clone, error, &tio->info);
+		switch (r) {
+		case 0:
+#ifdef DM_DEBUG
+			printk(KERN_INFO "clone %p bio %p endio done\n",
+			       clone, bio);
+#endif
+			/* Destroy sequencer bio */
+			bio->bi_private = md->bs;
+			bio_put(bio);
+			/* succeeded */
+			break;
+		case DM_ENDIO_INCOMPLETE:
+			/* FIXME */
+			printk(KERN_INFO "clone %p bio %p endio incomplete\n",
+			       clone, bio);
+			/* the target wants to handle the io without unmap */
+			__blk_put_request(q, clone);
+			clone->special = NULL;
+			clone->errors = 0;
+
+			if (!queue_in_tgt) {
+				DMERR("queue_in_tgt isn't implemented.");
+				BUG();
+			}
+			queue_in_tgt(tio->ti, clone, &tio->info);
+			blk_run_queue(q_orig);
+
+			return 0;
+		case DM_ENDIO_REQUEUE:
+			/* FIXME */
+			printk(KERN_INFO "clone %p bio %p endio requeue\n",
+			       clone, bio);
+			/*
+			 * The target wants to requeue the original request,
+			 * unthread sequencer bio and stop bio processing.
+			 */
+			clone->bio = NULL;
+			clone->biotail = clone->bio;
+			/* Destroy sequencer bio */
+			bio->bi_private = md->bs;
+			bio_put(bio);
+			/* Requeue original request */
+			dm_requeue_request(q_orig, orig);
+			break;
+		default:
+			/* FIXME */
+			printk(KERN_INFO "clone %p bio %p endio return %d\n",
+			       clone, bio, r);
+			if (r >= 0) {
+				DMWARN("unimplemented target endio return value: %d", r);
+				BUG();
+			}
+			/* Destroy sequencer bio */
+			bio->bi_private = md->bs;
+			bio_put(bio);
+			break;
+		}
+	}
+
+	/*
+	 * Finish original request
+	 */
+	uptodate = (error < 0)? error: !error;
+	r = end_that_request_first(orig, uptodate,
+				   orig->hard_cur_sectors);
+	/*
+	 * Recopy the original request fields that were updated
+	 * in blk_end_request() to the clone.
+	 */
+	blk_update_cloned_rq(orig, clone);
+
+	return r?1:0;
+}
+
+static struct bio *alloc_seq_bio(struct request *req, struct bio_set *bs)
+{
+	struct bio *clone;
+
+	clone = bio_alloc_bioset(GFP_NOIO, 0, bs);
+	clone->bi_sector = 0;
+	clone->bi_flags |= 1 << BIO_CLONED;
+	clone->bi_rw |= (1 << BIO_RW_RQCALLBACK);
+	clone->bi_vcnt = 0;
+	clone->bi_size = 0;
+	clone->bi_idx = 0;
+	clone->bi_end_io = clone_rq_endio;
+	bio_phys_segments(req->q, clone);
+	bio_hw_segments(req->q, clone);
+	clone->bi_private = req;
+	clone->bi_destructor = dm_bio_destructor;
+	clone->bi_flags &= ~(1 << BIO_SEG_VALID);
+#ifdef DM_DEBUG
+	printk(KERN_INFO "%s: bio %p\n", __FUNCTION__, clone);
+#endif
+	return clone;
+}
+
+static void setup_clone(struct request *clone, struct request *rq)
+{
+	memset(clone, 0, sizeof(struct request));
+	INIT_LIST_HEAD(&clone->queuelist);
+	INIT_LIST_HEAD(&clone->donelist);
+	clone->cmd_flags = (rq_data_dir(rq) | REQ_NOMERGE);
+	clone->cmd_type = rq->cmd_type;
+	clone->sector = rq->sector;
+	clone->hard_sector = rq->hard_sector;
+	clone->nr_sectors = rq->nr_sectors;
+	clone->hard_nr_sectors = rq->hard_nr_sectors;
+	clone->current_nr_sectors = rq->current_nr_sectors;
+	clone->hard_cur_sectors = rq->hard_cur_sectors;
+	clone->bio = rq->bio;
+	clone->biotail = rq->biotail;
+	INIT_HLIST_NODE(&clone->hash);
+	clone->start_time = jiffies;
+	clone->nr_phys_segments = rq->nr_phys_segments;
+	clone->nr_hw_segments = rq->nr_hw_segments;
+	clone->ioprio = rq->ioprio;
+	clone->buffer = rq->buffer;
+	clone->tag = -1;
+	clone->ref_count = 1;
+	clone->cmd_len = rq->cmd_len;
+	memcpy(clone->cmd, rq->cmd, sizeof(rq->cmd));
+	clone->data_len = rq->data_len;
+	clone->sense_len = rq->sense_len;
+	clone->data = rq->data;
+	clone->sense = rq->sense;
+}
+
+static int clone_and_map_request(struct dm_target *ti, struct request *rq,
+				 struct mapped_device *md)
+{
+	int r;
+	struct request *clone;
+	struct dm_target_io *tio;
+
+	tio = alloc_tio(md); /* only one for each original request */
+	if (!tio)
+		/* -ENOMEM, requeue */
+		return 1;
+	tio->io = alloc_io(md);
+	if (!tio->io) {
+		/* -ENOMEM, requeue */
+		free_tio(md, tio);
+		return 1;
+	}
+	tio->io->md = md;
+	tio->io->error = 0;
+	tio->ti = ti;
+	memset(&tio->info, 0, sizeof(tio->info));
+
+	clone = alloc_rq(md);
+	if (!clone) {
+		/* -ENOMEM, requeue */
+		free_io(md, tio->io);
+		free_tio(md, tio);
+		return 1;
+	}
+	setup_clone(clone, rq);
+	clone->bio = alloc_seq_bio(clone, md->bs);
+	clone->end_io = clone_end_request;
+	clone->end_io_data = tio;
+	clone->bio->bi_end_io = clone_rq_endio;
+	clone->bio->bi_private = tio;
+	clone->bio->bi_next = rq->bio;
+	tio->io->data.req = clone;
+	tio->io->parm.orig = rq;
+
+	atomic_inc(&md->pending);
+	r = ti->type->map_rq(ti, clone, &tio->info);
+	switch (r) {
+	case DM_MAPIO_REMAPPED:
+#ifdef DM_DEBUG
+		printk(KERN_INFO "clone %p remapped\n", clone);
+#endif
+		/* the clone has been remapped so dispatch it */
+		dm_dispatch_request(clone->q, clone);
+		break;
+	case DM_MAPIO_SUBMITTED:
+#ifdef DM_DEBUG
+		printk(KERN_INFO "clone %p submitted\n", clone);
+#endif
+		/* the target has taken the request to submit by itself */
+		break;
+	case DM_MAPIO_REQUEUE:
+		printk(KERN_INFO "clone %p removed\n", clone);
+		/* the target has requested to requeue the original request */
+		clone->end_io = NULL;
+		clone->end_io_data = NULL;
+		dec_rq_pending(tio);
+		free_io(md, tio->io);
+		free_tio(md, tio);
+		free_rq(md, clone);
+		printk(KERN_INFO "requeue request %p\n", rq);
+		return 1;
+	default:
+		printk(KERN_INFO "clone %p failure %d\n", clone, r);
+		if (r >= 0) {
+			DMWARN("unimplemented target map return value: %d", r);
+			BUG();
+		}
+		clone->end_io = NULL;
+		clone->end_io_data = NULL;
+
+		dec_rq_pending(tio);
+		free_io(md, tio->io);
+		free_tio(md, tio);
+		free_rq(md, clone);
+		printk(KERN_INFO "kill request %p\n", rq);
+		kill_request(rq);
+		break;
+	}
+
+	return 0;
+}
+
 /*-----------------------------------------------------------------
  * CRUD END
  *---------------------------------------------------------------*/
@@ -844,6 +1255,41 @@ static int dm_request(struct request_queue *q, struct bio *bio)
 	return 0;
 }
 
+/*
+ * q->request_fn for request based dm.
+ * called with q->queue_lock held
+ */
+static void dm_request_fn(struct request_queue *q)
+{
+	int r;
+	struct mapped_device *md = (struct mapped_device *)q->queuedata;
+	struct dm_table *map = dm_get_table(md);
+	struct dm_target *ti;
+	struct request *rq;
+
+	while (!blk_queue_plugged(q)) {
+		rq = elv_next_request(q);
+		if (!rq)
+			break;
+#ifdef DM_DEBUG
+		printk(KERN_INFO "got request %p\n", rq);
+#endif
+		ti = dm_table_find_target(map, rq->sector);
+
+		blkdev_dequeue_request(rq);
+		spin_unlock_irq(q->queue_lock);
+		r = clone_and_map_request(ti, rq, md);
+		spin_lock_irq(q->queue_lock);
+
+		if (r)
+			__dm_requeue_request(q, rq);
+	}
+
+	dm_table_put(map);
+
+	return;
+}
+
 static int dm_flush_all(struct request_queue *q, struct gendisk *disk,
 			sector_t *error_sector)
 {
@@ -870,6 +1316,30 @@ static void dm_unplug_all(struct request_queue *q)
 	}
 }
 
+static int dm_make_request(struct request_queue *q, struct bio *bio)
+{
+	/*
+	 * There is no use in forwarding any barrier request since we can't
+	 * guarantee it is (or can be) handled by the targets correctly.
+	 */
+	if (unlikely(bio_barrier(bio))) {
+		bio_endio(bio, bio->bi_size, -EOPNOTSUPP);
+		return 0;
+	}
+
+	return __make_request(q, bio);
+}
+
+static int dm_prep_rq (struct request_queue *q, struct request *req)
+{
+	struct mapped_device *md = q->queuedata;
+
+	if (test_bit(DMF_BLOCK_IO, &md->flags))
+		return BLKPREP_DEFER;
+
+	return BLKPREP_OK;
+}
+
 static int dm_any_congested(void *congested_data, int bdi_bits)
 {
 	int r;
@@ -997,10 +1467,14 @@ static struct mapped_device *alloc_dev(int minor)
 	atomic_set(&md->open_count, 0);
 	atomic_set(&md->event_nr, 0);
 
-	md->queue = blk_alloc_queue(GFP_KERNEL);
+	md->queue = blk_init_queue(dm_request_fn, NULL);
 	if (!md->queue)
 		goto bad1_free_minor;
 
+	md->queue->nr_requests = BLKDEV_MAX_RQ;
+	md->queue->nr_batching = 32;
+	md->queue->unplug_thresh = 4;
+	md->queue->unplug_delay = (3 * HZ) / 1000;
 	md->queue->queuedata = md;
 	md->queue->backing_dev_info.congested_fn = dm_any_congested;
 	md->queue->backing_dev_info.congested_data = md;
@@ -1013,9 +1487,13 @@ static struct mapped_device *alloc_dev(int minor)
 	if (!md->io_pool)
 		goto bad2;
 
+	md->rq_pool = mempool_create_slab_pool(MIN_IOS, _rq_cache);
+	if (!md->rq_pool)
+		goto bad3;
+
 	md->tio_pool = mempool_create_slab_pool(MIN_IOS, _tio_cache);
 	if (!md->tio_pool)
-		goto bad3;
+		goto bad4;
 
 	md->bs = bioset_create(16, 16);
 	if (!md->bs)
@@ -1023,7 +1501,7 @@ static struct mapped_device *alloc_dev(int minor)
 
 	md->disk = alloc_disk(1);
 	if (!md->disk)
-		goto bad4;
+		goto bad5;
 
 	atomic_set(&md->pending, 0);
 	init_waitqueue_head(&md->wait);
@@ -1047,10 +1525,12 @@ static struct mapped_device *alloc_dev(int minor)
 
 	return md;
 
- bad4:
+ bad5:
 	bioset_free(md->bs);
  bad_no_bioset:
 	mempool_destroy(md->tio_pool);
+ bad4:
+	mempool_destroy(md->rq_pool);
  bad3:
 	mempool_destroy(md->io_pool);
  bad2:
@@ -1073,6 +1553,7 @@ static void free_dev(struct mapped_device *md)
 		bdput(md->suspended_bdev);
 	}
 	mempool_destroy(md->tio_pool);
+	mempool_destroy(md->rq_pool);
 	mempool_destroy(md->io_pool);
 	bioset_free(md->bs);
 	del_gendisk(md->disk);
@@ -1508,6 +1989,40 @@ out:
 	return r;
 }
 
+/*
+ * Functions to enable and disable elevator support for
+ * individual devices.
+ */
+int dm_enable_elevator(struct mapped_device *md)
+{
+	if (md->queue->make_request_fn == dm_make_request)
+		return 0;
+
+	blk_queue_make_request(md->queue, dm_make_request);
+	blk_queue_prep_rq(md->queue, dm_prep_rq);
+	md->queue->unplug_fn = generic_unplug_device;
+	md->queue->backing_dev_info.congested_fn = NULL;
+	md->queue->backing_dev_info.congested_data = NULL;
+	set_bit(QUEUE_FLAG_CLUSTER, &md->queue->queue_flags);
+
+	return 0;
+}
+EXPORT_SYMBOL(dm_enable_elevator);
+
+void dm_disable_elevator(struct mapped_device *md)
+{
+	if (md->queue->make_request_fn == dm_request)
+		return;
+
+	blk_queue_make_request(md->queue, dm_request);
+	blk_queue_prep_rq(md->queue, NULL);
+	md->queue->unplug_fn = dm_unplug_all;
+	md->queue->backing_dev_info.congested_fn = dm_any_congested;
+	md->queue->backing_dev_info.congested_data = md;
+	clear_bit(QUEUE_FLAG_CLUSTER, &md->queue->queue_flags);
+}
+EXPORT_SYMBOL(dm_disable_elevator);
+
 /*-----------------------------------------------------------------
  * Event notification.
  *---------------------------------------------------------------*/
diff --git a/drivers/md/dm.h b/drivers/md/dm.h
index 462ee65..57e7edb 100644
--- a/drivers/md/dm.h
+++ b/drivers/md/dm.h
@@ -112,6 +112,8 @@ int dm_table_resume_targets(struct dm_table *t);
 int dm_table_any_congested(struct dm_table *t, int bdi_bits);
 void dm_table_unplug_all(struct dm_table *t);
 int dm_table_flush_all(struct dm_table *t);
+int dm_table_enable_elevator(struct dm_table *t);
+void dm_table_disable_elevator(struct dm_table *t);
 
 /*-----------------------------------------------------------------
  * A registry of target types.
@@ -182,5 +184,10 @@ void *dm_vcalloc(unsigned long nmemb, unsigned long elem_size);
 union map_info *dm_get_mapinfo(struct bio *bio);
 int dm_open_count(struct mapped_device *md);
 int dm_lock_for_deletion(struct mapped_device *md);
+void dm_dispatch_request(struct request_queue *q, struct request *rq);
+int dm_enable_elevator(struct mapped_device *md);
+void dm_disable_elevator(struct mapped_device *md);
+struct request_queue *dm_get_queue(struct mapped_device *md);
+void dm_put_queue(struct request_queue *q);
 
 #endif
diff --git a/include/linux/bio.h b/include/linux/bio.h
index 1ddef34..cb46e30 100644
--- a/include/linux/bio.h
+++ b/include/linux/bio.h
@@ -152,6 +152,7 @@ struct bio {
 #define BIO_RW_FAILFAST	3
 #define BIO_RW_SYNC	4
 #define BIO_RW_META	5
+#define BIO_RW_RQCALLBACK	6	/* Request callback */
 
 /*
  * upper 16 bits of bi_rw define the io priority of this bio
@@ -183,6 +184,7 @@ struct bio {
 #define bio_failfast(bio)	((bio)->bi_rw & (1 << BIO_RW_FAILFAST))
 #define bio_rw_ahead(bio)	((bio)->bi_rw & (1 << BIO_RW_AHEAD))
 #define bio_rw_meta(bio)	((bio)->bi_rw & (1 << BIO_RW_META))
+#define bio_rq_callback(bio)	((bio)->bi_rw & (1 << BIO_RW_RQCALLBACK))
 
 /*
  * will die
diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h
index b126c6f..7f72ea6 100644
--- a/include/linux/blkdev.h
+++ b/include/linux/blkdev.h
@@ -647,6 +647,7 @@ extern void register_disk(struct gendisk *dev);
 extern void generic_make_request(struct bio *bio);
 extern void blk_put_request(struct request *);
 extern void __blk_put_request(struct request_queue *, struct request *);
+extern int __make_request(struct request_queue *q, struct bio *bio);
 extern void blk_end_sync_rq(struct request *rq, int error);
 extern struct request *blk_get_request(struct request_queue *, int, gfp_t);
 extern void blk_insert_request(struct request_queue *, struct request *, int, void *);
diff --git a/include/linux/device-mapper.h b/include/linux/device-mapper.h
index 499f537..1e7748a 100644
--- a/include/linux/device-mapper.h
+++ b/include/linux/device-mapper.h
@@ -14,6 +14,7 @@ struct dm_target;
 struct dm_table;
 struct dm_dev;
 struct mapped_device;
+struct request;
 
 typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
 
@@ -45,6 +46,9 @@ typedef void (*dm_dtr_fn) (struct dm_target *ti);
 typedef int (*dm_map_fn) (struct dm_target *ti, struct bio *bio,
 			  union map_info *map_context);
 
+typedef int (*dm_map_request_fn) (struct dm_target *ti, struct request *clone,
+				  union map_info *map_context);
+
 /*
  * Returns:
  * < 0 : error (currently ignored)
@@ -57,6 +61,18 @@ typedef int (*dm_endio_fn) (struct dm_target *ti,
 			    struct bio *bio, int error,
 			    union map_info *map_context);
 
+typedef int (*dm_request_endio_first_fn) (struct dm_target *ti,
+					  struct request *clone, int error,
+					  union map_info *map_context);
+
+typedef int (*dm_request_endio_fn) (struct dm_target *ti,
+				    struct request *clone, int error,
+				    union map_info *map_context);
+
+typedef void (*dm_request_queue_in_tgt_fn) (struct dm_target *ti,
+					    struct request *clone,
+					    union map_info *map_context);
+
 typedef void (*dm_flush_fn) (struct dm_target *ti);
 typedef void (*dm_presuspend_fn) (struct dm_target *ti);
 typedef void (*dm_postsuspend_fn) (struct dm_target *ti);
@@ -98,7 +114,11 @@ struct target_type {
 	dm_ctr_fn ctr;
 	dm_dtr_fn dtr;
 	dm_map_fn map;
+	dm_map_request_fn map_rq;
 	dm_endio_fn end_io;
+	dm_request_endio_first_fn rq_end_io_first;
+	dm_request_endio_fn rq_end_io;
+	dm_request_queue_in_tgt_fn queue_in_tgt;
 	dm_flush_fn flush;
 	dm_presuspend_fn presuspend;
 	dm_postsuspend_fn postsuspend;
--
dm-devel mailing list
dm-devel@xxxxxxxxxx
https://www.redhat.com/mailman/listinfo/dm-devel

[Index of Archives]     [DM Crypt]     [Fedora Desktop]     [ATA RAID]     [Fedora Marketing]     [Fedora Packaging]     [Fedora SELinux]     [Yosemite Discussion]     [KDE Users]     [Fedora Docs]

  Powered by Linux