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; > +} > +

