On Mon, Mar 23, 2026 at 12:52:18PM +0000, John Garry wrote:
> On 23/03/2026 01:58, Benjamin Marzinski wrote:
> > On Tue, Mar 17, 2026 at 12:07:03PM +0000, John Garry wrote:
> > > For when no device handler is used, add ALUA support.
> > >
> > > This will be equivalent to when native SCSI multipathing is used.
> > >
> > > Essentially all the same handling is available as DH alua driver for
> > > rescan, request prep, sense handling.
> > >
> > > Signed-off-by: John Garry <[email protected]>
> > > ---
> > > drivers/scsi/scsi_alua.c | 93 +++++++++++++++++++++++++++++++++++++++
> > > drivers/scsi/scsi_error.c | 7 +++
> > > drivers/scsi/scsi_lib.c | 7 +++
> > > drivers/scsi/scsi_scan.c | 2 +
> > > drivers/scsi/scsi_sysfs.c | 4 +-
> > > include/scsi/scsi_alua.h | 14 ++++++
> > > 6 files changed, 126 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/scsi/scsi_alua.c b/drivers/scsi/scsi_alua.c
> > > index d3fcd887e5018..ee0229b1a9d12 100644
> > > --- a/drivers/scsi/scsi_alua.c
> > > +++ b/drivers/scsi/scsi_alua.c
> > > @@ -562,6 +562,90 @@ int scsi_alua_stpg_run(struct scsi_device *sdev,
> > > bool optimize)
> > > }
> > > EXPORT_SYMBOL_GPL(scsi_alua_stpg_run);
> > > +enum scsi_disposition scsi_alua_check_sense(struct scsi_device *sdev,
> > > + struct scsi_sense_hdr *sense_hdr)
> >
> > This seems like it should be shareable with scsi_dh_alua as well. In
> > might need to take a function to call for rescanning and have
> > alua_check_sense() be a wrapper around it, but since the force argument
> > to alua_check() is now always set to true in scsi_dh_alua, it's
> > unnecessary, so both it and scsi_device_alua_rescan() can have the
> > same arguments.
>
> Yeah, I tried it and I just thought that adding the rescan callback was a
> bit messy. I can go with the single function if we think it's better.
I would defer to the opinion of an acutal SCSI maintainer (which I am
not) on this.
>
> >
> > > +{
> > > + switch (sense_hdr->sense_key) {
> > > + case NOT_READY:
> > > + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
> > > + /*
> > > + * LUN Not Accessible - ALUA state transition
> > > + */
> > > + scsi_alua_handle_state_transition(sdev);
> > > + return NEEDS_RETRY;
> > > + }
> > > + break;
> > > + case UNIT_ATTENTION:
> > > + if (sense_hdr->asc == 0x04 && sense_hdr->ascq == 0x0a) {
> > > + /*
> > > + * LUN Not Accessible - ALUA state transition
> > > + */
> > > + scsi_alua_handle_state_transition(sdev);
> > > + return NEEDS_RETRY;
> > > + }
> > > + if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x00) {
> > > + /*
> > > + * Power On, Reset, or Bus Device Reset.
> > > + * Might have obscured a state transition,
> > > + * so schedule a recheck.
> > > + */
> > > + scsi_device_alua_rescan(sdev);
> > > + return ADD_TO_MLQUEUE;
> > > + }
> > > + if (sense_hdr->asc == 0x29 && sense_hdr->ascq == 0x04)
> > > + /*
> > > + * Device internal reset
> > > + */
> > > + return ADD_TO_MLQUEUE;
> > > + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x01)
> > > + /*
> > > + * Mode Parameters Changed
> > > + */
> > > + return ADD_TO_MLQUEUE;
> > > + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x06) {
> > > + /*
> > > + * ALUA state changed
> > > + */
> > > + scsi_device_alua_rescan(sdev);
> > > + return ADD_TO_MLQUEUE;
> > > + }
> > > + if (sense_hdr->asc == 0x2a && sense_hdr->ascq == 0x07) {
> > > + /*
> > > + * Implicit ALUA state transition failed
> > > + */
> > > + scsi_device_alua_rescan(sdev);
> > > + return ADD_TO_MLQUEUE;
> > > + }
> > > + if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x03)
> > > + /*
> > > + * Inquiry data has changed
> > > + */
> > > + return ADD_TO_MLQUEUE;
> > > + if (sense_hdr->asc == 0x3f && sense_hdr->ascq == 0x0e)
> > > + /*
> > > + * REPORTED_LUNS_DATA_HAS_CHANGED is reported
> > > + * when switching controllers on targets like
> > > + * Intel Multi-Flex. We can just retry.
> > > + */
> > > + return ADD_TO_MLQUEUE;
> > > + break;
> > > + }
> > > +
> > > + return SCSI_RETURN_NOT_HANDLED;
> > > +}
> > > +
> > > +static void alua_rtpg_work(struct work_struct *work)
> > > +{
> > > + struct alua_data *alua =
> > > + container_of(work, struct alua_data, work.work);
> > > + int ret;
> > > +
> > > + ret = scsi_alua_rtpg_run(alua->sdev);
> > > +
> > > + if (ret == -EAGAIN)
> > > + queue_delayed_work(kalua_wq, &alua->work, alua->interval * HZ);
> > > +}
> > > +
> > > int scsi_alua_sdev_init(struct scsi_device *sdev)
> > > {
> > > int rel_port, ret, tpgs;
> > > @@ -591,6 +675,7 @@ int scsi_alua_sdev_init(struct scsi_device *sdev)
> > > goto out_free_data;
> > > }
> > > + INIT_DELAYED_WORK(&sdev->alua->work, alua_rtpg_work);
> > > sdev->alua->sdev = sdev;
> > > sdev->alua->tpgs = tpgs;
> > > spin_lock_init(&sdev->alua->lock);
> > > @@ -638,6 +723,14 @@ bool scsi_device_alua_implicit(struct scsi_device
> > > *sdev)
> > > return sdev->alua->tpgs & TPGS_MODE_IMPLICIT;
> > > }
> > > +void scsi_device_alua_rescan(struct scsi_device *sdev)
> > > +{
> > > + struct alua_data *alua = sdev->alua;
> > > +
> > > + queue_delayed_work(kalua_wq, &alua->work,
> > > + msecs_to_jiffies(ALUA_RTPG_DELAY_MSECS));
> >
> > This code doesn't support triggering a new rtpg while the current one is
> > running. I'll leave it to people with more scsi expertise to say how
> > important that is, but the scsi_dh_alua code now will always trigger a
> > new rtpg in this case (or at least it would, with the issues from patch
> > 12 fixed).
> >
>
> If the work is running and we call queue_delayed_work() on the same
> work_struct, then it is enqueued again. If the work is pending and we call
> queue_delayed_work(), then it is not requeued (as it is already queued).
Oops. You're correct.
-Ben
> Thanks,
> John