public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/8] scsi-multipath: Basic ALUA support
@ 2026-03-10 11:49 John Garry
  2026-03-10 11:49 ` [PATCH 1/8] libmultipath: add mpath_call_for_all_devices() John Garry
                   ` (7 more replies)
  0 siblings, 8 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

This series adds basic ALUA support for native SCSI multipath. Only
support to send a one-time RTPG is added to get per-path information.

I want to add more ALUA support, for things like ALUA configuration from
device rescan. The DH-based ALUA code already supports this. However
separating the DH ALUA port group management code from the rest of the
DH code is difficult, so I am looking for suggestions on this. There is
a consensus to not reuse the device handler code, but it is intertwined
with the ALUA driver code in scsi_dh_alua.c

An initial framework is also added to send a periodic TUR per path, to
keep path information up-to-date.

This series is based on https://lore.kernel.org/linux-scsi/20260225153627.1032500-1-john.g.garry@oracle.com/T/#m76b3a2756124e13b5564c434a38f9c51f64f0bbc
and may be found at https://github.com/johnpgarry/linux/tree/scsi-multipath-pre-7.0-upstream-alua

John Garry (8):
  libmultipath: add mpath_call_for_all_devices()
  scsi: scsi_dh_alua: Do not attach for SCSI native multipath
  scsi: scsi_dh_alua: Pass submit_rtpg() a bool for extended header
    support
  scsi: Create a core ALUA driver
  scsi: scsi-multipath: Add basic ALUA support
  scsi: scsi-multipath: Maintain sdev->access_state
  scsi: scsi-multipath: Issue a periodic TUR per path
  scsi: scsi-multipath: Add stubbed scsi_multipath_dev_rescan()

 drivers/scsi/Kconfig                       |   9 +
 drivers/scsi/Makefile                      |   1 +
 drivers/scsi/device_handler/Kconfig        |   1 +
 drivers/scsi/device_handler/scsi_dh_alua.c | 202 +-------------------
 drivers/scsi/scsi_alua.c                   | 204 ++++++++++++++++++++
 drivers/scsi/scsi_multipath.c              | 206 ++++++++++++++++++++-
 drivers/scsi/scsi_scan.c                   |   2 +
 drivers/scsi/scsi_sysfs.c                  |   2 +-
 include/linux/multipath.h                  |   2 +
 include/scsi/scsi_alua.h                   |  50 +++++
 include/scsi/scsi_multipath.h              |   8 +
 lib/multipath.c                            |  15 ++
 12 files changed, 501 insertions(+), 201 deletions(-)
 create mode 100644 drivers/scsi/scsi_alua.c
 create mode 100644 include/scsi/scsi_alua.h

-- 
2.43.5


^ permalink raw reply	[flat|nested] 21+ messages in thread

* [PATCH 1/8] libmultipath: add mpath_call_for_all_devices()
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
@ 2026-03-10 11:49 ` John Garry
  2026-03-10 11:49 ` [PATCH 2/8] scsi: scsi_dh_alua: Do not attach for SCSI native multipath John Garry
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

Add mpath_call_for_all_devices(), which iteratively calls a CB function
for all devices.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 include/linux/multipath.h |  2 ++
 lib/multipath.c           | 15 +++++++++++++++
 2 files changed, 17 insertions(+)

diff --git a/include/linux/multipath.h b/include/linux/multipath.h
index 1aa70ae11a195..153eb4c0258ef 100644
--- a/include/linux/multipath.h
+++ b/include/linux/multipath.h
@@ -153,6 +153,8 @@ void mpath_delete_device(struct mpath_head *mpath_head,
 			struct mpath_device *mpath_device);
 int mpath_call_for_device(struct mpath_head *mpath_head,
 			int (*cb)(struct mpath_device *mpath_device));
+void mpath_call_for_all_devices(struct mpath_head *mpath_head,
+			void (*cb)(struct mpath_device *mpath_device));
 void mpath_clear_paths(struct mpath_head *mpath_head);
 void mpath_revalidate_paths(struct mpath_disk *mpath_disk,
 	void (*cb)(struct mpath_device *mpath_device, sector_t capacity));
diff --git a/lib/multipath.c b/lib/multipath.c
index bba13b18215ee..96c6730680729 100644
--- a/lib/multipath.c
+++ b/lib/multipath.c
@@ -105,6 +105,21 @@ int mpath_call_for_device(struct mpath_head *mpath_head,
 }
 EXPORT_SYMBOL_GPL(mpath_call_for_device);
 
+void mpath_call_for_all_devices(struct mpath_head *mpath_head,
+			void (*cb)(struct mpath_device *mpath_device))
+{
+	struct mpath_device *mpath_device;
+	int srcu_idx;
+
+	srcu_idx = srcu_read_lock(&mpath_head->srcu);
+	list_for_each_entry_srcu(mpath_device, &mpath_head->dev_list, siblings,
+				 srcu_read_lock_held(&mpath_head->srcu)) {
+		cb(mpath_device);
+	}
+	srcu_read_unlock(&mpath_head->srcu, srcu_idx);
+}
+EXPORT_SYMBOL_GPL(mpath_call_for_all_devices);
+
 bool mpath_clear_current_path(struct mpath_head *mpath_head,
 			struct mpath_device *mpath_device)
 {
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 2/8] scsi: scsi_dh_alua: Do not attach for SCSI native multipath
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
  2026-03-10 11:49 ` [PATCH 1/8] libmultipath: add mpath_call_for_all_devices() John Garry
@ 2026-03-10 11:49 ` 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
                   ` (5 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

For native SCSI multipath, we don't want device handlers involved, so do
not attach in this instance.

For now, SCSI multipath will not maintain sdev->access_state.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/device_handler/scsi_dh_alua.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 6fd89ae33059f..7b360e7f11a6d 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -1239,6 +1239,8 @@ static int alua_bus_attach(struct scsi_device *sdev)
 	struct alua_dh_data *h;
 	int err;
 
+	if (sdev->scsi_mpath_dev)
+		return SCSI_DH_DEV_UNSUPP;
 	h = kzalloc(sizeof(*h) , GFP_KERNEL);
 	if (!h)
 		return SCSI_DH_NOMEM;
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 3/8] scsi: scsi_dh_alua: Pass submit_rtpg() a bool for extended header support
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support 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 ` John Garry
  2026-03-10 11:49 ` [PATCH 4/8] scsi: Create a core ALUA driver John Garry
                   ` (4 subsequent siblings)
  7 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

Currently the pg flags is passed to submit_rtpg() to allow submit_rtpg()
know whether extended header support is available.

Pass a bool instead, as when separating the driver into a pure ALUA part,
we don't want to use pg flags.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/device_handler/scsi_dh_alua.c | 12 +++++++-----
 1 file changed, 7 insertions(+), 5 deletions(-)

diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 7b360e7f11a6d..e9edd45ae28a3 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -124,7 +124,8 @@ static void release_port_group(struct kref *kref)
  * @sdev: sdev the command should be sent to
  */
 static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
