From: Milan Broz <[email-masked]>

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 <[email-masked]>
Signed-off-by: Alasdair G Kergon <[email-masked]>

---

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

Index: linux/drivers/md/dm.c
===================================================================
--- linux.orig/drivers/md/dm.c	2007-07-11 14:36:41.000000000 +0100
+++ linux/drivers/md/dm.c	2007-07-11 14:36:48.000000000 +0100
@@ -806,6 +806,50 @@ static int __split_bio(struct mapped_dev
  * CRUD END
  *---------------------------------------------------------------*/
 
+static int dm_merge_bvec(request_queue_t *q, struct block_device *bi_bdev,
+			 sector_t bi_sector, unsigned bi_size,
+			 unsigned long bi_rw, 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 max_size;
+
+	if (unlikely(!map))
+		return 0;
+
+	ti = dm_table_find_target(map, bi_sector);
+
+	/*
+	 * Find maximum amount of I/O that won't need splitting
+	 */
+	max_sectors = min(max_io_len(md, bi_sector, ti),
+			  (sector_t) BIO_MAX_SECTORS);
+	max_size = (max_sectors << SECTOR_SHIFT) - bi_size;
+	if (max_size < 0)
+		max_size = 0;
+
+	/*
+	 * merge_bvec_fn() returns number of bytes
+	 * it can accept at this offset
+	 * max is precomputed maximal io size
+	 */
+	if (max_size && ti->type->merge)
+		max_size = ti->type->merge(ti, bi_sector, bi_size, bi_rw,
+					   biovec, max_size);
+
+	/*
+	 * Always allow an entire first page
+	 */
+	if (max_size <= biovec->bv_len && !(bi_size >> SECTOR_SHIFT))
+		max_size = biovec->bv_len;
+
+	dm_table_put(map);
+
+	return max_size;
+}
+
 /*
  * The request function that just remaps the bio built up by
  * dm_merge_bvec.
@@ -1036,6 +1080,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: linux/include/linux/device-mapper.h
===================================================================
--- linux.orig/include/linux/device-mapper.h	2007-07-11 14:36:24.000000000 +0100
+++ linux/include/linux/device-mapper.h	2007-07-11 14:36:48.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,10 @@ 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, sector_t bi_sector,
+			    unsigned int bi_size, unsigned long bi_rw,
+			    struct bio_vec *biovec, int max_size);
+
 void dm_error(const char *message);
 
 /*
@@ -107,6 +112,7 @@ struct target_type {
 	dm_status_fn status;
 	dm_message_fn message;
 	dm_ioctl_fn ioctl;
+	dm_merge_fn merge;
 };
 
 struct io_restrictions {
Index: linux/include/linux/dm-ioctl.h
===================================================================
--- linux.orig/include/linux/dm-ioctl.h	2007-05-13 02:45:56.000000000 +0100
+++ linux/include/linux/dm-ioctl.h	2007-07-11 14:36:48.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 */