From: Andi Kleen <[email-masked]>

Implement barrier support for single device DM devices

This patch implements barrier support in DM for the common case of dm linear
just remapping a single underlying device. In this case we can safely
pass the barrier through because there can be no reordering between
devices.

Signed-off-by: Andi Kleen <[email-masked]>

---
 drivers/md/dm-linear.c |    1 +
 drivers/md/dm-table.c  |   26 +++++++++++++++++++++++++-
 drivers/md/dm.c        |   14 ++++----------
 drivers/md/dm.h        |    2 ++
 4 files changed, 32 insertions(+), 11 deletions(-)

Index: linux-2.6.27/drivers/md/dm-linear.c
===================================================================
--- linux-2.6.27.orig/drivers/md/dm-linear.c	2008-10-23 23:44:15.000000000 +0100
+++ linux-2.6.27/drivers/md/dm-linear.c	2008-10-24 12:38:35.000000000 +0100
@@ -52,6 +52,7 @@ static int linear_ctr(struct dm_target *
 		ti->error = "dm-linear: Device lookup failed";
 		goto bad;
 	}
+	dm_table_support_barrier(ti->table);
 
 	ti->private = lc;
 	return 0;
Index: linux-2.6.27/drivers/md/dm-table.c
===================================================================
--- linux-2.6.27.orig/drivers/md/dm-table.c	2008-10-23 23:57:20.000000000 +0100
+++ linux-2.6.27/drivers/md/dm-table.c	2008-10-24 12:38:35.000000000 +0100
@@ -38,6 +38,9 @@ struct dm_table {
 	sector_t *highs;
 	struct dm_target *targets;
 
+	unsigned single_device : 1;
+	unsigned barrier_supported : 1;
+
 	/*
 	 * Indicates the rw permissions for the new logical
 	 * device.  This should be a combination of FMODE_READ
@@ -530,12 +533,21 @@ EXPORT_SYMBOL_GPL(dm_set_device_limits);
 int dm_get_device(struct dm_target *ti, const char *path, sector_t start,
 		  sector_t len, fmode_t mode, struct dm_dev **result)
 {
-	int r = __table_get_device(ti->table, ti, path,
+	struct dm_table *t = ti->table;
+	int r = __table_get_device(t, ti, path,
 				   start, len, mode, result);
 
 	if (!r)
 		dm_set_device_limits(ti, (*result)->bdev);
 
+	if (!r) {
+		/* Only got single device? */
+		if (t->devices.next->next == &t->devices)
+			t->single_device = 1;
+		else
+			t->single_device = 0;
+	}
+
 	return r;
 }
 
@@ -1024,6 +1036,16 @@ struct mapped_device *dm_table_get_md(st
 	return t->md;
 }
 
+int dm_table_barrier_ok(struct dm_table *t)
+{
+	return t->single_device && t->barrier_supported;
+}
+
+void dm_table_support_barrier(struct dm_table *t)
+{
+	t->barrier_supported = 1;
+}
+
 EXPORT_SYMBOL(dm_vcalloc);
 EXPORT_SYMBOL(dm_get_device);
 EXPORT_SYMBOL(dm_put_device);
@@ -1034,3 +1056,5 @@ EXPORT_SYMBOL(dm_table_get_md);
 EXPORT_SYMBOL(dm_table_put);
 EXPORT_SYMBOL(dm_table_get);
 EXPORT_SYMBOL(dm_table_unplug_all);
+EXPORT_SYMBOL(dm_table_barrier_ok);
+EXPORT_SYMBOL(dm_table_support_barrier);
Index: linux-2.6.27/drivers/md/dm.c
===================================================================
--- linux-2.6.27.orig/drivers/md/dm.c	2008-10-23 23:57:20.000000000 +0100
+++ linux-2.6.27/drivers/md/dm.c	2008-10-24 12:38:35.000000000 +0100
@@ -808,7 +808,10 @@ static int __split_bio(struct mapped_dev
 	ci.map = dm_get_table(md);
 	if (unlikely(!ci.map))
 		return -EIO;
-
+	if (unlikely(bio_barrier(bio) && !dm_table_barrier_ok(ci.map))) {
+		bio_endio(bio, -EOPNOTSUPP);
+		return 0;
+	}
 	ci.md = md;
 	ci.bio = bio;
 	ci.io = alloc_io(md);
@@ -892,15 +895,6 @@ static int dm_request(struct request_que
 	struct mapped_device *md = q->queuedata;
 	int cpu;
 
-	/*
-	 * 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, -EOPNOTSUPP);
-		return 0;
-	}
-
 	down_read(&md->io_lock);
 
 	cpu = part_stat_lock();
Index: linux-2.6.27/drivers/md/dm.h
===================================================================
--- linux-2.6.27.orig/drivers/md/dm.h	2008-10-23 23:44:15.000000000 +0100
+++ linux-2.6.27/drivers/md/dm.h	2008-10-24 12:38:35.000000000 +0100
@@ -51,6 +51,8 @@ int dm_table_any_congested(struct dm_tab
  * To check the return value from dm_table_find_target().
  */
 #define dm_target_is_valid(t) ((t)->table)
+int dm_table_barrier_ok(struct dm_table *t);
+void dm_table_support_barrier(struct dm_table *t);
 
 /*-----------------------------------------------------------------
  * A registry of target types.