-		       int bufflen, struct scsi_sense_hdr *sshdr, int flags)
+		       int bufflen, struct scsi_sense_hdr *sshdr,
+		       bool ext_hdr_unsupp)
 {
 	u8 cdb[MAX_COMMAND_SIZE];
 	blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
@@ -136,10 +137,10 @@ static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
 	/* Prepare the command. */
 	memset(cdb, 0x0, MAX_COMMAND_SIZE);
 	cdb[0] = MAINTENANCE_IN;
-	if (!(flags & ALUA_RTPG_EXT_HDR_UNSUPP))
-		cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
-	else
+	if (ext_hdr_unsupp)
 		cdb[1] = MI_REPORT_TARGET_PGS;
+	else
+		cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
 	put_unaligned_be32(bufflen, &cdb[6]);
 
 	return scsi_execute_cmd(sdev, cdb, opf, buff, bufflen,
@@ -566,7 +567,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
 
  retry:
 	err = 0;
-	retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr, pg->flags);
+	retval = submit_rtpg(sdev, buff, bufflen, &sense_hdr,
+			pg->flags & ALUA_RTPG_EXT_HDR_UNSUPP);
 
 	if (retval) {
 		/*
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 4/8] scsi: Create a core ALUA driver
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
                   ` (2 preceding siblings ...)
  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 ` John Garry
  2026-03-14  4:35   ` Benjamin Marzinski
  2026-03-10 11:49 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support John Garry
                   ` (3 subsequent siblings)
  7 siblings, 1 reply; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

Add a dedicated ALUA driver which can be used for native SCSI multipath
and also DH-based ALUA support.

Only core functions to submit a RTPG, STPG, tur, and also helper functions
are added.

The code from scsi_dh_alua.c to maintain the port groups is not added,
because it is quite intertwined with the DH code. However the port group
management code would be quite useful.

Hannes Reinecke originally authored this code.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/Kconfig                       |   9 +
 drivers/scsi/Makefile                      |   1 +
 drivers/scsi/device_handler/Kconfig        |   1 +
 drivers/scsi/device_handler/scsi_dh_alua.c | 198 +-------------------
 drivers/scsi/scsi_alua.c                   | 204 +++++++++++++++++++++
 include/scsi/scsi_alua.h                   |  50 +++++
 6 files changed, 269 insertions(+), 194 deletions(-)
 create mode 100644 drivers/scsi/scsi_alua.c
 create mode 100644 include/scsi/scsi_alua.h

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index cfab7ad1e3c2c..1a538c84ddf0a 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -86,6 +86,15 @@ config SCSI_MULTIPATH
 
 	  If unsure say N.
 
+config SCSI_ALUA
+	tristate "SPC-3 ALUA support"
+	depends on SCSI
+	help
+	  SCSI support for generic SPC-3 Asymmetric Logical Unit
+	  Access (ALUA).
+
+	  If unsure, say Y.
+
 comment "SCSI support type (disk, tape, CD-ROM)"
 	depends on SCSI
 
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 64b7a82828b81..053fb51d98092 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -153,6 +153,7 @@ obj-$(CONFIG_SCSI_ENCLOSURE)	+= ses.o
 
 obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
 
+obj-$(CONFIG_SCSI_ALUA) += scsi_alua.o
 # This goes last, so that "real" scsi devices probe earlier
 obj-$(CONFIG_SCSI_DEBUG)	+= scsi_debug.o
 scsi_mod-y			+= scsi.o hosts.o scsi_ioctl.o \
diff --git a/drivers/scsi/device_handler/Kconfig b/drivers/scsi/device_handler/Kconfig
index 368eb94c24562..ff06aea8c272c 100644
--- a/drivers/scsi/device_handler/Kconfig
+++ b/drivers/scsi/device_handler/Kconfig
@@ -35,6 +35,7 @@ config SCSI_DH_EMC
 config SCSI_DH_ALUA
 	tristate "SPC-3 ALUA Device Handler"
 	depends on SCSI_DH && SCSI
+	select SCSI_ALUA
 	help
 	  SCSI Device handler for generic SPC-3 Asymmetric Logical Unit
 	  Access (ALUA).
diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index e9edd45ae28a3..42fa6bf8bb480 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -14,31 +14,11 @@
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_dh.h>
+#include <scsi/scsi_alua.h>
 
 #define ALUA_DH_NAME "alua"
 #define ALUA_DH_VER "2.0"
 
-#define TPGS_SUPPORT_NONE		0x00
-#define TPGS_SUPPORT_OPTIMIZED		0x01
-#define TPGS_SUPPORT_NONOPTIMIZED	0x02
-#define TPGS_SUPPORT_STANDBY		0x04
-#define TPGS_SUPPORT_UNAVAILABLE	0x08
-#define TPGS_SUPPORT_LBA_DEPENDENT	0x10
-#define TPGS_SUPPORT_OFFLINE		0x40
-#define TPGS_SUPPORT_TRANSITION		0x80
-#define TPGS_SUPPORT_ALL		0xdf
-
-#define RTPG_FMT_MASK			0x70
-#define RTPG_FMT_EXT_HDR		0x10
-
-#define TPGS_MODE_UNINITIALIZED		 -1
-#define TPGS_MODE_NONE			0x0
-#define TPGS_MODE_IMPLICIT		0x1
-#define TPGS_MODE_EXPLICIT		0x2
-
-#define ALUA_RTPG_SIZE			128
-#define ALUA_FAILOVER_TIMEOUT		60
-#define ALUA_FAILOVER_RETRIES		5
 #define ALUA_RTPG_DELAY_MSECS		5
 #define ALUA_RTPG_RETRY_DELAY		2
 
@@ -119,70 +99,6 @@ static void release_port_group(struct kref *kref)
 	kfree_rcu(pg, rcu);
 }
 
-/*
- * submit_rtpg - Issue a REPORT TARGET GROUP STATES command
- * @sdev: sdev the command should be sent to
- */
-static int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
-		       int bufflen, struct scsi_sense_hdr *sshdr,
-		       bool ext_hdr_unsupp)
-{
-	u8 cdb[MAX_COMMAND_SIZE];
-	blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
-				REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
-	const struct scsi_exec_args exec_args = {
-		.sshdr = sshdr,
-	};
-
-	/* Prepare the command. */
-	memset(cdb, 0x0, MAX_COMMAND_SIZE);
-	cdb[0] = MAINTENANCE_IN;
-	if (ext_hdr_unsupp)
-		cdb[1] = MI_REPORT_TARGET_PGS;
-	else
-		cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
-	put_unaligned_be32(bufflen, &cdb[6]);
-
-	return scsi_execute_cmd(sdev, cdb, opf, buff, bufflen,
-				ALUA_FAILOVER_TIMEOUT * HZ,
-				ALUA_FAILOVER_RETRIES, &exec_args);
-}
-
-/*
- * submit_stpg - Issue a SET TARGET PORT GROUP command
- *
- * Currently we're only setting the current target port group state
- * to 'active/optimized' and let the array firmware figure out
- * the states of the remaining groups.
- */
-static int submit_stpg(struct scsi_device *sdev, int group_id,
-		       struct scsi_sense_hdr *sshdr)
-{
-	u8 cdb[MAX_COMMAND_SIZE];
-	unsigned char stpg_data[8];
-	int stpg_len = 8;
-	blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
-				REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
-	const struct scsi_exec_args exec_args = {
-		.sshdr = sshdr,
-	};
-
-	/* Prepare the data buffer */
-	memset(stpg_data, 0, stpg_len);
-	stpg_data[4] = SCSI_ACCESS_STATE_OPTIMAL;
-	put_unaligned_be16(group_id, &stpg_data[6]);
-
-	/* Prepare the command. */
-	memset(cdb, 0x0, MAX_COMMAND_SIZE);
-	cdb[0] = MAINTENANCE_OUT;
-	cdb[1] = MO_SET_TARGET_PGS;
-	put_unaligned_be32(stpg_len, &cdb[6]);
-
-	return scsi_execute_cmd(sdev, cdb, opf, stpg_data,
-				stpg_len, ALUA_FAILOVER_TIMEOUT * HZ,
-				ALUA_FAILOVER_RETRIES, &exec_args);
-}
-
 static struct alua_port_group *alua_find_get_pg(char *id_str, size_t id_size,
 						int group_id)
 {
@@ -265,58 +181,6 @@ static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
 	return pg;
 }
 
-/*
- * alua_check_tpgs - Evaluate TPGS setting
- * @sdev: device to be checked
- *
- * Examine the TPGS setting of the sdev to find out if ALUA
- * is supported.
- */
-static int alua_check_tpgs(struct scsi_device *sdev)
-{
-	int tpgs = TPGS_MODE_NONE;
-
-	/*
-	 * ALUA support for non-disk devices is fraught with
-	 * difficulties, so disable it for now.
-	 */
-	if (sdev->type != TYPE_DISK) {
-		sdev_printk(KERN_INFO, sdev,
-			    "%s: disable for non-disk devices\n",
-			    ALUA_DH_NAME);
-		return tpgs;
-	}
-
-	tpgs = scsi_device_tpgs(sdev);
-	switch (tpgs) {
-	case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
-		sdev_printk(KERN_INFO, sdev,
-			    "%s: supports implicit and explicit TPGS\n",
-			    ALUA_DH_NAME);
-		break;
-	case TPGS_MODE_EXPLICIT:
-		sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n",
-			    ALUA_DH_NAME);
-		break;
-	case TPGS_MODE_IMPLICIT:
-		sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
-			    ALUA_DH_NAME);
-		break;
-	case TPGS_MODE_NONE:
-		sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
-			    ALUA_DH_NAME);
-		break;
-	default:
-		sdev_printk(KERN_INFO, sdev,
-			    "%s: unsupported TPGS setting %d\n",
-			    ALUA_DH_NAME, tpgs);
-		tpgs = TPGS_MODE_NONE;
-		break;
-	}
-
-	return tpgs;
-}
-
 /*
  * alua_check_vpd - Evaluate INQUIRY vpd page 0x83
  * @sdev: device to be checked
@@ -393,27 +257,6 @@ static int alua_check_vpd(struct scsi_device *sdev, struct alua_dh_data *h,
 	return SCSI_DH_OK;
 }
 
-static char print_alua_state(unsigned char state)
-{
-	switch (state) {
-	case SCSI_ACCESS_STATE_OPTIMAL:
-		return 'A';
-	case SCSI_ACCESS_STATE_ACTIVE:
-		return 'N';
-	case SCSI_ACCESS_STATE_STANDBY:
-		return 'S';
-	case SCSI_ACCESS_STATE_UNAVAILABLE:
-		return 'U';
-	case SCSI_ACCESS_STATE_LBA:
-		return 'L';
-	case SCSI_ACCESS_STATE_OFFLINE:
-		return 'O';
-	case SCSI_ACCESS_STATE_TRANSITIONING:
-		return 'T';
-	default:
-		return 'X';
-	}
-}
 
 static void alua_handle_state_transition(struct scsi_device *sdev)
 {
@@ -500,30 +343,6 @@ static enum scsi_disposition alua_check_sense(struct scsi_device *sdev,
 	return SCSI_RETURN_NOT_HANDLED;
 }
 
-/*
- * alua_tur - Send a TEST UNIT READY
- * @sdev: device to which the TEST UNIT READY command should be send
- *
- * Send a TEST UNIT READY to @sdev to figure out the device state
- * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING,
- * SCSI_DH_OK if no error occurred, and SCSI_DH_IO otherwise.
- */
-static int alua_tur(struct scsi_device *sdev)
-{
-	struct scsi_sense_hdr sense_hdr;
-	int retval;
-
-	retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ,
-				      ALUA_FAILOVER_RETRIES, &sense_hdr);
-	if ((sense_hdr.sense_key == NOT_READY ||
-	     sense_hdr.sense_key == UNIT_ATTENTION) &&
-	    sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
-		return SCSI_DH_RETRY;
-	else if (retval)
-		return SCSI_DH_IO;
-	else
-		return SCSI_DH_OK;
-}
 
 /*
  * alua_rtpg - Evaluate REPORT TARGET GROUP STATES
@@ -722,17 +541,8 @@ static int alua_rtpg(struct scsi_device *sdev, struct alua_port_group *pg)
 
 	if (group_id_old != pg->group_id || state_old != pg->state ||
 		pref_old != pg->pref || valid_states_old != pg->valid_states)
-		sdev_printk(KERN_INFO, sdev,
-			"%s: port group %02x state %c %s supports %c%c%c%c%c%c%c\n",
-			ALUA_DH_NAME, pg->group_id, print_alua_state(pg->state),
-			pg->pref ? "preferred" : "non-preferred",
-			pg->valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
-			pg->valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
-			pg->valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l',
-			pg->valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
-			pg->valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
-			pg->valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
-			pg->valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');
+		alua_print_info(sdev, pg->group_id, pg->state, pg->pref,
+						pg->valid_states);
 
 	switch (pg->state) {
 	case SCSI_ACCESS_STATE_TRANSITIONING:
@@ -911,7 +721,7 @@ static void alua_rtpg_work(struct work_struct *work)
 		pg->flags &= ~ALUA_PG_RUN_RTPG;
 		spin_unlock_irqrestore(&pg->lock, flags);
 		if (state == SCSI_ACCESS_STATE_TRANSITIONING) {
-			if (alua_tur(sdev) == SCSI_DH_RETRY) {
+			if (alua_tur(sdev) == -EAGAIN) {
 				spin_lock_irqsave(&pg->lock, flags);
 				pg->flags &= ~ALUA_PG_RUNNING;
 				pg->flags |= ALUA_PG_RUN_RTPG;
diff --git a/drivers/scsi/scsi_alua.c b/drivers/scsi/scsi_alua.c
new file mode 100644
index 0000000000000..2e4102192dcb9
--- /dev/null
+++ b/drivers/scsi/scsi_alua.c
@@ -0,0 +1,204 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Generic SCSI-3 ALUA SCSI driver
+ *
+ * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
+ * All rights reserved.
+ */
+
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_alua.h>
+
+#define DRV_NAME "alua"
+
+#define ALUA_FAILOVER_RETRIES		5
+
+/*
+ * alua_check_tpgs - Evaluate TPGS setting
+ * @sdev: device to be checked
+ *
+ * Examine the TPGS setting of the sdev to find out if ALUA
+ * is supported.
+ */
+int alua_check_tpgs(struct scsi_device *sdev)
+{
+	int tpgs = TPGS_MODE_NONE;
+
+	/*
+	 * ALUA support for non-disk devices is fraught with
+	 * difficulties, so disable it for now.
+	 */
+	if (sdev->type != TYPE_DISK) {
+		sdev_printk(KERN_INFO, sdev,
+			    "%s: disable for non-disk devices\n",
+			    DRV_NAME);
+		return tpgs;
+	}
+
+	tpgs = scsi_device_tpgs(sdev);
+	switch (tpgs) {
+	case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
+		sdev_printk(KERN_INFO, sdev,
+			    "%s: supports implicit and explicit TPGS\n",
+			    DRV_NAME);
+		break;
+	case TPGS_MODE_EXPLICIT:
+		sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n",
+			    DRV_NAME);
+		break;
+	case TPGS_MODE_IMPLICIT:
+		sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
+			    DRV_NAME);
+		break;
+	case TPGS_MODE_NONE:
+		sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
+			    DRV_NAME);
+		break;
+	default:
+		sdev_printk(KERN_INFO, sdev,
+			    "%s: unsupported TPGS setting %d\n",
+			    DRV_NAME, tpgs);
+		tpgs = TPGS_MODE_NONE;
+		break;
+	}
+
+	return tpgs;
+}
+EXPORT_SYMBOL_GPL(alua_check_tpgs);
+
+/*
+ * alua_tur - Send a TEST UNIT READY
+ * @sdev: device to which the TEST UNIT READY command should be send
+ *
+ * Send a TEST UNIT READY to @sdev to figure out the device state
+ * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING,
+ * 0 if no error occurred, and SCSI_DH_IO otherwise.
+ */
+int alua_tur(struct scsi_device *sdev)
+{
+	struct scsi_sense_hdr sense_hdr;
+	int retval;
+
+	retval = scsi_test_unit_ready(sdev, ALUA_FAILOVER_TIMEOUT * HZ,
+				      ALUA_FAILOVER_RETRIES, &sense_hdr);
+	if ((sense_hdr.sense_key == NOT_READY ||
+	     sense_hdr.sense_key == UNIT_ATTENTION) &&
+	    sense_hdr.asc == 0x04 && sense_hdr.ascq == 0x0a)
+		return -EAGAIN;
+	else if (retval)
+		return -EIO;
+	else
+		return 0;
+}
+EXPORT_SYMBOL_GPL(alua_tur);
+
+/*
+ * submit_rtpg - Issue a REPORT TARGET GROUP STATES command
+ * @sdev: sdev the command should be sent to
+ */
+int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
+		       int bufflen, struct scsi_sense_hdr *sshdr,
+		       bool alua_rtpg_ext_hdr_unsupp)
+{
+	u8 cdb[MAX_COMMAND_SIZE];
+	blk_opf_t opf = REQ_OP_DRV_IN | REQ_FAILFAST_DEV |
+				REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+	const struct scsi_exec_args exec_args = {
+		.sshdr = sshdr,
+	};
+
+	/* Prepare the command. */
+	memset(cdb, 0x0, MAX_COMMAND_SIZE);
+	cdb[0] = MAINTENANCE_IN;
+	if (alua_rtpg_ext_hdr_unsupp)
+		cdb[1] = MI_REPORT_TARGET_PGS;
+	else
+		cdb[1] = MI_REPORT_TARGET_PGS | MI_EXT_HDR_PARAM_FMT;
+	put_unaligned_be32(bufflen, &cdb[6]);
+
+	return scsi_execute_cmd(sdev, cdb, opf, buff, bufflen,
+				ALUA_FAILOVER_TIMEOUT * HZ,
+				ALUA_FAILOVER_RETRIES, &exec_args);
+}
+EXPORT_SYMBOL_GPL(submit_rtpg);
+
+static char print_alua_state(unsigned char state)
+{
+	switch (state) {
+	case SCSI_ACCESS_STATE_OPTIMAL:
+		return 'A';
+	case SCSI_ACCESS_STATE_ACTIVE:
+		return 'N';
+	case SCSI_ACCESS_STATE_STANDBY:
+		return 'S';
+	case SCSI_ACCESS_STATE_UNAVAILABLE:
+		return 'U';
+	case SCSI_ACCESS_STATE_LBA:
+		return 'L';
+	case SCSI_ACCESS_STATE_OFFLINE:
+		return 'O';
+	case SCSI_ACCESS_STATE_TRANSITIONING:
+		return 'T';
+	default:
+		return 'X';
+	}
+}
+
+void alua_print_info(struct scsi_device *sdev, int group_id, int state,
+		int pref, int valid_states)
+{
+	sdev_printk(KERN_INFO, sdev,
+		"alua: port group %02x state %c %s supports %c%c%c%c%c%c%c\n",
+		group_id, print_alua_state(state),
+		pref ? "preferred" : "non-preferred",
+		valid_states&TPGS_SUPPORT_TRANSITION?'T':'t',
+		valid_states&TPGS_SUPPORT_OFFLINE?'O':'o',
+		valid_states&TPGS_SUPPORT_LBA_DEPENDENT?'L':'l',
+		valid_states&TPGS_SUPPORT_UNAVAILABLE?'U':'u',
+		valid_states&TPGS_SUPPORT_STANDBY?'S':'s',
+		valid_states&TPGS_SUPPORT_NONOPTIMIZED?'N':'n',
+		valid_states&TPGS_SUPPORT_OPTIMIZED?'A':'a');
+}
+EXPORT_SYMBOL_GPL(alua_print_info);
+
+/*
+ * submit_stpg - Issue a SET TARGET PORT GROUP command
+ *
+ * Currently we're only setting the current target port group state
+ * to 'active/optimized' and let the array firmware figure out
+ * the states of the remaining groups.
+ */
+int submit_stpg(struct scsi_device *sdev, int group_id,
+		       struct scsi_sense_hdr *sshdr)
+{
+	u8 cdb[MAX_COMMAND_SIZE];
+	unsigned char stpg_data[8];
+	int stpg_len = 8;
+	blk_opf_t opf = REQ_OP_DRV_OUT | REQ_FAILFAST_DEV |
+				REQ_FAILFAST_TRANSPORT | REQ_FAILFAST_DRIVER;
+	const struct scsi_exec_args exec_args = {
+		.sshdr = sshdr,
+	};
+
+	/* Prepare the data buffer */
+	memset(stpg_data, 0, stpg_len);
+	stpg_data[4] = SCSI_ACCESS_STATE_OPTIMAL;
+	put_unaligned_be16(group_id, &stpg_data[6]);
+
+	/* Prepare the command. */
+	memset(cdb, 0x0, MAX_COMMAND_SIZE);
+	cdb[0] = MAINTENANCE_OUT;
+	cdb[1] = MO_SET_TARGET_PGS;
+	put_unaligned_be32(stpg_len, &cdb[6]);
+
+	return scsi_execute_cmd(sdev, cdb, opf, stpg_data,
+				stpg_len, ALUA_FAILOVER_TIMEOUT * HZ,
+				ALUA_FAILOVER_RETRIES, &exec_args);
+}
+EXPORT_SYMBOL_GPL(submit_stpg);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("scsi_alua");
diff --git a/include/scsi/scsi_alua.h b/include/scsi/scsi_alua.h
new file mode 100644
index 0000000000000..51b5ed1836c99
--- /dev/null
+++ b/include/scsi/scsi_alua.h
@@ -0,0 +1,50 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Generic SCSI-3 ALUA SCSI Device Handler
+ *
+ * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
+ * All rights reserved.
+ */
+#ifndef _SCSI_ALUA_H
+#define _SCSI_ALUA_H
+
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/unaligned.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_eh.h>
+
+#define TPGS_SUPPORT_NONE		0x00
+#define TPGS_SUPPORT_OPTIMIZED		0x01
+#define TPGS_SUPPORT_NONOPTIMIZED	0x02
+#define TPGS_SUPPORT_STANDBY		0x04
+#define TPGS_SUPPORT_UNAVAILABLE	0x08
+#define TPGS_SUPPORT_LBA_DEPENDENT	0x10
+#define TPGS_SUPPORT_OFFLINE		0x40
+#define TPGS_SUPPORT_TRANSITION		0x80
+#define TPGS_SUPPORT_ALL		0xdf
+
+#define RTPG_FMT_MASK			0x70
+#define RTPG_FMT_EXT_HDR		0x10
+
+#define TPGS_MODE_UNINITIALIZED		 -1
+#define TPGS_MODE_NONE			0x0
+#define TPGS_MODE_IMPLICIT		0x1
+#define TPGS_MODE_EXPLICIT		0x2
+
+#define ALUA_RTPG_SIZE			128
+#define ALUA_FAILOVER_TIMEOUT		60
+
+int alua_check_tpgs(struct scsi_device *sdev);
+int submit_rtpg(struct scsi_device *sdev, unsigned char *buff,
+		       int bufflen, struct scsi_sense_hdr *sshdr,
+		       bool alua_rtpg_ext_hdr_unsupp);
+int alua_tur(struct scsi_device *sdev);
+void alua_print_info(struct scsi_device *sdev, int group_id, int state,
+		int pref, int valid_states);
+int submit_stpg(struct scsi_device *sdev, int group_id,
+		       struct scsi_sense_hdr *sshdr);
+#endif // _SCSI_ALUA_H
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
                   ` (3 preceding siblings ...)
  2026-03-10 11:49 ` [PATCH 4/8] scsi: Create a core ALUA driver John Garry
