[PATCH 02/13] scsi: alua: Create a core ALUA driver
From: John Garry
Date: Tue Mar 17 2026 - 08:13:17 EST
Add a dedicated ALUA driver which can be used for native SCSI multipath
and also DH-based ALUA support.
The core driver will provide ALUA support for when a scsi_device does not
have a DH attachment.
The core driver will provide functionality to handle RTPG and STPG, but
the scsi DH ALUA driver will be responsible for driving these when DH
attached.
New structure alua_data holds all ALUA-related scsi_device info.
Hannes Reinecke originally authored the kernel ALUA code.
Signed-off-by: John Garry <john.g.garry@xxxxxxxxxx>
---
drivers/scsi/Kconfig | 10 +++-
drivers/scsi/Makefile | 1 +
drivers/scsi/device_handler/Kconfig | 1 +
drivers/scsi/scsi.c | 7 +++
drivers/scsi/scsi_alua.c | 78 +++++++++++++++++++++++++++++
drivers/scsi/scsi_scan.c | 4 ++
drivers/scsi/scsi_sysfs.c | 3 ++
include/scsi/scsi_alua.h | 45 +++++++++++++++++
include/scsi/scsi_device.h | 1 +
9 files changed, 149 insertions(+), 1 deletion(-)
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 19d0884479a24..396cc0fda9fcc 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -76,8 +76,16 @@ config SCSI_LIB_KUNIT_TEST
If unsure say N.
-comment "SCSI support type (disk, tape, CD-ROM)"
+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)"
config BLK_DEV_SD
tristate "SCSI disk support"
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 16de3e41f94c4..90c25f36ea3a8 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/scsi.c b/drivers/scsi/scsi.c
index 76cdad063f7bc..fc90ee19bb962 100644
--- a/drivers/scsi/scsi.c
+++ b/drivers/scsi/scsi.c
@@ -58,6 +58,7 @@
#include <linux/unaligned.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_alua.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_dbg.h>
#include <scsi/scsi_device.h>
@@ -1042,12 +1043,17 @@ static int __init init_scsi(void)
error = scsi_sysfs_register();
if (error)
goto cleanup_sysctl;
+ error = scsi_alua_init();
+ if (error)
+ goto cleanup_sysfs;
scsi_netlink_init();
printk(KERN_NOTICE "SCSI subsystem initialized\n");
return 0;
+cleanup_sysfs:
+ scsi_sysfs_unregister();
cleanup_sysctl:
scsi_exit_sysctl();
cleanup_hosts:
@@ -1066,6 +1072,7 @@ static int __init init_scsi(void)
static void __exit exit_scsi(void)
{
scsi_netlink_exit();
+ scsi_exit_alua();
scsi_sysfs_unregister();
scsi_exit_sysctl();
scsi_exit_hosts();
diff --git a/drivers/scsi/scsi_alua.c b/drivers/scsi/scsi_alua.c
new file mode 100644
index 0000000000000..a5a67c6deff17
--- /dev/null
+++ b/drivers/scsi/scsi_alua.c
@@ -0,0 +1,78 @@
+// 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"
+
+static struct workqueue_struct *kalua_wq;
+
+int scsi_alua_sdev_init(struct scsi_device *sdev)
+{
+ int rel_port, ret, tpgs;
+
+ tpgs = scsi_device_tpgs(sdev);
+ if (!tpgs)
+ return 0;
+
+ sdev->alua = kzalloc(sizeof(*sdev->alua), GFP_KERNEL);
+ if (!sdev->alua)
+ return -ENOMEM;
+
+ sdev->alua->group_id = scsi_vpd_tpg_id(sdev, &rel_port);
+ sdev_printk(KERN_INFO, sdev,
+ "%s: group_id=%d\n",
+ DRV_NAME, sdev->alua->group_id);
+ if (sdev->alua->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__);
+ ret = -EIO;
+ goto out_free_data;
+ }
+
+ sdev->alua->sdev = sdev;
+ sdev->alua->tpgs = tpgs;
+
+ return 0;
+out_free_data:
+ kfree(sdev->alua);
+ sdev->alua = NULL;
+ return ret;
+}
+
+void scsi_alua_sdev_exit(struct scsi_device *sdev)
+{
+ kfree(sdev->alua);
+ sdev->alua = NULL;
+}
+
+int scsi_alua_init(void)
+{
+ kalua_wq = alloc_workqueue("kalua", WQ_MEM_RECLAIM | WQ_PERCPU, 0);
+ if (!kalua_wq)
+ return -ENOMEM;
+ return 0;
+}
+
+void scsi_exit_alua(void)
+{
+ destroy_workqueue(kalua_wq);
+}
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("scsi_alua");
diff --git a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
index 2cfcf1f5d6a46..3af64d1231445 100644
--- a/drivers/scsi/scsi_scan.c
+++ b/drivers/scsi/scsi_scan.c
@@ -38,6 +38,7 @@
#include <linux/unaligned.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_alua.h>
#include <scsi/scsi_cmnd.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_driver.h>
@@ -1123,6 +1124,9 @@ static int scsi_add_lun(struct scsi_device *sdev, unsigned char *inq_result,
sdev->max_queue_depth = sdev->queue_depth;
WARN_ON_ONCE(sdev->max_queue_depth > sdev->budget_map.depth);
+ if (scsi_device_tpgs(sdev))
+ scsi_alua_sdev_init(sdev);
+
/*
* Ok, the device is now all set up, we can
* register it and tell the rest of the kernel
diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 6b8c5c05f2944..6c4c3c22f6acf 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -16,6 +16,7 @@
#include <linux/bsg.h>
#include <scsi/scsi.h>
+#include <scsi/scsi_alua.h>
#include <scsi/scsi_device.h>
#include <scsi/scsi_host.h>
#include <scsi/scsi_tcq.h>
@@ -480,6 +481,8 @@ static void scsi_device_dev_release(struct device *dev)
sbitmap_free(&sdev->budget_map);
+ scsi_alua_sdev_exit(sdev);
+
mutex_lock(&sdev->inquiry_mutex);
vpd_pg0 = rcu_replace_pointer(sdev->vpd_pg0, vpd_pg0,
lockdep_is_held(&sdev->inquiry_mutex));
diff --git a/include/scsi/scsi_alua.h b/include/scsi/scsi_alua.h
new file mode 100644
index 0000000000000..07cdcb4f5b518
--- /dev/null
+++ b/include/scsi/scsi_alua.h
@@ -0,0 +1,45 @@
+// 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 <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+
+#if IS_ENABLED(CONFIG_SCSI_ALUA)
+
+struct alua_data {
+ int group_id;
+ int tpgs;
+ struct scsi_device *sdev;
+};
+
+int scsi_alua_sdev_init(struct scsi_device *sdev);
+void scsi_alua_sdev_exit(struct scsi_device *sdev);
+
+int scsi_alua_init(void);
+void scsi_exit_alua(void);
+#else //CONFIG_SCSI_ALUA
+
+static inline int scsi_alua_sdev_init(struct scsi_device *sdev)
+{
+ return 0;
+}
+static inline void scsi_alua_sdev_exit(struct scsi_device *sdev)
+{
+
+}
+static inline int scsi_alua_init(void)
+{
+ return 0;
+}
+static inline void scsi_exit_alua(void)
+{
+}
+#endif // CONFIG_SCSI_ALUA
+#endif // _SCSI_ALUA_H
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index d32f5841f4f85..c439e837dcaa6 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -295,6 +295,7 @@ struct scsi_device {
struct mutex state_mutex;
enum scsi_device_state sdev_state;
struct task_struct *quiesced_by;
+ struct alua_data *alua;
unsigned long sdev_data[];
} __attribute__((aligned(sizeof(unsigned long))));
--
2.43.5