From: John Garry <[email protected]>

Failover occurs when the scsi_cmnd has failed and it is discovered that the
target scsi_device has transport down.

For a scsi command which suffers failover, requeue the master bio of each
bio attached to its request.

A bio which for which failover occurs is handled in
scsi_mpath_clone_end_io(). Failover is detected for blk_path_error()
occuring, same as how dm-mpath detects this.

Signed-off-by: John Garry <[email protected]>
---
 drivers/scsi/scsi_multipath.c | 24 ++++++++++++++++++++++++
 1 file changed, 24 insertions(+)

diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 456351fee4b98..12200664ed676 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -256,10 +256,34 @@ void scsi_mpath_dev_clear_path(struct scsi_mpath_device 
*scsi_mpath_dev)
                mpath_synchronize(mpath_head);
 }
 
+static inline void bio_list_add_master(struct bio_list *bl,
+                               struct bio *master_bio)
+{
+       if (bl->tail)
+               bl->tail->bi_next = master_bio;
+       else
+               bl->head = master_bio;
+       bl->tail = master_bio;
+}
+
 static void scsi_mpath_clone_end_io(struct bio *clone)
 {
        struct bio *master_bio = clone->bi_private;
 
+       if (clone->bi_status && blk_path_error(clone->bi_status)) {
+               struct mpath_head *mpath_head =
+                               master_bio->bi_bdev->bd_disk->private_data;
+               unsigned long flags;
+
+               spin_lock_irqsave(&mpath_head->requeue_lock, flags);
+               bio_list_add_master(&mpath_head->requeue_list, master_bio);
+               spin_unlock_irqrestore(&mpath_head->requeue_lock, flags);
+               bio_put(clone);
+
+               mpath_schedule_requeue_work(mpath_head);
+               return;
+       }
+
        master_bio->bi_status = clone->bi_status;
        bio_put(clone);
        bio_endio(master_bio);
-- 
2.43.7


Reply via email to