LWN.net Logo

MidLayer updates - extending scsi_target support

From:  James.Smart@Emulex.Com
To:  <linux-scsi@vger.kernel.org>
Subject:  [PATCH 3/3] MidLayer updates - extending scsi_target support
Date:  Sat, 29 Jan 2005 09:03:07 -0500
Archive-link:  Article, Thread


Patch 3: 
  This patch extends scsi_target support:
  - Allows for driver-specific data to be allocated along with the
    target structure and accessible via the starget->hostdata pointer.
  - Adds scsi target alloc/configure/destory callbacks to the
    scsi host template.
  - Rearranges the calling sequences for scsi targets so that the
    target and slave alloc/configure/destory callbacks are in
    order (target before slave on alloc/configure).

-- James S



  This patch extends scsi_target support:
  - Allows for driver-specific data to be allocated along with the
    target structure and accessible via the starget->hostdata pointer.
  - Adds scsi target alloc/configure/destory callbacks to the
    scsi host template.
  - Rearranges the calling sequences for scsi targets so that the
    target and slave alloc/configure/destory callbacks are in
    order (target before slave on alloc/configure).


  Signed-off-by: James Smart <james.smart@emulex.com>

---

 b/drivers/scsi/scsi_priv.h          |    1 
 b/drivers/scsi/scsi_scan.c          |   21 +++---
 b/drivers/scsi/scsi_sysfs.c         |   54 +++++++++++++---
 b/drivers/scsi/scsi_transport_spi.c |    2 
 b/include/scsi/scsi_device.h        |    4 -
 b/include/scsi/scsi_host.h          |   64 +++++++++++++++++++
 6 files changed, 129 insertions(+), 17 deletions(-)

diff -puN a/include/scsi/scsi_host.h b/include/scsi/scsi_host.h
--- a/include/scsi/scsi_host.h	2005-01-28 11:16:59.000000000 -0500
+++ b/include/scsi/scsi_host.h	2005-01-28 11:16:59.000000000 -0500
@@ -227,6 +227,64 @@ struct scsi_host_template {
 	void (* slave_destroy)(struct scsi_device *);
 
 	/*
+	 * Before the mid layer attempts to scan for a new device attached
+	 * to a target where no target currently exists, it will call this
+	 * entry in your driver.  Should your driver need to allocate any
+	 * structs or perform any other init items in order to send commands
+	 * to a currently unused target, then this is where you can perform
+	 * those allocations. This is specifically so that drivers won't have
+	 * to perform any kind of "is this a new device" checks in their
+	 * queuecommand routine, thereby making the hot path a bit quicker.
+	 * Note: To have the scsi midlayer co-allocate driver-specific data
+	 * along with the scsi_target creation, set the target_data_sz
+	 * field below to the size of the allocation necessary. The driver
+	 * data can then be accessed via the starget->hostdata pointer.
+	 *
+	 * Return values: 0 on success, non-0 on failure
+	 *
+	 * Deallocation:  If the scsi midlayer scans detect a device on
+	 * the target, expect a call to target_configure(), along with
+	 * prolonged use of the target while devices are present.
+	 * A call will be made to target_destroy() if either the scan
+	 * detects no devices on the target, or once all attached devices
+	 * are removed (such as at module unload or reboot time).
+	 *
+	 * If the driver allocates any target related data elements
+	 * other than the data that can be co-allocated by the midlayer
+	 * (see target_data_sz), then the driver must implement a
+	 * target_destroy() function so that those elements can be
+	 * deallocated upon target removal.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* target_alloc)(struct scsi_target *);
+
+	/*
+	 * Once a scsi device has been detected on the target, the
+	 * midlayer will call this routine to allow the driver to
+	 * finish configuration for the target.
+	 *
+	 * Return values: 0 on success, non-0 on failure
+	 *  If failure, the attached scsi device(s) will be marked offline
+	 *  so that no access will occur. If you return failure, the
+	 *  slave_destroy routine will not be called, so ensure that proper
+	 *  data structure cleanup has been performed.
+	 *
+	 * Status: OPTIONAL
+	 */
+	int (* target_configure)(struct scsi_target *);
+
+	/*
+	 * Immediately prior to deallocating the target structure, and
+	 * after all activity to attached scsi devices has ceased, the
+	 * midlayer calls this point so that the driver may deallocate
+	 * and terminate any references to the target.
+	 *
+	 * Status: OPTIONAL
+	 */
+	void (* target_destroy)(struct scsi_target *);
+
+	/*
 	 * fill in this function to allow the queue depth of this host
 	 * to be changeable (on a per device basis).  returns either
 	 * the current queue depth setting (may be different from what
@@ -387,6 +445,12 @@ struct scsi_host_template {
 	struct device_attribute **sdev_attrs;
 
 	/*
+	 * Allocation lengths for host-specific data allocated along
+	 * with the SCSI structures.
+	 */
+	unsigned int target_data_sz;
+
+	/*
 	 * List of hosts per template.
 	 *
 	 * This is only for use by scsi_module.c for legacy templates.
diff -puN a/include/scsi/scsi_device.h b/include/scsi/scsi_device.h
--- a/include/scsi/scsi_device.h	2005-01-28 11:16:59.000000000 -0500
+++ b/include/scsi/scsi_device.h	2005-01-28 12:36:00.000000000 -0500
@@ -149,7 +149,9 @@ struct scsi_target {
 	unsigned int		id; /* target id ... replace
 				     * scsi_device.id eventually */
 	unsigned long		create:1; /* signal that it needs to be added */
-	unsigned long		starget_data[0];
+	void 			*hostdata; /* available to low-level driver */
+	unsigned long		starget_data[0]; /* for the transport */
+	/* starget_data must be the last element!!!! */
 } __attribute__((aligned(sizeof(unsigned long))));
 
 #define to_scsi_target(d)	container_of(d, struct scsi_target, dev)