@ 2026-03-10 11:49 ` John Garry
  2026-03-10 13:23   ` Hannes Reinecke
  2026-03-14  4:48   ` Benjamin Marzinski
  2026-03-10 11:49 ` [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state John Garry
                   ` (2 subsequent siblings)
  7 siblings, 2 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

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


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
                   ` (4 preceding siblings ...)
  2026-03-10 11:49 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support John Garry
@ 2026-03-10 11:49 ` John Garry
  2026-03-10 13:27   ` Hannes Reinecke
  2026-03-10 11:49 ` [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path John Garry
  2026-03-10 11:49 ` [PATCH 8/8] scsi: scsi-multipath: Add stubbed scsi_multipath_dev_rescan() John Garry
  7 siblings, 1 reply; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

Now that ALUA is supported, we can maintain sdev->access_state.

However, preferred_path is still not maintained as that that is related
to transitioning  state and we do not yet support that (for SCSI
multipath).

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/scsi_multipath.c | 1 +
 drivers/scsi/scsi_sysfs.c     | 2 +-
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 0a314080bf0a5..0c34b1151f5bf 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -772,6 +772,7 @@ int scsi_mpath_dev_alloc(struct scsi_device *sdev)
 	} else {
 		sdev->scsi_mpath_dev->alua_state = SCSI_ACCESS_STATE_OPTIMAL;
 	}
+	sdev->access_state = sdev->scsi_mpath_dev->alua_state;
 
 	sdev->scsi_mpath_dev->index = ida_alloc(&scsi_mpath_head->ida, GFP_KERNEL);
 	if (sdev->scsi_mpath_dev->index < 0) {
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 3b03ee00c8df3..e4fbf08e05f4f 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1113,7 +1113,7 @@ sdev_show_access_state(struct device *dev,
 	unsigned char access_state;
 	const char *access_state_name;
 
-	if (!sdev->handler)
+	if (!sdev->handler && !sdev->scsi_mpath_dev)
 		return -EINVAL;
 
 	access_state = (sdev->access_state & SCSI_ACCESS_STATE_MASK);
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
                   ` (5 preceding siblings ...)
  2026-03-10 11:49 ` [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state John Garry
@ 2026-03-10 11:49 ` John Garry
  2026-03-10 13:34   ` Hannes Reinecke
  2026-03-10 11:49 ` [PATCH 8/8] scsi: scsi-multipath: Add stubbed scsi_multipath_dev_rescan() John Garry
  7 siblings, 1 reply; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

To allow the initiator know of any ALUA configuration changes, issue a
periodic TUR.

multipathd does something similar for dm-multipath in terms of issuing
a periodic read per path.

The purpose of the TUR is that the target can update UA info in the TUR
response and the INI can handle it, but currently we don't for SCSI
multipath.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/scsi_multipath.c | 36 +++++++++++++++++++++++++++++++++++
 include/scsi/scsi_multipath.h |  1 +
 2 files changed, 37 insertions(+)

diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 0c34b1151f5bf..2b916c7af4bd7 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -4,6 +4,7 @@
  *
  */
 
+#include <linux/kthread.h>
 #include <scsi/scsi_alua.h>
 #include <scsi/scsi_cmnd.h>
 #include <scsi/scsi_driver.h>
@@ -124,6 +125,7 @@ static void scsi_mpath_head_release(struct device *dev)
 		container_of(dev, struct scsi_mpath_head, dev);
 	struct mpath_head *mpath_head = scsi_mpath_head->mpath_head;
 
+	WARN_ON_ONCE(kthread_stop(scsi_mpath_head->kua));
 	scsi_mpath_delete_head(scsi_mpath_head);
 	bioset_exit(&scsi_mpath_head->bio_pool);
 	ida_free(&scsi_multipath_dev_ida, scsi_mpath_head->index);
@@ -514,6 +516,29 @@ struct mpath_head_template smpdt_pr = {
 	.device_groups = mpath_device_groups,
 };
 
+static void scsi_mpath_cb_ua_thread(struct mpath_device *mpath_device)
+{
+	struct scsi_mpath_device *scsi_mpath_dev =
+			to_scsi_mpath_device(mpath_device);
+
+	if (alua_tur(scsi_mpath_dev->sdev))
+		sdev_printk(KERN_NOTICE, scsi_mpath_dev->sdev,
+			    "%s: No target port descriptors found\n",
+			    __func__);
+}
+
+static int scsi_mpath_ua_thread(void *data)
+{
+	struct scsi_mpath_head *scsi_mpath_head = data;
+
+	while (!kthread_should_stop()) {
+		mpath_call_for_all_devices(scsi_mpath_head->mpath_head,
+			scsi_mpath_cb_ua_thread);
+		msleep(5000);
+	}
+	return 0;
+}
+
 static struct scsi_mpath_head *scsi_mpath_alloc_head(void)
 {
 	struct scsi_mpath_head *scsi_mpath_head;
@@ -548,6 +573,17 @@ static struct scsi_mpath_head *scsi_mpath_alloc_head(void)
 		goto out_free_ida;
 	}
 
+	scsi_mpath_head->kua = kthread_create(scsi_mpath_ua_thread,
+			scsi_mpath_head, "scsi-multipath-kua-%d",
+			scsi_mpath_head->index);
+	if (IS_ERR(scsi_mpath_head->kua)) {
+		put_device(&scsi_mpath_head->dev);
+		goto out_free_ida;
+	}
+
+	set_user_nice(scsi_mpath_head->kua, 10);
+	wake_up_process(scsi_mpath_head->kua);
+
 	return scsi_mpath_head;
 
 out_free_ida:
diff --git a/include/scsi/scsi_multipath.h b/include/scsi/scsi_multipath.h
index 7c7ee2fb7def7..d30f2c41e17de 100644
--- a/include/scsi/scsi_multipath.h
+++ b/include/scsi/scsi_multipath.h
@@ -30,6 +30,7 @@ struct scsi_mpath_head {
 	struct mpath_head	*mpath_head;
 	struct device		dev;
 	int			index;
+	struct task_struct	*kua;
 };
 
 struct scsi_mpath_device {
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* [PATCH 8/8] scsi: scsi-multipath: Add stubbed scsi_multipath_dev_rescan()
  2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support John Garry
                   ` (6 preceding siblings ...)
  2026-03-10 11:49 ` [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path John Garry
@ 2026-03-10 11:49 ` John Garry
  7 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 11:49 UTC (permalink / raw)
  To: hch, kbusch, martin.petersen, james.bottomley, hare, bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay, John Garry

Add an empty function scsi_multipath_dev_rescan() to handle sdev rescans.

It should handle ALUA reconfiguration, and that will be possible when the
core ALUA driver can handle that.

Signed-off-by: John Garry <john.g.garry@oracle.com>
---
 drivers/scsi/scsi_multipath.c | 6 ++++++
 drivers/scsi/scsi_scan.c      | 2 ++
 include/scsi/scsi_multipath.h | 4 ++++
 3 files changed, 12 insertions(+)

diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
index 2b916c7af4bd7..97d835f1b6aaf 100644
--- a/drivers/scsi/scsi_multipath.c
+++ b/drivers/scsi/scsi_multipath.c
@@ -539,6 +539,12 @@ static int scsi_mpath_ua_thread(void *data)
 	return 0;
 }
 
+void scsi_multipath_dev_rescan(struct scsi_device *sdev)
+{
+	/* Handle ALUA reconfig */
+	dev_warn_once(&sdev->sdev_gendev, "mulitpath rescan not handled\n");
+}
+
 static struct scsi_mpath_head *scsi_mpath_alloc_head(void)
 {
 	struct scsi_mpath_head *scsi_mpath_head;
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index e22d3245d4b65..bf602daeac1db 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -1743,6 +1743,8 @@ int scsi_rescan_device(struct scsi_device *sdev)
 
 	if (sdev->handler && sdev->handler->rescan)
 		sdev->handler->rescan(sdev);
+	else if (sdev->scsi_mpath_dev)
+		scsi_multipath_dev_rescan(sdev);
 
 	if (dev->driver && try_module_get(dev->driver->owner)) {
 		struct scsi_driver *drv = to_scsi_driver(dev->driver);
diff --git a/include/scsi/scsi_multipath.h b/include/scsi/scsi_multipath.h
index d30f2c41e17de..9dc02a56e6342 100644
--- a/include/scsi/scsi_multipath.h
+++ b/include/scsi/scsi_multipath.h
@@ -78,6 +78,7 @@ void scsi_mpath_add_sysfs_link(struct scsi_device *sdev);
 void scsi_mpath_remove_sysfs_link(struct scsi_device *sdev);
 int scsi_mpath_get_head(struct scsi_mpath_head *);
 void scsi_mpath_put_head(struct scsi_mpath_head *);
+void scsi_multipath_dev_rescan(struct scsi_device *sdev);
 
 static inline void scsi_mpath_start_request(struct request *req)
 {
@@ -156,5 +157,8 @@ static inline void scsi_mpath_add_sysfs_link(struct scsi_device *sdev)
 static inline void scsi_mpath_remove_sysfs_link(struct scsi_device *sdev)
 {
 }
+static inline void scsi_multipath_dev_rescan(struct scsi_device *sdev)
+{
+}
 #endif /* CONFIG_SCSI_MULTIPATH */
 #endif /* _SCSI_SCSI_MULTIPATH_H */
-- 
2.43.5


^ permalink raw reply related	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 11:49 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support John Garry
@ 2026-03-10 13:23   ` Hannes Reinecke
  2026-03-10 15:52     ` John Garry
  2026-03-14  4:48   ` Benjamin Marzinski
  1 sibling, 1 reply; 21+ messages in thread
From: Hannes Reinecke @ 2026-03-10 13:23 UTC (permalink / raw)
  To: John Garry, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 3/10/26 12:49, John Garry wrote:
> 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];
>   };

Is there a specific reason why this cannot be in the generic code?
After all, if the device reports anything else than ALUA_STATE_OPTIMAL
or ALUA_STATE_ACTIVE I/O will fail, irrespective of multipath being
active.

I would love to see that in the generic SCSI code, independent on this 
patchset. It would allow us to simplify the device handler code, too,
as then device handler really would only be required for explicit
ALUA. (And could be ignored for scsi-multipathing).

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.com                               +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state
  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
  0 siblings, 1 reply; 21+ messages in thread
From: Hannes Reinecke @ 2026-03-10 13:27 UTC (permalink / raw)
  To: John Garry, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 3/10/26 12:49, John Garry wrote:
> Now that ALUA is supported, we can maintain sdev->access_state.
> 
> However, preferred_path is still not maintained as that that is related
> to transitioning  state and we do not yet support that (for SCSI
> multipath).
> 
There is an issue with the preferred path in general, namely that it 
overlays the ALUA states (ie you can have 'acive/non-optimized' _and_
the preferred path bit set). So it only makes sense for explicit ALUA
as then the preferred path bit gives us an indicator that we might /
should switch paths.

If we restrict ourselves to implicit ALUA (which I'm advocating anyway
for scsi-multipath) the preferred path becomes rather pointless as
we cannot influence path selection at all.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.com                               +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path
  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
  0 siblings, 1 reply; 21+ messages in thread
From: Hannes Reinecke @ 2026-03-10 13:34 UTC (permalink / raw)
  To: John Garry, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 3/10/26 12:49, John Garry wrote:
> To allow the initiator know of any ALUA configuration changes, issue a
> periodic TUR.
> 
> multipathd does something similar for dm-multipath in terms of issuing
> a periodic read per path.
> 
> The purpose of the TUR is that the target can update UA info in the TUR
> response and the INI can handle it, but currently we don't for SCSI
> multipath.
> 
> Signed-off-by: John Garry <john.g.garry@oracle.com>
> ---
>   drivers/scsi/scsi_multipath.c | 36 +++++++++++++++++++++++++++++++++++
>   include/scsi/scsi_multipath.h |  1 +
>   2 files changed, 37 insertions(+)
> 
> diff --git a/drivers/scsi/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
> index 0c34b1151f5bf..2b916c7af4bd7 100644
> --- a/drivers/scsi/scsi_multipath.c
> +++ b/drivers/scsi/scsi_multipath.c
> @@ -4,6 +4,7 @@
>    *
>    */
>   
> +#include <linux/kthread.h>
>   #include <scsi/scsi_alua.h>
>   #include <scsi/scsi_cmnd.h>
>   #include <scsi/scsi_driver.h>
> @@ -124,6 +125,7 @@ static void scsi_mpath_head_release(struct device *dev)
>   		container_of(dev, struct scsi_mpath_head, dev);
>   	struct mpath_head *mpath_head = scsi_mpath_head->mpath_head;
>   
> +	WARN_ON_ONCE(kthread_stop(scsi_mpath_head->kua));
>   	scsi_mpath_delete_head(scsi_mpath_head);
>   	bioset_exit(&scsi_mpath_head->bio_pool);
>   	ida_free(&scsi_multipath_dev_ida, scsi_mpath_head->index);
> @@ -514,6 +516,29 @@ struct mpath_head_template smpdt_pr = {
>   	.device_groups = mpath_device_groups,
>   };
>   
> +static void scsi_mpath_cb_ua_thread(struct mpath_device *mpath_device)
> +{
> +	struct scsi_mpath_device *scsi_mpath_dev =
> +			to_scsi_mpath_device(mpath_device);
> +
> +	if (alua_tur(scsi_mpath_dev->sdev))
> +		sdev_printk(KERN_NOTICE, scsi_mpath_dev->sdev,
> +			    "%s: No target port descriptors found\n",
> +			    __func__);
> +}
> +
> +static int scsi_mpath_ua_thread(void *data)
> +{
> +	struct scsi_mpath_head *scsi_mpath_head = data;
> +
> +	while (!kthread_should_stop()) {
> +		mpath_call_for_all_devices(scsi_mpath_head->mpath_head,
> +			scsi_mpath_cb_ua_thread);
> +		msleep(5000);
> +	}
> +	return 0;
> +}
> +
>   static struct scsi_mpath_head *scsi_mpath_alloc_head(void)
>   {
>   	struct scsi_mpath_head *scsi_mpath_head;
> @@ -548,6 +573,17 @@ static struct scsi_mpath_head *scsi_mpath_alloc_head(void)
>   		goto out_free_ida;
>   	}
>   
> +	scsi_mpath_head->kua = kthread_create(scsi_mpath_ua_thread,
> +			scsi_mpath_head, "scsi-multipath-kua-%d",
> +			scsi_mpath_head->index);
> +	if (IS_ERR(scsi_mpath_head->kua)) {
> +		put_device(&scsi_mpath_head->dev);
> +		goto out_free_ida;
> +	}
> +
> +	set_user_nice(scsi_mpath_head->kua, 10);
> +	wake_up_process(scsi_mpath_head->kua);
> +
>   	return scsi_mpath_head;
>   
>   out_free_ida:
> diff --git a/include/scsi/scsi_multipath.h b/include/scsi/scsi_multipath.h
> index 7c7ee2fb7def7..d30f2c41e17de 100644
> --- a/include/scsi/scsi_multipath.h
> +++ b/include/scsi/scsi_multipath.h
> @@ -30,6 +30,7 @@ struct scsi_mpath_head {
>   	struct mpath_head	*mpath_head;
>   	struct device		dev;
>   	int			index;
> +	struct task_struct	*kua;
>   };
>   
>   struct scsi_mpath_device {

Please, don't. We should _not_ go into the business of doing TUR path 
checkers.
Path checkers turned out to be a major issue for multipathing, and
are mostly pointless for things like FC where you get reliable
path information via RSCNs.
Additionally I would advocate for scsi-multipath to be a _simple_
implementation, restricting to the most common scenarios. Namely
implicit ALUA only and reliable fabric notifications.
If you have anything else, fine, use dm-multipath.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.com                               +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 13:23   ` Hannes Reinecke
@ 2026-03-10 15:52     ` John Garry
  2026-03-10 17:54       ` Hannes Reinecke
  0 siblings, 1 reply; 21+ messages in thread
From: John Garry @ 2026-03-10 15:52 UTC (permalink / raw)
  To: Hannes Reinecke, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 10/03/2026 13:23, Hannes Reinecke wrote:
>>       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];
>>   };
> 
> Is there a specific reason why this cannot be in the generic code?

Sure, it's possible....

> After all, if the device reports anything else than ALUA_STATE_OPTIMAL
> or ALUA_STATE_ACTIVE I/O will fail, irrespective of multipath being
> active.
> 
> I would love to see that in the generic SCSI code, independent on this 
> patchset. It would allow us to simplify the device handler code, too,
> as then device handler really would only be required for explicit
> ALUA. (And could be ignored for scsi-multipathing).

Right, so you would like to see alua_port_group management in a core 
ALUA driver as well, right?

If yes, to repeat, it is hard to separate the DH stuff out...but I can 
try. Examples I would need to deal with (and associated handling):

- alua_port_group members like dh_list
- alua_dh_data memebers like init_error
- everything in alua_queue_data

Thanks


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 6/8] scsi: scsi-multipath: Maintain sdev->access_state
  2026-03-10 13:27   ` Hannes Reinecke
@ 2026-03-10 15:54     ` John Garry
  0 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 15:54 UTC (permalink / raw)
  To: Hannes Reinecke, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 10/03/2026 13:27, Hannes Reinecke wrote:
>> However, preferred_path is still not maintained as that that is related
>> to transitioning  state and we do not yet support that (for SCSI
>> multipath).
>>
> There is an issue with the preferred path in general, namely that it 
> overlays the ALUA states (ie you can have 'acive/non-optimized' _and_
> the preferred path bit set). So it only makes sense for explicit ALUA
> as then the preferred path bit gives us an indicator that we might /
> should switch paths.
> 
> If we restrict ourselves to implicit ALUA (which I'm advocating anyway
> for scsi-multipath) the preferred path becomes rather pointless as
> we cannot influence path selection at all.

ok, fine, I won't touch for native scsi multipath.

Thanks

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 7/8] scsi: scsi-multipath: Issue a periodic TUR per path
  2026-03-10 13:34   ` Hannes Reinecke
@ 2026-03-10 17:21     ` John Garry
  0 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 17:21 UTC (permalink / raw)
  To: Hannes Reinecke, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 10/03/2026 13:34, Hannes Reinecke wrote:
>>       struct device        dev;
>>       int            index;
>> +    struct task_struct    *kua;
>>   };
>>   struct scsi_mpath_device {
> 
> Please, don't. We should _not_ go into the business of doing TUR path 
> checkers.
> Path checkers turned out to be a major issue for multipathing, and
> are mostly pointless for things like FC where you get reliable
> path information via RSCNs.
> Additionally I would advocate for scsi-multipath to be a _simple_
> implementation, restricting to the most common scenarios.

Sure,

> Namely
> implicit ALUA only and reliable fabric notifications.
> If you have anything else, fine, use dm-multipath.

ok, fine

I was just testing with linux target for this, which uses this in-band 
method AFAICS

Cheers

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 15:52     ` John Garry
@ 2026-03-10 17:54       ` Hannes Reinecke
  2026-03-10 18:13         ` John Garry
  0 siblings, 1 reply; 21+ messages in thread
From: Hannes Reinecke @ 2026-03-10 17:54 UTC (permalink / raw)
  To: John Garry, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 3/10/26 16:52, John Garry wrote:
> On 10/03/2026 13:23, Hannes Reinecke wrote:
>>>       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];
>>>   };
>>
>> Is there a specific reason why this cannot be in the generic code?
> 
> Sure, it's possible....
> 
>> After all, if the device reports anything else than ALUA_STATE_OPTIMAL
>> or ALUA_STATE_ACTIVE I/O will fail, irrespective of multipath being
>> active.
>>
>> I would love to see that in the generic SCSI code, independent on this 
>> patchset. It would allow us to simplify the device handler code, too,
>> as then device handler really would only be required for explicit
>> ALUA. (And could be ignored for scsi-multipathing).
> 
> Right, so you would like to see alua_port_group management in a core 
> ALUA driver as well, right?
> 
> If yes, to repeat, it is hard to separate the DH stuff out...but I can 
> try. Examples I would need to deal with (and associated handling):
> 
> - alua_port_group members like dh_list
> - alua_dh_data memebers like init_error
> - everything in alua_queue_data
> 
While the port group handling looks nice (and there certainly is
a certain neatness to it), it kinda assumes too much about the
internal layout of the hierarchy within the target.
Technically, a target is only required to provide a device
identifier, and a group id (such that you can match with
RTPG output). However, you have no idea which of the various
device IDs are part of the same enclosure; that information
is not required to be present.
So you cannot assume that group ID A reported from device X
is the same group as group ID A reported from device Y.
The only reliable way is to check with the RTPG output, as
that contains all device identifiers for the defined group
IDs.
But: caching RTPG output is problematic (as it'll change
whenever a path state change happens), and it'll need to
contain references to the SCSI devices, introducing all
sorts of locking issues and race conditions.
So probably I would not go down that way (at least initially),
but rather read RTPG during scanning, and set the values
directly in the scsi device.

We then need to re-read that information whenever we hit
a relevant sense code, but arguably we'll need to do that
anyway.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.com                               +49 911 74053 688
SUSE Software Solutions GmbH, Frankenstr. 146, 90461 Nürnberg
HRB 36809 (AG Nürnberg), GF: I. Totev, A. McDonald, W. Knoblich

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 17:54       ` Hannes Reinecke
@ 2026-03-10 18:13         ` John Garry
  0 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-10 18:13 UTC (permalink / raw)
  To: Hannes Reinecke, hch, kbusch, martin.petersen, james.bottomley,
	bmarzins
  Cc: jmeneghi, linux-nvme, sagi, axboe, linux-scsi, michael.christie,
	snitzer, dm-devel, linux-kernel, nilay

On 10/03/2026 17:54, Hannes Reinecke wrote:
>>
>> If yes, to repeat, it is hard to separate the DH stuff out...but I can 
>> try. Examples I would need to deal with (and associated handling):
>>
>> - alua_port_group members like dh_list
>> - alua_dh_data memebers like init_error
>> - everything in alua_queue_data
>>
> While the port group handling looks nice (and there certainly is
> a certain neatness to it), it kinda assumes too much about the
> internal layout of the hierarchy within the target.
> Technically, a target is only required to provide a device
> identifier, and a group id (such that you can match with
> RTPG output). However, you have no idea which of the various
> device IDs are part of the same enclosure; that information
> is not required to be present.
> So you cannot assume that group ID A reported from device X
> is the same group as group ID A reported from device Y.
> The only reliable way is to check with the RTPG output, as
> that contains all device identifiers for the defined group
> IDs.
> But: caching RTPG output is problematic (as it'll change
> whenever a path state change happens), and it'll need to
> contain references to the SCSI devices, introducing all
> sorts of locking issues and race conditions.
> So probably I would not go down that way (at least initially),
> but rather read RTPG during scanning, and set the values
> directly in the scsi device.
> 
> We then need to re-read that information whenever we hit
> a relevant sense code, but arguably we'll need to do that
> anyway.

Hmmm... what you are describing seems to be now more like what I have in 
this series except only I did not have the ALUA info stored in the 
scsi_device struct.

So if I go down these lines, maybe for sense-triggered updates I can 
have something where scsi-multipath.c or scsi_dh_alua.c can trigger the 
RTPG to be issued and this updates scsi_device ALUA info. It sounds 
simple enough, but probably isn't for scsi_dh_alua.c :)

Thanks!

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/8] scsi: Create a core ALUA driver
  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
  0 siblings, 1 reply; 21+ messages in thread
From: Benjamin Marzinski @ 2026-03-14  4:35 UTC (permalink / raw)
  To: John Garry
  Cc: hch, kbusch, martin.petersen, james.bottomley, hare, jmeneghi,
	linux-nvme, sagi, axboe, linux-scsi, michael.christie, snitzer,
	dm-devel, linux-kernel, nilay

On Tue, Mar 10, 2026 at 11:49:21AM +0000, John Garry wrote:
> Add a dedicated ALUA driver which can be used for native SCSI multipath
> and also DH-based ALUA support.
> 
> Only core functions to submit a RTPG, STPG, tur, and also helper functions
> are added.
> 
> The code from scsi_dh_alua.c to maintain the port groups is not added,
> because it is quite intertwined with the DH code. However the port group
> management code would be quite useful.
> 
> Hannes Reinecke originally authored this code.
> 
> Signed-off-by: John Garry <john.g.garry@oracle.com>
> diff --git a/drivers/scsi/scsi_alua.c b/drivers/scsi/scsi_alua.c
> new file mode 100644
> index 0000000000000..2e4102192dcb9
> --- /dev/null
> +++ b/drivers/scsi/scsi_alua.c
> @@ -0,0 +1,204 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +/*
> + * Generic SCSI-3 ALUA SCSI driver
> + *
> + * Copyright (C) 2007-2010 Hannes Reinecke, SUSE Linux Products GmbH.
> + * All rights reserved.
> + */
> +
> +#include <scsi/scsi.h>
> +#include <scsi/scsi_proto.h>
> +#include <scsi/scsi_dbg.h>
> +#include <scsi/scsi_eh.h>
> +#include <scsi/scsi_alua.h>
> +
> +#define DRV_NAME "alua"
> +
> +#define ALUA_FAILOVER_RETRIES		5
> +
> +/*
> + * alua_check_tpgs - Evaluate TPGS setting
> + * @sdev: device to be checked
> + *
> + * Examine the TPGS setting of the sdev to find out if ALUA
> + * is supported.
> + */
> +int alua_check_tpgs(struct scsi_device *sdev)
> +{
> +	int tpgs = TPGS_MODE_NONE;
> +
> +	/*
> +	 * ALUA support for non-disk devices is fraught with
> +	 * difficulties, so disable it for now.
> +	 */
> +	if (sdev->type != TYPE_DISK) {
> +		sdev_printk(KERN_INFO, sdev,
> +			    "%s: disable for non-disk devices\n",
> +			    DRV_NAME);
> +		return tpgs;
> +	}
> +
> +	tpgs = scsi_device_tpgs(sdev);
> +	switch (tpgs) {
> +	case TPGS_MODE_EXPLICIT|TPGS_MODE_IMPLICIT:
> +		sdev_printk(KERN_INFO, sdev,
> +			    "%s: supports implicit and explicit TPGS\n",
> +			    DRV_NAME);
> +		break;
> +	case TPGS_MODE_EXPLICIT:
> +		sdev_printk(KERN_INFO, sdev, "%s: supports explicit TPGS\n",
> +			    DRV_NAME);
> +		break;
> +	case TPGS_MODE_IMPLICIT:
> +		sdev_printk(KERN_INFO, sdev, "%s: supports implicit TPGS\n",
> +			    DRV_NAME);
> +		break;
> +	case TPGS_MODE_NONE:
> +		sdev_printk(KERN_INFO, sdev, "%s: not supported\n",
> +			    DRV_NAME);
> +		break;
> +	default:
> +		sdev_printk(KERN_INFO, sdev,
> +			    "%s: unsupported TPGS setting %d\n",
> +			    DRV_NAME, tpgs);
> +		tpgs = TPGS_MODE_NONE;
> +		break;
> +	}
> +
> +	return tpgs;
> +}
> +EXPORT_SYMBOL_GPL(alua_check_tpgs);
> +
> +/*
> + * alua_tur - Send a TEST UNIT READY
> + * @sdev: device to which the TEST UNIT READY command should be send
> + *
> + * Send a TEST UNIT READY to @sdev to figure out the device state
> + * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING,
> + * 0 if no error occurred, and SCSI_DH_IO otherwise.

Nitpick: The comment here still references SCSI_DH_ values

-Ben

> + */
> +int alua_tur(struct scsi_device *sdev)


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-10 11:49 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support John Garry
  2026-03-10 13:23   ` Hannes Reinecke
@ 2026-03-14  4:48   ` Benjamin Marzinski
  2026-03-16  9:25     ` John Garry
  1 sibling, 1 reply; 21+ messages in thread
From: Benjamin Marzinski @ 2026-03-14  4:48 UTC (permalink / raw)
  To: John Garry
  Cc: hch, kbusch, martin.petersen, james.bottomley, hare, jmeneghi,
	linux-nvme, sagi, axboe, linux-scsi, michael.christie, snitzer,
	dm-devel, linux-kernel, nilay

On Tue, Mar 10, 2026 at 11:49:22AM +0000, John Garry wrote:
> 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;

This check is odd. First, we don't set the state to 'transitioning' like
the comment says. We don't set alua_state at all, which ends up meaning
that it stays as 0 (SCSI_ACCESS_STATE_OPTIMAL). It seems like we should
explicitly set it and make the comment reflect that, if just to aid
understanding of the logic.

Nitpick: Also, this is the only place where we goto out. All the other
checks individually free the buffer and return directly. I realize that
all the other checks that exit early are errors, but it seems like we
could just return a variable at the end of the function.

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

If we get a UNIT ATTENTION, we end up failing scsi_mpath_dev_alloc(),
not retrying. Aside from the comment being wrong, it seems very brittle
to fail here, just because we got a UNIT ATTENTION.

-Ben

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


^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 4/8] scsi: Create a core ALUA driver
  2026-03-14  4:35   ` Benjamin Marzinski
@ 2026-03-16  9:12     ` John Garry
  0 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-16  9:12 UTC (permalink / raw)
  To: Benjamin Marzinski
  Cc: hch, kbusch, martin.petersen, james.bottomley, hare, jmeneghi,
	linux-nvme, sagi, axboe, linux-scsi, michael.christie, snitzer,
	dm-devel, linux-kernel, nilay

On 14/03/2026 04:35, Benjamin Marzinski wrote:
>> +
>> +/*
>> + * alua_tur - Send a TEST UNIT READY
>> + * @sdev: device to which the TEST UNIT READY command should be send
>> + *
>> + * Send a TEST UNIT READY to @sdev to figure out the device state
>> + * Returns SCSI_DH_RETRY if the sense code is NOT READY/ALUA TRANSITIONING,
>> + * 0 if no error occurred, and SCSI_DH_IO otherwise.
> Nitpick: The comment here still references SCSI_DH_ values

Sure, I'll pay attention to removing DH remnants.

Thanks

^ permalink raw reply	[flat|nested] 21+ messages in thread

* Re: [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support
  2026-03-14  4:48   ` Benjamin Marzinski
@ 2026-03-16  9:25     ` John Garry
  0 siblings, 0 replies; 21+ messages in thread
From: John Garry @ 2026-03-16  9:25 UTC (permalink / raw)
  To: Benjamin Marzinski
  Cc: hch, kbusch, martin.petersen, james.bottomley, hare, jmeneghi,
	linux-nvme, sagi, axboe, linux-scsi, michael.christie, snitzer,
	dm-devel, linux-kernel, nilay

On 14/03/2026 04:48, Benjamin Marzinski wrote:
>> +			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;
> This check is odd. First, we don't set the state to 'transitioning' like
> the comment says. We don't set alua_state at all, which ends up meaning
> that it stays as 0 (SCSI_ACCESS_STATE_OPTIMAL). It seems like we should
> explicitly set it and make the comment reflect that, if just to aid
> understanding of the logic.

Yeah, I have to admit that this is all bodged the a bit, as 
transitioning is not properly handled. Instead of "basic" ALUA support, 
this really is limited ALUA support.

I am now looking at a way to have a core scsi ALUA driver to handle all 
of this, but it is challenging as we need to continue ALUA DH support.

> 
> Nitpick: Also, this is the only place where we goto out. All the other
> checks individually free the buffer and return directly. I realize that
> all the other checks that exit early are errors, but it seems like we
> could just return a variable at the end of the function.

Sure, I can pay attention to this. However, as mentioned above, I am 
experimenting with moving any ALUA specifics into scsi core code.

> 
>> +
>> +		/*
>> +		 * 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;
>> +		}
> If we get a UNIT ATTENTION, we end up failing scsi_mpath_dev_alloc(),
> not retrying. Aside from the comment being wrong, it seems very brittle
> to fail here, just because we got a UNIT ATTENTION.

Ack

Thanks,
John



^ permalink raw reply	[flat|nested] 21+ messages in thread

end of thread, other threads:[~2026-03-16  9:25 UTC | newest]

Thread overview: 21+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-03-10 11:49 [PATCH 0/8] scsi-multipath: Basic ALUA support 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 ` [PATCH 5/8] scsi: scsi-multipath: Add basic ALUA support John Garry
2026-03-10 13:23   ` 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

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox