From: Kiyoshi Ueda 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 Signed-off-by: Jun'ichi Nomura 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 {