diff -puN a/drivers/scsi/scsi_sysfs.c b/drivers/scsi/scsi_sysfs.c
--- a/drivers/scsi/scsi_sysfs.c	2005-01-28 11:16:59.000000000 -0500
+++ b/drivers/scsi/scsi_sysfs.c	2005-01-28 22:25:55.000000000 -0500
@@ -171,6 +171,8 @@ void scsi_device_dev_release(struct devi
 	if (delete) {
 		struct scsi_target *starget = to_scsi_target(parent);
 		if (!starget->create) {
+			if (sdev->host->hostt->target_destroy)
+				sdev->host->hostt->target_destroy(starget);
 			transport_remove_device(&starget->dev);
 			device_del(parent);
 		}
@@ -575,11 +577,10 @@ static void scsi_target_dev_release(stru
  * Return value:
  * 	0 on Success / non-zero on Failure
  **/
-int scsi_sysfs_add_sdev(struct scsi_device *sdev)
+int scsi_sysfs_add_starget(struct scsi_target *starget)
 {
-	struct scsi_target *starget = sdev->sdev_target;
-	struct Scsi_Host *shost = sdev->host;
-	int error, i, create;
+	struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+	int error, create;
 	unsigned long flags;
 
 	spin_lock_irqsave(shost->host_lock, flags);
@@ -594,7 +595,28 @@ int scsi_sysfs_add_sdev(struct scsi_devi
 			return error;
 		}
 		transport_add_device(&starget->dev);
+		if (shost->hostt->target_configure) {
+			error = shost->hostt->target_configure(starget);
+			if (error) {
+				printk(KERN_ERR
+					"Driver Target Configure failed\n");
+				return error;
+			}
+		}
 	}
+	return 0;
+}
+
+/**
+ * scsi_sysfs_add_sdev - add scsi device to sysfs
+ * @sdev:	scsi_device to add
+ *
+ * Return value:
+ * 	0 on Success / non-zero on Failure
+ **/
+int scsi_sysfs_add_sdev(struct scsi_device *sdev)
+{
+	int error, i;
 
 	if ((error = scsi_device_set_state(sdev, SDEV_RUNNING)) != 0)
 		return error;
@@ -790,6 +812,11 @@ int scsi_is_sdev_device(const struct dev
 }
 EXPORT_SYMBOL(scsi_is_sdev_device);
 
+
+#define TGT_ALLOC_FAILURE_MSG	KERN_ERR "%s: Driver Target Allocation" \
+	" failure during SCSI scanning. Some SCSI devices might not be" \
+	" configured\n"
+
 int scsi_sysfs_target_initialize(struct scsi_device *sdev)
 {
 	struct scsi_target *starget = NULL;
@@ -797,7 +824,7 @@ int scsi_sysfs_target_initialize(struct 
 	struct scsi_device *device;
 	struct device *dev = NULL;
 	unsigned long flags;
-	int create = 0;
+	int create = 0, error;
 
 	spin_lock_irqsave(shost->host_lock, flags);
 	/*
@@ -816,7 +843,8 @@ int scsi_sysfs_target_initialize(struct 
 			
 	if (!starget) {
 		const int size = sizeof(*starget) +
-			shost->transportt->target_size;
+			shost->transportt->target_size +
+			shost->hostt->target_data_sz;
 		starget = kmalloc(size, GFP_ATOMIC);
 		if (!starget) {
 			printk(KERN_ERR "%s: allocation failure\n", __FUNCTION__);
@@ -825,6 +853,10 @@ int scsi_sysfs_target_initialize(struct 
 			return -ENOMEM;
 		}
 		memset(starget, 0, size);
+		if (shost->hostt->target_data_sz)
+			starget->hostdata = (void *)starget +
+				(sizeof(*starget) +
+				 shost->transportt->target_size);
 		dev = &starget->dev;
 		device_initialize(dev);
 		dev->parent = get_device(&shost->shost_gendev);
@@ -846,8 +878,16 @@ int scsi_sysfs_target_initialize(struct 
 	sdev->sdev_target = starget;
 	list_add_tail(&sdev->siblings, &shost->__devices);
 	spin_unlock_irqrestore(shost->host_lock, flags);
-	if (create)
+	if (create) {
 		transport_setup_device(&starget->dev);
+		if (shost->hostt->target_alloc) {
+			error = shost->hostt->target_alloc(starget);
+			if (error) {
+				printk(TGT_ALLOC_FAILURE_MSG, __FUNCTION__);
+				return error;
+			}
+		}
+	}
 	return 0;
 }
 
diff -puN a/drivers/scsi/scsi_scan.c b/drivers/scsi/scsi_scan.c
--- a/drivers/scsi/scsi_scan.c	2005-01-28 12:36:48.000000000 -0500
+++ b/drivers/scsi/scsi_scan.c	2005-01-28 12:52:50.000000000 -0500
@@ -256,6 +256,11 @@ static struct scsi_device *scsi_alloc_sd
 
 	scsi_sysfs_device_initialize(sdev);
 
+	/* NOTE: this target initialization code depends critically on
+	 * lun scanning being sequential. */
+	if (scsi_sysfs_target_initialize(sdev))
+		goto out_remove_siblings;
+
 	if (shost->hostt->slave_alloc) {
 		ret = shost->hostt->slave_alloc(sdev);
 		if (ret) {
@@ -269,11 +274,6 @@ static struct scsi_device *scsi_alloc_sd
 		}
 	}
 
-	/* NOTE: this target initialisation code depends critically on
-	 * lun scanning being sequential. */
-	if (scsi_sysfs_target_initialize(sdev))
-		goto out_remove_siblings;
-
 	return sdev;
 
 out_remove_siblings:
@@ -281,9 +281,6 @@ out_remove_siblings:
 	list_del(&sdev->siblings);
 	list_del(&sdev->same_target_siblings);
 	spin_unlock_irqrestore(shost->host_lock, flags);
-
-	if (shost->hostt->slave_destroy)
-		shost->hostt->slave_destroy(sdev);
 out_device_destroy:
 	transport_destroy_device(&sdev->sdev_gendev);
 	scsi_free_queue(sdev->request_queue);
@@ -622,6 +619,14 @@ static int scsi_add_lun(struct scsi_devi
 
 	transport_configure_device(&sdev->sdev_gendev);
 
+	/*
+	 * If the target fails to add, return no response so that
+	 * device is not considered present.
+	 */
+	if (scsi_sysfs_add_starget(sdev->sdev_target))
+		/* failure - act as if there is no device */
+		return SCSI_SCAN_NO_RESPONSE;
+
 	if (sdev->host->hostt->slave_configure)
 		sdev->host->hostt->slave_configure(sdev);
 
diff -puN a/drivers/scsi/scsi_priv.h b/drivers/scsi/scsi_priv.h
--- a/drivers/scsi/scsi_priv.h	2005-01-28 12:45:26.000000000 -0500
+++ b/drivers/scsi/scsi_priv.h	2005-01-28 12:45:55.000000000 -0500
@@ -143,6 +143,7 @@ extern void scsi_exit_sysctl(void);
 /* scsi_sysfs.c */
 extern void scsi_device_dev_release(struct device *);
 extern int scsi_sysfs_add_sdev(struct scsi_device *);
+extern int scsi_sysfs_add_starget(struct scsi_target *);
 extern int scsi_sysfs_add_host(struct Scsi_Host *);
 extern int scsi_sysfs_register(void);
 extern void scsi_sysfs_unregister(void);
diff -puN a/drivers/scsi/scsi_transport_spi.c
b/drivers/scsi/scsi_transport_spi.c
--- a/drivers/scsi/scsi_transport_spi.c	2005-01-28 12:55:58.000000000 -0500
+++ b/drivers/scsi/scsi_transport_spi.c	2005-01-28 12:56:11.000000000 -0500
@@ -28,8 +28,8 @@
 #include <asm/scatterlist.h>
 #include <asm/io.h>
 #include <scsi/scsi.h>
-#include "scsi_priv.h"
 #include <scsi/scsi_device.h>
+#include "scsi_priv.h"
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_request.h>
 #include <scsi/scsi_eh.h>
_

Copyright © 2005, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds