All of lore.kernel.org
 help / color / mirror / Atom feed
* [RFC v1 0/8] scsi: Multipath support for scsi disk devices.
@ 2024-11-09  4:45 himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 1/8] scsi: Add multipath device support himanshu.madhani
                   ` (9 more replies)
  0 siblings, 10 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

Hello Folks,

Here is a very early RFC for multipath support in the scsi layer. This patch series
implements native multipath support for scsi disks devices.

In this series, I am providing conceptual changes which still needs work. However,
I wanted to get this RFC out to get community feedback on the direction of changes.

This RFC follows NVMe multipath implementation closely for SCSI multipath. Currently,
SCSI multipath only supports disk devices which advertises ALUA (Asymmetric Logical
Unit Access) capability in the Inquiry response data.

Patches are split as following

Patch 1: Add new SCSI multipath files and makefile changes for enabling multipath support.
Patch 2: Adds changes to scsi_host structure for multipath support
Patch 3: Adds error handling capability to the multipath changes.
Patch 4: Wires up commpletion path for the request
Patch 5: Adds sysfs hooks for displaying iopolicy and state.
Patch 6: Adds changes to use ALUA handler for multipath
Patch 7: Adds changes in sd driver for multipath.
Patch 8: Adds changes to scsi_debug driver for ALUA testing.

Here's list of TO-DO that will be addressed in next RFC version

1. Cleanup sysfs directory structure and only show first multipath device.
2. Test failover scenario with multiple disks and injecting errors with IO.
3. Test updating iopolicy while running IO and make sure path failover happens.
4. cleanup ALUA code to integrate more closely with new multipath code.
5. Performance numbers for the multipath disks.
6. PR ops are not yet handled by this series and will be added in next RFC.

Thanks,
Himanshu

Himanshu Madhani (8):
  scsi: Add multipath device support
  scsi: create multipath capable scsi host
  scsi: Add error handling capability for multipath
  scsi: Complete multipath request
  scsi: Add scsi multipath sysfs hooks
  scsi: Add multipath suppport for device handler
  scsi: Add multipath disk init code for sd driver
  scsi_debug: Add module parameter for ALUA multipath

 drivers/scsi/Kconfig                       |  12 +
 drivers/scsi/Makefile                      |   2 +
 drivers/scsi/device_handler/scsi_dh_alua.c |  15 +
 drivers/scsi/hosts.c                       |  12 +
 drivers/scsi/scsi_debug.c                  |  16 +-
 drivers/scsi/scsi_dh.c                     |   3 +
 drivers/scsi/scsi_error.c                  |   8 +
 drivers/scsi/scsi_lib.c                    |  25 +
 drivers/scsi/scsi_multipath.c              | 896 +++++++++++++++++++++
 drivers/scsi/scsi_sysfs.c                  | 104 +++
 drivers/scsi/sd.c                          |  83 ++
 include/scsi/scsi.h                        |   1 +
 include/scsi/scsi_device.h                 |  64 ++
 include/scsi/scsi_host.h                   |   7 +
 include/scsi/scsi_multipath.h              |  86 ++
 15 files changed, 1332 insertions(+), 2 deletions(-)
 create mode 100644 drivers/scsi/scsi_multipath.c
 create mode 100644 include/scsi/scsi_multipath.h


base-commit: 128faa1845a2d5b0178b986f3bd18fb38cc08cc2
-- 
2.41.0.rc2


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

* [RFC v1 1/8] scsi: Add multipath device support
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-12 21:09   ` Bart Van Assche
  2024-11-09  4:45 ` [RFC v1 2/8] scsi: create multipath capable scsi host himanshu.madhani
                   ` (8 subsequent siblings)
  9 siblings, 1 reply; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

- Add multipath device support to scsi_device
- Add multipath support to scsi_host
- Add Kconfig and Makefile
- Create new scsi_multipath.[ch] files

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/Kconfig          |  12 +
 drivers/scsi/Makefile         |   2 +
 drivers/scsi/scsi_multipath.c | 896 ++++++++++++++++++++++++++++++++++
 include/scsi/scsi_device.h    |  64 +++
 include/scsi/scsi_host.h      |   7 +
 include/scsi/scsi_multipath.h |  86 ++++
 6 files changed, 1067 insertions(+)
 create mode 100644 drivers/scsi/scsi_multipath.c
 create mode 100644 include/scsi/scsi_multipath.h

diff --git a/drivers/scsi/Kconfig b/drivers/scsi/Kconfig
index 37c24ffea65c..d1298fac774c 100644
--- a/drivers/scsi/Kconfig
+++ b/drivers/scsi/Kconfig
@@ -76,6 +76,18 @@ config SCSI_LIB_KUNIT_TEST
 
 	  If unsure say N.
 
+config SCSI_MULTIPATH
+	bool "SCSI multipath support"
+	depends on SCSI
+	depends on SCSI_DH  && SCSI_DH_ALUA
+	help
+	  This option enables support for native SCSI multipath support for
+	  SCSI host. This option depends on Asymmetric Logical Unit Access
+	  support to be enabled on the device. If this option is enabled a
+	  single /dev/mpathXsdY device will show up for each SCSI host.
+
+	  If unsure say N.
+
 comment "SCSI support type (disk, tape, CD-ROM)"
 	depends on SCSI
 
diff --git a/drivers/scsi/Makefile b/drivers/scsi/Makefile
index 1313ddf2fd1a..017795bc224d 100644
--- a/drivers/scsi/Makefile
+++ b/drivers/scsi/Makefile
@@ -154,6 +154,8 @@ obj-$(CONFIG_SCSI_ENCLOSURE)	+= ses.o
 
 obj-$(CONFIG_SCSI_HISI_SAS) += hisi_sas/
 
