diff mbox series

[RFC,v1,7/8] scsi: Add multipath disk init code for sd driver

Message ID 20241109044529.992935-8-himanshu.madhani@oracle.com
State New
Headers show
Series scsi: Multipath support for scsi disk devices. | expand

Commit Message

Himanshu Madhani Nov. 9, 2024, 4:45 a.m. UTC
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 mbox series

Patch

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);