From: Milan Broz <mbroz@redhat.com>

When processing the second part of a split bio resubmitted to the same
device we should skip the bookkeeping and avoid requesting io_lock
recursively.  Also fix a possible deadlock if the wait loop gets
interrupted.

Signed-off-by: Milan Broz <mbroz@redhat.com>
Signed-off-by: Alasdair G Kergon <agk@redhat.com>
---
 drivers/md/dm.c |   25 +++++++++++++++++++++----
 1 files changed, 21 insertions(+), 4 deletions(-)

Index: current-quilt/drivers/md/dm.c
===================================================================
--- current-quilt.orig/drivers/md/dm.c	2007-07-25 21:04:54.000000000 +0100
+++ current-quilt/drivers/md/dm.c	2007-07-25 21:04:55.000000000 +0100
@@ -564,6 +564,12 @@ static int clone2_endio(struct bio *bio,
 	return error;
 }
 
+static int is_clone2(struct mapped_device *md, struct bio *bio)
+{
+	return (bio->bi_end_io == clone2_endio &&
+		bio->bi_private == md);
+}
+
 static sector_t max_io_len(struct mapped_device *md,
 			   sector_t sector, struct dm_target *ti)
 {
@@ -817,6 +823,15 @@ static int dm_request(request_queue_t *q
 		return 0;
 	}
 
+	/*
+	 * If this is the second part of a split bio,
+	 * always map it immediately.
+	 */
+	if (is_clone2(md, bio)) {
+		r = __split_bio(md, bio);
+		goto out_req;
+	}
+
 	down_read(&md->io_lock);
 
 	disk_stat_inc(dm_disk(md), ios[rw]);
@@ -1265,10 +1280,11 @@ EXPORT_SYMBOL_GPL(dm_put);
 /*
  * Process the deferred bios
  */
-static void __flush_deferred_io(struct mapped_device *md, struct bio *c)
+static void flush_deferred_io(struct mapped_device *md, struct bio *c)
 {
 	struct bio *n;
 
+	down_read(&md->io_lock);
 	while (c) {
 		n = c->bi_next;
 		c->bi_next = NULL;
@@ -1276,6 +1292,7 @@ static void __flush_deferred_io(struct m
 			bio_io_error(c, c->bi_size);
 		c = n;
 	}
+	up_read(&md->io_lock);
 }
 
 /*
@@ -1436,8 +1453,8 @@ int dm_suspend(struct mapped_device *md,
 	if (atomic_read(&md->pending)) {
 		clear_bit(DMF_BLOCK_IO, &md->flags);
 		def = bio_list_get(&md->deferred);
-		__flush_deferred_io(md, def);
 		up_write(&md->io_lock);
+		flush_deferred_io(md, def);
 		unlock_fs(md);
 		goto out; /* pushback list is already flushed, so skip flush */
 	}
@@ -1464,8 +1481,8 @@ flush_and_out:
 		spin_unlock_irqrestore(&md->pushback_lock, flags);
 
 		def = bio_list_get(&md->deferred);
-		__flush_deferred_io(md, def);
 		up_write(&md->io_lock);
+		flush_deferred_io(md, def);
 	}
 
 out:
@@ -1503,8 +1520,8 @@ int dm_resume(struct mapped_device *md)
 	clear_bit(DMF_BLOCK_IO, &md->flags);
 
 	def = bio_list_get(&md->deferred);
-	__flush_deferred_io(md, def);
 	up_write(&md->io_lock);
+	flush_deferred_io(md, def);
 
 	unlock_fs(md);