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 <[email protected]> --- 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