+obj-$(CONFIG_SCSI_MULTIPATH) += scsi_multipath.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/scsi_multipath.c b/drivers/scsi/scsi_multipath.c
new file mode 100644
index 000000000000..45684704b9e2
--- /dev/null
+++ b/drivers/scsi/scsi_multipath.c
@@ -0,0 +1,896 @@
+// SPDX-License-Indentifier: GPL-2.0
+/*
+ * Copyright (c) 2024 Himanshu Madhani
+ *
+ * SCSI Multipath support using ALUA (Asymmetric Logical Unit Access)
+ * capable devices.
+ */
+
+#include <linux/bio.h>
+#include <linux/moduleparam.h>
+#include <linux/topology.h>
+#include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_dh.h>
+#include <scsi/scsi_proto.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_multipath.h>
+
+bool scsi_multipath = true;
+module_param(scsi_multipath, bool, 0444);
+MODULE_PARM_DESC(scsi_multipath,
+    "turn on native support for multiple scsi devices \n"
+    "set this value to false to disable multipath, \n");
+
+static const char *scsi_iopolicy_names[] = {
+	[SCSI_MPATH_IOPOLICY_NUMA]	= "numa",
+	[SCSI_MPATH_IOPOLICY_RR]	= "round-robin",
+};
+
+static int iopolicy = SCSI_MPATH_IOPOLICY_NUMA;
+
+/*
+ * SCSI multipath will only allow 'NUMA' or 'round-robin' policy for IO.
+ * In Future, if more apropriate IO-policy is introduced will be added
+ * based on community feedback.
+ */
+static int scsi_set_iopolicy(const char *val, const struct kernel_param *kp)
+{
+	if (!val)
+		return -EINVAL;
+	if (!strncmp(val, "numa", 4))
+		iopolicy = SCSI_MPATH_IOPOLICY_NUMA;
+	else if (!strncmp(val, "round-robin", 11))
+		iopolicy = SCSI_MPATH_IOPOLICY_RR;
+	else
+		return -EINVAL;
+
+	return 0;
+}
+
+static int scsi_get_iopolicy(char *buf, const struct kernel_param *kp)
+{
+	return sprintf(buf, "%s\n", scsi_iopolicy_names[iopolicy]);
+}
+
+module_param_call(iopolicy, scsi_set_iopolicy, scsi_get_iopolicy,
+    &iopolicy, 0644);
+MODULE_PARM_DESC(iopolicy,
+    "Default multipath I/O policy; 'numa' (default) or 'round-robin'");
+
+void scsi_mpath_default_iopolicy(struct scsi_device *sdev)
+{
+	sdev->mpath_iopolicy = iopolicy;
+}
+
+void scsi_multipath_iopolicy_update(struct scsi_device *sdev, int iopolicy)
+{
+	struct Scsi_Host *shost =  sdev->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	int old_iopolicy = READ_ONCE(sdev->mpath_iopolicy);
+
+	if (old_iopolicy == iopolicy)
+		return;
+
+	WRITE_ONCE(sdev->mpath_iopolicy, iopolicy);
+
+	/* iopoliocy changes clear the multipath */
+	mutex_lock(&mpath_dev->mpath_lock);
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry)
+		scsi_mpath_clear_paths(shost);
+	mutex_unlock(&mpath_dev->mpath_lock);
+
+	sdev_printk(KERN_NOTICE, sdev, "Multipath iopolocy changed from %s to %s\n",
+	    scsi_iopolicy_names[old_iopolicy], scsi_iopolicy_names[iopolicy]);
+}
+
+bool scsi_mpath_clear_current_path(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	bool changed = false;
+	int node;
+
+	if (!sdev)
+		return changed;
+
+	for_each_node(node) {
+		if (sdev == rcu_access_pointer(mpath_dev->current_path[node])) {
+			rcu_assign_pointer(mpath_dev->current_path[node], NULL);
+			changed = true;
+		}
+	}
+
+	return changed;
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_clear_current_path);
+
+void scsi_mpath_clear_paths(struct Scsi_Host *shost)
+{
+	struct scsi_device *sdev;
+	int srcu_idx;
+
+	srcu_idx = srcu_read_lock(&shost->mpath_dev->srcu);
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry) {
+		scsi_mpath_clear_current_path(sdev);
+		kblockd_schedule_work(&shost->mpath_dev->mpath_requeue_work);
+	}
+	srcu_read_unlock(&shost->mpath_dev->srcu, srcu_idx);
+
+}
+
+static inline bool scsi_mpath_state_is_live(enum scsi_mpath_access_state state)
+{
+	if (state == SCSI_MPATH_OPTIMAL ||
+	    state == SCSI_MPATH_ACTIVE)
+		return true;
+
+	return false;
+}
+
+/* Check for path error */
+static inline bool scsi_is_mpath_error(struct scsi_cmnd *scmd)
+{
+	struct request *req = scsi_cmd_to_rq(scmd);
+	struct scsi_device *sdev = req->q->queuedata;
+
+	if (sdev->handler && sdev->handler->prep_fn) {
+		blk_status_t ret = sdev->handler->prep_fn(sdev, req);
+
+		if (ret != BLK_STS_OK)
+			return true;
+	}
+
+	return false;
+}
+
+static bool scsi_mpath_is_disabled(struct scsi_device *sdev)
+{
+	enum scsi_device_state sdev_state = sdev->sdev_state;
+
+	/*
+	 * if device multipath state is not set to LIVE
+	 * then return true
+	 */
+	if (!scsi_mpath_state_is_live(sdev->mpath_state))
+		return true;
+
+	/*
+	 * Do not treat DELETING as a disabled path as I/O should
+	 * still be able to complete assuming that scsi_device is
+	 * within timeout limit.
+	 * Otherwise I/O will fail immeadiately and return to
+	 * requeue list
+	 */
+	if (sdev_state != SDEV_RUNNING && sdev_state != SDEV_CANCEL)
+		return true;
+
+	return false;
+}
+
+/* handle failover request for path */
+void scsi_mpath_failover_req(struct request *req)
+{
+	struct scsi_cmnd *scmd = blk_mq_rq_to_pdu(req);
+	struct scsi_device *sdev = scmd->device;
+	struct Scsi_Host *shost = scmd->device->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	unsigned long flags;
+	struct bio *bio;
+
+	if (!scsi_device_online(sdev) || sdev->was_reset || sdev->locked)
+		return;
+
+	scsi_mpath_clear_current_path(sdev);
+
+	/*
+	 * if we got device handler error, we know that device is alive but not
+	 * ready to process command. kick off a requeue of scsi command and try
+	 * other available path
+	 */
+	if (scsi_is_mpath_error(scmd)) {
+		/*
+		 * Set flag as pending and requeue bio for retry on
+		 * another path
+		 */
+		set_bit(SCSI_MPATH_DISK_IO_PENDING, &sdev->mpath_flags);
+		queue_work(shost->work_q, &mpath_dev->mpath_requeue_work);
+	}
+
+	/*
+	 * following logic tries to steal bio, check if the bio has polled
+	 * operation, if yes, then clear polled reqeust and reqeue bio
+	 */
+	spin_lock_irqsave(&mpath_dev->mpath_requeue_lock, flags);
+	for (bio = req->bio; bio; bio = bio->bi_next) {
+		bio_set_dev(bio, req->q->disk->part0);
+		if (bio->bi_opf & REQ_POLLED) {
+			bio->bi_opf &= ~REQ_POLLED;
+			bio->bi_cookie = BLK_QC_T_NONE;
+		}
+	}
+	blk_steal_bios(&mpath_dev->mpath_requeue_list, req);
+	spin_unlock_irqrestore(&mpath_dev->mpath_requeue_lock, flags);
+
+	scmd->result = 0;
+
+	blk_mq_end_request(req, 0);
+
+	kblockd_schedule_work(&mpath_dev->mpath_requeue_work);
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_failover_req);
+
+static inline bool scsi_mpath_is_optimized(struct scsi_device *sdev)
+{
+	return (!scsi_device_online(sdev) &&
+	    ((sdev->mpath_state == SCSI_MPATH_OPTIMAL) ||
+	     (sdev->mpath_state == SCSI_MPATH_ACTIVE)));
+}
+
+static struct scsi_device *scsi_next_mpath_sdev(struct Scsi_Host *shost,
+			struct scsi_device *sdev)
+{
+	sdev = list_next_or_null_rcu(&shost->mpath_sdev, &sdev->siblings,
+	    struct scsi_device, siblings);
+
+	if (sdev)
+		return sdev;
+
+	return list_first_or_null_rcu(&shost->mpath_sdev, struct scsi_device,
+	    siblings);
+}
+
+static struct scsi_device *scsi_mpath_round_robin_path(struct Scsi_Host *shost,
+	int node, struct scsi_device *old_sdev)
+{
+	struct scsi_device *sdev, *found = NULL;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+
+	if (list_is_singular(&shost->mpath_sdev)) {
+		if(scsi_mpath_is_disabled(old_sdev))
+			return NULL;
+		return old_sdev;
+	}
+
+	for (sdev = scsi_next_mpath_sdev(shost, old_sdev);
+	    sdev && sdev != old_sdev;
+	    sdev = scsi_next_mpath_sdev(shost, sdev)) {
+		if (scsi_mpath_is_disabled(sdev))
+			continue;
+		if (sdev->mpath_state == SCSI_MPATH_OPTIMAL) {
+			found = sdev;
+			goto out;
+		}
+		if (sdev->mpath_state == SCSI_MPATH_ACTIVE)
+			found = sdev;
+	}
+
+	if (!scsi_mpath_is_disabled(old_sdev) &&
+	    (old_sdev->mpath_state == SCSI_MPATH_OPTIMAL ||
+	    (!found && old_sdev->mpath_state == SCSI_MPATH_ACTIVE)))
+		return old_sdev;
+
+	if (!found)
+		return NULL;
+out:
+	rcu_assign_pointer(mpath_dev->current_path[node], found);
+
+	return found;
+}
+
+/*
+ * Search path based on iopolicy and numa node affinity
+ * and return the scsi_device for that path
+ */
+inline struct scsi_device *__scsi_find_path(struct Scsi_Host *shost, int node)
+{
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	int found_distance = INT_MAX, fallback_distance = INT_MAX, distance;
+	struct scsi_device *sdev_found = NULL, *sdev_fallback = NULL, *sdev;
+
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry) {
+		if (scsi_mpath_is_disabled(sdev))
+			continue;
+
+		if (sdev->mpath_numa_node != NUMA_NO_NODE &&
+		    (READ_ONCE(sdev->mpath_iopolicy) == SCSI_MPATH_IOPOLICY_NUMA))
+			distance = node_distance(node, sdev->mpath_numa_node);
+		else
+			distance = LOCAL_DISTANCE;
+
+		switch(sdev->mpath_state) {
+		case SCSI_MPATH_OPTIMAL:
+		    if (distance < found_distance) {
+			    found_distance = distance;
+			    sdev_found = sdev;
+		    }
+		    break;
+		case SCSI_MPATH_ACTIVE:
+		    if (distance < fallback_distance) {
+			    fallback_distance = distance;
+			    sdev_fallback = sdev;
+		    }
+		    break;
+		default:
+		    break;
+		}
+	}
+
+	if (!sdev_found)
+		sdev_found = sdev_fallback;
+
+	if (sdev_found)
+		rcu_assign_pointer(mpath_dev->current_path[node], sdev_found);
+
+	return sdev_found;
+}
+
+inline struct scsi_device *scsi_find_path(struct Scsi_Host *shost)
+{
+	int node = numa_node_id();
+	struct scsi_device *sdev;
+
+	sdev = srcu_dereference(shost->mpath_dev->current_path[node],
+	    &shost->mpath_dev->srcu);
+
+	if (unlikely(!sdev))
+		sdev = __scsi_find_path(shost, node);
+
+	if (READ_ONCE(sdev->mpath_iopolicy) == SCSI_MPATH_IOPOLICY_RR)
+		return scsi_mpath_round_robin_path(shost, node, sdev);
+
+	if (unlikely(!scsi_mpath_is_optimized(sdev)))
+		return __scsi_find_path(shost, node);
+
+	return sdev;
+}
+
+void scsi_mpath_requeue_work(struct work_struct *work)
+{
+	struct scsi_mpath *mpath_dev =
+	    container_of(work, struct scsi_mpath, mpath_requeue_work);
+	struct bio *bio, *next;
+
+	spin_lock_irq(&mpath_dev->mpath_requeue_lock);
+	next = bio_list_get(&mpath_dev->mpath_requeue_list);
+	spin_unlock(&mpath_dev->mpath_requeue_lock);
+
+	while ((bio = next) != NULL) {
+		next = bio->bi_next;
+		bio->bi_next = NULL;
+		submit_bio_noacct(bio);
+	}
+}
+
+void scsi_mpath_set_live(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	int ret;
+
+	if (!sdev->mpath_disk)
+		return;
+
+	if (!test_and_set_bit(SCSI_MPATH_DISK_LIVE, &sdev->mpath_flags)) {
+		ret = device_add_disk(&sdev->sdev_dev, sdev->mpath_disk, NULL);
+		if (ret) {
+			clear_bit(SCSI_MPATH_DISK_LIVE, &sdev->mpath_flags);
+			return;
+		}
+	}
+
+	pr_info("Attached SCSI %s disk\n", sdev->mpath_disk->disk_name);
+
+	mutex_lock(&mpath_dev->mpath_lock);
+	if (scsi_mpath_is_optimized(sdev)) {
+		int node, srcu_idx;
+
+		srcu_idx = srcu_read_lock(&mpath_dev->srcu);
+		for_each_online_node(node)
+			__scsi_find_path(shost, node);
+		srcu_read_unlock(&mpath_dev->srcu, srcu_idx);
+	}
+	mutex_unlock(&mpath_dev->mpath_lock);
+
+	synchronize_srcu(&mpath_dev->srcu);
+	kblockd_schedule_work(&mpath_dev->mpath_requeue_work);
+}
+
+/**
+ * Callback function for activating multipath devices
+ */
+static void activate_mpath(void *data, int err)
+{
+	struct scsi_device *sdev = data;
+	struct scsi_mpath_dh_data *mpath_h = sdev->mpath_pg_data;
+	bool retry = false;
+
+	if (!mpath_h)
+		return;
+
+	switch (err) {
+	case SCSI_DH_OK:
+		break;
+	case SCSI_DH_NOSYS:
+		sdev_printk(KERN_ERR, sdev,
+			"Could not failover the device scsi_dh_%s, Error %d\n",
+			sdev->handler->name, err);
+		scsi_mpath_clear_current_path(sdev);
+		break;
+	case SCSI_DH_DEV_TEMP_BUSY:
+		sdev_printk(KERN_ERR, sdev,
+			"Device Handler Path Busy\n");
+		break;
+	case SCSI_DH_RETRY:
+		sdev_printk(KERN_ERR, sdev,
+			"Device Handler Path Retry \n");
+		retry = true;
+		fallthrough;
+	case SCSI_DH_IMM_RETRY:
+	case SCSI_DH_RES_TEMP_UNAVAIL:
+		sdev_printk(KERN_ERR, sdev,
+			"Device Handler Path Unavailable, Clear current path \n");
+		if ((mpath_h->state == SCSI_ACCESS_STATE_OFFLINE) ||
+		    (mpath_h->state == SCSI_ACCESS_STATE_UNAVAILABLE))
+			scsi_mpath_clear_current_path(sdev);
+		err = 0;
+		break;
+	case SCSI_DH_DEV_OFFLINED:
+	default:
+		sdev_printk(KERN_ERR, sdev, "Device Handler Path offlined \n");
+		scsi_mpath_clear_current_path(sdev);
+		break;
+	}
+
+	if (retry)
+		set_bit(SCSI_MPATH_DISK_IO_PENDING, &sdev->mpath_flags);
+
+        if (scsi_mpath_state_is_live(sdev->mpath_state))
+		scsi_mpath_set_live(sdev);
+}
+
+void scsi_activate_path(struct scsi_device *sdev)
+{
+	struct request_queue *q = sdev->mpath_disk->queue;
+	struct scsi_mpath_dh_data *mpath_dh = sdev->mpath_pg_data;
+
+	if (!mpath_dh)
+		return;
+
+        if (!(scsi_mpath_state_is_live(sdev->mpath_state))) {
+		sdev_printk(KERN_INFO, sdev, "Path state is not live \n");
+                return;
+	}
+
+	if (!blk_queue_dying(q))
+		scsi_dh_activate(q, activate_mpath, sdev);
+	else
+		activate_mpath(sdev, SCSI_DH_OK);
+}
+
+static void scsi_activate_mpath_work(struct work_struct *work)
+{
+        struct scsi_device *sdev = container_of(work,
+            struct scsi_device, activate_mpath);
+
+	if (!sdev)
+		return;
+
+	scsi_activate_path(sdev);
+}
+
+int scsi_mpath_add_disk(struct scsi_device *sdev)
+{
+	if (!sdev->mpath_pg_data) {
+		/* Re initialize ALUA */
+		sdev->handler->rescan(sdev);
+	} else {
+		sdev->mpath_state = SCSI_MPATH_OPTIMAL;
+		scsi_mpath_set_live(sdev);
+	}
+
+	return (test_bit(SCSI_MPATH_DISK_LIVE, &sdev->mpath_flags));
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_add_disk);
+
+int scsi_multipath_init(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath_dh_data *h;
+	struct scsi_mpath *mpath_dev;
+	int ret = -ENOMEM;
+
+	mpath_dev = kzalloc(sizeof(struct scsi_mpath), GFP_KERNEL);
+	if (!mpath_dev)
+		return ret;
+
+	h = kzalloc(sizeof(struct scsi_mpath_dh_data), GFP_KERNEL);
+	if (!h)
+		goto out_mpath_dev;
+
+	sdev->mpath_pg_data = h;
+
+	ret = init_srcu_struct(&mpath_dev->srcu);
+	if (ret) {
+		cleanup_srcu_struct(&mpath_dev->srcu);
+		goto out_handler;
+	}
+
+	shost->mpath_dev = mpath_dev;
+
+	mutex_init(&mpath_dev->mpath_lock);
+	bio_list_init(&mpath_dev->mpath_requeue_list);
+	spin_lock_init(&mpath_dev->mpath_requeue_lock);
+	INIT_WORK(&mpath_dev->mpath_requeue_work, scsi_mpath_requeue_work);
+	INIT_LIST_HEAD(&mpath_dev->mpath_list);
+	INIT_WORK(&sdev->activate_mpath, scsi_activate_mpath_work);
+	INIT_LIST_HEAD(&sdev->mpath_entry);
+	sdev->mpath_numa_node = NUMA_NO_NODE;
+	sdev->is_shared = 1;
+
+	return 0;
+
+out_handler:
+	kfree(h);
+out_mpath_dev:
+	if (mpath_dev)
+		kfree(mpath_dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(scsi_multipath_init);
+
+static bool scsi_available_mpath(struct Scsi_Host *shost)
+{
+	struct scsi_device *sdev;
+
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry) {
+		if (scsi_device_online(sdev))
+			return true;
+	}
+	return false;
+}
+
+/*  called when shost is being freed */
+void scsi_mpath_dev_release(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath *mpath_dev;
+
+	if (!shost->mpath_dev)
+		return;
+
+	mpath_dev = shost->mpath_dev;
+	cancel_work_sync(&mpath_dev->mpath_requeue_work);
+	cleanup_srcu_struct(&mpath_dev->srcu);
+
+	if (sdev->mpath_pg_data)
+                kfree(sdev->mpath_pg_data);
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_dev_release);
+
+void scsi_put_mpath_sdev(struct scsi_device *sdev)
+{
+	scsi_device_put(sdev);
+}
+
+void scsi_mpath_revalidate_path(struct gendisk *mpath_disk, sector_t capacity)
+{
+	struct Scsi_Host *shost = mpath_disk->private_data;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	struct scsi_device *sdev;
+	int srcu_idx;
+	int node;
+
+	if (!shost->mpath_dev)
+		return;
+
+	srcu_idx = srcu_read_lock(&mpath_dev->srcu);
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry) {
+		if (capacity != get_capacity(sdev->mpath_disk))
+			clear_bit(SCSI_MPATH_DISK_LIVE, &sdev->mpath_flags);
+	}
+	srcu_read_unlock(&mpath_dev->srcu, srcu_idx);
+
+	for_each_node(node)
+		rcu_assign_pointer(mpath_dev->current_path[node], NULL);
+	kblockd_schedule_work(&mpath_dev->mpath_requeue_work);
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_revalidate_path);
+
+static int scsi_mpath_open(struct gendisk *disk, blk_mode_t mode)
+{
+	if (!scsi_get_device(disk->private_data))
+		return -ENXIO;
+
+	return 0;
+}
+
+static void scsi_mpath_release(struct gendisk *disk)
+{
+	struct Scsi_Host *shost = disk->private_data;
+	struct scsi_device *sdev;
+	int srcu_idx;
+
+	srcu_idx = srcu_read_lock(&shost->mpath_dev->srcu);
+	sdev = scsi_find_path(shost);
+	srcu_read_unlock(&shost->mpath_dev->srcu, srcu_idx);
+}
+
+int scsi_mpath_failover_disposition(struct scsi_cmnd *scmd)
+{
+	struct request *req = scsi_cmd_to_rq(scmd);
+
+	if (req->cmd_flags & REQ_SCSI_MPATH) {
+		if (scsi_is_mpath_error(scmd) ||
+		    blk_queue_dying(req->q)) {
+			return NEEDS_RETRY;
+		}
+	} else {
+		if (blk_queue_dying(req->q))
+			return SUCCESS;
+	}
+
+	return SUCCESS;
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_failover_disposition);
+
+static void scsi_multipath_submit_bio(struct bio *bio)
+{
+	struct Scsi_Host *shost = bio->bi_bdev->bd_disk->private_data;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	struct scsi_device *sdev;
+	int srcu_idx;
+
+	/*
+	 * The scsi device might be going away and the bio might be
+	 * moved to a difference queue via blk_steal_bios(), so we
+	 * need to use bio_split pool from the original queue to
+	 * allocate the bvecs from.
+	 */
+	bio = bio_split_to_limits(bio);
+	if (!bio)
+		return;
+
+	srcu_idx = srcu_read_lock(&mpath_dev->srcu);
+	sdev = scsi_find_path(shost);
+	if (likely(sdev)) {
+		bio_set_dev(bio, bio->bi_bdev->bd_disk->part0);
+		bio->bi_opf |= REQ_SCSI_MPATH;
+		submit_bio_noacct(bio);
+	} else if (scsi_available_mpath(shost)) {
+		sdev_printk(KERN_NOTICE, NULL,
+		    "No Usable Path - Requeing I/O \n");
+
+		spin_lock_irq(&mpath_dev->mpath_requeue_lock);
+		bio_list_add(&mpath_dev->mpath_requeue_list, bio);
+		spin_unlock_irq(&mpath_dev->mpath_requeue_lock);
+	} else {
+		sdev_printk(KERN_NOTICE, NULL,
+		    "No available path = Failing I/O \n");
+
+		bio_io_error(bio);
+	}
+	srcu_read_unlock(&mpath_dev->srcu, srcu_idx);
+}
+
+static int scsi_mpath_get_unique_id(struct gendisk *disk, u8 id[16],
+    enum blk_unique_id type)
+{
+	struct Scsi_Host *shost = disk->private_data;
+	struct scsi_device *sdev;
+	int srcu_idx, ret = -EWOULDBLOCK;
+
+	srcu_idx = srcu_read_lock(&shost->mpath_dev->srcu);
+	sdev = scsi_find_path(shost);
+	if (sdev)
+		ret = scsi_mpath_unique_id(sdev, id, type);
+	srcu_read_unlock(&shost->mpath_dev->srcu, srcu_idx);
+
+	return ret;
+}
+
+const struct block_device_operations scsi_mpath_ops = {
+	.owner          = THIS_MODULE,
+	.submit_bio	= scsi_multipath_submit_bio,
+	.open		= scsi_mpath_open,
+	.release	= scsi_mpath_release,
+	.get_unique_id	= scsi_mpath_get_unique_id,
+};
+
+int scsi_mpath_unique_id(struct scsi_device *sdev, u8 id[16],
+		enum blk_unique_id type)
+{
+	struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
+
+	if (type != BLK_UID_NAA)
+		return -EINVAL;
+
+	if (strncmp(dh_data->device_id_str, id, 16) == 0)
+		return dh_data->device_id_len;
+
+	return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_unique_id);
+
+int scsi_mpath_unique_lun_id(struct scsi_device *sdev)
+{
+	struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
+	char device_id_str[20];
+	int ret = -EINVAL;
+
+	ret = scsi_vpd_lun_id(sdev, device_id_str, dh_data->device_id_len);
+	if (ret < 0)
+		return ret;
+
+	if (strncmp(dh_data->device_id_str, device_id_str,
+	    dh_data->device_id_len) == 0)
+		return -EINVAL;
+
+	return 0;
+}
+
+/*
+ * Allocate Disk for Multipath Device
+ */
+int scsi_mpath_alloc_disk(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+	struct queue_limits lim;
+
+	/*
+	 * Don't allocate mpath disk if ALUA handler is not attached
+	 */
+	if (!sdev->handler || strncmp(sdev->handler->name, "alua", 4) != 0) {
+		sdev_printk(KERN_NOTICE, sdev,
+		    "No Handler or correct handler attached for multipath \n");
+		return 0;
+	}
+
+	/*
+	 * Add multipath disk only if scsi host supports multipath modparam
+	 */
+	if (!scsi_multipath) {
+		sdev_printk(KERN_NOTICE, sdev,
+		    "%s Handler attached but modparam scsi_multipath is set to false \n",
+		    sdev->handler->name);
+		return 0;
+	}
+
+	if (scsi_mpath_unique_lun_id(sdev) == 0) {
+		sdev_printk(KERN_NOTICE, sdev,
+		    "existing sdev with path, return\n");
+		return 0;
+	}
+
+	blk_set_stacking_limits(&lim);
+
+	lim.features |= BLK_FEAT_IO_STAT | BLK_FEAT_NOWAIT | BLK_FEAT_POLL;
+	lim.max_zone_append_sectors = 0;
+	lim.dma_alignment = 3;
+
+	sdev->mpath_disk = blk_alloc_disk(&lim, sdev->mpath_numa_node);
+	if (IS_ERR(sdev->mpath_disk))
+		return PTR_ERR(sdev->mpath_disk);
+
+	sdev->mpath_disk->private_data = shost;
+	sdev->mpath_disk->fops = &scsi_mpath_ops;
+
+	list_add_tail(&shost->mpath_sdev, &sdev->mpath_entry);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_alloc_disk);
+
+void scsi_mpath_start_request(struct request *req)
+{
+	struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
+	struct scsi_device *sdev = cmd->device;
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+
+	if (!blk_queue_io_stat(sdev->mpath_disk->queue) ||
+	    blk_rq_is_passthrough(req))
+		return;
+
+	req->rq_flags |= SCSI_MPATH_IO_STATS;
+	mpath_dev->mpath_start_time = bdev_start_io_acct(sdev->mpath_disk->part0,
+	    req_op(req), jiffies);
+}
+
+void scsi_mpath_end_request(struct request *req)
+{
+	struct scsi_cmnd *cmd = blk_mq_rq_to_pdu(req);
+	struct scsi_device *sdev = cmd->device;
+	struct Scsi_Host *shost = sdev->host;
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+
+	if (!(req->rq_flags & SCSI_MPATH_IO_STATS))
+		return;
+
+	bdev_end_io_acct(sdev->mpath_disk->part0, req_op(req),
+	    blk_rq_bytes(req) >> SECTOR_SHIFT,
+	    mpath_dev->mpath_start_time);
+}
+
+void scsi_mpath_kick_requeue_lists(struct Scsi_Host *shost)
+{
+	struct scsi_mpath *mpath_dev = shost->mpath_dev;
+	struct scsi_device *sdev;
+	int srcu_idx;
+
+	srcu_idx = srcu_read_lock(&mpath_dev->srcu);
+	list_for_each_entry_rcu(sdev, &shost->mpath_sdev, mpath_entry) {
+		if (sdev->is_shared)
+			continue;
+
+		kblockd_schedule_work(&mpath_dev->mpath_requeue_work);
+		if (sdev->sdev_state == SDEV_RUNNING)
+			disk_uevent(sdev->mpath_disk, KOBJ_CHANGE);
+	}
+	srcu_read_unlock(&mpath_dev->srcu, srcu_idx);
+}
+
+void scsi_mpath_shutdown_disk(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+
+	if (!sdev->mpath_disk)
+		return;
+
+	if (test_and_clear_bit(SCSI_MPATH_DISK_LIVE, &sdev->mpath_flags)) {
+		synchronize_srcu(&shost->mpath_dev->srcu);
+		kblockd_schedule_work(&shost->mpath_dev->mpath_requeue_work);
+		del_gendisk(sdev->mpath_disk);
+	}
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_shutdown_disk);
+
+void scsi_mpath_remove_disk(struct scsi_device *sdev)
+{
+	struct Scsi_Host *shost = sdev->host;
+
+	if (!sdev->mpath_disk)
+		return;
+
+	if (!sdev->is_shared)
+		return;
+
+	/* Make sure All pending bio's are cleaned up */
+	kblockd_schedule_work(&shost->mpath_dev->mpath_requeue_work);
+	flush_work(&shost->mpath_dev->mpath_requeue_work);
+	put_disk(sdev->mpath_disk);
+}
+EXPORT_SYMBOL_GPL(scsi_mpath_remove_disk);
+
+int scsi_mpath_update_state(struct scsi_device *sdev)
+{
+        struct scsi_mpath_dh_data *mpath_h;
+
+        mpath_h = sdev->mpath_pg_data;
+        if (!mpath_h)
+		return SCSI_MPATH_UNAVAILABLE;
+
+	switch(mpath_h->state) {
+		case SCSI_ACCESS_STATE_OPTIMAL:
+			sdev->mpath_state = SCSI_MPATH_OPTIMAL;
+			break;
+		case SCSI_ACCESS_STATE_ACTIVE:
+			sdev->mpath_state = SCSI_MPATH_ACTIVE;
+			break;
+		case SCSI_ACCESS_STATE_STANDBY:
+			sdev->mpath_state = SCSI_MPATH_STANDBY;
+			break;
+		case SCSI_ACCESS_STATE_UNAVAILABLE:
+			sdev->mpath_state = SCSI_MPATH_UNAVAILABLE;
+			break;
+		case SCSI_ACCESS_STATE_TRANSITIONING:
+			sdev->mpath_state = SCSI_MPATH_TRANSITIONING;
+			break;
+		case SCSI_ACCESS_STATE_OFFLINE:
+		default:
+                    sdev->mpath_state = SCSI_MPATH_OFFLINE;
+		    break;
+	}
+
+	return sdev->mpath_state;
+}
diff --git a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
index 9c540f5468eb..b46e06a01179 100644
--- a/include/scsi/scsi_device.h
+++ b/include/scsi/scsi_device.h
@@ -9,6 +9,8 @@
 #include <scsi/scsi.h>
 #include <linux/atomic.h>
 #include <linux/sbitmap.h>
+#include <scsi/scsi_multipath.h>
+#include <scsi/scsi_host.h>
 
 struct bsg_device;
 struct device;
@@ -100,6 +102,11 @@ struct scsi_vpd {
 	unsigned char	data[];
 };
 
+/*
+ * Mark bio as coming from scsi multipath node
+ */
+#define REQ_SCSI_MPATH		REQ_DRV
+
 struct scsi_device {
 	struct Scsi_Host *host;
 	struct request_queue *request_queue;
@@ -120,6 +127,7 @@ struct scsi_device {
 	unsigned short last_queue_full_count; /* scsi_track_queue_full() */
 	unsigned long last_queue_full_time;	/* last queue full time */
 	unsigned long queue_ramp_up_period;	/* ramp up period in jiffies */
+
 #define SCSI_DEFAULT_RAMP_UP_PERIOD	(120 * HZ)
 
 	unsigned long last_queue_ramp_up;	/* last queue ramp up time */
@@ -265,6 +273,25 @@ struct scsi_device {
 	struct device		sdev_gendev,
 				sdev_dev;
 
+#ifdef	CONFIG_SCSI_MULTIPATH
+	int				is_shared; 	/* Set Multipath flag  */
+	int				mpath_first_path; /* Indicate if this was first path */
+	struct gendisk          	*mpath_disk;	/* Multipath disk */
+	int				mpath_numa_node; /* NUMA node for Path  */
+	enum scsi_mpath_access_state	mpath_state;	/* Multipath State */
+	enum scsi_mpath_iopolicy	mpath_iopolicy;	/* IO Policy */
+	struct list_head		mpath_entry;	/* list of all mpath_sdevs */
+	struct scsi_mpath_dh_data	*mpath_pg_data; /* Place holder for Port group data */
+	struct work_struct		activate_mpath; /* Activate path work */
+	atomic_t			nr_mpath;	/* Number of Active mpath */
+
+#define SCSI_MPATH_DISK_LIVE            0
+#define SCSI_MPATH_DISK_IO_PENDING      1
+#define SCSI_MPATH_IO_STATS             2
+
+	unsigned long           mpath_flags;		/* flag for multipath devices*/
+#endif
+
 	struct work_struct	requeue_work;
 
 	struct scsi_device_handler *handler;
@@ -294,6 +321,43 @@ struct scsi_device {
 #define sdev_dbg(sdev, fmt, a...) \
 	dev_dbg(&(sdev)->sdev_gendev, fmt, ##a)
 
+#ifdef CONFIG_SCSI_MULTIPATH
+extern bool scsi_multipath;
+extern const struct block_device_operations scsi_mpath_ops;
+
+static inline bool scsi_sdev_use_alua(struct scsi_device *sdev)
+{
+	return sdev->handler_data != NULL;
+}
+
+static inline bool scsi_disk_is_multipath(struct gendisk *disk)
+{
+	return disk->fops == &scsi_mpath_ops;
+}
+
+static inline bool scsi_mpath_enabled(struct scsi_device *sdev)
+{
+	return IS_ENABLED(CONFIG_SCSI_MULTIPATH);
+}
+static inline bool scsi_is_sdev_multipath(struct scsi_device *sdev)
+{
+	return IS_ENABLED(CONFIG_SCSI_MULTIPATH) && sdev->mpath_disk;
+}
+#else
+#define scsi_multipath	false;
+static inline bool scsi_disk_is_multipath(struct gendisk *disk)
+{
+	return false;
+}
+static inline bool scsi_mpath_enabled(struct scsi_device *sdev)
+{
+	return false;
+}
+static inline bool scsi_is_sdev_multipath(struct scsi_device *sdev)
+{
+	return false;
+}
+#endif
 /*
  * like scmd_printk, but the device name is passed in
  * as a string pointer
diff --git a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
index 2b4ab0369ffb..d20def053254 100644
--- a/include/scsi/scsi_host.h
+++ b/include/scsi/scsi_host.h
@@ -571,6 +571,12 @@ struct Scsi_Host {
 	/* Area to keep a shared tag map */
 	struct blk_mq_tag_set	tag_set;
 
+#ifdef	CONFIG_SCSI_MULTIPATH
+	struct scsi_mpath	*mpath_dev;
+	struct list_head	mpath_sdev;
+	int			mpath_alua_grpid; /* Grounp ID for ALUA devices */
+#endif
+
 	atomic_t host_blocked;
 
 	unsigned int host_failed;	   /* commands that failed.
@@ -761,6 +767,7 @@ static inline int scsi_host_in_recovery(struct Scsi_Host *shost)
 		shost->tmf_in_progress;
 }
 
+
 extern int scsi_queue_work(struct Scsi_Host *, struct work_struct *);
 extern void scsi_flush_work(struct Scsi_Host *);
 
diff --git a/include/scsi/scsi_multipath.h b/include/scsi/scsi_multipath.h
new file mode 100644
index 000000000000..b441241c8316
--- /dev/null
+++ b/include/scsi/scsi_multipath.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _SCSI_SCSI_MULTIPATH_H
+#define _SCSI_SCSI_MULTIPATH_H
+
+#include <linux/list.h>
+#include <linux/types.h>
+#include <linux/rcupdate.h>
+#include <linux/workqueue.h>
+#include <linux/mutex.h>
+#include <linux/blk-mq.h>
+#include <scsi/scsi.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_host.h>
+
+struct scsi_device;
+
+enum scsi_mpath_iopolicy {
+	SCSI_MPATH_IOPOLICY_NUMA,
+	SCSI_MPATH_IOPOLICY_RR,
+};
+
+enum scsi_mpath_access_state {
+	SCSI_MPATH_OPTIMAL	= SCSI_ACCESS_STATE_OPTIMAL,
+	SCSI_MPATH_ACTIVE	= SCSI_ACCESS_STATE_ACTIVE,
+	SCSI_MPATH_STANDBY	= SCSI_ACCESS_STATE_STANDBY,
+	SCSI_MPATH_UNAVAILABLE	= SCSI_ACCESS_STATE_UNAVAILABLE,
+	SCSI_MPATH_LBA		= SCSI_ACCESS_STATE_LBA,
+	SCSI_MPATH_OFFLINE	= SCSI_ACCESS_STATE_OFFLINE,
+	SCSI_MPATH_TRANSITIONING = SCSI_ACCESS_STATE_TRANSITIONING,
+	SCSI_MPATH_INVALID	= 0xFF
+};
+
+struct scsi_mpath_dh_data {
+	const char	*hndlr_name; /* device Handler name */
+	int	group_id;		/* Group ID reported from RTPG cmd */
+	int	tpgs;			/* Target Port Groups reported from RTPG cmd */
+	int	state;			/* Target Port Group State */
+	char	*device_id_str;		/* Multipath Device String */
+	int	device_id_len;		/* Device ID Length */
+	int	valid_states;		/* states from RTPG cmd */
+	int	prefrence;		/* Path prefrence for Port Group from RTPG cmd */
+	int	is_active;		/* Current Sdev is active */
+};
+
+struct scsi_mpath {
+	struct srcu_struct 	srcu;
+	struct Scsi_Host	*shost;	/*Scsi_Host where this mpath belong */
+	struct list_head        mpath_list;  /* list of multipath scsi_device   */
+	struct	bio_list	mpath_requeue_list; /* list for requeing bio */
+	spinlock_t		mpath_requeue_lock;
+	struct work_struct	mpath_requeue_work; /* work struct for requeue */
+	struct mutex            mpath_lock;
+	unsigned long		mpath_start_time;
+	struct delayed_work	activate_mpath; /* Path Activation work */
+	struct scsi_device __rcu *current_path[]; /* scsi_device of current path */
+};
+
+extern void scsi_mpath_default_iopolicy(struct scsi_device *);
+extern void scsi_mpath_unfreeze(struct Scsi_Host *);
+extern void scsi_mpath_wait_freeze(struct Scsi_Host *);
+extern void scsi_mpath_start_freeze(struct Scsi_Host *);
+extern void scsi_mpath_failover_req(struct request *);
+extern void scsi_mpath_start_request(struct request *);
+extern void scsi_mpath_end_request(struct request *);
+extern void scsi_kick_requeue_lists(struct Scsi_Host *);
+extern bool scsi_mpath_clear_current_path(struct scsi_device *);
+int scsi_multipath_init(struct scsi_device *);
+extern int scsi_mpath_failover_disposition(struct scsi_cmnd *);
+int scsi_mpath_alloc_disk(struct scsi_device *);
+extern void scsi_mpath_remove_disk(struct scsi_device *);
+extern void scsi_mpath_shutdown_disk(struct scsi_device *);
+void scsi_put_mpath_sdev(struct scsi_device *);
+void scsi_mpath_requeue_work(struct work_struct *);
+extern void scsi_mpath_dev_release(struct scsi_device *);
+void scsi_mpath_kick_requeue_lists(struct Scsi_Host *);
+int scsi_mpath_update_state(struct scsi_device *);
+extern int scsi_mpath_add_disk(struct scsi_device *);
+void scsi_mpath_set_live(struct scsi_device *);
+void scsi_activate_path(struct scsi_device *);
+void scsi_multipath_iopolicy_update(struct scsi_device *, int);
+void scsi_mpath_clear_paths(struct Scsi_Host *);
+int scsi_mpath_unique_lun_id(struct scsi_device *);
+
+extern void scsi_mpath_revalidate_path(struct gendisk *, sector_t);
+extern int scsi_mpath_unique_id(struct scsi_device *sdev, u8 id[16], enum blk_unique_id type);
+#endif /* _SCSI_SCSI_MULTIPATH_H */
-- 
2.41.0.rc2


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

* [RFC v1 2/8] scsi: create multipath capable scsi host
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 1/8] scsi: Add multipath device support himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-10 21:11   ` Bart Van Assche
  2024-11-09  4:45 ` [RFC v1 3/8] scsi: Add error handling capability for multipath himanshu.madhani
                   ` (7 subsequent siblings)
  9 siblings, 1 reply; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

- Create multipath capable scsi host

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/hosts.c | 12 ++++++++++++
 1 file changed, 12 insertions(+)

diff --git a/drivers/scsi/hosts.c b/drivers/scsi/hosts.c
index e021f1106bea..3cedb2a9af7b 100644
--- a/drivers/scsi/hosts.c
+++ b/drivers/scsi/hosts.c
@@ -39,6 +39,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_cmnd.h>
+#include <scsi/scsi_multipath.h>
 
 #include "scsi_priv.h"
 #include "scsi_logging.h"
@@ -394,6 +395,14 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
 	struct Scsi_Host *shost;
 	int index;
 
+#ifdef CONFIG_SCSI_MULTIPATH
+	struct scsi_mpath *mpath_dev;
+	size_t	size = sizeof(*mpath_dev);
+
+	size += num_possible_nodes() * sizeof(struct mpath_dev *);
+	privsize = privsize + size;
+#endif
+
 	shost = kzalloc(sizeof(struct Scsi_Host) + privsize, GFP_KERNEL);
 	if (!shost)
 		return NULL;
@@ -409,6 +418,9 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
 	init_waitqueue_head(&shost->host_wait);
 	mutex_init(&shost->scan_mutex);
 
+#ifdef CONFIG_SCSI_MULTIPATH
+	INIT_LIST_HEAD(&shost->mpath_sdev);
+#endif
 	index = ida_alloc(&host_index_ida, GFP_KERNEL);
 	if (index < 0) {
 		kfree(shost);
-- 
2.41.0.rc2


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

* [RFC v1 3/8] scsi: Add error handling capability for multipath
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 1/8] scsi: Add multipath device support himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 2/8] scsi: create multipath capable scsi host himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 4/8] scsi: Complete multipath request himanshu.madhani
                   ` (6 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

For multipath capable devices call scsi_mpath_failover_disposition() to
kick off failover to another path. This will call path selector
algorithm to pick active path for the failover.

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/scsi_error.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c
index 612489afe8d2..d5d1b20928a6 100644
--- a/drivers/scsi/scsi_error.c
+++ b/drivers/scsi/scsi_error.c
@@ -40,6 +40,7 @@
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsi_dh.h>
 #include <scsi/scsi_devinfo.h>
+#include <scsi/scsi_multipath.h>
 #include <scsi/sg.h>
 
 #include "scsi_priv.h"
@@ -2047,6 +2048,13 @@ enum scsi_disposition scsi_decide_disposition(struct scsi_cmnd *scmd)
 
 maybe_retry:
 
+	/*
+	 * For SCSI Multipath check if there are path errors to
+	 * trigger failover to available path
+	 */
+	if (scsi_mpath_enabled(scmd->device))
+		return scsi_mpath_failover_disposition(scmd);
+
 	/* we requeue for retry because the error was retryable, and
 	 * the request was not marked fast fail.  Note that above,
 	 * even if the request is marked fast fail, we still requeue
-- 
2.41.0.rc2


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

* [RFC v1 4/8] scsi: Complete multipath request
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (2 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 3/8] scsi: Add error handling capability for multipath himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 5/8] scsi: Add scsi multipath sysfs hooks himanshu.madhani
                   ` (5 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

Add check for multipath reqeust when scsi_complete is called.
For error handling case, call scsi_mpath_failover_req() to
complete the multipath IO.

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/scsi_lib.c | 25 +++++++++++++++++++++++++
 include/scsi/scsi.h     |  1 +
 2 files changed, 26 insertions(+)

diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c
index 0561b318dade..1c8113abc154 100644
--- a/drivers/scsi/scsi_lib.c
+++ b/drivers/scsi/scsi_lib.c
@@ -33,6 +33,7 @@
 #include <scsi/scsi_eh.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h> /* scsi_init_limits() */
+#include <scsi/scsi_multipath.h>
 #include <scsi/scsi_dh.h>
 
 #include <trace/events/scsi.h>
@@ -620,6 +621,14 @@ static void scsi_run_queue_async(struct scsi_device *sdev)
 	}
 }
 
+static inline void __scsi_mpath_end_request(struct request *req,
+    blk_status_t status)
+{
+	if (req->cmd_flags & REQ_SCSI_MPATH)
+		scsi_mpath_end_request(req);
+	blk_mq_end_request(req, status);
+}
+
 /* Returns false when no more bytes to process, true if there are more */
 static bool scsi_end_request(struct request *req, blk_status_t error,
 		unsigned int bytes)
@@ -661,6 +670,9 @@ static bool scsi_end_request(struct request *req, blk_status_t error,
 	 */
 	percpu_ref_get(&q->q_usage_counter);
 
+	if (req->cmd_flags & REQ_SCSI_MPATH)
+		scsi_mpath_end_request(req);
+
 	__blk_mq_end_request(req, error);
 
 	scsi_run_queue_async(sdev);
@@ -1528,6 +1540,9 @@ static void scsi_complete(struct request *rq)
 	case ADD_TO_MLQUEUE:
 		scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
 		break;
+	case FAILOVER:
+		scsi_mpath_failover_req(rq);
+		break;
 	default:
 		scsi_eh_scmd_add(cmd);
 		break;
@@ -1840,6 +1855,9 @@ static blk_status_t scsi_queue_rq(struct blk_mq_hw_ctx *hctx,
 	memset(cmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE);
 	cmd->submitter = SUBMITTED_BY_BLOCK_LAYER;
 
+	if (req->cmd_flags & REQ_SCSI_MPATH)
+		scsi_mpath_start_request(req);
+
 	blk_mq_start_request(req);
 	reason = scsi_dispatch_cmd(cmd);
 	if (reason) {
@@ -2811,6 +2829,9 @@ EXPORT_SYMBOL(scsi_target_resume);
 
 static int __scsi_internal_device_block_nowait(struct scsi_device *sdev)
 {
+	if (scsi_mpath_enabled(sdev))
+		scsi_mpath_clear_current_path(sdev);
+
 	if (scsi_device_set_state(sdev, SDEV_BLOCK))
 		return scsi_device_set_state(sdev, SDEV_CREATED_BLOCK);
 
@@ -2927,6 +2948,10 @@ int scsi_internal_device_unblock_nowait(struct scsi_device *sdev,
 		return -EINVAL;
 	}
 
+	/* For multipath device set the path live */
+	if (scsi_mpath_enabled(sdev))
+		scsi_mpath_set_live(sdev);
+
 	/*
 	 * Try to transition the scsi device to SDEV_RUNNING or one of the
 	 * offlined states and goose the device queue if successful.
diff --git a/include/scsi/scsi.h b/include/scsi/scsi.h
index 96b350366670..544153a01b3f 100644
--- a/include/scsi/scsi.h
+++ b/include/scsi/scsi.h
@@ -103,6 +103,7 @@ enum scsi_disposition {
 	TIMEOUT_ERROR		= 0x2007,
 	SCSI_RETURN_NOT_HANDLED	= 0x2008,
 	FAST_IO_FAIL		= 0x2009,
+	FAILOVER		= 0x2010,
 };
 
 /*
-- 
2.41.0.rc2


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

* [RFC v1 5/8] scsi: Add scsi multipath sysfs hooks
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (3 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 4/8] scsi: Complete multipath request himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-09  4:45 ` [RFC v1 6/8] scsi: Add multipath suppport for device handler himanshu.madhani
                   ` (4 subsequent siblings)
  9 siblings, 0 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

Add Sysfs hook to
- Show current multipath state
- Show and update multipath iopolicy

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/scsi_sysfs.c | 104 ++++++++++++++++++++++++++++++++++++++
 1 file changed, 104 insertions(+)

diff --git a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
index 32f94db6d6bf..cc7dc5c30d2c 100644
--- a/drivers/scsi/scsi_sysfs.c
+++ b/drivers/scsi/scsi_sysfs.c
@@ -1198,6 +1198,103 @@ sdev_show_preferred_path(struct device *dev,
 static DEVICE_ATTR(preferred_path, S_IRUGO, sdev_show_preferred_path, NULL);
 #endif
 
+#ifdef CONFIG_SCSI_MULTIPATH
+static const struct {
+	unsigned char	value;
+	char		*name;
+} scsi_multipath_iopolicy[] = {
+	{ SCSI_MPATH_IOPOLICY_NUMA, "NUMA" },
+	{ SCSI_MPATH_IOPOLICY_RR, "Round-Robin" },
+};
+static const char *scsi_mpath_policy_name(unsigned char policy)
+{
+	int i;
+	char *name = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(scsi_multipath_iopolicy); i++) {
+		if (scsi_multipath_iopolicy[i].value == policy) {
+			name = scsi_multipath_iopolicy[i].name;
+			break;
+		}
+	}
+	return name;
+}
+
+static ssize_t
+sdev_show_multipath_iopolicy(struct device *dev,
+			     struct device_attribute *attr,
+			     char *buf)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	const char *name = scsi_mpath_policy_name(sdev->mpath_iopolicy);
+
+	if (!sdev->mpath_disk)
+		return -EINVAL;
+
+	return sysfs_emit(buf, "%s\n", name);
+}
+
+static ssize_t sdev_store_multipath_iopolicy(struct device *dev,
+    struct device_attribute *attr, const char *buf, size_t count)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(scsi_multipath_iopolicy); i++) {
+		if (sysfs_streq(buf, scsi_mpath_policy_name(i))) {
+			scsi_multipath_iopolicy_update(sdev, i);
+			return count;
+		}
+	}
+
+	return -EINVAL;
+}
+static DEVICE_ATTR(multipath_iopolicy, S_IRUGO, sdev_show_multipath_iopolicy,
+    sdev_store_multipath_iopolicy);
+
+static const struct {
+	unsigned char	value;
+	char		*name;
+} scsi_mpath_states[] = {
+	{ SCSI_MPATH_OPTIMAL,	"active/optimized" },
+	{ SCSI_MPATH_ACTIVE,	"active/non-optimized" },
+	{ SCSI_MPATH_STANDBY,	"standby" },
+	{ SCSI_MPATH_UNAVAILABLE,"unavailable" },
+	{ SCSI_MPATH_LBA,	"lba-dependent" },
+	{ SCSI_MPATH_OFFLINE,	"offline" },
+	{ SCSI_MPATH_TRANSITIONING,"transitioning" },
+};
+
+static const char *scsi_mpath_state_names(unsigned char state)
+{
+	int i;
+	char *name = NULL;
+
+	for (i = 0; i < ARRAY_SIZE(scsi_mpath_states); i++) {
+		if (scsi_mpath_states[i].value == state) {
+		    name = scsi_mpath_states[i].name;
+		    break;
+		}
+	}
+	return name;
+}
+
+static ssize_t
+sdev_show_multipath_state(struct device *dev,
+			  struct device_attribute *attr,
+			  char *buf)
+{
+	struct scsi_device *sdev = to_scsi_device(dev);
+	const char *name = scsi_mpath_state_names(sdev->mpath_state);
+
+	if (!sdev->mpath_disk)
+		return -EINVAL;
+
+	return sysfs_emit(buf, "%s\n", name);
+}
+static DEVICE_ATTR(multipath_state, S_IRUGO, sdev_show_multipath_state, NULL);
+#endif
+
 static ssize_t
 sdev_show_queue_ramp_up_period(struct device *dev,
 			       struct device_attribute *attr,
@@ -1335,6 +1432,10 @@ static struct attribute *scsi_sdev_attrs[] = {
 	&dev_attr_dh_state.attr,
 	&dev_attr_access_state.attr,
 	&dev_attr_preferred_path.attr,
+#endif
+#ifdef CONFIG_SCSI_MULTIPATH
+	&dev_attr_multipath_iopolicy.attr,
+	&dev_attr_multipath_state.attr,
 #endif
 	&dev_attr_queue_ramp_up_period.attr,
 	&dev_attr_cdl_supported.attr,
@@ -1500,6 +1601,9 @@ void __scsi_remove_device(struct scsi_device *sdev)
 	} else
 		put_device(&sdev->sdev_dev);
 
+	if (scsi_is_sdev_multipath(sdev))
+		scsi_mpath_dev_release(sdev);
+
 	/*
 	 * Stop accepting new requests and wait until all queuecommand() and
 	 * scsi_run_queue() invocations have finished before tearing down the
-- 
2.41.0.rc2


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

* [RFC v1 6/8] scsi: Add multipath suppport for device handler
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (4 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 5/8] scsi: Add scsi multipath sysfs hooks himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-09  7:29   ` kernel test robot
  2024-11-09  8:10   ` kernel test robot
  2024-11-09  4:45 ` [RFC v1 7/8] scsi: Add multipath disk init code for sd driver himanshu.madhani
                   ` (3 subsequent siblings)
  9 siblings, 2 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

Add multipath initialization during handler attachemnet for DH.
Also initialize multipath port group data for scsi_device.

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/device_handler/scsi_dh_alua.c | 15 +++++++++++++++
 drivers/scsi/scsi_dh.c                     |  3 +++
 2 files changed, 18 insertions(+)

diff --git a/drivers/scsi/device_handler/scsi_dh_alua.c b/drivers/scsi/device_handler/scsi_dh_alua.c
index 4eb0837298d4..29bd6517a2e3 100644
--- a/drivers/scsi/device_handler/scsi_dh_alua.c
+++ b/drivers/scsi/device_handler/scsi_dh_alua.c
@@ -258,6 +258,21 @@ static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
 		return tmp_pg;
 	}
 
+	if (scsi_mpath_enabled(sdev)) {
+		struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
+
+		dh_data->group_id = pg->group_id;
+		dh_data->tpgs = pg->tpgs;
+		dh_data->state = pg->state;
+		dh_data->valid_states = pg->valid_states;
+		dh_data->prefrence = pg->pref;
+		dh_data->is_active = 1;
+		dh_data->device_id_str = kstrdup(pg->device_id_str, GFP_KERNEL);
+		dh_data->device_id_len = pg->device_id_len;
+
+		sdev->host->mpath_alua_grpid = pg->group_id;
+	}
+
 	list_add(&pg->node, &port_group_list);
 	spin_unlock(&port_group_lock);
 
diff --git a/drivers/scsi/scsi_dh.c b/drivers/scsi/scsi_dh.c
index 7b56e00c7df6..d61eddc3c1f8 100644
--- a/drivers/scsi/scsi_dh.c
+++ b/drivers/scsi/scsi_dh.c
@@ -129,6 +129,9 @@ static int scsi_dh_handler_attach(struct scsi_device *sdev,
 	if (!try_module_get(scsi_dh->module))
 		return -EINVAL;
 
+	if (scsi_mpath_enabled(sdev))
+		scsi_multipath_init(sdev);
+
 	error = scsi_dh->attach(sdev);
 	if (error != SCSI_DH_OK) {
 		switch (error) {
-- 
2.41.0.rc2


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

* [RFC v1 7/8] scsi: Add multipath disk init code for sd driver
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (5 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 6/8] scsi: Add multipath suppport for device handler himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-09  8:51   ` kernel test robot
  2024-11-09  4:45 ` [RFC v1 8/8] scsi_debug: Add module parameter for ALUA multipath himanshu.madhani
                   ` (2 subsequent siblings)
  9 siblings, 1 reply; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

This patch adds allocation and initialization code to
scsi disk driver.

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/sd.c | 83 +++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 83 insertions(+)

diff --git a/drivers/scsi/sd.c b/drivers/scsi/sd.c
index 41e2dfa2d67d..b4727b599794 100644
--- a/drivers/scsi/sd.c
+++ b/drivers/scsi/sd.c
@@ -1483,6 +1483,9 @@ static void sd_uninit_command(struct scsi_cmnd *SCpnt)
 
 static bool sd_need_revalidate(struct gendisk *disk, struct scsi_disk *sdkp)
 {
+	if (scsi_is_sdev_multipath(sdkp->device))
+		return true;
+
 	if (sdkp->device->removable || sdkp->write_prot) {
 		if (disk_check_media_change(disk))
 			return true;
@@ -1892,6 +1895,10 @@ static int sd_get_unique_id(struct gendisk *disk, u8 id[16],
 		if (len == 16)
 			break;
 	}
+
+	if (scsi_mpath_enabled(sdev))
+		ret = scsi_mpath_unique_id(sdev, id, type);
+
 out_unlock:
 	rcu_read_unlock();
 	return ret;
@@ -3817,6 +3824,33 @@ static int sd_revalidate_disk(struct gendisk *disk)
 	if (sdkp->media_present && scsi_device_supports_vpd(sdp))
 		sd_read_cpr(sdkp);
 
+	/* for multipath device, Adjust queue limits for MPATH disk */
+	if (scsi_is_sdev_multipath(sdp)) {
+		struct queue_limits *mpath_lim = &sdp->mpath_disk->queue->limits;
+
+		blk_mq_freeze_queue(sdp->mpath_disk->queue);
+		lim = queue_limits_start_update(sdp->mpath_disk->queue);
+		lim.logical_block_size = mpath_lim->logical_block_size;
+		lim.physical_block_size = mpath_lim->physical_block_size;
+		lim.io_min = mpath_lim->io_min;
+		lim.io_opt = mpath_lim->io_opt;
+		queue_limits_stack_bdev(&lim, sdp->mpath_disk->part0, 0,
+		    sdp->mpath_disk->disk_name);
+
+		sdp->mpath_disk->flags |= GENHD_FL_HIDDEN;
+
+		set_capacity_and_notify(sdp->mpath_disk,
+		    logical_to_sectors(sdp, sdkp->capacity));
+
+		err = queue_limits_commit_update(sdp->mpath_disk->queue, &lim);
+
+		scsi_mpath_revalidate_path(sdp->mpath_disk,
+		    logical_to_sectors(sdp, sdkp->capacity));
+
+		blk_mq_unfreeze_queue(sdp->mpath_disk->queue);
+		if (err)
+			return err;
+	}
 	/*
 	 * For a zoned drive, revalidating the zones can be done only once
 	 * the gendisk capacity is set. So if this fails, set back the gendisk
@@ -3943,6 +3977,9 @@ static int sd_probe(struct device *dev)
 	if (!sdkp)
 		goto out;
 
+	if (scsi_mpath_enabled(sdp) && sdp->is_shared)
+		scsi_mpath_alloc_disk(sdp);
+
 	gd = blk_mq_alloc_disk_for_queue(sdp->request_queue,
 					 &sd_bio_compl_lkclass);
 	if (!gd)
@@ -3960,6 +3997,10 @@ static int sd_probe(struct device *dev)
 		goto out_free_index;
 	}
 
+	if (scsi_is_sdev_multipath(sdp))
+		snprintf(sdp->mpath_disk->disk_name, DISK_NAME_LEN, "mpath%dsd%d",
+		    sdp->host->host_no, index);
+
 	sdkp->device = sdp;
 	sdkp->disk = gd;
 	sdkp->index = index;
@@ -4021,6 +4062,21 @@ static int sd_probe(struct device *dev)
 			sdp->host->rpm_autosuspend_delay);
 	}
 
+	if (scsi_is_sdev_multipath(sdp)) {
+		sdp->mpath_disk->major = sd_major((index & 0xf0) >> 4);
+		sdp->mpath_disk->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
+		sdp->mpath_disk->minors = SD_MINORS;
+
+		scsi_mpath_add_disk(sdp);
+
+		if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
+			device_unregister(&sdkp->disk_dev);
+			clear_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags);
+			put_disk(sdp->mpath_disk);
+			goto out;
+		}
+	}
+
 	error = device_add_disk(dev, gd, NULL);
 	if (error) {
 		device_unregister(&sdkp->disk_dev);
@@ -4074,12 +4130,20 @@ static int sd_remove(struct device *dev)
 		sd_shutdown(dev);
 
 	put_disk(sdkp->disk);
+
+	if (scsi_is_sdev_multipath(sdkp->device))
+		scsi_mpath_remove_disk(sdkp->device);
+
 	return 0;
 }
 
 static void scsi_disk_release(struct device *dev)
 {
 	struct scsi_disk *sdkp = to_scsi_disk(dev);
+	struct scsi_device *sdp = to_scsi_device(dev);
+
+	if (scsi_is_sdev_multipath(sdp))
+		scsi_mpath_dev_release(sdp);
 
 	ida_free(&sd_index_ida, sdkp->index);
 	put_device(&sdkp->device->sdev_gendev);
@@ -4171,6 +4235,25 @@ static void sd_shutdown(struct device *dev)
 	if (pm_runtime_suspended(dev))
 		return;
 
+	if (scsi_is_sdev_multipath(sdkp->device)) {
+		struct scsi_device *sdp = sdkp->device;
+		bool last_path = false;
+
+		if (scsi_mpath_clear_current_path(sdp))
+			synchronize_srcu(&sdp->host->mpath_dev->srcu);
+
+		mutex_lock(&sdp->host->mpath_dev->mpath_lock);
+		list_del_rcu(&sdp->siblings);
+		if (list_empty(&sdp->host->mpath_sdev)) {
+			list_del_init(&sdp->mpath_entry);
+			last_path = true;
+		}
+		mutex_unlock(&sdp->host->mpath_dev->mpath_lock);
+
+		if (last_path)
+			scsi_mpath_shutdown_disk(sdp);
+	}
+
 	if (sdkp->WCE && sdkp->media_present) {
 		sd_printk(KERN_NOTICE, sdkp, "Synchronizing SCSI cache\n");
 		sd_sync_cache(sdkp);
-- 
2.41.0.rc2


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

* [RFC v1 8/8] scsi_debug: Add module parameter for ALUA multipath
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (6 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 7/8] scsi: Add multipath disk init code for sd driver himanshu.madhani
@ 2024-11-09  4:45 ` himanshu.madhani
  2024-11-10 21:15 ` [RFC v1 0/8] scsi: Multipath support for scsi disk devices Bart Van Assche
  2024-11-22 14:27 ` Hannes Reinecke
  9 siblings, 0 replies; 18+ messages in thread
From: himanshu.madhani @ 2024-11-09  4:45 UTC (permalink / raw)
  To: martin.petersen, linux-scsi

From: Himanshu Madhani <himanshu.madhani@oracle.com>

Signed-off-by: Himanshu Madhani <himanshu.madhani@oracle.com>
---
 drivers/scsi/scsi_debug.c | 16 ++++++++++++++--
 1 file changed, 14 insertions(+), 2 deletions(-)

diff --git a/drivers/scsi/scsi_debug.c b/drivers/scsi/scsi_debug.c
index 9be2a6a00530..811d3005c0a5 100644
--- a/drivers/scsi/scsi_debug.c
+++ b/drivers/scsi/scsi_debug.c
@@ -167,6 +167,7 @@ static const char *sdebug_version_date = "20210520";
 #define DEF_TUR_MS_TO_READY 0
 #define DEF_UUID_CTL 0
 #define JDELAY_OVERRIDDEN -9999
+#define DEF_ALUA_MPATH	0
 
 /* Default parameters for ZBC drives */
 #define DEF_ZBC_ZONE_SIZE_MB	128
@@ -884,6 +885,8 @@ static bool write_since_sync;
 static bool sdebug_statistics = DEF_STATISTICS;
 static bool sdebug_wp;
 static bool sdebug_allow_restart;
+static unsigned int sdebug_alua_mpath = DEF_ALUA_MPATH;
+
 static enum {
 	BLK_ZONED_NONE	= 0,
 	BLK_ZONED_HA	= 1,
@@ -2070,8 +2073,14 @@ static int resp_inquiry(struct scsi_cmnd *scp, struct sdebug_dev_info *devip)
 	arr[3] = 2;    /* response_data_format==2 */
 	arr[4] = SDEBUG_LONG_INQ_SZ - 5;
 	arr[5] = (int)have_dif_prot;	/* PROTECT bit */
-	if (sdebug_vpd_use_hostno == 0)
-		arr[5] |= 0x10; /* claim: implicit TPGS */
+	if (sdebug_vpd_use_hostno == 0) {
+		 arr[5] |= 0x10;
+	} else {
+		if (sdebug_alua_mpath == 1)
+			arr[5] |= 0x11;
+		else
+			arr[5] |= 0x10;
+	}
 	arr[6] = 0x10; /* claim: MultiP */
 	/* arr[6] |= 0x40; ... claim: EncServ (enclosure services) */
 	arr[7] = 0xa; /* claim: LINKED + CMDQUE */
@@ -6643,6 +6652,7 @@ module_param_named(zone_max_open, sdeb_zbc_max_open, int, S_IRUGO);
 module_param_named(zone_nr_conv, sdeb_zbc_nr_conv, int, S_IRUGO);
 module_param_named(zone_size_mb, sdeb_zbc_zone_size_mb, int, S_IRUGO);
 module_param_named(allow_restart, sdebug_allow_restart, bool, S_IRUGO | S_IWUSR);
+module_param_named(alua_mpath, sdebug_alua_mpath, int, S_IRUGO | S_IWUSR);
 
 MODULE_AUTHOR("Eric Youngdale + Douglas Gilbert");
 MODULE_DESCRIPTION("SCSI debug adapter driver");
@@ -6722,6 +6732,8 @@ MODULE_PARM_DESC(zone_max_open, "Maximum number of open zones; [0] for no limit
 MODULE_PARM_DESC(zone_nr_conv, "Number of conventional zones (def=1)");
 MODULE_PARM_DESC(zone_size_mb, "Zone size in MiB (def=auto)");
 MODULE_PARM_DESC(allow_restart, "Set scsi_device's allow_restart flag(def=0)");
+MODULE_PARM_DESC(alua_mpath,
+	"\t 1 = implicit alua \n \t 2 = Explicit & Implicit ALUA, \n \t 0 = No ALUA Support (Default) \n");
 
 #define SDEBUG_INFO_LEN 256
 static char sdebug_info[SDEBUG_INFO_LEN];
-- 
2.41.0.rc2


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

* Re: [RFC v1 6/8] scsi: Add multipath suppport for device handler
  2024-11-09  4:45 ` [RFC v1 6/8] scsi: Add multipath suppport for device handler himanshu.madhani
@ 2024-11-09  7:29   ` kernel test robot
  2024-11-09  8:10   ` kernel test robot
  1 sibling, 0 replies; 18+ messages in thread
From: kernel test robot @ 2024-11-09  7:29 UTC (permalink / raw)
  To: himanshu.madhani; +Cc: oe-kbuild-all

Hi,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 128faa1845a2d5b0178b986f3bd18fb38cc08cc2]

url:    https://github.com/intel-lab-lkp/linux/commits/himanshu-madhani-oracle-com/scsi-Add-multipath-device-support/20241109-124908
base:   128faa1845a2d5b0178b986f3bd18fb38cc08cc2
patch link:    https://lore.kernel.org/r/20241109044529.992935-7-himanshu.madhani%40oracle.com
patch subject: [RFC v1 6/8] scsi: Add multipath suppport for device handler
config: x86_64-rhel-8.3 (https://download.01.org/0day-ci/archive/20241109/202411091556.VY6C3maf-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241109/202411091556.VY6C3maf-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411091556.VY6C3maf-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/scsi/device_handler/scsi_dh_alua.c: In function 'alua_alloc_pg':
>> drivers/scsi/device_handler/scsi_dh_alua.c:262:58: error: 'struct scsi_device' has no member named 'mpath_pg_data'
     262 |                 struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
         |                                                          ^~
>> drivers/scsi/device_handler/scsi_dh_alua.c:273:27: error: 'struct Scsi_Host' has no member named 'mpath_alua_grpid'
     273 |                 sdev->host->mpath_alua_grpid = pg->group_id;
         |                           ^~


vim +262 drivers/scsi/device_handler/scsi_dh_alua.c

   207	
   208	/*
   209	 * alua_alloc_pg - Allocate a new port_group structure
   210	 * @sdev: scsi device
   211	 * @group_id: port group id
   212	 * @tpgs: target port group settings
   213	 *
   214	 * Allocate a new port_group structure for a given
   215	 * device.
   216	 */
   217	static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
   218						     int group_id, int tpgs)
   219	{
   220		struct alua_port_group *pg, *tmp_pg;
   221	
   222		pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL);
   223		if (!pg)
   224			return ERR_PTR(-ENOMEM);
   225	
   226		pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str,
   227						    sizeof(pg->device_id_str));
   228		if (pg->device_id_len <= 0) {
   229			/*
   230			 * TPGS supported but no device identification found.
   231			 * Generate private device identification.
   232			 */
   233			sdev_printk(KERN_INFO, sdev,
   234				    "%s: No device descriptors found\n",
   235				    ALUA_DH_NAME);
   236			pg->device_id_str[0] = '\0';
   237			pg->device_id_len = 0;
   238		}
   239		pg->group_id = group_id;
   240		pg->tpgs = tpgs;
   241		pg->state = SCSI_ACCESS_STATE_OPTIMAL;
   242		pg->valid_states = TPGS_SUPPORT_ALL;
   243		if (optimize_stpg)
   244			pg->flags |= ALUA_OPTIMIZE_STPG;
   245		kref_init(&pg->kref);
   246		INIT_DELAYED_WORK(&pg->rtpg_work, alua_rtpg_work);
   247		INIT_LIST_HEAD(&pg->rtpg_list);
   248		INIT_LIST_HEAD(&pg->node);
   249		INIT_LIST_HEAD(&pg->dh_list);
   250		spin_lock_init(&pg->lock);
   251	
   252		spin_lock(&port_group_lock);
   253		tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len,
   254					  group_id);
   255		if (tmp_pg) {
   256			spin_unlock(&port_group_lock);
   257			kfree(pg);
   258			return tmp_pg;
   259		}
   260	
   261		if (scsi_mpath_enabled(sdev)) {
 > 262			struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
   263	
   264			dh_data->group_id = pg->group_id;
   265			dh_data->tpgs = pg->tpgs;
   266			dh_data->state = pg->state;
   267			dh_data->valid_states = pg->valid_states;
   268			dh_data->prefrence = pg->pref;
   269			dh_data->is_active = 1;
   270			dh_data->device_id_str = kstrdup(pg->device_id_str, GFP_KERNEL);
   271			dh_data->device_id_len = pg->device_id_len;
   272	
 > 273			sdev->host->mpath_alua_grpid = pg->group_id;
   274		}
   275	
   276		list_add(&pg->node, &port_group_list);
   277		spin_unlock(&port_group_lock);
   278	
   279		return pg;
   280	}
   281	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [RFC v1 6/8] scsi: Add multipath suppport for device handler
  2024-11-09  4:45 ` [RFC v1 6/8] scsi: Add multipath suppport for device handler himanshu.madhani
  2024-11-09  7:29   ` kernel test robot
@ 2024-11-09  8:10   ` kernel test robot
  1 sibling, 0 replies; 18+ messages in thread
From: kernel test robot @ 2024-11-09  8:10 UTC (permalink / raw)
  To: himanshu.madhani; +Cc: llvm, oe-kbuild-all

Hi,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 128faa1845a2d5b0178b986f3bd18fb38cc08cc2]

url:    https://github.com/intel-lab-lkp/linux/commits/himanshu-madhani-oracle-com/scsi-Add-multipath-device-support/20241109-124908
base:   128faa1845a2d5b0178b986f3bd18fb38cc08cc2
patch link:    https://lore.kernel.org/r/20241109044529.992935-7-himanshu.madhani%40oracle.com
patch subject: [RFC v1 6/8] scsi: Add multipath suppport for device handler
config: x86_64-kexec (https://download.01.org/0day-ci/archive/20241109/202411091541.tWU8XhvE-lkp@intel.com/config)
compiler: clang version 19.1.3 (https://github.com/llvm/llvm-project ab51eccf88f5321e7c60591c5546b254b6afab99)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241109/202411091541.tWU8XhvE-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411091541.tWU8XhvE-lkp@intel.com/

All errors (new ones prefixed by >>):

   In file included from drivers/scsi/device_handler/scsi_dh_alua.c:15:
   In file included from include/scsi/scsi_eh.h:5:
   In file included from include/linux/scatterlist.h:8:
   In file included from include/linux/mm.h:2213:
   include/linux/vmstat.h:504:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     504 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     505 |                            item];
         |                            ~~~~
   include/linux/vmstat.h:511:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     511 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     512 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
   include/linux/vmstat.h:518:36: warning: arithmetic between different enumeration types ('enum node_stat_item' and 'enum lru_list') [-Wenum-enum-conversion]
     518 |         return node_stat_name(NR_LRU_BASE + lru) + 3; // skip "nr_"
         |                               ~~~~~~~~~~~ ^ ~~~
   include/linux/vmstat.h:524:43: warning: arithmetic between different enumeration types ('enum zone_stat_item' and 'enum numa_stat_item') [-Wenum-enum-conversion]
     524 |         return vmstat_text[NR_VM_ZONE_STAT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~ ^
     525 |                            NR_VM_NUMA_EVENT_ITEMS +
         |                            ~~~~~~~~~~~~~~~~~~~~~~
>> drivers/scsi/device_handler/scsi_dh_alua.c:262:46: error: no member named 'mpath_pg_data' in 'struct scsi_device'
     262 |                 struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
         |                                                      ~~~~  ^
>> drivers/scsi/device_handler/scsi_dh_alua.c:273:15: error: no member named 'mpath_alua_grpid' in 'struct Scsi_Host'
     273 |                 sdev->host->mpath_alua_grpid = pg->group_id;
         |                 ~~~~~~~~~~  ^
   4 warnings and 2 errors generated.


vim +262 drivers/scsi/device_handler/scsi_dh_alua.c

   207	
   208	/*
   209	 * alua_alloc_pg - Allocate a new port_group structure
   210	 * @sdev: scsi device
   211	 * @group_id: port group id
   212	 * @tpgs: target port group settings
   213	 *
   214	 * Allocate a new port_group structure for a given
   215	 * device.
   216	 */
   217	static struct alua_port_group *alua_alloc_pg(struct scsi_device *sdev,
   218						     int group_id, int tpgs)
   219	{
   220		struct alua_port_group *pg, *tmp_pg;
   221	
   222		pg = kzalloc(sizeof(struct alua_port_group), GFP_KERNEL);
   223		if (!pg)
   224			return ERR_PTR(-ENOMEM);
   225	
   226		pg->device_id_len = scsi_vpd_lun_id(sdev, pg->device_id_str,
   227						    sizeof(pg->device_id_str));
   228		if (pg->device_id_len <= 0) {
   229			/*
   230			 * TPGS supported but no device identification found.
   231			 * Generate private device identification.
   232			 */
   233			sdev_printk(KERN_INFO, sdev,
   234				    "%s: No device descriptors found\n",
   235				    ALUA_DH_NAME);
   236			pg->device_id_str[0] = '\0';
   237			pg->device_id_len = 0;
   238		}
   239		pg->group_id = group_id;
   240		pg->tpgs = tpgs;
   241		pg->state = SCSI_ACCESS_STATE_OPTIMAL;
   242		pg->valid_states = TPGS_SUPPORT_ALL;
   243		if (optimize_stpg)
   244			pg->flags |= ALUA_OPTIMIZE_STPG;
   245		kref_init(&pg->kref);
   246		INIT_DELAYED_WORK(&pg->rtpg_work, alua_rtpg_work);
   247		INIT_LIST_HEAD(&pg->rtpg_list);
   248		INIT_LIST_HEAD(&pg->node);
   249		INIT_LIST_HEAD(&pg->dh_list);
   250		spin_lock_init(&pg->lock);
   251	
   252		spin_lock(&port_group_lock);
   253		tmp_pg = alua_find_get_pg(pg->device_id_str, pg->device_id_len,
   254					  group_id);
   255		if (tmp_pg) {
   256			spin_unlock(&port_group_lock);
   257			kfree(pg);
   258			return tmp_pg;
   259		}
   260	
   261		if (scsi_mpath_enabled(sdev)) {
 > 262			struct scsi_mpath_dh_data *dh_data = sdev->mpath_pg_data;
   263	
   264			dh_data->group_id = pg->group_id;
   265			dh_data->tpgs = pg->tpgs;
   266			dh_data->state = pg->state;
   267			dh_data->valid_states = pg->valid_states;
   268			dh_data->prefrence = pg->pref;
   269			dh_data->is_active = 1;
   270			dh_data->device_id_str = kstrdup(pg->device_id_str, GFP_KERNEL);
   271			dh_data->device_id_len = pg->device_id_len;
   272	
 > 273			sdev->host->mpath_alua_grpid = pg->group_id;
   274		}
   275	
   276		list_add(&pg->node, &port_group_list);
   277		spin_unlock(&port_group_lock);
   278	
   279		return pg;
   280	}
   281	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [RFC v1 7/8] scsi: Add multipath disk init code for sd driver
  2024-11-09  4:45 ` [RFC v1 7/8] scsi: Add multipath disk init code for sd driver himanshu.madhani
@ 2024-11-09  8:51   ` kernel test robot
  0 siblings, 0 replies; 18+ messages in thread
From: kernel test robot @ 2024-11-09  8:51 UTC (permalink / raw)
  To: himanshu.madhani; +Cc: oe-kbuild-all

Hi,

[This is a private test report for your RFC patch.]
kernel test robot noticed the following build errors:

[auto build test ERROR on 128faa1845a2d5b0178b986f3bd18fb38cc08cc2]

url:    https://github.com/intel-lab-lkp/linux/commits/himanshu-madhani-oracle-com/scsi-Add-multipath-device-support/20241109-124908
base:   128faa1845a2d5b0178b986f3bd18fb38cc08cc2
patch link:    https://lore.kernel.org/r/20241109044529.992935-8-himanshu.madhani%40oracle.com
patch subject: [RFC v1 7/8] scsi: Add multipath disk init code for sd driver
config: x86_64-rhel-8.3 (https://download.01.org/0day-ci/archive/20241109/202411091606.uMaHJONG-lkp@intel.com/config)
compiler: gcc-12 (Debian 12.2.0-14) 12.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241109/202411091606.uMaHJONG-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411091606.uMaHJONG-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/scsi/sd.c: In function 'sd_revalidate_disk':
>> drivers/scsi/sd.c:3829:54: error: 'struct scsi_device' has no member named 'mpath_disk'
    3829 |                 struct queue_limits *mpath_lim = &sdp->mpath_disk->queue->limits;
         |                                                      ^~
   drivers/scsi/sd.c:3831:40: error: 'struct scsi_device' has no member named 'mpath_disk'
    3831 |                 blk_mq_freeze_queue(sdp->mpath_disk->queue);
         |                                        ^~
   drivers/scsi/sd.c:3832:52: error: 'struct scsi_device' has no member named 'mpath_disk'
    3832 |                 lim = queue_limits_start_update(sdp->mpath_disk->queue);
         |                                                    ^~
   drivers/scsi/sd.c:3837:50: error: 'struct scsi_device' has no member named 'mpath_disk'
    3837 |                 queue_limits_stack_bdev(&lim, sdp->mpath_disk->part0, 0,
         |                                                  ^~
   drivers/scsi/sd.c:3838:24: error: 'struct scsi_device' has no member named 'mpath_disk'
    3838 |                     sdp->mpath_disk->disk_name);
         |                        ^~
   drivers/scsi/sd.c:3840:20: error: 'struct scsi_device' has no member named 'mpath_disk'
    3840 |                 sdp->mpath_disk->flags |= GENHD_FL_HIDDEN;
         |                    ^~
   drivers/scsi/sd.c:3842:44: error: 'struct scsi_device' has no member named 'mpath_disk'
    3842 |                 set_capacity_and_notify(sdp->mpath_disk,
         |                                            ^~
   drivers/scsi/sd.c:3845:53: error: 'struct scsi_device' has no member named 'mpath_disk'
    3845 |                 err = queue_limits_commit_update(sdp->mpath_disk->queue, &lim);
         |                                                     ^~
   drivers/scsi/sd.c:3847:47: error: 'struct scsi_device' has no member named 'mpath_disk'
    3847 |                 scsi_mpath_revalidate_path(sdp->mpath_disk,
         |                                               ^~
   drivers/scsi/sd.c:3850:42: error: 'struct scsi_device' has no member named 'mpath_disk'
    3850 |                 blk_mq_unfreeze_queue(sdp->mpath_disk->queue);
         |                                          ^~
   drivers/scsi/sd.c: In function 'sd_probe':
>> drivers/scsi/sd.c:3980:43: error: 'struct scsi_device' has no member named 'is_shared'
    3980 |         if (scsi_mpath_enabled(sdp) && sdp->is_shared)
         |                                           ^~
   drivers/scsi/sd.c:4001:29: error: 'struct scsi_device' has no member named 'mpath_disk'
    4001 |                 snprintf(sdp->mpath_disk->disk_name, DISK_NAME_LEN, "mpath%dsd%d",
         |                             ^~
   drivers/scsi/sd.c:4066:20: error: 'struct scsi_device' has no member named 'mpath_disk'
    4066 |                 sdp->mpath_disk->major = sd_major((index & 0xf0) >> 4);
         |                    ^~
   drivers/scsi/sd.c:4067:20: error: 'struct scsi_device' has no member named 'mpath_disk'
    4067 |                 sdp->mpath_disk->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
         |                    ^~
   drivers/scsi/sd.c:4068:20: error: 'struct scsi_device' has no member named 'mpath_disk'
    4068 |                 sdp->mpath_disk->minors = SD_MINORS;
         |                    ^~
   In file included from include/linux/log2.h:12,
                    from include/asm-generic/getorder.h:8,
                    from arch/x86/include/asm/page.h:90,
                    from arch/x86/include/asm/processor.h:20,
                    from include/linux/sched.h:13,
                    from include/linux/mempool.h:8,
                    from include/linux/bio.h:8,
                    from include/linux/bio-integrity.h:5,
                    from drivers/scsi/sd.c:36:
>> drivers/scsi/sd.c:4072:31: error: 'SCSI_MPATH_DISK_LIVE' undeclared (first use in this function); did you mean 'SCSI_MPATH_INVALID'?
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                               ^~~~~~~~~~~~~~~~~~~~
   include/linux/bitops.h:44:32: note: in definition of macro 'bitop'
      44 |         ((__builtin_constant_p(nr) &&                                   \
         |                                ^~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
   drivers/scsi/sd.c:4072:31: note: each undeclared identifier is reported only once for each function it appears in
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                               ^~~~~~~~~~~~~~~~~~~~
   include/linux/bitops.h:44:32: note: in definition of macro 'bitop'
      44 |         ((__builtin_constant_p(nr) &&                                   \
         |                                ^~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
>> drivers/scsi/sd.c:4072:57: error: 'struct scsi_device' has no member named 'mpath_flags'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                                                         ^~
   include/linux/bitops.h:45:44: note: in definition of macro 'bitop'
      45 |           __builtin_constant_p((uintptr_t)(addr) != (uintptr_t)NULL) && \
         |                                            ^~~~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
>> drivers/scsi/sd.c:4072:57: error: 'struct scsi_device' has no member named 'mpath_flags'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                                                         ^~
   include/linux/bitops.h:46:23: note: in definition of macro 'bitop'
      46 |           (uintptr_t)(addr) != (uintptr_t)NULL &&                       \
         |                       ^~~~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
>> drivers/scsi/sd.c:4072:57: error: 'struct scsi_device' has no member named 'mpath_flags'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                                                         ^~
   include/linux/bitops.h:47:57: note: in definition of macro 'bitop'
      47 |           __builtin_constant_p(*(const unsigned long *)(addr))) ?       \
         |                                                         ^~~~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
>> drivers/scsi/sd.c:4072:57: error: 'struct scsi_device' has no member named 'mpath_flags'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                                                         ^~
   include/linux/bitops.h:48:24: note: in definition of macro 'bitop'
      48 |          const##op(nr, addr) : op(nr, addr))
         |                        ^~~~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
>> drivers/scsi/sd.c:4072:57: error: 'struct scsi_device' has no member named 'mpath_flags'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                                                         ^~
   include/linux/bitops.h:48:39: note: in definition of macro 'bitop'
      48 |          const##op(nr, addr) : op(nr, addr))
         |                                       ^~~~
   drivers/scsi/sd.c:4072:22: note: in expansion of macro 'test_bit'
    4072 |                 if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
         |                      ^~~~~~~~
   drivers/scsi/sd.c:4074:61: error: 'struct scsi_device' has no member named 'mpath_flags'
    4074 |                         clear_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags);
         |                                                             ^~
   drivers/scsi/sd.c:4075:37: error: 'struct scsi_device' has no member named 'mpath_disk'
    4075 |                         put_disk(sdp->mpath_disk);
         |                                     ^~
   drivers/scsi/sd.c: In function 'sd_shutdown':
>> drivers/scsi/sd.c:4243:52: error: 'struct Scsi_Host' has no member named 'mpath_dev'
    4243 |                         synchronize_srcu(&sdp->host->mpath_dev->srcu);
         |                                                    ^~
   drivers/scsi/sd.c:4245:38: error: 'struct Scsi_Host' has no member named 'mpath_dev'
    4245 |                 mutex_lock(&sdp->host->mpath_dev->mpath_lock);
         |                                      ^~
>> drivers/scsi/sd.c:4247:42: error: 'struct Scsi_Host' has no member named 'mpath_sdev'
    4247 |                 if (list_empty(&sdp->host->mpath_sdev)) {
         |                                          ^~
>> drivers/scsi/sd.c:4248:43: error: 'struct scsi_device' has no member named 'mpath_entry'
    4248 |                         list_del_init(&sdp->mpath_entry);
         |                                           ^~
   drivers/scsi/sd.c:4251:40: error: 'struct Scsi_Host' has no member named 'mpath_dev'
    4251 |                 mutex_unlock(&sdp->host->mpath_dev->mpath_lock);
         |                                        ^~


vim +3829 drivers/scsi/sd.c

  3699	
  3700	/**
  3701	 *	sd_revalidate_disk - called the first time a new disk is seen,
  3702	 *	performs disk spin up, read_capacity, etc.
  3703	 *	@disk: struct gendisk we care about
  3704	 **/
  3705	static int sd_revalidate_disk(struct gendisk *disk)
  3706	{
  3707		struct scsi_disk *sdkp = scsi_disk(disk);
  3708		struct scsi_device *sdp = sdkp->device;
  3709		sector_t old_capacity = sdkp->capacity;
  3710		struct queue_limits lim;
  3711		unsigned char *buffer;
  3712		unsigned int dev_max;
  3713		int err;
  3714	
  3715		SCSI_LOG_HLQUEUE(3, sd_printk(KERN_INFO, sdkp,
  3716					      "sd_revalidate_disk\n"));
  3717	
  3718		/*
  3719		 * If the device is offline, don't try and read capacity or any
  3720		 * of the other niceties.
  3721		 */
  3722		if (!scsi_device_online(sdp))
  3723			goto out;
  3724	
  3725		buffer = kmalloc(SD_BUF_SIZE, GFP_KERNEL);
  3726		if (!buffer) {
  3727			sd_printk(KERN_WARNING, sdkp, "sd_revalidate_disk: Memory "
  3728				  "allocation failure.\n");
  3729			goto out;
  3730		}
  3731	
  3732		sd_spinup_disk(sdkp);
  3733	
  3734		lim = queue_limits_start_update(sdkp->disk->queue);
  3735	
  3736		/*
  3737		 * Without media there is no reason to ask; moreover, some devices
  3738		 * react badly if we do.
  3739		 */
  3740		if (sdkp->media_present) {
  3741			sd_read_capacity(sdkp, &lim, buffer);
  3742			/*
  3743			 * Some USB/UAS devices return generic values for mode pages
  3744			 * until the media has been accessed. Trigger a READ operation
  3745			 * to force the device to populate mode pages.
  3746			 */
  3747			if (sdp->read_before_ms)
  3748				sd_read_block_zero(sdkp);
  3749			/*
  3750			 * set the default to rotational.  All non-rotational devices
  3751			 * support the block characteristics VPD page, which will
  3752			 * cause this to be updated correctly and any device which
  3753			 * doesn't support it should be treated as rotational.
  3754			 */
  3755			lim.features |= (BLK_FEAT_ROTATIONAL | BLK_FEAT_ADD_RANDOM);
  3756	
  3757			if (scsi_device_supports_vpd(sdp)) {
  3758				sd_read_block_provisioning(sdkp);
  3759				sd_read_block_limits(sdkp, &lim);
  3760				sd_read_block_limits_ext(sdkp);
  3761				sd_read_block_characteristics(sdkp, &lim);
  3762				sd_zbc_read_zones(sdkp, &lim, buffer);
  3763			}
  3764	
  3765			sd_config_discard(sdkp, &lim, sd_discard_mode(sdkp));
  3766	
  3767			sd_print_capacity(sdkp, old_capacity);
  3768	
  3769			sd_read_write_protect_flag(sdkp, buffer);
  3770			sd_read_cache_type(sdkp, buffer);
  3771			sd_read_io_hints(sdkp, buffer);
  3772			sd_read_app_tag_own(sdkp, buffer);
  3773			sd_read_write_same(sdkp, buffer);
  3774			sd_read_security(sdkp, buffer);
  3775			sd_config_protection(sdkp, &lim);
  3776		}
  3777	
  3778		/*
  3779		 * We now have all cache related info, determine how we deal
  3780		 * with flush requests.
  3781		 */
  3782		sd_set_flush_flag(sdkp, &lim);
  3783	
  3784		/* Initial block count limit based on CDB TRANSFER LENGTH field size. */
  3785		dev_max = sdp->use_16_for_rw ? SD_MAX_XFER_BLOCKS : SD_DEF_XFER_BLOCKS;
  3786	
  3787		/* Some devices report a maximum block count for READ/WRITE requests. */
  3788		dev_max = min_not_zero(dev_max, sdkp->max_xfer_blocks);
  3789		lim.max_dev_sectors = logical_to_sectors(sdp, dev_max);
  3790	
  3791		if (sd_validate_min_xfer_size(sdkp))
  3792			lim.io_min = logical_to_bytes(sdp, sdkp->min_xfer_blocks);
  3793		else
  3794			lim.io_min = 0;
  3795	
  3796		/*
  3797		 * Limit default to SCSI host optimal sector limit if set. There may be
  3798		 * an impact on performance for when the size of a request exceeds this
  3799		 * host limit.
  3800		 */
  3801		lim.io_opt = sdp->host->opt_sectors << SECTOR_SHIFT;
  3802		if (sd_validate_opt_xfer_size(sdkp, dev_max)) {
  3803			lim.io_opt = min_not_zero(lim.io_opt,
  3804					logical_to_bytes(sdp, sdkp->opt_xfer_blocks));
  3805		}
  3806	
  3807		sdkp->first_scan = 0;
  3808	
  3809		set_capacity_and_notify(disk, logical_to_sectors(sdp, sdkp->capacity));
  3810		sd_config_write_same(sdkp, &lim);
  3811		kfree(buffer);
  3812	
  3813		blk_mq_freeze_queue(sdkp->disk->queue);
  3814		err = queue_limits_commit_update(sdkp->disk->queue, &lim);
  3815		blk_mq_unfreeze_queue(sdkp->disk->queue);
  3816		if (err)
  3817			return err;
  3818	
  3819		/*
  3820		 * Query concurrent positioning ranges after
  3821		 * queue_limits_commit_update() unlocked q->limits_lock to avoid
  3822		 * deadlock with q->sysfs_dir_lock and q->sysfs_lock.
  3823		 */
  3824		if (sdkp->media_present && scsi_device_supports_vpd(sdp))
  3825			sd_read_cpr(sdkp);
  3826	
  3827		/* for multipath device, Adjust queue limits for MPATH disk */
  3828		if (scsi_is_sdev_multipath(sdp)) {
> 3829			struct queue_limits *mpath_lim = &sdp->mpath_disk->queue->limits;
  3830	
  3831			blk_mq_freeze_queue(sdp->mpath_disk->queue);
  3832			lim = queue_limits_start_update(sdp->mpath_disk->queue);
  3833			lim.logical_block_size = mpath_lim->logical_block_size;
  3834			lim.physical_block_size = mpath_lim->physical_block_size;
  3835			lim.io_min = mpath_lim->io_min;
  3836			lim.io_opt = mpath_lim->io_opt;
  3837			queue_limits_stack_bdev(&lim, sdp->mpath_disk->part0, 0,
  3838			    sdp->mpath_disk->disk_name);
  3839	
  3840			sdp->mpath_disk->flags |= GENHD_FL_HIDDEN;
  3841	
  3842			set_capacity_and_notify(sdp->mpath_disk,
  3843			    logical_to_sectors(sdp, sdkp->capacity));
  3844	
  3845			err = queue_limits_commit_update(sdp->mpath_disk->queue, &lim);
  3846	
> 3847			scsi_mpath_revalidate_path(sdp->mpath_disk,
  3848			    logical_to_sectors(sdp, sdkp->capacity));
  3849	
  3850			blk_mq_unfreeze_queue(sdp->mpath_disk->queue);
  3851			if (err)
  3852				return err;
  3853		}
  3854		/*
  3855		 * For a zoned drive, revalidating the zones can be done only once
  3856		 * the gendisk capacity is set. So if this fails, set back the gendisk
  3857		 * capacity to 0.
  3858		 */
  3859		if (sd_zbc_revalidate_zones(sdkp))
  3860			set_capacity_and_notify(disk, 0);
  3861	
  3862	 out:
  3863		return 0;
  3864	}
  3865	
  3866	/**
  3867	 *	sd_unlock_native_capacity - unlock native capacity
  3868	 *	@disk: struct gendisk to set capacity for
  3869	 *
  3870	 *	Block layer calls this function if it detects that partitions
  3871	 *	on @disk reach beyond the end of the device.  If the SCSI host
  3872	 *	implements ->unlock_native_capacity() method, it's invoked to
  3873	 *	give it a chance to adjust the device capacity.
  3874	 *
  3875	 *	CONTEXT:
  3876	 *	Defined by block layer.  Might sleep.
  3877	 */
  3878	static void sd_unlock_native_capacity(struct gendisk *disk)
  3879	{
  3880		struct scsi_device *sdev = scsi_disk(disk)->device;
  3881	
  3882		if (sdev->host->hostt->unlock_native_capacity)
  3883			sdev->host->hostt->unlock_native_capacity(sdev);
  3884	}
  3885	
  3886	/**
  3887	 *	sd_format_disk_name - format disk name
  3888	 *	@prefix: name prefix - ie. "sd" for SCSI disks
  3889	 *	@index: index of the disk to format name for
  3890	 *	@buf: output buffer
  3891	 *	@buflen: length of the output buffer
  3892	 *
  3893	 *	SCSI disk names starts at sda.  The 26th device is sdz and the
  3894	 *	27th is sdaa.  The last one for two lettered suffix is sdzz
  3895	 *	which is followed by sdaaa.
  3896	 *
  3897	 *	This is basically 26 base counting with one extra 'nil' entry
  3898	 *	at the beginning from the second digit on and can be
  3899	 *	determined using similar method as 26 base conversion with the
  3900	 *	index shifted -1 after each digit is computed.
  3901	 *
  3902	 *	CONTEXT:
  3903	 *	Don't care.
  3904	 *
  3905	 *	RETURNS:
  3906	 *	0 on success, -errno on failure.
  3907	 */
  3908	static int sd_format_disk_name(char *prefix, int index, char *buf, int buflen)
  3909	{
  3910		const int base = 'z' - 'a' + 1;
  3911		char *begin = buf + strlen(prefix);
  3912		char *end = buf + buflen;
  3913		char *p;
  3914		int unit;
  3915	
  3916		p = end - 1;
  3917		*p = '\0';
  3918		unit = base;
  3919		do {
  3920			if (p == begin)
  3921				return -EINVAL;
  3922			*--p = 'a' + (index % unit);
  3923			index = (index / unit) - 1;
  3924		} while (index >= 0);
  3925	
  3926		memmove(begin, p, end - p);
  3927		memcpy(buf, prefix, strlen(prefix));
  3928	
  3929		return 0;
  3930	}
  3931	
  3932	/**
  3933	 *	sd_probe - called during driver initialization and whenever a
  3934	 *	new scsi device is attached to the system. It is called once
  3935	 *	for each scsi device (not just disks) present.
  3936	 *	@dev: pointer to device object
  3937	 *
  3938	 *	Returns 0 if successful (or not interested in this scsi device 
  3939	 *	(e.g. scanner)); 1 when there is an error.
  3940	 *
  3941	 *	Note: this function is invoked from the scsi mid-level.
  3942	 *	This function sets up the mapping between a given 
  3943	 *	<host,channel,id,lun> (found in sdp) and new device name 
  3944	 *	(e.g. /dev/sda). More precisely it is the block device major 
  3945	 *	and minor number that is chosen here.
  3946	 *
  3947	 *	Assume sd_probe is not re-entrant (for time being)
  3948	 *	Also think about sd_probe() and sd_remove() running coincidentally.
  3949	 **/
  3950	static int sd_probe(struct device *dev)
  3951	{
  3952		struct scsi_device *sdp = to_scsi_device(dev);
  3953		struct scsi_disk *sdkp;
  3954		struct gendisk *gd;
  3955		int index;
  3956		int error;
  3957	
  3958		scsi_autopm_get_device(sdp);
  3959		error = -ENODEV;
  3960		if (sdp->type != TYPE_DISK &&
  3961		    sdp->type != TYPE_ZBC &&
  3962		    sdp->type != TYPE_MOD &&
  3963		    sdp->type != TYPE_RBC)
  3964			goto out;
  3965	
  3966		if (!IS_ENABLED(CONFIG_BLK_DEV_ZONED) && sdp->type == TYPE_ZBC) {
  3967			sdev_printk(KERN_WARNING, sdp,
  3968				    "Unsupported ZBC host-managed device.\n");
  3969			goto out;
  3970		}
  3971	
  3972		SCSI_LOG_HLQUEUE(3, sdev_printk(KERN_INFO, sdp,
  3973						"sd_probe\n"));
  3974	
  3975		error = -ENOMEM;
  3976		sdkp = kzalloc(sizeof(*sdkp), GFP_KERNEL);
  3977		if (!sdkp)
  3978			goto out;
  3979	
> 3980		if (scsi_mpath_enabled(sdp) && sdp->is_shared)
  3981			scsi_mpath_alloc_disk(sdp);
  3982	
  3983		gd = blk_mq_alloc_disk_for_queue(sdp->request_queue,
  3984						 &sd_bio_compl_lkclass);
  3985		if (!gd)
  3986			goto out_free;
  3987	
  3988		index = ida_alloc(&sd_index_ida, GFP_KERNEL);
  3989		if (index < 0) {
  3990			sdev_printk(KERN_WARNING, sdp, "sd_probe: memory exhausted.\n");
  3991			goto out_put;
  3992		}
  3993	
  3994		error = sd_format_disk_name("sd", index, gd->disk_name, DISK_NAME_LEN);
  3995		if (error) {
  3996			sdev_printk(KERN_WARNING, sdp, "SCSI disk (sd) name length exceeded.\n");
  3997			goto out_free_index;
  3998		}
  3999	
  4000		if (scsi_is_sdev_multipath(sdp))
  4001			snprintf(sdp->mpath_disk->disk_name, DISK_NAME_LEN, "mpath%dsd%d",
  4002			    sdp->host->host_no, index);
  4003	
  4004		sdkp->device = sdp;
  4005		sdkp->disk = gd;
  4006		sdkp->index = index;
  4007		sdkp->max_retries = SD_MAX_RETRIES;
  4008		atomic_set(&sdkp->openers, 0);
  4009		atomic_set(&sdkp->device->ioerr_cnt, 0);
  4010	
  4011		if (!sdp->request_queue->rq_timeout) {
  4012			if (sdp->type != TYPE_MOD)
  4013				blk_queue_rq_timeout(sdp->request_queue, SD_TIMEOUT);
  4014			else
  4015				blk_queue_rq_timeout(sdp->request_queue,
  4016						     SD_MOD_TIMEOUT);
  4017		}
  4018	
  4019		device_initialize(&sdkp->disk_dev);
  4020		sdkp->disk_dev.parent = get_device(dev);
  4021		sdkp->disk_dev.class = &sd_disk_class;
  4022		dev_set_name(&sdkp->disk_dev, "%s", dev_name(dev));
  4023	
  4024		error = device_add(&sdkp->disk_dev);
  4025		if (error) {
  4026			put_device(&sdkp->disk_dev);
  4027			goto out;
  4028		}
  4029	
  4030		dev_set_drvdata(dev, sdkp);
  4031	
  4032		gd->major = sd_major((index & 0xf0) >> 4);
  4033		gd->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
  4034		gd->minors = SD_MINORS;
  4035	
  4036		gd->fops = &sd_fops;
  4037		gd->private_data = sdkp;
  4038	
  4039		/* defaults, until the device tells us otherwise */
  4040		sdp->sector_size = 512;
  4041		sdkp->capacity = 0;
  4042		sdkp->media_present = 1;
  4043		sdkp->write_prot = 0;
  4044		sdkp->cache_override = 0;
  4045		sdkp->WCE = 0;
  4046		sdkp->RCD = 0;
  4047		sdkp->ATO = 0;
  4048		sdkp->first_scan = 1;
  4049		sdkp->max_medium_access_timeouts = SD_MAX_MEDIUM_TIMEOUTS;
  4050	
  4051		sd_revalidate_disk(gd);
  4052	
  4053		if (sdp->removable) {
  4054			gd->flags |= GENHD_FL_REMOVABLE;
  4055			gd->events |= DISK_EVENT_MEDIA_CHANGE;
  4056			gd->event_flags = DISK_EVENT_FLAG_POLL | DISK_EVENT_FLAG_UEVENT;
  4057		}
  4058	
  4059		blk_pm_runtime_init(sdp->request_queue, dev);
  4060		if (sdp->rpm_autosuspend) {
  4061			pm_runtime_set_autosuspend_delay(dev,
  4062				sdp->host->rpm_autosuspend_delay);
  4063		}
  4064	
  4065		if (scsi_is_sdev_multipath(sdp)) {
  4066			sdp->mpath_disk->major = sd_major((index & 0xf0) >> 4);
  4067			sdp->mpath_disk->first_minor = ((index & 0xf) << 4) | (index & 0xfff00);
  4068			sdp->mpath_disk->minors = SD_MINORS;
  4069	
  4070			scsi_mpath_add_disk(sdp);
  4071	
  4072			if (!test_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags)) {
  4073				device_unregister(&sdkp->disk_dev);
  4074				clear_bit(SCSI_MPATH_DISK_LIVE, &sdp->mpath_flags);
  4075				put_disk(sdp->mpath_disk);
  4076				goto out;
  4077			}
  4078		}
  4079	
  4080		error = device_add_disk(dev, gd, NULL);
  4081		if (error) {
  4082			device_unregister(&sdkp->disk_dev);
  4083			put_disk(gd);
  4084			goto out;
  4085		}
  4086	
  4087		if (sdkp->security) {
  4088			sdkp->opal_dev = init_opal_dev(sdkp, &sd_sec_submit);
  4089			if (sdkp->opal_dev)
  4090				sd_printk(KERN_NOTICE, sdkp, "supports TCG Opal\n");
  4091		}
  4092	
  4093		sd_printk(KERN_NOTICE, sdkp, "Attached SCSI %sdisk\n",
  4094			  sdp->removable ? "removable " : "");
  4095		scsi_autopm_put_device(sdp);
  4096	
  4097		return 0;
  4098	
  4099	 out_free_index:
  4100		ida_free(&sd_index_ida, index);
  4101	 out_put:
  4102		put_disk(gd);
  4103	 out_free:
  4104		kfree(sdkp);
  4105	 out:
  4106		scsi_autopm_put_device(sdp);
  4107		return error;
  4108	}
  4109	

-- 
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki

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

* Re: [RFC v1 2/8] scsi: create multipath capable scsi host
  2024-11-09  4:45 ` [RFC v1 2/8] scsi: create multipath capable scsi host himanshu.madhani
@ 2024-11-10 21:11   ` Bart Van Assche
  0 siblings, 0 replies; 18+ messages in thread
From: Bart Van Assche @ 2024-11-10 21:11 UTC (permalink / raw)
  To: himanshu.madhani, martin.petersen, linux-scsi

On 11/8/24 8:45 PM, himanshu.madhani@oracle.com wrote:
>   #include "scsi_priv.h"
>   #include "scsi_logging.h"
> @@ -394,6 +395,14 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
>   	struct Scsi_Host *shost;
>   	int index;
>   
> +#ifdef CONFIG_SCSI_MULTIPATH
> +	struct scsi_mpath *mpath_dev;
> +	size_t	size = sizeof(*mpath_dev);
> +
> +	size += num_possible_nodes() * sizeof(struct mpath_dev *);
> +	privsize = privsize + size;
> +#endif
> +
>   	shost = kzalloc(sizeof(struct Scsi_Host) + privsize, GFP_KERNEL);
>   	if (!shost)
>   		return NULL;
> @@ -409,6 +418,9 @@ struct Scsi_Host *scsi_host_alloc(const struct scsi_host_template *sht, int priv
>   	init_waitqueue_head(&shost->host_wait);
>   	mutex_init(&shost->scan_mutex);
>   
> +#ifdef CONFIG_SCSI_MULTIPATH
> +	INIT_LIST_HEAD(&shost->mpath_sdev);
> +#endif
>   	index = ida_alloc(&host_index_ida, GFP_KERNEL);
>   	if (index < 0) {
>   		kfree(shost);

 From Documentation/process/4.Coding.rst: "As a general rule, #ifdef use
should be confined to header files whenever possible." Please follow
this advice.

Thanks,

Bart.

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

* Re: [RFC v1 0/8] scsi: Multipath support for scsi disk devices.
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (7 preceding siblings ...)
  2024-11-09  4:45 ` [RFC v1 8/8] scsi_debug: Add module parameter for ALUA multipath himanshu.madhani
@ 2024-11-10 21:15 ` Bart Van Assche
  2024-11-12 20:46   ` Himanshu Madhani
  2024-11-22 14:27 ` Hannes Reinecke
  9 siblings, 1 reply; 18+ messages in thread
From: Bart Van Assche @ 2024-11-10 21:15 UTC (permalink / raw)
  To: himanshu.madhani, martin.petersen, linux-scsi


On 11/8/24 8:45 PM, himanshu.madhani@oracle.com wrote:
> Here is a very early RFC for multipath support in the scsi layer. This patch series
> implements native multipath support for scsi disks devices.
> 
> In this series, I am providing conceptual changes which still needs work. However,
> I wanted to get this RFC out to get community feedback on the direction of changes.
> 
> This RFC follows NVMe multipath implementation closely for SCSI multipath. Currently,
> SCSI multipath only supports disk devices which advertises ALUA (Asymmetric Logical
> Unit Access) capability in the Inquiry response data.

Something very important is missing from the cover letter, namely a
motivation of why this initiative has been started. Why to add native
multipath support to the SCSI core instead of using dm-multipath? Isn't
one of the goals of the Linux kernel not to duplicate functionality that
already exists? How does the new infrastructure compare with
dm-multipath from the point of view of performance and functionality?

Thanks,

Bart.


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

* Re: [RFC v1 0/8] scsi: Multipath support for scsi disk devices.
  2024-11-10 21:15 ` [RFC v1 0/8] scsi: Multipath support for scsi disk devices Bart Van Assche
@ 2024-11-12 20:46   ` Himanshu Madhani
  0 siblings, 0 replies; 18+ messages in thread
From: Himanshu Madhani @ 2024-11-12 20:46 UTC (permalink / raw)
  To: Bart Van Assche, martin.petersen, linux-scsi

Hi Bart,

On 11/10/24 13:15, Bart Van Assche wrote:
> 
> On 11/8/24 8:45 PM, himanshu.madhani@oracle.com wrote:
>> Here is a very early RFC for multipath support in the scsi layer. This 
>> patch series
>> implements native multipath support for scsi disks devices.
>>
>> In this series, I am providing conceptual changes which still needs 
>> work. However,
>> I wanted to get this RFC out to get community feedback on the 
>> direction of changes.
>>
>> This RFC follows NVMe multipath implementation closely for SCSI 
>> multipath. Currently,
>> SCSI multipath only supports disk devices which advertises ALUA 
>> (Asymmetric Logical
>> Unit Access) capability in the Inquiry response data.
> 
> Something very important is missing from the cover letter, namely a
> motivation of why this initiative has been started. Why to add native
> multipath support to the SCSI core instead of using dm-multipath? Isn't
> one of the goals of the Linux kernel not to duplicate functionality that
> already exists? How does the new infrastructure compare with
> dm-multipath from the point of view of performance and functionality?
> 

Sorry about missing motivation section in the cover letter. I'll add 
that in v2 when I am ready to send an updated version of this RFC.

Here's motivation

1. Having native multipath provides a seamless configuration and setting 
of multipath with SCSI, which does not involve any other dependencies. 
Especially discovery and assembly of raid array. My motivation with 
native SCSI multipath is to avoid having any 3rd party daemon to do the 
discovery and assembly of multipath devices, which can sometimes create 
issues if devices are not discovered properly. The implementation of 
native multipath will avoid all that additional steps and by virtue will 
provide plug-n-play capability for SCSI multipath configurations. Also, 
having native support will help modernize SCSI code with respect to 
multipath support and provide tighter integration for SCSI stack.


2. On the performance point of view, I believe that switching to RCU 
based path selection logic will provide faster path fail-over and will 
improve overall IO latency. In this RFC, I have not spent time on 
performance collection. I am hoping to provide more comprehensive data 
with the next RFC update.

I do not believe this is duplication of functionality since I am 
providing in-kernel multipath option which will provide users a choice 
of using native v/s out of kernel multipath implementation based on 
their needs.


> Thanks,
> 
> Bart.
> 

-- 
Himanshu Madhani                                Oracle Linux Engineering


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

* Re: [RFC v1 1/8] scsi: Add multipath device support
  2024-11-09  4:45 ` [RFC v1 1/8] scsi: Add multipath device support himanshu.madhani
@ 2024-11-12 21:09   ` Bart Van Assche
  2024-11-13  0:20     ` Himanshu Madhani
  0 siblings, 1 reply; 18+ messages in thread
From: Bart Van Assche @ 2024-11-12 21:09 UTC (permalink / raw)
  To: himanshu.madhani, martin.petersen, linux-scsi

On 11/8/24 8:45 PM, himanshu.madhani@oracle.com wrote:
> +		switch(sdev->mpath_state) {
> +		case SCSI_MPATH_OPTIMAL:
> +		    if (distance < found_distance) {
> +			    found_distance = distance;
> +			    sdev_found = sdev;
> +		    }

Please follow the Linux kernel coding style. As an example, I have never
seen anyone else indenting statements under a case label in the kernel
with four spaces. You may want to run the entire patch series through
clang-format.

Thanks,

Bart.

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

* Re: [RFC v1 1/8] scsi: Add multipath device support
  2024-11-12 21:09   ` Bart Van Assche
@ 2024-11-13  0:20     ` Himanshu Madhani
  0 siblings, 0 replies; 18+ messages in thread
From: Himanshu Madhani @ 2024-11-13  0:20 UTC (permalink / raw)
  To: Bart Van Assche; +Cc: Martin Petersen, linux-scsi@vger.kernel.org



> On Nov 12, 2024, at 13:09, Bart Van Assche <bvanassche@acm.org> wrote:
> 
> On 11/8/24 8:45 PM, himanshu.madhani@oracle.com wrote:
>> + switch(sdev->mpath_state) {
>> + case SCSI_MPATH_OPTIMAL:
>> +    if (distance < found_distance) {
>> +    found_distance = distance;
>> +    sdev_found = sdev;
>> +    }
> 
> Please follow the Linux kernel coding style. As an example, I have never
> seen anyone else indenting statements under a case label in the kernel
> with four spaces. You may want to run the entire patch series through
> clang-format.
> 
Sure will do that. 
> Thanks,
> 
> Bart.


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

* Re: [RFC v1 0/8] scsi: Multipath support for scsi disk devices.
  2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
                   ` (8 preceding siblings ...)
  2024-11-10 21:15 ` [RFC v1 0/8] scsi: Multipath support for scsi disk devices Bart Van Assche
@ 2024-11-22 14:27 ` Hannes Reinecke
  9 siblings, 0 replies; 18+ messages in thread
From: Hannes Reinecke @ 2024-11-22 14:27 UTC (permalink / raw)
  To: himanshu.madhani, martin.petersen, linux-scsi

On 11/9/24 05:45, himanshu.madhani@oracle.com wrote:
> From: Himanshu Madhani <himanshu.madhani@oracle.com>
> 
> Hello Folks,
> 
> Here is a very early RFC for multipath support in the scsi layer. This patch series
> implements native multipath support for scsi disks devices.
> 
> In this series, I am providing conceptual changes which still needs work. However,
> I wanted to get this RFC out to get community feedback on the direction of changes.
> 
> This RFC follows NVMe multipath implementation closely for SCSI multipath. Currently,
> SCSI multipath only supports disk devices which advertises ALUA (Asymmetric Logical
> Unit Access) capability in the Inquiry response data.
> 
First of all, thank you for doing this.
Had been on my to-do list for a long time.

However, the one crucial thing why I kept pushing it back is:

Residuals.

NVMe native multipathing works because NVMe is a 'all-or-nothing' 
protocol, ie either the entire I/O had been completed, or nothing has 
happened.
Which means for any failure we can safely retry the entire I/O on a 
different path (that's the 'steal_bio' thingie), knowing that it's safe
to do so.

For SCSI, however, this is not the case; it's perfectly valid for a 
target to do a partial completion, and ask the initiator to retry the
remainders. And this partial completion might be at any position within
the bvec, requiring us to resend the bio from a random starting position.
Meaning we cannot do a blind 'steal_bio' thing.

So: have you evaluated you series wrt to residuals?
Have you _measured_ if residuals are happening?
Have you considered your patchset how residuals could be
treated?
(It _might_ be possible to resend the entire I/O over to another path,
even if the command had been partially completed. That's perfectly safe
for reads, but for writes you have to be extremely careful to not cause
a data corruption. We had some fun discussions here over at the NVMe 
side ...)

And: please drop the device handler thingie for this, and concentrate
on ALUA. No point in carrying legacy stuff around.
_AND_ you have to evaluate the ALUA settings anyway to get a decent
path selection.

Cheers,

Hannes
-- 
Dr. Hannes Reinecke                  Kernel Storage Architect
hare@suse.de                                +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] 18+ messages in thread

end of thread, other threads:[~2024-11-22 14:27 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2024-11-09  4:45 [RFC v1 0/8] scsi: Multipath support for scsi disk devices himanshu.madhani
2024-11-09  4:45 ` [RFC v1 1/8] scsi: Add multipath device support himanshu.madhani
2024-11-12 21:09   ` Bart Van Assche
2024-11-13  0:20     ` Himanshu Madhani
2024-11-09  4:45 ` [RFC v1 2/8] scsi: create multipath capable scsi host himanshu.madhani
2024-11-10 21:11   ` Bart Van Assche
2024-11-09  4:45 ` [RFC v1 3/8] scsi: Add error handling capability for multipath himanshu.madhani
2024-11-09  4:45 ` [RFC v1 4/8] scsi: Complete multipath request himanshu.madhani
2024-11-09  4:45 ` [RFC v1 5/8] scsi: Add scsi multipath sysfs hooks himanshu.madhani
2024-11-09  4:45 ` [RFC v1 6/8] scsi: Add multipath suppport for device handler himanshu.madhani
2024-11-09  7:29   ` kernel test robot
2024-11-09  8:10   ` kernel test robot
2024-11-09  4:45 ` [RFC v1 7/8] scsi: Add multipath disk init code for sd driver himanshu.madhani
2024-11-09  8:51   ` kernel test robot
2024-11-09  4:45 ` [RFC v1 8/8] scsi_debug: Add module parameter for ALUA multipath himanshu.madhani
2024-11-10 21:15 ` [RFC v1 0/8] scsi: Multipath support for scsi disk devices Bart Van Assche
2024-11-12 20:46   ` Himanshu Madhani
2024-11-22 14:27 ` Hannes Reinecke

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.