From: Kiyoshi Ueda <[email-masked]>

This patch implements the feature which the multipath target returns
the pushback request if needed.

The pushback request can be accepted by the dm core only when:
  1). The suspend is being issued with 'DMF_NOFLUSH_SUSPENDING' flag.
      (Otherwise, the bio is returned to applications with EIO.)
>From the practical multipath usage, the pushback request should be
needed only when:
  2). No valid paths.
  3). queue_if_no_path is specified.

To check whether queue_if_no_path is specified or not, you need to
check both queue_if_no_path and saved_queue_if_no_path, because
presuspend saves original queue_if_no_path value to
saved_queue_if_no_path.

So multipath target must check the conditions above to request pushback.
The check for 2) already exists in both of map_io() and do_end_io().
So this patch adds the '__PUSHBACK()' macro to check 1) and 3), and
use it after the check for 2).

Test results:
See the test results of the patch 6.

The patch is for:
    2.6.18-mm2 + patch 1-6

Signed-off-by: Kiyoshi Ueda <[email-masked]>
Signed-off-by: Jun'ichi Nomura <[email-masked]>

Index: linux-2.6.18-rc7/drivers/md/dm-mpath.c
===================================================================
--- linux-2.6.18-rc7.orig/drivers/md/dm-mpath.c	2006-10-13 17:43:11.000000000 +0100
+++ linux-2.6.18-rc7/drivers/md/dm-mpath.c	2006-10-13 17:43:16.000000000 +0100
@@ -282,6 +282,23 @@ failed:
 	m->current_pg = NULL;
 }
 
+/*
+ * Check if multipath target needs to push back bios to device-mapper core.
+ * This macro checks if:
+ *   o The device is in the process of being suspended with 'noflush' flag,
+ *     because the dm core accepts pushback request only in that case.
+ *     (dm_noflush_suspending() returns true only in that case.)
+ *   o queue_if_no_path had been set before the suspend process has begun.
+ *     (In the suspend process, the presuspend hook will have run and
+ *      moved the real queue_if_no_path state into saved_queue_if_no_path.
+ *      So in that case, queue_if_no_path should be 0 and
+ *      saved_queue_if_no_path should be 1.)
+ *
+ * (m)->lock must be held before using this macro.
+ */
+#define __PUSHBACK(m) (!(m)->queue_if_no_path && (m)->saved_queue_if_no_path && \
+		       dm_noflush_suspending((m)->ti))
+
 static int map_io(struct multipath *m, struct bio *bio, struct mpath_io *mpio,
 		  unsigned was_queued)
 {
@@ -311,9 +328,12 @@ static int map_io(struct multipath *m, s
 			queue_work(kmultipathd, &m->process_queued_ios);
 		pgpath = NULL;
 		r = DM_MAPIO_SUBMITTED;
-	} else if (!pgpath)
+	} else if (!pgpath) {
 		r = -EIO;		/* Failed */
-	else
+
+		if (__PUSHBACK(m))
+			r = DM_MAPIO_REQUEUE;
+	} else
 		bio->bi_bdev = pgpath->path.dev->bdev;
 
 	mpio->pgpath = pgpath;
@@ -374,6 +394,14 @@ static void dispatch_queued_ios(struct m
 			bio_endio(bio, bio->bi_size, r);
 		else if (r == DM_MAPIO_REMAPPED)
 			generic_make_request(bio);
+		else if (r == DM_MAPIO_REQUEUE)
+			/*
+			 * end_io handles the requeue request by
+			 * returning the bio with error status.
+			 * We don't return the r value to end_io,
+			 * since it is probably not needed.
+			 */
+			bio_endio(bio, bio->bi_size, -EIO);
 
 		bio = next;
 	}
@@ -781,7 +809,7 @@ static int multipath_map(struct dm_targe
 	map_context->ptr = mpio;
 	bio->bi_rw |= (1 << BIO_RW_FAILFAST);
 	r = map_io(m, bio, mpio, 0);
-	if (r < 0)
+	if (r < 0 || r == DM_MAPIO_REQUEUE)
 		mempool_free(mpio, m->mpio_pool);
 
 	return r;
@@ -1005,7 +1033,10 @@ static int do_end_io(struct multipath *m
 
 	spin_lock_irqsave(&m->lock, flags);
 	if (!m->nr_valid_paths) {
-		if (!m->queue_if_no_path) {
+		if (__PUSHBACK(m)) {
+			spin_unlock_irqrestore(&m->lock, flags);
+			return DM_ENDIO_REQUEUE;
+		} else if (!m->queue_if_no_path) {
 			spin_unlock_irqrestore(&m->lock, flags);
 			return -EIO;
 		} else {