| 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>
_