From: Milan Broz <mbroz@redhat.com>

Introduce a bvec merge function for device mapper devices
for dynamic size restrictions.

This code ensures the requested biovec lies within a single
target and then calls a target-specific function to check
against any constraints imposed by underlying devices.

Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>

---

 drivers/md/dm.c               |   37 +++++++++++++++++++++++++++++++++++++
 include/linux/device-mapper.h |    5 +++++
 include/linux/dm-ioctl.h      |    4 ++--
 3 files changed, 44 insertions(+), 2 deletions(-)

Index: current-quilt/drivers/md/dm.c
===================================================================
--- current-quilt.orig/drivers/md/dm.c	2007-05-01 18:01:32.000000000 +0100
+++ current-quilt/drivers/md/dm.c	2007-05-01 18:28:30.000000000 +0100
@@ -792,6 +792,42 @@ static void __split_bio(struct mapped_de
  * CRUD END
  *---------------------------------------------------------------*/
 
+static int dm_merge_bvec(request_queue_t *q, struct bio *bio,
+			 struct bio_vec *biovec)
+{
+	struct mapped_device *md = q->queuedata;
+	struct dm_table *map = dm_get_table(md);
+	struct dm_target *ti;
+	sector_t max_sectors;
+	int len;
+
+	if (unlikely(!map))
+		return 0;
+
+	ti = dm_table_find_target(map, bio->bi_sector);
+
+	/*
+	 * Find maximum bytes of I/O that won't need splitting
+	 */
+	max_sectors = min(max_io_len(md, bio->bi_sector, ti), (sector_t) BIO_MAX_SECTORS);
+	len = (max_sectors << SECTOR_SHIFT) - bio->bi_size;
+	if (len < 0)
+		len = 0;
+
+	if (len > 0 && ti->type->merge)
+		len = ti->type->merge(ti, bio, biovec, len);
+
+	/*
+	 * Always allow an entire first page
+	 */
+	if (len <= biovec->bv_len && bio_sectors(bio) == 0)
+		len = biovec->bv_len;
+
+	dm_table_put(map);
+
+	return len;
+}
+
 /*
  * The request function that just remaps the bio built up by
  * dm_merge_bvec.
@@ -1003,6 +1039,7 @@ static struct mapped_device *alloc_dev(i
 	blk_queue_bounce_limit(md->queue, BLK_BOUNCE_ANY);
 	md->queue->unplug_fn = dm_unplug_all;
 	md->queue->issue_flush_fn = dm_flush_all;
+	blk_queue_merge_bvec(md->queue, dm_merge_bvec);
 
 	md->io_pool = mempool_create_slab_pool(MIN_IOS, _io_cache);
 	if (!md->io_pool)
Index: current-quilt/include/linux/device-mapper.h
===================================================================
--- current-quilt.orig/include/linux/device-mapper.h	2007-05-01 18:01:31.000000000 +0100
+++ current-quilt/include/linux/device-mapper.h	2007-05-01 18:28:30.000000000 +0100
@@ -14,6 +14,7 @@ struct dm_target;
 struct dm_table;
 struct dm_dev;
 struct mapped_device;
+struct bio_vec;
 
 typedef enum { STATUSTYPE_INFO, STATUSTYPE_TABLE } status_type_t;
 
@@ -72,6 +73,9 @@ typedef int (*dm_ioctl_fn) (struct dm_ta
 			    struct file *filp, unsigned int cmd,
 			    unsigned long arg);
 
+typedef int (*dm_merge_fn) (struct dm_target *ti, struct bio *bio,
+			    struct bio_vec *biovec, int len);
+
 void dm_error(const char *message);
 
 /*
@@ -107,6 +111,7 @@ struct target_type {
 	dm_status_fn status;
 	dm_message_fn message;
 	dm_ioctl_fn ioctl;
+	dm_merge_fn merge;
 };
 
 struct io_restrictions {
Index: current-quilt/include/linux/dm-ioctl.h
===================================================================
--- current-quilt.orig/include/linux/dm-ioctl.h	2007-05-01 17:41:14.000000000 +0100
+++ current-quilt/include/linux/dm-ioctl.h	2007-05-01 18:28:30.000000000 +0100
@@ -285,9 +285,9 @@ typedef char ioctl_struct[308];
 #define DM_DEV_SET_GEOMETRY	_IOWR(DM_IOCTL, DM_DEV_SET_GEOMETRY_CMD, struct dm_ioctl)
 
 #define DM_VERSION_MAJOR	4
-#define DM_VERSION_MINOR	11
+#define DM_VERSION_MINOR	12
 #define DM_VERSION_PATCHLEVEL	0
-#define DM_VERSION_EXTRA	"-ioctl (2006-10-12)"
+#define DM_VERSION_EXTRA	"-ioctl (2007-04-02)"
 
 /* Status bits */
 #define DM_READONLY_FLAG	(1 << 0) /* In/Out */