* [PATCH] [Update #2] suspending I/Os to a device
@ 2004-09-30 20:57 James.Smart
2004-09-30 21:40 ` James Bottomley
0 siblings, 1 reply; 6+ messages in thread
From: James.Smart @ 2004-09-30 20:57 UTC (permalink / raw)
To: James.Smart, linux-scsi
Cc: hch, andmike, James.Bottomley, mort, andrew.vasquez
Folks,
Here is the suspend i/o patch, updated to run with host and target attributes for obtaining the timeout values. As a matter of course - this adds host and target attribute use to the FC transport.
This patch depends on the bitkeeper scsi-target-2.6 tree.
-- James S
diff -uNr scsi-target-2.6/drivers/s390/scsi/zfcp_scsi.c scsi-target-2.6.patch/drivers/s390/scsi/zfcp_scsi.c
--- scsi-target-2.6/drivers/s390/scsi/zfcp_scsi.c 2004-09-12 19:36:58.000000000 -0400
+++ scsi-target-2.6.patch/drivers/s390/scsi/zfcp_scsi.c 2004-09-30 10:46:45.000000000 -0400
@@ -844,7 +844,7 @@
struct zfcp_unit *unit;
unit = (struct zfcp_unit *) sdev->hostdata;
- fc_port_id(sdev) = unit->port->d_id;
+ fc_sdev_port_id(sdev) = unit->port->d_id;
}
static void
@@ -853,7 +853,7 @@
struct zfcp_unit *unit;
unit = (struct zfcp_unit *) sdev->hostdata;
- fc_port_name(sdev) = unit->port->wwpn;
+ fc_sdev_port_name(sdev) = unit->port->wwpn;
}
static void
@@ -862,16 +862,16 @@
struct zfcp_unit *unit;
unit = (struct zfcp_unit *) sdev->hostdata;
- fc_node_name(sdev) = unit->port->wwnn;
+ fc_sdev_node_name(sdev) = unit->port->wwnn;
}
struct fc_function_template zfcp_transport_functions = {
- .get_port_id = zfcp_get_port_id,
- .get_port_name = zfcp_get_port_name,
- .get_node_name = zfcp_get_node_name,
- .show_port_id = 1,
- .show_port_name = 1,
- .show_node_name = 1,
+ .get_sdev_port_id = zfcp_get_port_id,
+ .get_sdev_port_name = zfcp_get_port_name,
+ .get_sdev_node_name = zfcp_get_node_name,
+ .show_sdev_port_id = 1,
+ .show_sdev_port_name = 1,
+ .show_sdev_node_name = 1,
};
/**
diff -uNr scsi-target-2.6/drivers/scsi/qla2xxx/qla_os.c scsi-target-2.6.patch/drivers/scsi/qla2xxx/qla_os.c
--- scsi-target-2.6/drivers/scsi/qla2xxx/qla_os.c 2004-09-12 19:37:26.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/qla2xxx/qla_os.c 2004-09-30 10:46:45.000000000 -0400
@@ -4460,13 +4460,13 @@
list_for_each_entry(fc, &ha->fcports, list) {
if (fc->os_target_id == sdev->id) {
- fc_port_id(sdev) = fc->d_id.b.domain << 16 |
+ fc_sdev_port_id(sdev) = fc->d_id.b.domain << 16 |
fc->d_id.b.area << 8 |
fc->d_id.b.al_pa;
return;
}
}
- fc_port_id(sdev) = -1;
+ fc_sdev_port_id(sdev) = -1;
}
static void
@@ -4477,12 +4477,12 @@
list_for_each_entry(fc, &ha->fcports, list) {
if (fc->os_target_id == sdev->id) {
- fc_port_name(sdev) =
+ fc_sdev_port_name(sdev) =
__be64_to_cpu(*(uint64_t *)fc->port_name);
return;
}
}
- fc_port_name(sdev) = -1;
+ fc_sdev_port_name(sdev) = -1;
}
static void
@@ -4493,21 +4493,21 @@
list_for_each_entry(fc, &ha->fcports, list) {
if (fc->os_target_id == sdev->id) {
- fc_node_name(sdev) =
+ fc_sdev_node_name(sdev) =
__be64_to_cpu(*(uint64_t *)fc->node_name);
return;
}
}
- fc_node_name(sdev) = -1;
+ fc_sdev_node_name(sdev) = -1;
}
static struct fc_function_template qla2xxx_transport_functions = {
- .get_port_id = qla2xxx_get_port_id,
- .show_port_id = 1,
- .get_port_name = qla2xxx_get_port_name,
- .show_port_name = 1,
- .get_node_name = qla2xxx_get_node_name,
- .show_node_name = 1,
+ .get_sdev_port_id = qla2xxx_get_port_id,
+ .show_sdev_port_id = 1,
+ .get_sdev_port_name = qla2xxx_get_port_name,
+ .show_sdev_port_name = 1,
+ .get_sdev_node_name = qla2xxx_get_node_name,
+ .show_sdev_node_name = 1,
};
/**
diff -uNr scsi-target-2.6/drivers/scsi/scsi.c scsi-target-2.6.patch/drivers/scsi/scsi.c
--- scsi-target-2.6/drivers/scsi/scsi.c 2004-09-12 19:37:17.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi.c 2004-09-30 10:46:45.000000000 -0400
@@ -518,6 +518,26 @@
/* return 0 (because the command has been processed) */
goto out;
}
+
+ /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */
+ if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) {
+ /*
+ * in SDEV_BLOCK, the command is just put back on the device
+ * queue. The suspend state has already blocked the queue so
+ * future requests should not occur until the device
+ * transitions out of the suspend state.
+ */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+
+ SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n"));
+
+ /*
+ * NOTE: rtn is still zero here because we don't need the
+ * queue to be plugged on return (it's already stopped)
+ */
+ goto out;
+ }
+
/* Assign a unique nonzero serial_number. */
/* XXX(hch): this is racy */
if (++serial_number == 0)
@@ -1103,8 +1123,8 @@
/**
* scsi_device_cancel - cancel outstanding IO to this device
- * @sdev: pointer to struct scsi_device
- * @data: pointer to cancel value.
+ * @sdev: Pointer to struct scsi_device
+ * @recovery: Boolean instructing function to recover device or not.
*
**/
int scsi_device_cancel(struct scsi_device *sdev, int recovery)
diff -uNr scsi-target-2.6/drivers/scsi/scsi_lib.c scsi-target-2.6.patch/drivers/scsi/scsi_lib.c
--- scsi-target-2.6/drivers/scsi/scsi_lib.c 2004-09-12 19:37:17.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_lib.c 2004-09-30 10:46:45.000000000 -0400
@@ -1642,6 +1642,7 @@
case SDEV_CREATED:
case SDEV_OFFLINE:
case SDEV_QUIESCE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
@@ -1669,11 +1670,22 @@
}
break;
+ case SDEV_BLOCK:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
case SDEV_CANCEL:
switch (oldstate) {
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_OFFLINE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
@@ -1779,3 +1791,84 @@
device_for_each_child(&starget->dev, NULL, device_resume_fn);
}
EXPORT_SYMBOL(scsi_target_resume);
+
+/**
+ * scsi_internal_device_block - internal function to put a device
+ * temporarily into the SDEV_BLOCK state
+ * @sdev: device to block
+ *
+ * Block request made by scsi lld's to temporarily stop all
+ * scsi commands on the specified device. Called from interrupt
+ * or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_BLOCK state
+ * (which must be a legal transition). When the device is in this
+ * state, all commands are deferred until the scsi lld reenables
+ * the device with scsi_device_unblock or device_block_tmo fires.
+ * This routine assumes the host_lock is held on entry.
+ **/
+int
+scsi_internal_device_block(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ unsigned long flags;
+ int err = 0;
+
+ err = scsi_device_set_state(sdev, SDEV_BLOCK);
+ if (err)
+ return err;
+
+ /*
+ * The device has transitioned to SDEV_BLOCK. Stop the
+ * block layer from calling the midlayer with this device's
+ * request queue.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_block);
+
+/**
+ * scsi_internal_device_unblock - resume a device after a block request
+ * @sdev: device to resume
+ *
+ * Called by scsi lld's or the midlayer to restart the device queue
+ * for the previously suspended scsi device. Called from interrupt or
+ * normal process context.
+ *
+ * Returns zero if successful or error if not.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state
+ * (which must be a legal transition) allowing the midlayer to
+ * goose the queue for this device. This routine assumes the
+ * host_lock is held upon entry.
+ **/
+int
+scsi_internal_device_unblock(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ int err;
+ unsigned long flags;
+
+ /*
+ * Try to transition the scsi device to SDEV_RUNNING
+ * and goose the device queue if successful.
+ */
+ err = scsi_device_set_state(sdev, SDEV_RUNNING);
+ if (err)
+ return err;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_unblock);
diff -uNr scsi-target-2.6/drivers/scsi/scsi_priv.h scsi-target-2.6.patch/drivers/scsi/scsi_priv.h
--- scsi-target-2.6/drivers/scsi/scsi_priv.h 2004-09-12 19:37:18.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_priv.h 2004-09-30 10:46:45.000000000 -0400
@@ -153,4 +153,13 @@
extern struct class sdev_class;
extern struct bus_type scsi_bus_type;
+/*
+ * internal scsi timeout functions: for use by mid-layer and transport
+ * classes.
+ */
+
+#define SCSI_DEVICE_BLOCK_MAX_TIMEOUT (HZ*60)
+extern int scsi_internal_device_block(struct scsi_device *sdev);
+extern int scsi_internal_device_unblock(struct scsi_device *sdev);
+
#endif /* _SCSI_PRIV_H */
diff -uNr scsi-target-2.6/drivers/scsi/scsi_sysfs.c scsi-target-2.6.patch/drivers/scsi/scsi_sysfs.c
--- scsi-target-2.6/drivers/scsi/scsi_sysfs.c 2004-09-27 12:40:09.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_sysfs.c 2004-09-30 10:46:52.000000000 -0400
@@ -30,6 +30,7 @@
{ SDEV_DEL, "deleted" },
{ SDEV_QUIESCE, "quiesce" },
{ SDEV_OFFLINE, "offline" },
+ { SDEV_BLOCK, "blocked" },
};
const char *scsi_device_state_name(enum scsi_device_state state)
@@ -779,6 +780,7 @@
class_device_initialize(&starget->transport_classdev);
starget->transport_classdev.dev = &starget->dev;
starget->transport_classdev.class = shost->transportt->target_class;
+ starget->shost = shost;
snprintf(starget->transport_classdev.class_id, BUS_ID_SIZE,
"target%d:%d:%d",
shost->host_no, sdev->channel, sdev->id);
diff -uNr scsi-target-2.6/drivers/scsi/scsi_transport_fc.c scsi-target-2.6.patch/drivers/scsi/scsi_transport_fc.c
--- scsi-target-2.6/drivers/scsi/scsi_transport_fc.c 2004-09-12 19:37:18.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_transport_fc.c 2004-09-30 10:46:45.000000000 -0400
@@ -23,14 +23,19 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
+#include "scsi_priv.h"
#define FC_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)
static void transport_class_release(struct class_device *class_dev);
+static void starget_class_release(struct class_device *class_dev);
+static void host_class_release(struct class_device *class_dev);
-#define FC_NUM_ATTRS 3 /* increase this if you add attributes */
+#define FC_NUM_ATTRS 4 /* increase this if you add attributes */
#define FC_OTHER_ATTRS 0 /* increase this if you add "always on"
* attributes */
+#define FC_STARGET_NUM_ATTRS 1
+#define FC_HOST_NUM_ATTRS 1
struct fc_internal {
struct scsi_transport_template t;
@@ -40,6 +45,12 @@
/* The array of null terminated pointers to attributes
* needed by scsi_sysfs.c */
struct class_device_attribute *attrs[FC_NUM_ATTRS + FC_OTHER_ATTRS + 1];
+
+ struct class_device_attribute private_starget_attrs[FC_STARGET_NUM_ATTRS];
+ struct class_device_attribute *starget_attrs[FC_STARGET_NUM_ATTRS + 1];
+
+ struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
+ struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];
};
#define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t)
@@ -49,24 +60,71 @@
.release = transport_class_release,
};
+struct class fc_starget_class = {
+ .name = "fc_target",
+ .release = starget_class_release,
+};
+
+struct class fc_host_class = {
+ .name = "fc_host",
+ .release = host_class_release,
+};
+
static __init int fc_transport_init(void)
{
+ int error = class_register(&fc_host_class);
+ if (error)
+ return error;
+ error = class_register(&fc_starget_class);
+ if (error) {
+ return error;
+ }
return class_register(&fc_transport_class);
}
static void __exit fc_transport_exit(void)
{
class_unregister(&fc_transport_class);
+ class_unregister(&fc_starget_class);
+ class_unregister(&fc_host_class);
}
-static int fc_setup_transport_attrs(struct scsi_device *sdev)
+static int fc_setup_sdev_transport_attrs(struct scsi_device *sdev)
{
- /* I'm not sure what values are invalid. We should pick some invalid
- * values for the defaults */
- fc_node_name(sdev) = -1;
- fc_port_name(sdev) = -1;
- fc_port_id(sdev) = -1;
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per device.
+ */
+ fc_sdev_node_name(sdev) = -1;
+ fc_sdev_port_name(sdev) = -1;
+ fc_sdev_port_id(sdev) = -1;
+ fc_sdev_dev_loss_tmo(sdev) = -1;
+ init_timer(&fc_sdev_dev_loss_timer(sdev));
+ return 0;
+}
+static int fc_setup_starget_transport_attrs(struct scsi_target *starget)
+{
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per target.
+ */
+ fc_starget_dev_loss_tmo(starget) = -1;
+ init_timer(&fc_starget_dev_loss_timer(starget));
+ return 0;
+}
+
+static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
+{
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per host.
+ */
+ fc_host_link_down_tmo(shost) = -1;
+ init_timer(&fc_host_link_down_timer(shost));
return 0;
}
@@ -76,23 +134,40 @@
put_device(&sdev->sdev_gendev);
}
-#define fc_transport_show_function(field, format_string, cast) \
- \
+static void starget_class_release(struct class_device *class_dev)
+{
+ struct scsi_target *starget = transport_class_to_starget(class_dev);
+ put_device(&starget->dev);
+}
+
+static void host_class_release(struct class_device *class_dev)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(class_dev);
+ put_device(&shost->shost_gendev);
+}
+
+
+
+/*
+ * Sdev Attribute Management
+ */
+
+#define fc_sdev_show_function(field, format_string, cast) \
static ssize_t \
-show_fc_transport_##field (struct class_device *cdev, char *buf) \
+show_fc_sdev_##field (struct class_device *cdev, char *buf) \
{ \
struct scsi_device *sdev = transport_class_to_sdev(cdev); \
- struct fc_transport_attrs *tp; \
+ struct fc_sdev_attrs *tp; \
struct fc_internal *i = to_fc_internal(sdev->host->transportt); \
- tp = (struct fc_transport_attrs *)&sdev->sdev_data; \
- if (i->f->get_##field) \
- i->f->get_##field(sdev); \
+ tp = (struct fc_sdev_attrs *)&sdev->sdev_data; \
+ if (i->f->get_sdev_##field) \
+ i->f->get_sdev_##field(sdev); \
return snprintf(buf, 20, format_string, cast tp->field); \
}
-#define fc_transport_store_function(field, format_string) \
+#define fc_sdev_store_function(field, format_string) \
static ssize_t \
-store_fc_transport_##field(struct class_device *cdev, const char *buf, \
+store_fc_sdev_##field(struct class_device *cdev, const char *buf, \
size_t count) \
{ \
int val; \
@@ -100,50 +175,195 @@
struct fc_internal *i = to_fc_internal(sdev->host->transportt); \
\
val = simple_strtoul(buf, NULL, 0); \
- i->f->set_##field(sdev, val); \
+ i->f->set_sdev_##field(sdev, val); \
+ return count; \
+}
+
+#define fc_sdev_rd_attr(field, format_string) \
+ fc_sdev_show_function(field, format_string, ) \
+static CLASS_DEVICE_ATTR(sdev_##field, S_IRUGO, \
+ show_fc_sdev_##field, NULL)
+
+#define fc_sdev_rd_attr_cast(field, format_string, cast) \
+ fc_sdev_show_function(field, format_string, (cast)) \
+static CLASS_DEVICE_ATTR(sdev_##field, S_IRUGO, \
+ show_fc_sdev_##field, NULL)
+
+#define fc_sdev_rw_attr(field, format_string) \
+ fc_sdev_show_function(field, format_string, ) \
+ fc_sdev_store_function(field, format_string) \
+static CLASS_DEVICE_ATTR(sdev_##field, S_IRUGO | S_IWUSR, \
+ show_fc_sdev_##field, \
+ store_fc_sdev_##field)
+
+#define SETUP_SDEV_ATTRIBUTE_RD(field) \
+ i->private_attrs[count] = class_device_attr_sdev_##field; \
+ i->private_attrs[count].attr.mode = S_IRUGO; \
+ i->private_attrs[count].store = NULL; \
+ i->attrs[count] = &i->private_attrs[count]; \
+ if (i->f->show_sdev_##field) \
+ count++
+
+#define SETUP_SDEV_ATTRIBUTE_RW(field) \
+ i->private_attrs[count] = class_device_attr_sdev_##field; \
+ if (!i->f->set_sdev_##field) { \
+ i->private_attrs[count].attr.mode = S_IRUGO; \
+ i->private_attrs[count].store = NULL; \
+ } \
+ i->attrs[count] = &i->private_attrs[count]; \
+ if (i->f->show_sdev_##field) \
+ count++
+
+/* The FC Tranport Sdev Attributes: */
+fc_sdev_rd_attr_cast(node_name, "0x%llx\n", unsigned long long);
+fc_sdev_rd_attr_cast(port_name, "0x%llx\n", unsigned long long);
+fc_sdev_rd_attr(port_id, "0x%06x\n");
+fc_sdev_rw_attr(dev_loss_tmo, "%d\n");
+
+
+/*
+ * Remote Port Attribute Management
+ */
+
+#define fc_starget_show_function(field, format_string, cast) \
+static ssize_t \
+show_fc_starget_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = starget->shost; \
+ struct fc_starget_attrs *tp; \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ tp = (struct fc_starget_attrs *)&starget->starget_data; \
+ if (i->f->get_starget_##field) \
+ i->f->get_starget_##field(starget); \
+ return snprintf(buf, 20, format_string, cast tp->field); \
+}
+
+#define fc_starget_store_function(field, format_string) \
+static ssize_t \
+store_fc_starget_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = starget->shost; \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_starget_##field(starget, val); \
+ return count; \
+}
+
+#define fc_starget_rd_attr(field, format_string) \
+ fc_starget_show_function(field, format_string, ) \
+static CLASS_DEVICE_ATTR(starget_##field, S_IRUGO, \
+ show_fc_starget_##field, NULL)
+
+#define fc_starget_rd_attr_cast(field, format_string, cast) \
+ fc_starget_show_function(field, format_string, (cast)) \
+static CLASS_DEVICE_ATTR(starget_##field, S_IRUGO, \
+ show_fc_starget_##field, NULL)
+
+#define fc_starget_rw_attr(field, format_string) \
+ fc_starget_show_function(field, format_string, ) \
+ fc_starget_store_function(field, format_string) \
+static CLASS_DEVICE_ATTR(starget_##field, S_IRUGO | S_IWUSR, \
+ show_fc_starget_##field, \
+ store_fc_starget_##field)
+
+#define SETUP_STARGET_ATTRIBUTE_RD(field) \
+ i->private_starget_attrs[count] = class_device_attr_starget_##field;\
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
+ count++
+
+#define SETUP_STARGET_ATTRIBUTE_RW(field) \
+ i->private_starget_attrs[count] = class_device_attr_starget_##field;\
+ if (!i->f->set_starget_##field) { \
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ } \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
+ count++
+
+/* The FC Tranport Remote Port (Target) Attributes: */
+fc_starget_rw_attr(dev_loss_tmo, "%d\n");
+
+
+/*
+ * Host Attribute Management
+ */
+
+#define fc_host_show_function(field, format_string, cast) \
+static ssize_t \
+show_fc_host_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_host_attrs *tp; \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ tp = (struct fc_host_attrs *)&shost->shost_data; \
+ if (i->f->get_host_##field) \
+ i->f->get_host_##field(shost); \
+ return snprintf(buf, 20, format_string, cast tp->field); \
+}
+
+#define fc_host_store_function(field, format_string) \
+static ssize_t \
+store_fc_host_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_host_##field(shost, val); \
return count; \
}
-#define fc_transport_rd_attr(field, format_string) \
- fc_transport_show_function(field, format_string, ) \
-static CLASS_DEVICE_ATTR(field, S_IRUGO, \
- show_fc_transport_##field, NULL)
-
-#define fc_transport_rd_attr_cast(field, format_string, cast) \
- fc_transport_show_function(field, format_string, (cast)) \
-static CLASS_DEVICE_ATTR( field, S_IRUGO, \
- show_fc_transport_##field, NULL)
-
-#define fc_transport_rw_attr(field, format_string) \
- fc_transport_show_function(field, format_string, ) \
- fc_transport_store_function(field, format_string) \
-static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR, \
- show_fc_transport_##field, \
- store_fc_transport_##field)
-
-/* the FiberChannel Tranport Attributes: */
-fc_transport_rd_attr_cast(node_name, "0x%llx\n", unsigned long long);
-fc_transport_rd_attr_cast(port_name, "0x%llx\n", unsigned long long);
-fc_transport_rd_attr(port_id, "0x%06x\n");
-
-#define SETUP_ATTRIBUTE_RD(field) \
- i->private_attrs[count] = class_device_attr_##field; \
- i->private_attrs[count].attr.mode = S_IRUGO; \
- i->private_attrs[count].store = NULL; \
- i->attrs[count] = &i->private_attrs[count]; \
- if (i->f->show_##field) \
- count++
-
-#define SETUP_ATTRIBUTE_RW(field) \
- i->private_attrs[count] = class_device_attr_##field; \
- if (!i->f->set_##field) { \
- i->private_attrs[count].attr.mode = S_IRUGO; \
- i->private_attrs[count].store = NULL; \
- } \
- i->attrs[count] = &i->private_attrs[count]; \
- if (i->f->show_##field) \
+#define fc_host_rd_attr(field, format_string) \
+ fc_host_show_function(field, format_string, ) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rd_attr_cast(field, format_string, cast) \
+ fc_host_show_function(field, format_string, (cast)) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rw_attr(field, format_string) \
+ fc_host_show_function(field, format_string, ) \
+ fc_host_store_function(field, format_string) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO | S_IWUSR, \
+ show_fc_host_##field, \
+ store_fc_host_##field)
+
+#define SETUP_HOST_ATTRIBUTE_RD(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
count++
+#define SETUP_HOST_ATTRIBUTE_RW(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ if (!i->f->set_host_##field) { \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ } \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
+ count++
+
+/* The FC Tranport Host Attributes: */
+fc_host_rw_attr(link_down_tmo, "%d\n");
+
+
+
struct scsi_transport_template *
fc_attach_transport(struct fc_function_template *ft)
{
@@ -158,13 +378,28 @@
i->t.device_attrs = &i->attrs[0];
i->t.device_class = &fc_transport_class;
- i->t.device_setup = &fc_setup_transport_attrs;
- i->t.device_size = sizeof(struct fc_transport_attrs);
+ i->t.device_setup = &fc_setup_sdev_transport_attrs;
+ i->t.device_size = sizeof(struct fc_sdev_attrs);
+
+ i->t.target_attrs = &i->starget_attrs[0];
+ i->t.target_class = &fc_starget_class;
+ i->t.target_setup = &fc_setup_starget_transport_attrs;
+ i->t.target_size = sizeof(struct fc_starget_attrs);
+
+ i->t.host_attrs = &i->host_attrs[0];
+ i->t.host_class = &fc_host_class;
+ i->t.host_setup = &fc_setup_host_transport_attrs;
+ i->t.host_size = sizeof(struct fc_host_attrs);
i->f = ft;
- SETUP_ATTRIBUTE_RD(port_id);
- SETUP_ATTRIBUTE_RD(port_name);
- SETUP_ATTRIBUTE_RD(node_name);
+
+ /*
+ * setup sdev attributes
+ */
+ SETUP_SDEV_ATTRIBUTE_RD(port_id);
+ SETUP_SDEV_ATTRIBUTE_RD(port_name);
+ SETUP_SDEV_ATTRIBUTE_RD(node_name);
+ SETUP_SDEV_ATTRIBUTE_RW(dev_loss_tmo);
BUG_ON(count > FC_NUM_ATTRS);
@@ -172,6 +407,31 @@
i->attrs[count] = NULL;
+
+ /*
+ * setup remote port (target) attributes
+ */
+ count=0;
+ SETUP_STARGET_ATTRIBUTE_RW(dev_loss_tmo);
+
+ BUG_ON(count > FC_STARGET_NUM_ATTRS);
+
+ /* Setup the always-on attributes here */
+
+ i->starget_attrs[count] = NULL;
+
+
+ /* setup host attributes */
+ count=0;
+ SETUP_HOST_ATTRIBUTE_RW(link_down_tmo);
+
+ BUG_ON(count > FC_HOST_NUM_ATTRS);
+
+ /* Setup the always-on attributes here */
+
+ i->host_attrs[count] = NULL;
+
+
return &i->t;
}
EXPORT_SYMBOL(fc_attach_transport);
@@ -185,6 +445,277 @@
EXPORT_SYMBOL(fc_release_transport);
+
+/**
+ * fc_timeout_blocked_sdev - Timeout handler for blocked scsi devices
+ * that fail to recover in the alloted time.
+ * @data: scsi device that failed to reappear in the alloted time.
+ **/
+static void fc_timeout_blocked_sdev(unsigned long data)
+{
+ struct scsi_device *sdev = (struct scsi_device *)data;
+
+ dev_printk(KERN_ERR, &sdev->sdev_gendev,
+ "blocked device time out: device resuming\n");
+
+ /*
+ * set the device going again ... if the scsi device hasn't been
+ * resumed this will probably result in I/O errors if the host still
+ * isn't ready.
+ */
+ scsi_internal_device_unblock(sdev);
+}
+
+/**
+ * fc_device_block - temporarily block a scsi device.
+ * @sdev: scsi device to block.
+ *
+ * scsi lld's with a FC transport call the fc_device_block function to
+ * temporarily stop all scsi commands to the device specified. Called
+ * from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * The timeout and timer attributes are extracted from the fc transport
+ * attributes associated with the scsi device. This routine assumes no
+ * locks are held on entry.
+ **/
+int
+fc_device_block(struct scsi_device *sdev)
+{
+ int timeout = fc_sdev_dev_loss_tmo(sdev);
+ struct timer_list *timer = &fc_sdev_dev_loss_timer(sdev);
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ scsi_internal_device_block(sdev);
+
+ /* The scsi lld blocks this device for the timeout period only. */
+ timer->data = (unsigned long)sdev;
+ timer->expires = jiffies + timeout * HZ;
+ timer->function = fc_timeout_blocked_sdev;
+ add_timer(timer);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_device_block);
+
+/**
+ * fc_device_unblock - unblock a scsi device following a fc_device_block
+ * request.
+ * @sdev: device managed by target providing target id.
+ *
+ * scsi lld's with a FC transport call this routine to unblock a scsi device
+ * following a fc_device_block request. The unblock causes IO to resume to the
+ * scsi lld. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_device_unblock(struct scsi_device *sdev)
+{
+ /*
+ * Stop the scsi device timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ del_timer_sync(&fc_sdev_dev_loss_timer(sdev));
+ scsi_internal_device_unblock(sdev);
+}
+EXPORT_SYMBOL(fc_device_unblock);
+
+/**
+ * fc_timeout_blocked_tgt - Timeout handler for blocked scsi targets
+ * that fail to recover in the alloted time.
+ * @data: scsi target that failed to reappear in the alloted time.
+ **/
+static void fc_timeout_blocked_tgt(unsigned long data)
+{
+ struct scsi_target *starget = (struct scsi_target *)data;
+ struct scsi_device *sdev;
+
+ dev_printk(KERN_ERR, &starget->dev,
+ "blocked target time out: target resuming\n");
+
+ shost_for_each_device(sdev, starget->shost) {
+ if (sdev->id == starget->id) {
+ /*
+ * set the device going again ... if the scsi lld didn't
+ * unblock this device, then IO errors will probably
+ * result if the host still isn't ready.
+ */
+ scsi_internal_device_unblock(sdev);
+ }
+ }
+}
+
+/**
+ * fc_target_block - block a target by temporarily putting all its scsi devices
+ * into the SDEV_BLOCK state.
+ * @starget: scsi target managed by this fc scsi lldd.
+ *
+ * scsi lldd's with a FC transport call this routine to temporarily stop all
+ * scsi commands to all devices managed by this scsi target. Called
+ * from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * The timeout and timer types are extracted from the fc transport
+ * attributes from the caller's target pointer. This routine assumes no
+ * locks are held on entry.
+ **/
+int
+fc_target_block(struct scsi_target *starget)
+{
+ struct scsi_device *sdev;
+ int timeout = fc_starget_dev_loss_tmo(starget);
+ struct timer_list *timer = &fc_starget_dev_loss_timer(starget);
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ shost_for_each_device(sdev, starget->shost) {
+ if (sdev->id == starget->id)
+ scsi_internal_device_block(sdev);
+ }
+
+ /* The scsi lld blocks this target for the timeout period only. */
+ timer->data = (unsigned long)starget;
+ timer->expires = jiffies + timeout * HZ;
+ timer->function = fc_timeout_blocked_tgt;
+ add_timer(timer);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_target_block);
+
+/**
+ * fc_target_unblock - unblock a target following a fc_target_block request.
+ * @starget: scsi target managed by this fc scsi lldd.
+ *
+ * scsi lld's with a FC transport call this routine to restart IO to all
+ * devices associated with the caller's scsi target following a fc_target_block
+ * request. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_target_unblock(struct scsi_target *starget)
+{
+ struct scsi_device *sdev;
+
+ /*
+ * Stop the target timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ del_timer_sync(&fc_starget_dev_loss_timer(starget));
+
+ shost_for_each_device(sdev, starget->shost) {
+ if (sdev->id == starget->id)
+ scsi_internal_device_unblock(sdev);
+ }
+}
+EXPORT_SYMBOL(fc_target_unblock);
+
+/**
+ * fc_timeout_blocked_host - Timeout handler for blocked scsi hosts
+ * that fail to recover in the alloted time.
+ * @data: scsi host that failed to recover its devices in the alloted
+ * time.
+ **/
+static void fc_timeout_blocked_host(unsigned long data)
+{
+ struct Scsi_Host *shost = (struct Scsi_Host *)data;
+ struct scsi_device *sdev;
+
+ dev_printk(KERN_ERR, &shost->shost_gendev,
+ "blocked host time out: host resuming\n");
+
+ shost_for_each_device(sdev, shost) {
+ /*
+ * set the device going again ... if the scsi lld didn't
+ * unblock this device, then IO errors will probably
+ * result if the host still isn't ready.
+ */
+ scsi_internal_device_unblock(sdev);
+ }
+}
+
+/**
+ * fc_host_block - block all scsi devices managed by the calling host temporarily
+ * by putting each device in the SDEV_BLOCK state.
+ * @shost: scsi host pointer that contains all scsi device siblings.
+ *
+ * scsi lld's with a FC transport call this routine to temporarily stop all
+ * scsi commands to all devices managed by this host. Called
+ * from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * The timeout and timer types are extracted from the fc transport
+ * attributes from the caller's host pointer. This routine assumes no
+ * locks are held on entry.
+ **/
+int
+fc_host_block(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+ int timeout = fc_host_link_down_tmo(shost);
+ struct timer_list *timer = &fc_host_link_down_timer(shost);
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ shost_for_each_device(sdev, shost) {
+ scsi_internal_device_block(sdev);
+ }
+
+ /* The scsi lld blocks this host for the timeout period only. */
+ timer->data = (unsigned long)shost;
+ timer->expires = jiffies + timeout * HZ;
+ timer->function = fc_timeout_blocked_host;
+ add_timer(timer);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_host_block);
+
+/**
+ * fc_host_unblock - unblock all devices managed by this host following a
+ * fc_host_block request.
+ * @shost: scsi host containing all scsi device siblings to unblock.
+ *
+ * scsi lld's with a FC transport call this routine to restart IO to all scsi
+ * devices managed by the specified scsi host following an fc_host_block
+ * request. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_host_unblock(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+
+ /*
+ * Stop the host timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ del_timer_sync(&fc_host_link_down_timer(shost));
+ shost_for_each_device(sdev, shost) {
+ scsi_internal_device_unblock(sdev);
+ }
+}
+EXPORT_SYMBOL(fc_host_unblock);
+
MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
diff -uNr scsi-target-2.6/include/scsi/scsi_device.h scsi-target-2.6.patch/include/scsi/scsi_device.h
--- scsi-target-2.6/include/scsi/scsi_device.h 2004-09-27 12:40:09.000000000 -0400
+++ scsi-target-2.6.patch/include/scsi/scsi_device.h 2004-09-30 10:46:45.000000000 -0400
@@ -30,6 +30,9 @@
* originate in the mid-layer) */
SDEV_OFFLINE, /* Device offlined (by error handling or
* user request */
+ SDEV_BLOCK, /* Device blocked by scsi lld. No scsi
+ * commands from user or midlayer should be issued
+ * to the scsi lld. */
};
struct scsi_device {
@@ -136,6 +139,7 @@
*/
struct scsi_target {
struct scsi_device *starget_sdev_user;
+ struct Scsi_Host *shost;
struct device dev;
unsigned int channel;
unsigned int id; /* target id ... replace
diff -uNr scsi-target-2.6/include/scsi/scsi_transport_fc.h scsi-target-2.6.patch/include/scsi/scsi_transport_fc.h
--- scsi-target-2.6/include/scsi/scsi_transport_fc.h 2004-09-12 19:38:48.000000000 -0400
+++ scsi-target-2.6.patch/include/scsi/scsi_transport_fc.h 2004-09-30 10:46:45.000000000 -0400
@@ -24,33 +24,86 @@
struct scsi_transport_template;
-struct fc_transport_attrs {
+struct fc_sdev_attrs {
int port_id;
uint64_t node_name;
uint64_t port_name;
+ uint32_t dev_loss_tmo; /* Device loss timeout in seconds. */
+ struct timer_list dev_loss_timer;
};
/* accessor functions */
-#define fc_port_id(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->port_id)
-#define fc_node_name(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->node_name)
-#define fc_port_name(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->port_name)
+#define fc_sdev_port_id(x) \
+ (((struct fc_sdev_attrs *)&(x)->sdev_data)->port_id)
+#define fc_sdev_node_name(x) \
+ (((struct fc_sdev_attrs *)&(x)->sdev_data)->node_name)
+#define fc_sdev_port_name(x) \
+ (((struct fc_sdev_attrs *)&(x)->sdev_data)->port_name)
+#define fc_sdev_dev_loss_tmo(x) \
+ (((struct fc_sdev_attrs *)&(x)->sdev_data)->dev_loss_tmo)
+#define fc_sdev_dev_loss_timer(x) \
+ (((struct fc_sdev_attrs *)&(x)->sdev_data)->dev_loss_timer)
+
+struct fc_starget_attrs { /* aka fc_target_attrs */
+ uint32_t dev_loss_tmo; /* Remote Port loss timeout in seconds. */
+ struct timer_list dev_loss_timer;
+};
+
+#define fc_starget_dev_loss_tmo(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo)
+#define fc_starget_dev_loss_timer(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_timer)
+
+struct fc_host_attrs {
+ uint32_t link_down_tmo; /* Link Down timeout in seconds. */
+ struct timer_list link_down_timer;
+};
+
+#define fc_host_link_down_tmo(x) \
+ (((struct fc_host_attrs *)&(x)->shost_data)->link_down_tmo)
+#define fc_host_link_down_timer(x) \
+ (((struct fc_host_attrs *)&(x)->shost_data)->link_down_timer)
+
/* The functions by which the transport class and the driver communicate */
struct fc_function_template {
- void (*get_port_id)(struct scsi_device *);
- void (*get_node_name)(struct scsi_device *);
- void (*get_port_name)(struct scsi_device *);
- /* The driver sets these to tell the transport class it
+ void (*get_sdev_port_id)(struct scsi_device *);
+ void (*get_sdev_node_name)(struct scsi_device *);
+ void (*get_sdev_port_name)(struct scsi_device *);
+ void (*get_sdev_dev_loss_tmo)(struct scsi_device *);
+ void (*set_sdev_dev_loss_tmo)(struct scsi_device *, uint32_t);
+
+ void (*get_starget_dev_loss_tmo)(struct scsi_target *);
+ void (*set_starget_dev_loss_tmo)(struct scsi_target *, uint32_t);
+
+ void (*get_host_link_down_tmo)(struct Scsi_Host *);
+ void (*set_host_link_down_tmo)(struct Scsi_Host *, uint32_t);
+
+ /*
+ * The driver sets these to tell the transport class it
* wants the attributes displayed in sysfs. If the show_ flag
* is not set, the attribute will be private to the transport
- * class */
- unsigned long show_port_id:1;
- unsigned long show_node_name:1;
- unsigned long show_port_name:1;
+ * class
+ */
+ unsigned long show_sdev_port_id:1;
+ unsigned long show_sdev_node_name:1;
+ unsigned long show_sdev_port_name:1;
+ unsigned long show_sdev_dev_loss_tmo:1;
+
+ unsigned long show_starget_dev_loss_tmo:1;
+
+ unsigned long show_host_link_down_tmo:1;
+
/* Private Attributes */
};
struct scsi_transport_template *fc_attach_transport(struct fc_function_template *);
void fc_release_transport(struct scsi_transport_template *);
+int fc_device_block(struct scsi_device *sdev);
+void fc_device_unblock(struct scsi_device *sdev);
+int fc_target_block(struct scsi_target *starget);
+void fc_target_unblock(struct scsi_target *starget);
+int fc_host_block(struct Scsi_Host *shost);
+void fc_host_unblock(struct Scsi_Host *shost);
#endif /* SCSI_TRANSPORT_FC_H */
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] [Update #2] suspending I/Os to a device
2004-09-30 20:57 [PATCH] [Update #2] suspending I/Os to a device James.Smart
@ 2004-09-30 21:40 ` James Bottomley
0 siblings, 0 replies; 6+ messages in thread
From: James Bottomley @ 2004-09-30 21:40 UTC (permalink / raw)
To: James.Smart
Cc: SCSI Mailing List, hch, Mike Anderson, Martin Hicks,
andrew.vasquez
On Thu, 2004-09-30 at 16:57, James.Smart@Emulex.Com wrote:
> Folks,
>
> Here is the suspend i/o patch, updated to run with host and target attributes for obtaining the timeout values. As a matter of course - this adds host and target attribute use to the FC transport.
>
> This patch depends on the bitkeeper scsi-target-2.6 tree.
Well, it's getting better, but I'm slightly confused by the attribute
levels.
Surely node_name, port_name and port_id are target attributes only?
Also, since disconnection by cable pull can only happen at the host or
target level, there's surely no need for a device dev_loss_tmo
parameter?
> struct scsi_target {
> struct scsi_device *starget_sdev_user;
> + struct Scsi_Host *shost;
> struct device dev;
> unsigned int channel;
> unsigned int id; /* target id ... replace
This is unnecessary. You can deduce the scsi host from the device's
parent. If you look at the sym2 updates, you see things like:
struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+fc_target_unblock(struct scsi_target *starget)
> +{
> + struct scsi_device *sdev;
> +
> + /*
> + * Stop the target timer first. Take no action on the del_timer
> + * failure as the state machine state change will validate the
> + * transaction.
> + */
> + del_timer_sync(&fc_starget_dev_loss_timer(starget));
> +
> + shost_for_each_device(sdev, starget->shost) {
> + if (sdev->id == starget->id)
> + scsi_internal_device_unblock(sdev);
> + }
This is wrong, See how iteration over host is done in
scsi_lib.c:scsi_target_resume
James
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] [Update #2] suspending I/Os to a device
@ 2004-10-01 13:54 James.Smart
2004-10-01 16:02 ` Patrick Mansfield
0 siblings, 1 reply; 6+ messages in thread
From: James.Smart @ 2004-10-01 13:54 UTC (permalink / raw)
To: James.Bottomley; +Cc: linux-scsi, hch, andmike, mort, andrew.vasquez
Well, I wasn't going to clean up the fc sdev attributes yet. But - what the heck...
Here's an update the addresses James's comments:
- fc_transport class now contains target devices. The node_name, port_name, port_id attributes have been moved to the scsi target.
- The fc transport functions for block/unblock by sdev has been removed.
- starget->shost has been reverted, using dev_to_shost() instead.
- the fc transport target block functions converted to use device_for_each_child().
-- James S
diff -uNr scsi-target-2.6/drivers/s390/scsi/zfcp_scsi.c scsi-target-2.6.patch/drivers/s390/scsi/zfcp_scsi.c
--- scsi-target-2.6/drivers/s390/scsi/zfcp_scsi.c 2004-09-12 19:36:58.000000000 -0400
+++ scsi-target-2.6.patch/drivers/s390/scsi/zfcp_scsi.c 2004-10-01 07:02:12.000000000 -0400
@@ -48,6 +48,8 @@
static struct zfcp_unit *zfcp_unit_lookup(struct zfcp_adapter *, int, scsi_id_t,
scsi_lun_t);
+static struct zfcp_port * zfcp_port_lookup(struct zfcp_adapter *, int,
+ scsi_id_t);
static struct device_attribute *zfcp_sysfs_sdev_attrs[];
@@ -398,6 +400,26 @@
out:
return retval;
}
+/*
+ * function: zfcp_unit_tgt_lookup
+ *
+ * purpose:
+ *
+ * returns:
+ *
+ * context:
+ */
+static struct zfcp_port *
+zfcp_port_lookup(struct zfcp_adapter *adapter, int channel, scsi_id_t id)
+{
+ struct zfcp_port *port;
+
+ list_for_each_entry(port, &adapter->port_list_head, list) {
+ if (id == port->scsi_id)
+ return port;
+ }
+ return (zfcp_port *)NULL;
+}
/*
* function: zfcp_scsi_eh_abort_handler
@@ -839,39 +861,63 @@
* Support functions for FC transport class
*/
static void
-zfcp_get_port_id(struct scsi_device *sdev)
+zfcp_get_port_id(struct scsi_target *starget)
{
- struct zfcp_unit *unit;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
- unit = (struct zfcp_unit *) sdev->hostdata;
- fc_port_id(sdev) = unit->port->d_id;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_port_id(starget) = port->d_id;
+ else
+ fc_starget_port_id(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
static void
-zfcp_get_port_name(struct scsi_device *sdev)
+zfcp_get_port_name(struct scsi_target *starget)
{
- struct zfcp_unit *unit;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
- unit = (struct zfcp_unit *) sdev->hostdata;
- fc_port_name(sdev) = unit->port->wwpn;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_port_name(starget) = port->wwpn;
+ else
+ fc_starget_port_name(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
static void
-zfcp_get_node_name(struct scsi_device *sdev)
+zfcp_get_node_name(struct scsi_target *starget)
{
- struct zfcp_unit *unit;
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ struct zfcp_adapter *adapter = (struct zfcp_adapter *)shost->hostdata[0];
+ struct zfcp_port *port;
+ unsigned long flags;
- unit = (struct zfcp_unit *) sdev->hostdata;
- fc_node_name(sdev) = unit->port->wwnn;
+ read_lock_irqsave(&zfcp_data.config_lock, flags);
+ port = zfcp_port_lookup(adapter, starget->channel, starget->id);
+ if (port)
+ fc_starget_node_name(starget) = port->wwnn;
+ else
+ fc_starget_node_name(starget) = -1;
+ read_unlock_irqrestore(&zfcp_data.config_lock, flags);
}
struct fc_function_template zfcp_transport_functions = {
- .get_port_id = zfcp_get_port_id,
- .get_port_name = zfcp_get_port_name,
- .get_node_name = zfcp_get_node_name,
- .show_port_id = 1,
- .show_port_name = 1,
- .show_node_name = 1,
+ .get_starget_port_id = zfcp_get_port_id,
+ .get_starget_port_name = zfcp_get_port_name,
+ .get_starget_node_name = zfcp_get_node_name,
+ .show_starget_port_id = 1,
+ .show_starget_port_name = 1,
+ .show_starget_node_name = 1,
};
/**
diff -uNr scsi-target-2.6/drivers/scsi/qla2xxx/qla_os.c scsi-target-2.6.patch/drivers/scsi/qla2xxx/qla_os.c
--- scsi-target-2.6/drivers/scsi/qla2xxx/qla_os.c 2004-09-12 19:37:26.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/qla2xxx/qla_os.c 2004-10-01 07:02:19.000000000 -0400
@@ -4453,61 +4453,64 @@
}
static void
-qla2xxx_get_port_id(struct scsi_device *sdev)
+qla2xxx_get_port_id(struct scsi_target *starget)
{
- scsi_qla_host_t *ha = to_qla_host(sdev->host);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
struct fc_port *fc;
list_for_each_entry(fc, &ha->fcports, list) {
- if (fc->os_target_id == sdev->id) {
- fc_port_id(sdev) = fc->d_id.b.domain << 16 |
+ if (fc->os_target_id == starget->id) {
+ fc_starget_port_id(starget) = fc->d_id.b.domain << 16 |
fc->d_id.b.area << 8 |
fc->d_id.b.al_pa;
return;
}
}
- fc_port_id(sdev) = -1;
+ fc_starget_port_id(starget) = -1;
}
static void
-qla2xxx_get_port_name(struct scsi_device *sdev)
+qla2xxx_get_port_name(struct scsi_target *starget)
{
- scsi_qla_host_t *ha = to_qla_host(sdev->host);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
struct fc_port *fc;
list_for_each_entry(fc, &ha->fcports, list) {
- if (fc->os_target_id == sdev->id) {
- fc_port_name(sdev) =
+ if (fc->os_target_id == starget->id) {
+ fc_starget_port_name(starget) =
__be64_to_cpu(*(uint64_t *)fc->port_name);
return;
}
}
- fc_port_name(sdev) = -1;
+ fc_starget_port_name(starget) = -1;
}
static void
-qla2xxx_get_node_name(struct scsi_device *sdev)
+qla2xxx_get_node_name(struct scsi_target *starget)
{
- scsi_qla_host_t *ha = to_qla_host(sdev->host);
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent);
+ scsi_qla_host_t *ha = to_qla_host(shost);
struct fc_port *fc;
list_for_each_entry(fc, &ha->fcports, list) {
- if (fc->os_target_id == sdev->id) {
- fc_node_name(sdev) =
+ if (fc->os_target_id == starget->id) {
+ fc_starget_node_name(starget) =
__be64_to_cpu(*(uint64_t *)fc->node_name);
return;
}
}
- fc_node_name(sdev) = -1;
+ fc_starget_node_name(starget) = -1;
}
static struct fc_function_template qla2xxx_transport_functions = {
- .get_port_id = qla2xxx_get_port_id,
- .show_port_id = 1,
- .get_port_name = qla2xxx_get_port_name,
- .show_port_name = 1,
- .get_node_name = qla2xxx_get_node_name,
- .show_node_name = 1,
+ .get_starget_port_id = qla2xxx_get_port_id,
+ .show_starget_port_id = 1,
+ .get_starget_port_name = qla2xxx_get_port_name,
+ .show_starget_port_name = 1,
+ .get_starget_node_name = qla2xxx_get_node_name,
+ .show_starget_node_name = 1,
};
/**
diff -uNr scsi-target-2.6/drivers/scsi/scsi.c scsi-target-2.6.patch/drivers/scsi/scsi.c
--- scsi-target-2.6/drivers/scsi/scsi.c 2004-09-12 19:37:17.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi.c 2004-09-30 10:46:45.000000000 -0400
@@ -518,6 +518,26 @@
/* return 0 (because the command has been processed) */
goto out;
}
+
+ /* Check to see if the scsi lld put this device into state SDEV_BLOCK. */
+ if (unlikely(cmd->device->sdev_state == SDEV_BLOCK)) {
+ /*
+ * in SDEV_BLOCK, the command is just put back on the device
+ * queue. The suspend state has already blocked the queue so
+ * future requests should not occur until the device
+ * transitions out of the suspend state.
+ */
+ scsi_queue_insert(cmd, SCSI_MLQUEUE_DEVICE_BUSY);
+
+ SCSI_LOG_MLQUEUE(3, printk("queuecommand : device blocked \n"));
+
+ /*
+ * NOTE: rtn is still zero here because we don't need the
+ * queue to be plugged on return (it's already stopped)
+ */
+ goto out;
+ }
+
/* Assign a unique nonzero serial_number. */
/* XXX(hch): this is racy */
if (++serial_number == 0)
@@ -1103,8 +1123,8 @@
/**
* scsi_device_cancel - cancel outstanding IO to this device
- * @sdev: pointer to struct scsi_device
- * @data: pointer to cancel value.
+ * @sdev: Pointer to struct scsi_device
+ * @recovery: Boolean instructing function to recover device or not.
*
**/
int scsi_device_cancel(struct scsi_device *sdev, int recovery)
diff -uNr scsi-target-2.6/drivers/scsi/scsi_lib.c scsi-target-2.6.patch/drivers/scsi/scsi_lib.c
--- scsi-target-2.6/drivers/scsi/scsi_lib.c 2004-09-12 19:37:17.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_lib.c 2004-09-30 10:46:45.000000000 -0400
@@ -1642,6 +1642,7 @@
case SDEV_CREATED:
case SDEV_OFFLINE:
case SDEV_QUIESCE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
@@ -1669,11 +1670,22 @@
}
break;
+ case SDEV_BLOCK:
+ switch (oldstate) {
+ case SDEV_CREATED:
+ case SDEV_RUNNING:
+ break;
+ default:
+ goto illegal;
+ }
+ break;
+
case SDEV_CANCEL:
switch (oldstate) {
case SDEV_CREATED:
case SDEV_RUNNING:
case SDEV_OFFLINE:
+ case SDEV_BLOCK:
break;
default:
goto illegal;
@@ -1779,3 +1791,84 @@
device_for_each_child(&starget->dev, NULL, device_resume_fn);
}
EXPORT_SYMBOL(scsi_target_resume);
+
+/**
+ * scsi_internal_device_block - internal function to put a device
+ * temporarily into the SDEV_BLOCK state
+ * @sdev: device to block
+ *
+ * Block request made by scsi lld's to temporarily stop all
+ * scsi commands on the specified device. Called from interrupt
+ * or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_BLOCK state
+ * (which must be a legal transition). When the device is in this
+ * state, all commands are deferred until the scsi lld reenables
+ * the device with scsi_device_unblock or device_block_tmo fires.
+ * This routine assumes the host_lock is held on entry.
+ **/
+int
+scsi_internal_device_block(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ unsigned long flags;
+ int err = 0;
+
+ err = scsi_device_set_state(sdev, SDEV_BLOCK);
+ if (err)
+ return err;
+
+ /*
+ * The device has transitioned to SDEV_BLOCK. Stop the
+ * block layer from calling the midlayer with this device's
+ * request queue.
+ */
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_stop_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_block);
+
+/**
+ * scsi_internal_device_unblock - resume a device after a block request
+ * @sdev: device to resume
+ *
+ * Called by scsi lld's or the midlayer to restart the device queue
+ * for the previously suspended scsi device. Called from interrupt or
+ * normal process context.
+ *
+ * Returns zero if successful or error if not.
+ *
+ * Notes:
+ * This routine transitions the device to the SDEV_RUNNING state
+ * (which must be a legal transition) allowing the midlayer to
+ * goose the queue for this device. This routine assumes the
+ * host_lock is held upon entry.
+ **/
+int
+scsi_internal_device_unblock(struct scsi_device *sdev)
+{
+ request_queue_t *q = sdev->request_queue;
+ int err;
+ unsigned long flags;
+
+ /*
+ * Try to transition the scsi device to SDEV_RUNNING
+ * and goose the device queue if successful.
+ */
+ err = scsi_device_set_state(sdev, SDEV_RUNNING);
+ if (err)
+ return err;
+
+ spin_lock_irqsave(q->queue_lock, flags);
+ blk_start_queue(q);
+ spin_unlock_irqrestore(q->queue_lock, flags);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(scsi_internal_device_unblock);
diff -uNr scsi-target-2.6/drivers/scsi/scsi_priv.h scsi-target-2.6.patch/drivers/scsi/scsi_priv.h
--- scsi-target-2.6/drivers/scsi/scsi_priv.h 2004-09-12 19:37:18.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_priv.h 2004-09-30 10:46:45.000000000 -0400
@@ -153,4 +153,13 @@
extern struct class sdev_class;
extern struct bus_type scsi_bus_type;
+/*
+ * internal scsi timeout functions: for use by mid-layer and transport
+ * classes.
+ */
+
+#define SCSI_DEVICE_BLOCK_MAX_TIMEOUT (HZ*60)
+extern int scsi_internal_device_block(struct scsi_device *sdev);
+extern int scsi_internal_device_unblock(struct scsi_device *sdev);
+
#endif /* _SCSI_PRIV_H */
diff -uNr scsi-target-2.6/drivers/scsi/scsi_sysfs.c scsi-target-2.6.patch/drivers/scsi/scsi_sysfs.c
--- scsi-target-2.6/drivers/scsi/scsi_sysfs.c 2004-09-27 12:40:09.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_sysfs.c 2004-10-01 07:10:59.000000000 -0400
@@ -30,6 +30,7 @@
{ SDEV_DEL, "deleted" },
{ SDEV_QUIESCE, "quiesce" },
{ SDEV_OFFLINE, "offline" },
+ { SDEV_BLOCK, "blocked" },
};
const char *scsi_device_state_name(enum scsi_device_state state)
diff -uNr scsi-target-2.6/drivers/scsi/scsi_transport_fc.c scsi-target-2.6.patch/drivers/scsi/scsi_transport_fc.c
--- scsi-target-2.6/drivers/scsi/scsi_transport_fc.c 2004-09-12 19:37:18.000000000 -0400
+++ scsi-target-2.6.patch/drivers/scsi/scsi_transport_fc.c 2004-10-01 07:43:30.000000000 -0400
@@ -23,23 +23,31 @@
#include <scsi/scsi_host.h>
#include <scsi/scsi_transport.h>
#include <scsi/scsi_transport_fc.h>
+#include "scsi_priv.h"
#define FC_PRINTK(x, l, f, a...) printk(l "scsi(%d:%d:%d:%d): " f, (x)->host->host_no, (x)->channel, (x)->id, (x)->lun , ##a)
static void transport_class_release(struct class_device *class_dev);
+static void host_class_release(struct class_device *class_dev);
-#define FC_NUM_ATTRS 3 /* increase this if you add attributes */
-#define FC_OTHER_ATTRS 0 /* increase this if you add "always on"
- * attributes */
+#define FC_STARGET_NUM_ATTRS 4 /* increase this if you add attributes */
+#define FC_STARGET_OTHER_ATTRS 0 /* increase this if you add "always on"
+ * attributes */
+#define FC_HOST_NUM_ATTRS 1
struct fc_internal {
struct scsi_transport_template t;
struct fc_function_template *f;
/* The actual attributes */
- struct class_device_attribute private_attrs[FC_NUM_ATTRS];
+ struct class_device_attribute private_starget_attrs[
+ FC_STARGET_NUM_ATTRS];
/* The array of null terminated pointers to attributes
* needed by scsi_sysfs.c */
- struct class_device_attribute *attrs[FC_NUM_ATTRS + FC_OTHER_ATTRS + 1];
+ struct class_device_attribute *starget_attrs[
+ FC_STARGET_NUM_ATTRS + FC_STARGET_OTHER_ATTRS + 1];
+
+ struct class_device_attribute private_host_attrs[FC_HOST_NUM_ATTRS];
+ struct class_device_attribute *host_attrs[FC_HOST_NUM_ATTRS + 1];
};
#define to_fc_internal(tmpl) container_of(tmpl, struct fc_internal, t)
@@ -49,101 +57,211 @@
.release = transport_class_release,
};
+struct class fc_host_class = {
+ .name = "fc_host",
+ .release = host_class_release,
+};
+
static __init int fc_transport_init(void)
{
+ int error = class_register(&fc_host_class);
+ if (error)
+ return error;
return class_register(&fc_transport_class);
}
static void __exit fc_transport_exit(void)
{
class_unregister(&fc_transport_class);
+ class_unregister(&fc_host_class);
}
-static int fc_setup_transport_attrs(struct scsi_device *sdev)
+static int fc_setup_starget_transport_attrs(struct scsi_target *starget)
{
- /* I'm not sure what values are invalid. We should pick some invalid
- * values for the defaults */
- fc_node_name(sdev) = -1;
- fc_port_name(sdev) = -1;
- fc_port_id(sdev) = -1;
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per target.
+ */
+ fc_starget_node_name(starget) = -1;
+ fc_starget_port_name(starget) = -1;
+ fc_starget_port_id(starget) = -1;
+ fc_starget_dev_loss_tmo(starget) = -1;
+ init_timer(&fc_starget_dev_loss_timer(starget));
+ return 0;
+}
+static int fc_setup_host_transport_attrs(struct Scsi_Host *shost)
+{
+ /*
+ * Set default values easily detected by the midlayer as
+ * failure cases. The scsi lldd is responsible for initializing
+ * all transport attributes to valid values per host.
+ */
+ fc_host_link_down_tmo(shost) = -1;
+ init_timer(&fc_host_link_down_timer(shost));
return 0;
}
static void transport_class_release(struct class_device *class_dev)
{
- struct scsi_device *sdev = transport_class_to_sdev(class_dev);
- put_device(&sdev->sdev_gendev);
+ struct scsi_target *starget = transport_class_to_starget(class_dev);
+ put_device(&starget->dev);
}
-#define fc_transport_show_function(field, format_string, cast) \
- \
+static void host_class_release(struct class_device *class_dev)
+{
+ struct Scsi_Host *shost = transport_class_to_shost(class_dev);
+ put_device(&shost->shost_gendev);
+}
+
+
+/*
+ * Remote Port Attribute Management
+ */
+
+#define fc_starget_show_function(field, format_string, cast) \
static ssize_t \
-show_fc_transport_##field (struct class_device *cdev, char *buf) \
+show_fc_starget_##field (struct class_device *cdev, char *buf) \
{ \
- struct scsi_device *sdev = transport_class_to_sdev(cdev); \
- struct fc_transport_attrs *tp; \
- struct fc_internal *i = to_fc_internal(sdev->host->transportt); \
- tp = (struct fc_transport_attrs *)&sdev->sdev_data; \
- if (i->f->get_##field) \
- i->f->get_##field(sdev); \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct fc_starget_attrs *tp; \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ tp = (struct fc_starget_attrs *)&starget->starget_data; \
+ if (i->f->get_starget_##field) \
+ i->f->get_starget_##field(starget); \
return snprintf(buf, 20, format_string, cast tp->field); \
}
-#define fc_transport_store_function(field, format_string) \
+#define fc_starget_store_function(field, format_string) \
static ssize_t \
-store_fc_transport_##field(struct class_device *cdev, const char *buf, \
+store_fc_starget_##field(struct class_device *cdev, const char *buf, \
size_t count) \
{ \
int val; \
- struct scsi_device *sdev = transport_class_to_sdev(cdev); \
- struct fc_internal *i = to_fc_internal(sdev->host->transportt); \
+ struct scsi_target *starget = transport_class_to_starget(cdev); \
+ struct Scsi_Host *shost = dev_to_shost(starget->dev.parent); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
\
val = simple_strtoul(buf, NULL, 0); \
- i->f->set_##field(sdev, val); \
+ i->f->set_starget_##field(starget, val); \
return count; \
}
-#define fc_transport_rd_attr(field, format_string) \
- fc_transport_show_function(field, format_string, ) \
+#define fc_starget_rd_attr(field, format_string) \
+ fc_starget_show_function(field, format_string, ) \
+static CLASS_DEVICE_ATTR(field, S_IRUGO, \
+ show_fc_starget_##field, NULL)
+
+#define fc_starget_rd_attr_cast(field, format_string, cast) \
+ fc_starget_show_function(field, format_string, (cast)) \
static CLASS_DEVICE_ATTR(field, S_IRUGO, \
- show_fc_transport_##field, NULL)
+ show_fc_starget_##field, NULL)
-#define fc_transport_rd_attr_cast(field, format_string, cast) \
- fc_transport_show_function(field, format_string, (cast)) \
-static CLASS_DEVICE_ATTR( field, S_IRUGO, \
- show_fc_transport_##field, NULL)
-
-#define fc_transport_rw_attr(field, format_string) \
- fc_transport_show_function(field, format_string, ) \
- fc_transport_store_function(field, format_string) \
+#define fc_starget_rw_attr(field, format_string) \
+ fc_starget_show_function(field, format_string, ) \
+ fc_starget_store_function(field, format_string) \
static CLASS_DEVICE_ATTR(field, S_IRUGO | S_IWUSR, \
- show_fc_transport_##field, \
- store_fc_transport_##field)
+ show_fc_starget_##field, \
+ store_fc_starget_##field)
-/* the FiberChannel Tranport Attributes: */
-fc_transport_rd_attr_cast(node_name, "0x%llx\n", unsigned long long);
-fc_transport_rd_attr_cast(port_name, "0x%llx\n", unsigned long long);
-fc_transport_rd_attr(port_id, "0x%06x\n");
-
-#define SETUP_ATTRIBUTE_RD(field) \
- i->private_attrs[count] = class_device_attr_##field; \
- i->private_attrs[count].attr.mode = S_IRUGO; \
- i->private_attrs[count].store = NULL; \
- i->attrs[count] = &i->private_attrs[count]; \
- if (i->f->show_##field) \
+#define SETUP_STARGET_ATTRIBUTE_RD(field) \
+ i->private_starget_attrs[count] = class_device_attr_##field; \
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
count++
-#define SETUP_ATTRIBUTE_RW(field) \
- i->private_attrs[count] = class_device_attr_##field; \
- if (!i->f->set_##field) { \
- i->private_attrs[count].attr.mode = S_IRUGO; \
- i->private_attrs[count].store = NULL; \
- } \
- i->attrs[count] = &i->private_attrs[count]; \
- if (i->f->show_##field) \
+#define SETUP_STARGET_ATTRIBUTE_RW(field) \
+ i->private_starget_attrs[count] = class_device_attr_##field; \
+ if (!i->f->set_starget_##field) { \
+ i->private_starget_attrs[count].attr.mode = S_IRUGO; \
+ i->private_starget_attrs[count].store = NULL; \
+ } \
+ i->starget_attrs[count] = &i->private_starget_attrs[count]; \
+ if (i->f->show_starget_##field) \
count++
+/* The FC Tranport Remote Port (Target) Attributes: */
+fc_starget_rd_attr_cast(node_name, "0x%llx\n", unsigned long long);
+fc_starget_rd_attr_cast(port_name, "0x%llx\n", unsigned long long);
+fc_starget_rd_attr(port_id, "0x%06x\n");
+fc_starget_rw_attr(dev_loss_tmo, "%d\n");
+
+
+/*
+ * Host Attribute Management
+ */
+
+#define fc_host_show_function(field, format_string, cast) \
+static ssize_t \
+show_fc_host_##field (struct class_device *cdev, char *buf) \
+{ \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_host_attrs *tp; \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ tp = (struct fc_host_attrs *)&shost->shost_data; \
+ if (i->f->get_host_##field) \
+ i->f->get_host_##field(shost); \
+ return snprintf(buf, 20, format_string, cast tp->field); \
+}
+
+#define fc_host_store_function(field, format_string) \
+static ssize_t \
+store_fc_host_##field(struct class_device *cdev, const char *buf, \
+ size_t count) \
+{ \
+ int val; \
+ struct Scsi_Host *shost = transport_class_to_shost(cdev); \
+ struct fc_internal *i = to_fc_internal(shost->transportt); \
+ \
+ val = simple_strtoul(buf, NULL, 0); \
+ i->f->set_host_##field(shost, val); \
+ return count; \
+}
+
+#define fc_host_rd_attr(field, format_string) \
+ fc_host_show_function(field, format_string, ) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rd_attr_cast(field, format_string, cast) \
+ fc_host_show_function(field, format_string, (cast)) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO, \
+ show_fc_host_##field, NULL)
+
+#define fc_host_rw_attr(field, format_string) \
+ fc_host_show_function(field, format_string, ) \
+ fc_host_store_function(field, format_string) \
+static CLASS_DEVICE_ATTR(host_##field, S_IRUGO | S_IWUSR, \
+ show_fc_host_##field, \
+ store_fc_host_##field)
+
+#define SETUP_HOST_ATTRIBUTE_RD(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
+ count++
+
+#define SETUP_HOST_ATTRIBUTE_RW(field) \
+ i->private_host_attrs[count] = class_device_attr_host_##field; \
+ if (!i->f->set_host_##field) { \
+ i->private_host_attrs[count].attr.mode = S_IRUGO; \
+ i->private_host_attrs[count].store = NULL; \
+ } \
+ i->host_attrs[count] = &i->private_host_attrs[count]; \
+ if (i->f->show_host_##field) \
+ count++
+
+/* The FC Tranport Host Attributes: */
+fc_host_rw_attr(link_down_tmo, "%d\n");
+
+
+
struct scsi_transport_template *
fc_attach_transport(struct fc_function_template *ft)
{
@@ -156,21 +274,43 @@
memset(i, 0, sizeof(struct fc_internal));
- i->t.device_attrs = &i->attrs[0];
- i->t.device_class = &fc_transport_class;
- i->t.device_setup = &fc_setup_transport_attrs;
- i->t.device_size = sizeof(struct fc_transport_attrs);
+ i->t.target_attrs = &i->starget_attrs[0];
+ i->t.target_class = &fc_transport_class;
+ i->t.target_setup = &fc_setup_starget_transport_attrs;
+ i->t.target_size = sizeof(struct fc_starget_attrs);
+
+ i->t.host_attrs = &i->host_attrs[0];
+ i->t.host_class = &fc_host_class;
+ i->t.host_setup = &fc_setup_host_transport_attrs;
+ i->t.host_size = sizeof(struct fc_host_attrs);
i->f = ft;
- SETUP_ATTRIBUTE_RD(port_id);
- SETUP_ATTRIBUTE_RD(port_name);
- SETUP_ATTRIBUTE_RD(node_name);
+
+ /*
+ * setup remote port (target) attributes
+ */
+ SETUP_STARGET_ATTRIBUTE_RD(port_id);
+ SETUP_STARGET_ATTRIBUTE_RD(port_name);
+ SETUP_STARGET_ATTRIBUTE_RD(node_name);
+ SETUP_STARGET_ATTRIBUTE_RW(dev_loss_tmo);
- BUG_ON(count > FC_NUM_ATTRS);
+ BUG_ON(count > FC_STARGET_NUM_ATTRS);
/* Setup the always-on attributes here */
- i->attrs[count] = NULL;
+ i->starget_attrs[count] = NULL;
+
+
+ /* setup host attributes */
+ count=0;
+ SETUP_HOST_ATTRIBUTE_RW(link_down_tmo);
+
+ BUG_ON(count > FC_HOST_NUM_ATTRS);
+
+ /* Setup the always-on attributes here */
+
+ i->host_attrs[count] = NULL;
+
return &i->t;
}
@@ -185,6 +325,204 @@
EXPORT_SYMBOL(fc_release_transport);
+
+/**
+ * fc_device_block - called by target functions to block a scsi device
+ * @dev: scsi device
+ * @data: unused
+ **/
+static int fc_device_block(struct device *dev, void *data)
+{
+ scsi_internal_device_block(to_scsi_device(dev));
+ return 0;
+}
+
+/**
+ * fc_device_unblock - called by target functions to unblock a scsi device
+ * @dev: scsi device
+ * @data: unused
+ **/
+static int fc_device_unblock(struct device *dev, void *data)
+{
+ scsi_internal_device_unblock(to_scsi_device(dev));
+ return 0;
+}
+
+/**
+ * fc_timeout_blocked_tgt - Timeout handler for blocked scsi targets
+ * that fail to recover in the alloted time.
+ * @data: scsi target that failed to reappear in the alloted time.
+ **/
+static void fc_timeout_blocked_tgt(unsigned long data)
+{
+ struct scsi_target *starget = (struct scsi_target *)data;
+
+ dev_printk(KERN_ERR, &starget->dev,
+ "blocked target time out: target resuming\n");
+
+ /*
+ * set the device going again ... if the scsi lld didn't
+ * unblock this device, then IO errors will probably
+ * result if the host still isn't ready.
+ */
+ device_for_each_child(&starget->dev, NULL, fc_device_unblock);
+}
+
+/**
+ * fc_target_block - block a target by temporarily putting all its scsi devices
+ * into the SDEV_BLOCK state.
+ * @starget: scsi target managed by this fc scsi lldd.
+ *
+ * scsi lldd's with a FC transport call this routine to temporarily stop all
+ * scsi commands to all devices managed by this scsi target. Called
+ * from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * The timeout and timer types are extracted from the fc transport
+ * attributes from the caller's target pointer. This routine assumes no
+ * locks are held on entry.
+ **/
+int
+fc_target_block(struct scsi_target *starget)
+{
+ int timeout = fc_starget_dev_loss_tmo(starget);
+ struct timer_list *timer = &fc_starget_dev_loss_timer(starget);
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ device_for_each_child(&starget->dev, NULL, fc_device_block);
+
+ /* The scsi lld blocks this target for the timeout period only. */
+ timer->data = (unsigned long)starget;
+ timer->expires = jiffies + timeout * HZ;
+ timer->function = fc_timeout_blocked_tgt;
+ add_timer(timer);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_target_block);
+
+/**
+ * fc_target_unblock - unblock a target following a fc_target_block request.
+ * @starget: scsi target managed by this fc scsi lldd.
+ *
+ * scsi lld's with a FC transport call this routine to restart IO to all
+ * devices associated with the caller's scsi target following a fc_target_block
+ * request. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_target_unblock(struct scsi_target *starget)
+{
+ /*
+ * Stop the target timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ del_timer_sync(&fc_starget_dev_loss_timer(starget));
+
+ device_for_each_child(&starget->dev, NULL, fc_device_unblock);
+}
+EXPORT_SYMBOL(fc_target_unblock);
+
+/**
+ * fc_timeout_blocked_host - Timeout handler for blocked scsi hosts
+ * that fail to recover in the alloted time.
+ * @data: scsi host that failed to recover its devices in the alloted
+ * time.
+ **/
+static void fc_timeout_blocked_host(unsigned long data)
+{
+ struct Scsi_Host *shost = (struct Scsi_Host *)data;
+ struct scsi_device *sdev;
+
+ dev_printk(KERN_ERR, &shost->shost_gendev,
+ "blocked host time out: host resuming\n");
+
+ shost_for_each_device(sdev, shost) {
+ /*
+ * set the device going again ... if the scsi lld didn't
+ * unblock this device, then IO errors will probably
+ * result if the host still isn't ready.
+ */
+ scsi_internal_device_unblock(sdev);
+ }
+}
+
+/**
+ * fc_host_block - block all scsi devices managed by the calling host temporarily
+ * by putting each device in the SDEV_BLOCK state.
+ * @shost: scsi host pointer that contains all scsi device siblings.
+ *
+ * scsi lld's with a FC transport call this routine to temporarily stop all
+ * scsi commands to all devices managed by this host. Called
+ * from interrupt or normal process context.
+ *
+ * Returns zero if successful or error if not
+ *
+ * Notes:
+ * The timeout and timer types are extracted from the fc transport
+ * attributes from the caller's host pointer. This routine assumes no
+ * locks are held on entry.
+ **/
+int
+fc_host_block(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+ int timeout = fc_host_link_down_tmo(shost);
+ struct timer_list *timer = &fc_host_link_down_timer(shost);
+
+ if (timeout < 0 || timeout > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
+ return -EINVAL;
+
+ shost_for_each_device(sdev, shost) {
+ scsi_internal_device_block(sdev);
+ }
+
+ /* The scsi lld blocks this host for the timeout period only. */
+ timer->data = (unsigned long)shost;
+ timer->expires = jiffies + timeout * HZ;
+ timer->function = fc_timeout_blocked_host;
+ add_timer(timer);
+
+ return 0;
+}
+EXPORT_SYMBOL(fc_host_block);
+
+/**
+ * fc_host_unblock - unblock all devices managed by this host following a
+ * fc_host_block request.
+ * @shost: scsi host containing all scsi device siblings to unblock.
+ *
+ * scsi lld's with a FC transport call this routine to restart IO to all scsi
+ * devices managed by the specified scsi host following an fc_host_block
+ * request. Called from interrupt or normal process context.
+ *
+ * Notes:
+ * This routine assumes no locks are held on entry.
+ **/
+void
+fc_host_unblock(struct Scsi_Host *shost)
+{
+ struct scsi_device *sdev;
+
+ /*
+ * Stop the host timer first. Take no action on the del_timer
+ * failure as the state machine state change will validate the
+ * transaction.
+ */
+ del_timer_sync(&fc_host_link_down_timer(shost));
+ shost_for_each_device(sdev, shost) {
+ scsi_internal_device_unblock(sdev);
+ }
+}
+EXPORT_SYMBOL(fc_host_unblock);
+
MODULE_AUTHOR("Martin Hicks");
MODULE_DESCRIPTION("FC Transport Attributes");
MODULE_LICENSE("GPL");
diff -uNr scsi-target-2.6/include/scsi/scsi_device.h scsi-target-2.6.patch/include/scsi/scsi_device.h
--- scsi-target-2.6/include/scsi/scsi_device.h 2004-09-27 12:40:09.000000000 -0400
+++ scsi-target-2.6.patch/include/scsi/scsi_device.h 2004-10-01 07:09:15.000000000 -0400
@@ -30,6 +30,9 @@
* originate in the mid-layer) */
SDEV_OFFLINE, /* Device offlined (by error handling or
* user request */
+ SDEV_BLOCK, /* Device blocked by scsi lld. No scsi
+ * commands from user or midlayer should be issued
+ * to the scsi lld. */
};
struct scsi_device {
diff -uNr scsi-target-2.6/include/scsi/scsi_transport_fc.h scsi-target-2.6.patch/include/scsi/scsi_transport_fc.h
--- scsi-target-2.6/include/scsi/scsi_transport_fc.h 2004-09-12 19:38:48.000000000 -0400
+++ scsi-target-2.6.patch/include/scsi/scsi_transport_fc.h 2004-10-01 06:18:25.000000000 -0400
@@ -24,33 +24,68 @@
struct scsi_transport_template;
-struct fc_transport_attrs {
+struct fc_starget_attrs { /* aka fc_target_attrs */
int port_id;
uint64_t node_name;
uint64_t port_name;
+ uint32_t dev_loss_tmo; /* Remote Port loss timeout in seconds. */
+ struct timer_list dev_loss_timer;
};
-/* accessor functions */
-#define fc_port_id(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->port_id)
-#define fc_node_name(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->node_name)
-#define fc_port_name(x) (((struct fc_transport_attrs *)&(x)->sdev_data)->port_name)
+#define fc_starget_port_id(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->port_id)
+#define fc_starget_node_name(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->node_name)
+#define fc_starget_port_name(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->port_name)
+#define fc_starget_dev_loss_tmo(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_tmo)
+#define fc_starget_dev_loss_timer(x) \
+ (((struct fc_starget_attrs *)&(x)->starget_data)->dev_loss_timer)
+
+struct fc_host_attrs {
+ uint32_t link_down_tmo; /* Link Down timeout in seconds. */
+ struct timer_list link_down_timer;
+};
+
+#define fc_host_link_down_tmo(x) \
+ (((struct fc_host_attrs *)&(x)->shost_data)->link_down_tmo)
+#define fc_host_link_down_timer(x) \
+ (((struct fc_host_attrs *)&(x)->shost_data)->link_down_timer)
+
/* The functions by which the transport class and the driver communicate */
struct fc_function_template {
- void (*get_port_id)(struct scsi_device *);
- void (*get_node_name)(struct scsi_device *);
- void (*get_port_name)(struct scsi_device *);
- /* The driver sets these to tell the transport class it
+ void (*get_starget_port_id)(struct scsi_target *);
+ void (*get_starget_node_name)(struct scsi_target *);
+ void (*get_starget_port_name)(struct scsi_target *);
+ void (*get_starget_dev_loss_tmo)(struct scsi_target *);
+ void (*set_starget_dev_loss_tmo)(struct scsi_target *, uint32_t);
+
+ void (*get_host_link_down_tmo)(struct Scsi_Host *);
+ void (*set_host_link_down_tmo)(struct Scsi_Host *, uint32_t);
+
+ /*
+ * The driver sets these to tell the transport class it
* wants the attributes displayed in sysfs. If the show_ flag
* is not set, the attribute will be private to the transport
- * class */
- unsigned long show_port_id:1;
- unsigned long show_node_name:1;
- unsigned long show_port_name:1;
+ * class
+ */
+ unsigned long show_starget_port_id:1;
+ unsigned long show_starget_node_name:1;
+ unsigned long show_starget_port_name:1;
+ unsigned long show_starget_dev_loss_tmo:1;
+
+ unsigned long show_host_link_down_tmo:1;
+
/* Private Attributes */
};
struct scsi_transport_template *fc_attach_transport(struct fc_function_template *);
void fc_release_transport(struct scsi_transport_template *);
+int fc_target_block(struct scsi_target *starget);
+void fc_target_unblock(struct scsi_target *starget);
+int fc_host_block(struct Scsi_Host *shost);
+void fc_host_unblock(struct Scsi_Host *shost);
#endif /* SCSI_TRANSPORT_FC_H */
^ permalink raw reply [flat|nested] 6+ messages in thread
* Re: [PATCH] [Update #2] suspending I/Os to a device
2004-10-01 13:54 James.Smart
@ 2004-10-01 16:02 ` Patrick Mansfield
0 siblings, 0 replies; 6+ messages in thread
From: Patrick Mansfield @ 2004-10-01 16:02 UTC (permalink / raw)
To: James.Smart
Cc: James.Bottomley, linux-scsi, hch, andmike, mort, andrew.vasquez
On Fri, Oct 01, 2004 at 09:54:53AM -0400, James.Smart@Emulex.Com wrote:
> +/**
> + * fc_host_block - block all scsi devices managed by the calling host temporarily
> + * by putting each device in the SDEV_BLOCK state.
> + * @shost: scsi host pointer that contains all scsi device siblings.
> + *
> + * scsi lld's with a FC transport call this routine to temporarily stop all
> + * scsi commands to all devices managed by this host. Called
> + * from interrupt or normal process context.
> + *
> + * Returns zero if successful or error if not
> + *
> + * Notes:
> + * The timeout and timer types are extracted from the fc transport
> + * attributes from the caller's host pointer. This routine assumes no
> + * locks are held on entry.
> + **/
> +int
> +fc_host_block(struct Scsi_Host *shost)
What is the difference between these new fc_host_block/unblock and
the current scsi_block/unblock_requests?
-- Patrick Mansfield
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] [Update #2] suspending I/Os to a device
@ 2004-10-01 16:15 James.Smart
0 siblings, 0 replies; 6+ messages in thread
From: James.Smart @ 2004-10-01 16:15 UTC (permalink / raw)
To: patmans; +Cc: James.Bottomley, linux-scsi, hch, andmike, mort, andrew.vasquez
The difference is, the fc functions:
- Changes the actual sdev state to indicate a "blocked" state (you can see it in sysfs, etc)
- Starts a timer that will automatically unblock the sdev w/o lld intervention
-- james
> What is the difference between these new fc_host_block/unblock and
> the current scsi_block/unblock_requests?
^ permalink raw reply [flat|nested] 6+ messages in thread
* RE: [PATCH] [Update #2] suspending I/Os to a device
@ 2004-10-01 16:21 James.Smart
0 siblings, 0 replies; 6+ messages in thread
From: James.Smart @ 2004-10-01 16:21 UTC (permalink / raw)
To: James.Smart, patmans
Cc: James.Bottomley, linux-scsi, hch, andmike, mort, andrew.vasquez
I should expand a little further...
I view them as having a different use:
scsi_xxblock_requests - used for very short duration stalls where hardware/driver is in a short transitory state (request queues are temporarily full, etc).
fc_host_xxblock - used for long durations of I/O stoppage (on the order of 30 seconds ?). I/O is stopped while a link may be jittering, cable accidentally kicked out and reinserted, etc.
-- James
> -----Original Message-----
> From: Smart, James
> Sent: Friday, October 01, 2004 12:15 PM
> To: 'Patrick Mansfield'
> Cc: James.Bottomley@SteelEye.com; linux-scsi@vger.kernel.org;
> hch@infradead.org; andmike@us.ibm.com; mort@wildopensource.com;
> andrew.vasquez@qlogic.com
> Subject: RE: [PATCH] [Update #2] suspending I/Os to a device
>
>
> The difference is, the fc functions:
> - Changes the actual sdev state to indicate a "blocked" state
> (you can see it in sysfs, etc)
> - Starts a timer that will automatically unblock the sdev w/o
> lld intervention
>
> -- james
>
> > What is the difference between these new fc_host_block/unblock and
> > the current scsi_block/unblock_requests?
>
^ permalink raw reply [flat|nested] 6+ messages in thread
end of thread, other threads:[~2004-10-01 16:21 UTC | newest]
Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2004-09-30 20:57 [PATCH] [Update #2] suspending I/Os to a device James.Smart
2004-09-30 21:40 ` James Bottomley
-- strict thread matches above, loose matches on Subject: below --
2004-10-01 13:54 James.Smart
2004-10-01 16:02 ` Patrick Mansfield
2004-10-01 16:15 James.Smart
2004-10-01 16:21 James.Smart
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox