[PATCH v5 18/27] scsi-multipath: failover handling

From: John Garry

Date: Mon Jul 27 2026 - 11:06:57 EST


From: John Garry <john.garry@xxxxxxxxx>

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 <john.g.garry@xxxxxxxxxx>
---
drivers/scsi/scsi_multipath.c | 39 +++++++++++++++++++++++++++++++++++
1 file changed, 39 insertions(+)

diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 582deeb403119..ec611f26d7960 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -249,10 +249,49 @@ void scsi_mpath_dev_clear_path(struct scsi_mpath_device *scsi_mpath_dev)
mpath_synchronize(mpath_head);
}

+static inline void bio_list_add_clone(struct bio_list *bl,
+ struct bio *clone)
+{
+ struct bio *master_bio = clone->bi_private;
+
+ if (bl->tail)
+ bl->tail->bi_next = master_bio;
+ else
+ bl->head = master_bio;
+ bl->tail = master_bio;
+ bio_put(clone);
+}
+
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 block_device *bi_bdev = clone->bi_bdev;
+ struct request_queue *q = bi_bdev->bd_queue;
+ struct scsi_device *sdev = scsi_device_from_queue(q);
+ struct scsi_mpath_device *scsi_mpath_dev;
+ struct mpath_device *mpath_device;
+ struct mpath_head *mpath_head;
+ unsigned long flags;
+
+ if (!sdev)
+ goto end_bio;
+
+ scsi_mpath_dev = sdev->scsi_mpath_dev;
+ mpath_device = &scsi_mpath_dev->mpath_device;
+ mpath_head = mpath_device->mpath_head;
+
+ spin_lock_irqsave(&mpath_head->requeue_lock, flags);
+ bio_list_add_clone(&mpath_head->requeue_list, clone);
+ spin_unlock_irqrestore(&mpath_head->requeue_lock, flags);
+
+ mpath_schedule_requeue_work(mpath_head);
+ put_device(&sdev->sdev_gendev);
+ return;
+ }
+
+end_bio:
master_bio->bi_status = clone->bi_status;
bio_put(clone);
bio_endio(master_bio);
--
2.43.7