From: Mike Snitzer <[email-masked]>

Each mapping in a DM table must be properly aligned on HW sector
boundaries.  This is particularly important when a DM table is composed
of mappings for devices with different HW sector sizes.

Signed-off-by: Mike Snitzer <[email-masked]>
---
 drivers/md/dm-table.c |   43 +++++++++++++++++++++++++++++++------------
 1 files changed, 31 insertions(+), 12 deletions(-)

Index: linux-2.6.29/drivers/md/dm-table.c
===================================================================
--- linux-2.6.29.orig/drivers/md/dm-table.c	2009-04-03 23:13:18.000000000 +0100
+++ linux-2.6.29/drivers/md/dm-table.c	2009-04-08 10:50:57.000000000 +0100
@@ -384,15 +384,34 @@ static void close_dev(struct dm_dev_inte
 /*
  * If possible, this checks an area of a destination device is valid.
  */
-static int check_device_area(struct dm_dev_internal *dd, sector_t start,
-			     sector_t len)
+static int check_device_area(struct dm_target *ti, struct block_device *bdev,
+			     sector_t start, sector_t len)
 {
-	sector_t dev_size = dd->dm_dev.bdev->bd_inode->i_size >> SECTOR_SHIFT;
+	sector_t dev_size = bdev->bd_inode->i_size >> SECTOR_SHIFT;
+	struct io_restrictions *rs = &ti->limits;
+	sector_t dev_hardsectors = rs->hardsect_size >> SECTOR_SHIFT;
+	char b[BDEVNAME_SIZE];
 
 	if (!dev_size)
 		return 1;
 
-	return ((start < dev_size) && (len <= (dev_size - start)));
+	if (!((start < dev_size) && (len <= (dev_size - start)))) {
+		DMWARN("device %s too small for target", bdevname(bdev, b));
+		return 0;
+	}
+
+	if (dev_hardsectors > 1) {
+		if ((ti->begin % dev_hardsectors) ||
+		    (start % dev_hardsectors) ||
+		    (len % dev_hardsectors)) {
+			DMWARN("device %s mapping (%lu %lu ... %lu) "
+			       "is not HW sector aligned",
+			       bdevname(bdev, b), ti->begin, len, start);
+			return 0;
+		}
+	}
+
+	return 1;
 }
 
 /*
@@ -478,14 +497,7 @@ static int __table_get_device(struct dm_
 	}
 	atomic_inc(&dd->count);
 
-	if (!check_device_area(dd, start, len)) {
-		DMWARN("device %s too small for target", path);
-		dm_put_device(ti, &dd->dm_dev);
-		return -EINVAL;
-	}
-
 	*result = &dd->dm_dev;
-
 	return 0;
 }
 
@@ -553,9 +565,16 @@ int dm_get_device(struct dm_target *ti, 
 	int r = __table_get_device(ti->table, ti, path,
 				   start, len, mode, result);
 
-	if (!r)
+	if (!r) {
 		dm_set_device_limits(ti, (*result)->bdev);
 
+		if (!check_device_area(ti, (*result)->bdev,
+				       start, len)) {
+			dm_put_device(ti, *result);
+			return -EINVAL;
+		}
+	}
+
 	return r;
 }