Re: [PATCH 09/24] scsi-multipath: failover handling
From: Benjamin Marzinski
Date: Wed Mar 04 2026 - 00:47:16 EST
On Wed, Feb 25, 2026 at 03:36:12PM +0000, John Garry wrote:
> For a scmd which suffers failover, requeue the master bio of each bio
> attached to its request.
>
> A handler is added in the scsi_driver structure to lookup a
> mpath_disk from a request. This is needed because the scsi_disk structure
> will manage the mpath_disk, and the code core has no method to look this
> up from the scsi_scmnd.
>
> Failover occurs when the scsi_cmnd has failed and it is discovered that the
> original scsi_device has transport down.
>
> Signed-off-by: John Garry <john.g.garry@xxxxxxxxxx>
> ---
> drivers/scsi/scsi_error.c | 12 ++++++
> drivers/scsi/scsi_lib.c | 9 +++-
> drivers/scsi/scsi_multipath.c | 80 +++++++++++++++++++++++++++++++++++
> include/scsi/scsi.h | 1 +
> include/scsi/scsi_driver.h | 3 ++
> include/scsi/scsi_multipath.h | 14 ++++++
> 6 files changed, 118 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
> index c3e0f792e921f..16b1f84fc552c 100644
> --- a/drivers/scsi/scsi_multipath.c
> +++ b/drivers/scsi/scsi_multipath.c
> @@ -518,6 +518,86 @@ void scsi_mpath_put_head(struct scsi_mpath_head *scsi_mpath_head)
> }
> EXPORT_SYMBOL_GPL(scsi_mpath_put_head);
>
> +bool scsi_is_mpath_request(struct request *req)
> +{
> + return is_mpath_request(req);
> +}
> +EXPORT_SYMBOL_GPL(scsi_is_mpath_request);
> +
> +static inline void bio_list_add_clone_master(struct bio_list *bl,
> + struct bio *clone)
> +{
> + struct scsi_mpath_clone_bio *scsi_mpath_clone_bio;
> + struct bio *master_bio;
> +
> + if (clone->bi_next)
> + bio_list_add_clone_master(bl, clone->bi_next);
> +
> + scsi_mpath_clone_bio = scsi_mpath_to_master_bio(clone);
> + master_bio = scsi_mpath_clone_bio->master_bio;
> +
> + if (bl->tail)
> + bl->tail->bi_next = master_bio;
> + else
> + bl->head = master_bio;
> +
> + bl->tail = master_bio;
> +
> + bio_put(clone);
> +}
> +
> +void scsi_mpath_failover_req(struct request *req)
> +{
> + struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
> + struct scsi_device *sdev = scmd->device;
> + struct scsi_driver *drv = to_scsi_driver(sdev->sdev_gendev.driver);
> + struct mpath_disk *mpath_disk = drv->to_mpath_disk(req);
> + struct scsi_mpath_device *scsi_mpath_dev = sdev->scsi_mpath_dev;
> + struct mpath_head *mpath_head = mpath_disk->mpath_head;
> + unsigned long flags;
> +
> + scsi_mpath_dev_clear_path(scsi_mpath_dev);
> +
> + spin_lock_irqsave(&mpath_head->requeue_lock, flags);
> + bio_list_add_clone_master(&mpath_head->requeue_list, req->bio);
> + spin_unlock_irqrestore(&mpath_head->requeue_lock, flags);
> + req->bio = NULL;
> + req->biotail = NULL;
> + req->__data_len = 0;
> +
> + /* End old request with clone detached */
> + scmd->result = 0;
> + blk_mq_end_request(req, 0);
> +
> + kblockd_schedule_work(&mpath_head->requeue_work);
> +}
> +
> +static inline bool scsi_is_mpath_error(struct scsi_cmnd *scmd)
> +{
> + struct scsi_device *sdev = scmd->device;
> +
> + if (sdev->sdev_state == SDEV_TRANSPORT_OFFLINE)
> + return true;
> + return false;
> +}
> +
> +int scsi_mpath_failover_disposition(struct scsi_cmnd *scmd)
> +{
> + struct request *req = scsi_cmd_to_rq(scmd);
> +
> + if (is_mpath_request(req)) {
> + if (scsi_is_mpath_error(scmd) ||
> + blk_queue_dying(req->q))
> + return FAILOVER;
> + return NEEDS_RETRY;
> + } else {
nitpick: this else block is unnecessary.
-Ben
> + if (blk_queue_dying(req->q))
> + return SUCCESS;
> + }
> +
> + return SUCCESS;
> +}
> +