From: John Garry <john.g.garry@oracle.com>
To: hch@lst.de, kbusch@kernel.org, martin.petersen@oracle.com,
james.bottomley@hansenpartnership.com, hare@suse.com,
bmarzins@redhat.com
Cc: jmeneghi@redhat.com, linux-nvme@lists.infradead.org,
sagi@grimberg.me, axboe@fb.com, linux-scsi@vger.kernel.org,
michael.christie@oracle.com, snitzer@kernel.org,
dm-devel@lists.linux.dev, linux-kernel@vger.kernel.org,
nilay@linux.ibm.com, John Garry <john.g.garry@oracle.com>
Subject: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
Date: Tue, 10 Mar 2026 11:49:22 +0000 [thread overview]
Message-ID: <20260310114925.1222263-6-john.g.garry@oracle.com> (raw)
In-Reply-To: <20260310114925.1222263-1-john.g.garry@oracle.com>
Add basic support just to get the per-port group state.
This support does not account of state transitioning, sdev port group
reconfiguration, etc, required for full support.
libmultipath callbacks scsi_mpath_is_optimized() and
scsi_mpath_is_disabled() are updated to take account of the ALUA-provided
path information.
As before, for no ALUA support (and scsi_multipath_always on) we assume
that the paths are all optimized.
Much of this code in scsi_mpath_alua_init() is copied from scsi_dh_alua.c,
originally authored by Hannes Reinecke.
Signed-off-by: John Garry <john.g.garry@oracle.com>
---
drivers/scsi/scsi_multipath.c | 163 ++++++++++++++++++++++++++++++++--
include/scsi/scsi_multipath.h | 3 +
2 files changed, 160 insertions(+), 6 deletions(-)
diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 1489c7e979167..0a314080bf0a5 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -4,6 +4,7 @@
*
*/
+#include <scsi/scsi_alua.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_driver.h>
#include <scsi/scsi_proto.h>
@@ -346,18 +347,29 @@ static bool scsi_mpath_is_disabled(struct mpath_device *mpath_device)
to_scsi_mpath_device(mpath_device);
struct scsi_device *sdev = scsi_mpath_dev->sdev;
enum scsi_device_state sdev_state = sdev->sdev_state;
+ int alua_state = scsi_mpath_dev->alua_state;
if (sdev_state == SDEV_RUNNING || sdev_state == SDEV_CANCEL)
return false;
- return true;
+ if (alua_state == SCSI_ACCESS_STATE_OPTIMAL ||
+ alua_state == SCSI_ACCESS_STATE_ACTIVE)
+ return true;
+
+ return false;
}
static bool scsi_mpath_is_optimized(struct mpath_device *mpath_device)
{
+ struct scsi_mpath_device *scsi_mpath_dev =
+ to_scsi_mpath_device(mpath_device);
+
if (scsi_mpath_is_disabled(mpath_device))
return false;
- return true;
+ if (scsi_mpath_dev->alua_state == SCSI_ACCESS_STATE_OPTIMAL)
+ return true;
+ return false;
+
}
/* Until we have ALUA support, we're always optimised */
@@ -366,7 +378,7 @@ static enum mpath_access_state scsi_mpath_get_access_state(
{
if (scsi_mpath_is_disabled(mpath_device))
return MPATH_STATE_INVALID;
- return MPATH_STATE_OPTIMIZED;
+ return scsi_mpath_is_optimized(mpath_device);
}
static bool scsi_mpath_available_path(struct mpath_device *mpath_device, bool *available)
@@ -579,16 +591,147 @@ static void scsi_multipath_sdev_uninit(struct scsi_device *sdev)
sdev->scsi_mpath_dev = NULL;
}
+static int scsi_mpath_alua_init(struct scsi_device *sdev)
+{
+ struct scsi_mpath_device *scsi_mpath_dev = sdev->scsi_mpath_dev;
+ struct scsi_sense_hdr sense_hdr;
+ int len, k, off, bufflen = ALUA_RTPG_SIZE;
+ unsigned char *desc, *buff;
+ unsigned int tpg_desc_tbl_off;
+ int group_id, rel_port = -1;
+ bool ext_hdr_unsupp = false;
+ int ret;
+
+ group_id = scsi_vpd_tpg_id(sdev, &rel_port);
+ if (group_id < 0) {
+ /*
+ * Internal error; TPGS supported but required
+ * VPD identification descriptors not present.
+ * Disable ALUA support.
+ */
+ sdev_printk(KERN_INFO, sdev,
+ "%s: No target port descriptors found\n",
+ __func__);
+ return -EIO;
+ }
+
+ buff = kzalloc(bufflen, GFP_KERNEL);
+ if (!buff)
+ return -ENOMEM;
+ retry:
+ ret = submit_rtpg(sdev, buff, bufflen, &sense_hdr,
+ ext_hdr_unsupp);
+
+ if (ret) {
+ if (ret < 0 || !scsi_sense_valid(&sense_hdr)) {
+ sdev_printk(KERN_INFO, sdev,
+ "%s: rtpg failed, result %d\n",
+ __func__, ret);
+ kfree(buff);
+ if (ret < 0)
+ return -EBUSY;
+ if (host_byte(ret) == DID_NO_CONNECT)
+ return -ENODEV;
+ return -EIO;
+ }
+
+ /*
+ * submit_rtpg() has failed on existing arrays
+ * when requesting extended header info, and
+ * the array doesn't support extended headers,
+ * even though it shouldn't according to T10.
+ * The retry without rtpg_ext_hdr_req set
+ * handles this.
+ * Note: some arrays return a sense key of ILLEGAL_REQUEST
+ * with ASC 00h if they don't support the extended header.
+ */
+ if (ext_hdr_unsupp &&
+ sense_hdr.sense_key == ILLEGAL_REQUEST) {
+ ext_hdr_unsupp = true;
+ goto retry;
+ }
+ /*
+ * If the array returns with 'ALUA state transition'
+ * sense code here it cannot return RTPG data during
+ * transition. So set the state to 'transitioning' directly.
+ */
+ if (sense_hdr.sense_key == NOT_READY &&
+ sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
+ goto out;
+
+ /*
+ * Retry on any other UNIT ATTENTION occurred.
+ */
+ if (sense_hdr.sense_key == UNIT_ATTENTION) {
+ scsi_print_sense_hdr(sdev, __func__, &sense_hdr);
+ kfree(buff);
+ return -EAGAIN;
+ }
+ sdev_printk(KERN_ERR, sdev, "%s: rtpg failed\n",
+ __func__);
+ scsi_print_sense_hdr(sdev, __func__, &sense_hdr);
+ kfree(buff);
+ return -EIO;
+ }
+
+ len = get_unaligned_be32(&buff[0]) + 4;
+
+ if (len > bufflen) {
+ /* Resubmit with the correct length */
+ kfree(buff);
+ bufflen = len;
+ buff = kmalloc(bufflen, GFP_KERNEL);
+ if (!buff) {
+ /* Temporary failure, bypass */
+ return -EBUSY;
+ }
+ goto retry;
+ }
+
+ if ((buff[4] & RTPG_FMT_MASK) == RTPG_FMT_EXT_HDR)
+ tpg_desc_tbl_off = 8;
+ else
+ tpg_desc_tbl_off = 4;
+
+ for (k = tpg_desc_tbl_off, desc = buff + tpg_desc_tbl_off;
+ k < len;
+ k += off, desc += off) {
+ u16 group_id_found = get_unaligned_be16(&desc[2]);
+
+ if (group_id_found == group_id) {
+ int valid_states, state, pref;
+
+ state = desc[0] & 0x0f;
+ pref = desc[0] >> 7;
+ valid_states = desc[1];
+
+ alua_print_info(sdev, group_id, state, pref, valid_states);
+
+ scsi_mpath_dev->alua_state = state;
+ scsi_mpath_dev->alua_pref = pref;
+ scsi_mpath_dev->alua_valid_states = valid_states;
+ goto out;
+ }
+
+ off = 8 + (desc[7] * 4);
+ }
+
+out:
+ kfree(buff);
+ return 0;
+}
+
int scsi_mpath_dev_alloc(struct scsi_device *sdev)
{
struct scsi_mpath_head *scsi_mpath_head;
- int ret;
+ int ret, tpgs;
if (!scsi_multipath)
return 0;
- if (!scsi_device_tpgs(sdev) && !scsi_multipath_always) {
- sdev_printk(KERN_NOTICE, sdev, "tpgs are required for multipath support\n");
+ tpgs = alua_check_tpgs(sdev);
+ if (!(tpgs & TPGS_MODE_IMPLICIT) && !scsi_multipath_always) {
+ sdev_printk(KERN_DEBUG, sdev, "IMPLICIT TPGS are required for multipath support\n");
return 0;
}
@@ -622,6 +765,14 @@ int scsi_mpath_dev_alloc(struct scsi_device *sdev)
sdev->scsi_mpath_dev->scsi_mpath_head = scsi_mpath_head;
found:
+ if (tpgs & TPGS_MODE_IMPLICIT) {
+ ret = scsi_mpath_alua_init(sdev);
+ if (ret)
+ goto out_put_head;
+ } else {
+ sdev->scsi_mpath_dev->alua_state = SCSI_ACCESS_STATE_OPTIMAL;
+ }
+
sdev->scsi_mpath_dev->index = ida_alloc(&scsi_mpath_head->ida, GFP_KERNEL);
if (sdev->scsi_mpath_dev->index < 0) {
ret = sdev->scsi_mpath_dev->index;
diff --git a/include/scsi/scsi_multipath.h b/include/scsi/scsi_multipath.h
index 2011447f482d6..7c7ee2fb7def7 100644
--- a/include/scsi/scsi_multipath.h
+++ b/include/scsi/scsi_multipath.h
@@ -38,6 +38,9 @@ struct scsi_mpath_device {
int index;
atomic_t nr_active;
struct scsi_mpath_head *scsi_mpath_head;
+ int alua_state;
+ int alua_pref;
+ int alua_valid_states;
char device_id_str[SCSI_MPATH_DEVICE_ID_LEN];
};
--
2.43.5
next prev parent reply other threads:[~2026-03-10 11:51 UTC|newest]
Thread overview: 21+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic " John Garry
2026-03-10 11:49 ` [PATCH 1/8] libmultipath: add mpath_call_for_all_devices() John Garry
2026-03-10 11:49 ` [PATCH 2/8] scsi: scsi_dh_alua: Do not attach for SCSI native multipath John Garry
2026-03-10 11:49 ` [PATCH 3/8] scsi: scsi_dh_alua: Pass submit_rtpg() a bool for extended header support John Garry
2026-03-10 11:49 ` [PATCH 4/8] scsi: Create a core ALUA driver John Garry
2026-03-14 4:35 ` Benjamin Marzinski
2026-03-16 9:12 ` John Garry
2026-03-10 11:49 ` John Garry [this message]
2026-03-10 13:23 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support Hannes Reinecke
2026-03-10 15:52 ` John Garry
2026-03-10 17:54 ` Hannes Reinecke
2026-03-10 18:13 ` John Garry
2026-03-14 4:48 ` Benjamin Marzinski
2026-03-16 9:25 ` John Garry
2026-03-10 11:49 ` [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state John Garry
2026-03-10 13:27 ` Hannes Reinecke
2026-03-10 15:54 ` John Garry
2026-03-10 11:49 ` [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path John Garry
2026-03-10 13:34 ` Hannes Reinecke
2026-03-10 17:21 ` John Garry
2026-03-10 11:49 ` [PATCH 8/8] scsi: scsi-multipath: Add stubbed scsi_multipath_dev_rescan() John Garry
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260310114925.1222263-6-john.g.garry@oracle.com \
--to=john.g.garry@oracle.com \
--cc=axboe@fb.com \
--cc=bmarzins@redhat.com \
--cc=dm-devel@lists.linux.dev \
--cc=hare@suse.com \
--cc=hch@lst.de \
--cc=james.bottomley@hansenpartnership.com \
--cc=jmeneghi@redhat.com \
--cc=kbusch@kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-nvme@lists.infradead.org \
--cc=linux-scsi@vger.kernel.org \
--cc=martin.petersen@oracle.com \
--cc=michael.christie@oracle.com \
--cc=nilay@linux.ibm.com \
--cc=sagi@grimberg.me \
--cc=snitzer@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®