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 <[email protected]>
> ---
>  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;
> +}
> +


Reply via email to