mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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


  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®