Fix typos concerning hierarchy
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_fc.c
index 05989f1..58afdb4 100644 (file)
@@ -26,7 +26,6 @@
  */
 #include <linux/module.h>
 #include <linux/init.h>
-#include <linux/sched.h>       /* workqueue stuff, HZ */
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -241,15 +240,16 @@ fc_bitfield_name_search(remote_port_roles, fc_remote_port_role_names)
 #define FC_MGMTSRVR_PORTID             0x00000a
 
 
-static void fc_timeout_deleted_rport(void *data);
-static void fc_scsi_scan_rport(void *data);
+static void fc_timeout_deleted_rport(struct work_struct *work);
+static void fc_timeout_fail_rport_io(struct work_struct *work);
+static void fc_scsi_scan_rport(struct work_struct *work);
 
 /*
  * Attribute counts pre object type...
  * Increase these values if you add attributes
  */
 #define FC_STARGET_NUM_ATTRS   3
-#define FC_RPORT_NUM_ATTRS     9
+#define FC_RPORT_NUM_ATTRS     10
 #define FC_HOST_NUM_ATTRS      17
 
 struct fc_internal {
@@ -479,7 +479,8 @@ fc_host_post_event(struct Scsi_Host *shost, u32 event_number,
        event->event_code = event_code;
        event->event_data = event_data;
 
-       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
+       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+                             GFP_KERNEL);
        if (err && (err != -ESRCH))     /* filter no recipient errors */
                /* nlmsg_multicast already kfree_skb'd */
                goto send_fail;
@@ -513,7 +514,7 @@ EXPORT_SYMBOL(fc_host_post_event);
  **/
 void
 fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
-               u32 data_len, char * data_buf, u32 vendor_id)
+               u32 data_len, char * data_buf, u64 vendor_id)
 {
        struct sk_buff *skb;
        struct nlmsghdr *nlh;
@@ -553,7 +554,8 @@ fc_host_post_vendor_event(struct Scsi_Host *shost, u32 event_number,
        event->event_code = FCH_EVT_VENDOR_UNIQUE;
        memcpy(&event->event_data, data_buf, data_len);
 
-       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS);
+       err = nlmsg_multicast(scsi_nl_sock, skb, 0, SCSI_NL_GRP_FC_EVENTS,
+                             GFP_KERNEL);
        if (err && (err != -ESRCH))     /* filter no recipient errors */
                /* nlmsg_multicast already kfree_skb'd */
                goto send_vendor_fail;
@@ -622,11 +624,14 @@ store_fc_rport_##field(struct class_device *cdev, const char *buf,        \
        struct fc_rport *rport = transport_class_to_rport(cdev);        \
        struct Scsi_Host *shost = rport_to_shost(rport);                \
        struct fc_internal *i = to_fc_internal(shost->transportt);      \
+       char *cp;                                                       \
        if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||              \
            (rport->port_state == FC_PORTSTATE_DELETED) ||              \
            (rport->port_state == FC_PORTSTATE_NOTPRESENT))             \
                return -EBUSY;                                          \
-       val = simple_strtoul(buf, NULL, 0);                             \
+       val = simple_strtoul(buf, &cp, 0);                              \
+       if (*cp && (*cp != '\n'))                                       \
+               return -EINVAL;                                         \
        i->f->set_rport_##field(rport, val);                            \
        return count;                                                   \
 }
@@ -708,6 +713,13 @@ static FC_CLASS_DEVICE_ATTR(rport, title, S_IRUGO,                 \
        if (i->f->show_rport_##field)                                   \
                count++
 
+#define SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(field)                                \
+{                                                                      \
+       i->private_rport_attrs[count] = class_device_attr_rport_##field; \
+       i->rport_attrs[count] = &i->private_rport_attrs[count];         \
+       count++;                                                        \
+}
+
 
 /* The FC Transport Remote Port Attributes: */
 
@@ -740,12 +752,14 @@ store_fc_rport_dev_loss_tmo(struct class_device *cdev, const char *buf,
        struct fc_rport *rport = transport_class_to_rport(cdev);
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
+       char *cp;
        if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
            (rport->port_state == FC_PORTSTATE_DELETED) ||
            (rport->port_state == FC_PORTSTATE_NOTPRESENT))
                return -EBUSY;
-       val = simple_strtoul(buf, NULL, 0);
-       if ((val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+       val = simple_strtoul(buf, &cp, 0);
+       if ((*cp && (*cp != '\n')) ||
+           (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
                return -EINVAL;
        i->f->set_rport_dev_loss_tmo(rport, val);
        return count;
@@ -795,6 +809,44 @@ static FC_CLASS_DEVICE_ATTR(rport, roles, S_IRUGO,
 fc_private_rport_rd_enum_attr(port_state, FC_PORTSTATE_MAX_NAMELEN);
 fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);
 
+/*
+ * fast_io_fail_tmo attribute
+ */
+static ssize_t
+show_fc_rport_fast_io_fail_tmo (struct class_device *cdev, char *buf)
+{
+       struct fc_rport *rport = transport_class_to_rport(cdev);
+
+       if (rport->fast_io_fail_tmo == -1)
+               return snprintf(buf, 5, "off\n");
+       return snprintf(buf, 20, "%d\n", rport->fast_io_fail_tmo);
+}
+
+static ssize_t
+store_fc_rport_fast_io_fail_tmo(struct class_device *cdev, const char *buf,
+                          size_t count)
+{
+       int val;
+       char *cp;
+       struct fc_rport *rport = transport_class_to_rport(cdev);
+
+       if ((rport->port_state == FC_PORTSTATE_BLOCKED) ||
+           (rport->port_state == FC_PORTSTATE_DELETED) ||
+           (rport->port_state == FC_PORTSTATE_NOTPRESENT))
+               return -EBUSY;
+       if (strncmp(buf, "off", 3) == 0)
+               rport->fast_io_fail_tmo = -1;
+       else {
+               val = simple_strtoul(buf, &cp, 0);
+               if ((*cp && (*cp != '\n')) ||
+                   (val < 0) || (val >= rport->dev_loss_tmo))
+                       return -EINVAL;
+               rport->fast_io_fail_tmo = val;
+       }
+       return count;
+}
+static FC_CLASS_DEVICE_ATTR(rport, fast_io_fail_tmo, S_IRUGO | S_IWUSR,
+       show_fc_rport_fast_io_fail_tmo, store_fc_rport_fast_io_fail_tmo);
 
 
 /*
@@ -803,7 +855,7 @@ fc_private_rport_rd_attr(scsi_target_id, "%d\n", 20);
 
 /*
  * Note: in the target show function we recognize when the remote
- *  port is in the heirarchy and do not allow the driver to get
+ *  port is in the hierarchy and do not allow the driver to get
  *  involved in sysfs functions. The driver only gets involved if
  *  it's the "old" style that doesn't use rports.
  */
@@ -880,8 +932,11 @@ store_fc_host_##field(struct class_device *cdev, const char *buf,  \
        int val;                                                        \
        struct Scsi_Host *shost = transport_class_to_shost(cdev);       \
        struct fc_internal *i = to_fc_internal(shost->transportt);      \
+       char *cp;                                                       \
                                                                        \
-       val = simple_strtoul(buf, NULL, 0);                             \
+       val = simple_strtoul(buf, &cp, 0);                              \
+       if (*cp && (*cp != '\n'))                                       \
+               return -EINVAL;                                         \
        i->f->set_host_##field(shost, val);                             \
        return count;                                                   \
 }
@@ -1481,6 +1536,8 @@ fc_attach_transport(struct fc_function_template *ft)
        SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(roles);
        SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(port_state);
        SETUP_PRIVATE_RPORT_ATTRIBUTE_RD(scsi_target_id);
+       if (ft->terminate_rport_io)
+               SETUP_PRIVATE_RPORT_ATTRIBUTE_RW(fast_io_fail_tmo);
 
        BUG_ON(count > FC_RPORT_NUM_ATTRS);
 
@@ -1552,10 +1609,10 @@ fc_flush_work(struct Scsi_Host *shost)
  * @delay:     jiffies to delay the work queuing
  *
  * Return value:
- *     0 on success / != 0 for error
+ *     1 on success / 0 already queued / < 0 for error
  **/
 static int
-fc_queue_devloss_work(struct Scsi_Host *shost, struct work_struct *work,
+fc_queue_devloss_work(struct Scsi_Host *shost, struct delayed_work *work,
                                unsigned long delay)
 {
        if (unlikely(!fc_host_devloss_work_q(shost))) {
@@ -1651,18 +1708,32 @@ EXPORT_SYMBOL(fc_remove_host);
  * fc_starget_delete - called to delete the scsi decendents of an rport
  *                  (target and all sdevs)
  *
- * @data:      remote port to be operated on.
+ * @work:      remote port to be operated on.
  **/
 static void
-fc_starget_delete(void *data)
+fc_starget_delete(struct work_struct *work)
 {
-       struct fc_rport *rport = (struct fc_rport *)data;
+       struct fc_rport *rport =
+               container_of(work, struct fc_rport, stgt_delete_work);
        struct Scsi_Host *shost = rport_to_shost(rport);
        unsigned long flags;
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+
+       /*
+        * Involve the LLDD if possible. All io on the rport is to
+        * be terminated, either as part of the dev_loss_tmo callback
+        * processing, or via the terminate_rport_io function.
+        */
+       if (i->f->dev_loss_tmo_callbk)
+               i->f->dev_loss_tmo_callbk(rport);
+       else if (i->f->terminate_rport_io)
+               i->f->terminate_rport_io(rport);
 
        spin_lock_irqsave(shost->host_lock, flags);
        if (rport->flags & FC_RPORT_DEVLOSS_PENDING) {
                spin_unlock_irqrestore(shost->host_lock, flags);
+               if (!cancel_delayed_work(&rport->fail_io_work))
+                       fc_flush_devloss(shost);
                if (!cancel_delayed_work(&rport->dev_loss_work))
                        fc_flush_devloss(shost);
                spin_lock_irqsave(shost->host_lock, flags);
@@ -1677,18 +1748,16 @@ fc_starget_delete(void *data)
 /**
  * fc_rport_final_delete - finish rport termination and delete it.
  *
- * @data:      remote port to be deleted.
+ * @work:      remote port to be deleted.
  **/
 static void
-fc_rport_final_delete(void *data)
+fc_rport_final_delete(struct work_struct *work)
 {
-       struct fc_rport *rport = (struct fc_rport *)data;
+       struct fc_rport *rport =
+               container_of(work, struct fc_rport, rport_delete_work);
        struct device *dev = &rport->dev;
        struct Scsi_Host *shost = rport_to_shost(rport);
-
-       /* Delete SCSI target and sdevs */
-       if (rport->scsi_target_id != -1)
-               fc_starget_delete(data);
+       struct fc_internal *i = to_fc_internal(shost->transportt);
 
        /*
         * if a scan is pending, flush the SCSI Host work_q so that 
@@ -1697,6 +1766,14 @@ fc_rport_final_delete(void *data)
        if (rport->flags & FC_RPORT_SCAN_PENDING)
                scsi_flush_work(shost);
 
+       /* Delete SCSI target and sdevs */
+       if (rport->scsi_target_id != -1)
+               fc_starget_delete(&rport->stgt_delete_work);
+       else if (i->f->dev_loss_tmo_callbk)
+               i->f->dev_loss_tmo_callbk(rport);
+       else if (i->f->terminate_rport_io)
+               i->f->terminate_rport_io(rport);
+
        transport_remove_device(dev);
        device_del(dev);
        transport_destroy_device(dev);
@@ -1748,11 +1825,13 @@ fc_rport_create(struct Scsi_Host *shost, int channel,
        if (fci->f->dd_fcrport_size)
                rport->dd_data = &rport[1];
        rport->channel = channel;
+       rport->fast_io_fail_tmo = -1;
 
-       INIT_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport, rport);
-       INIT_WORK(&rport->scan_work, fc_scsi_scan_rport, rport);
-       INIT_WORK(&rport->stgt_delete_work, fc_starget_delete, rport);
-       INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete, rport);
+       INIT_DELAYED_WORK(&rport->dev_loss_work, fc_timeout_deleted_rport);
+       INIT_DELAYED_WORK(&rport->fail_io_work, fc_timeout_fail_rport_io);
+       INIT_WORK(&rport->scan_work, fc_scsi_scan_rport);
+       INIT_WORK(&rport->stgt_delete_work, fc_starget_delete);
+       INIT_WORK(&rport->rport_delete_work, fc_rport_final_delete);
 
        spin_lock_irqsave(shost->host_lock, flags);
 
@@ -1882,7 +1961,7 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel,
                        }
 
                        if (match) {
-                               struct work_struct *work = 
+                               struct delayed_work *work =
                                                        &rport->dev_loss_work;
 
                                memcpy(&rport->node_name, &ids->node_name,
@@ -1913,11 +1992,13 @@ fc_remote_port_add(struct Scsi_Host *shost, int channel,
                                /* restart the target */
 
                                /*
-                                * Stop the target timer first. Take no action
+                                * Stop the target timers first. Take no action
                                 * on the del_timer failure as the state
                                 * machine state change will validate the
                                 * transaction.
                                 */
+                               if (!cancel_delayed_work(&rport->fail_io_work))
+                                       fc_flush_devloss(shost);
                                if (!cancel_delayed_work(work))
                                        fc_flush_devloss(shost);
 
@@ -2061,6 +2142,7 @@ void
 fc_remote_port_delete(struct fc_rport  *rport)
 {
        struct Scsi_Host *shost = rport_to_shost(rport);
+       struct fc_internal *i = to_fc_internal(shost->transportt);
        int timeout = rport->dev_loss_tmo;
        unsigned long flags;
 
@@ -2091,6 +2173,12 @@ fc_remote_port_delete(struct fc_rport  *rport)
 
        scsi_target_block(&rport->dev);
 
+       /* see if we need to kill io faster than waiting for device loss */
+       if ((rport->fast_io_fail_tmo != -1) &&
+           (rport->fast_io_fail_tmo < timeout) && (i->f->terminate_rport_io))
+               fc_queue_devloss_work(shost, &rport->fail_io_work,
+                                       rport->fast_io_fail_tmo * HZ);
+
        /* cap the length the devices can be blocked until they are deleted */
        fc_queue_devloss_work(shost, &rport->dev_loss_work, timeout * HZ);
 }
@@ -2150,6 +2238,8 @@ fc_remote_port_rolechg(struct fc_rport  *rport, u32 roles)
                 * machine state change will validate the
                 * transaction.
                 */
+               if (!cancel_delayed_work(&rport->fail_io_work))
+                       fc_flush_devloss(shost);
                if (!cancel_delayed_work(&rport->dev_loss_work))
                        fc_flush_devloss(shost);
 
@@ -2175,12 +2265,13 @@ EXPORT_SYMBOL(fc_remote_port_rolechg);
  *                       was a SCSI target (thus was blocked), and failed
  *                       to return in the alloted time.
  * 
- * @data:      rport target that failed to reappear in the alloted time.
+ * @work:      rport target that failed to reappear in the alloted time.
  **/
 static void
-fc_timeout_deleted_rport(void  *data)
+fc_timeout_deleted_rport(struct work_struct *work)
 {
-       struct fc_rport *rport = (struct fc_rport *)data;
+       struct fc_rport *rport =
+               container_of(work, struct fc_rport, dev_loss_work.work);
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
        unsigned long flags;
@@ -2271,14 +2362,38 @@ fc_timeout_deleted_rport(void  *data)
 }
 
 /**
+ * fc_timeout_fail_rport_io - Timeout handler for a fast io failing on a
+ *                       disconnected SCSI target.
+ *
+ * @work:      rport to terminate io on.
+ *
+ * Notes: Only requests the failure of the io, not that all are flushed
+ *    prior to returning.
+ **/
+static void
+fc_timeout_fail_rport_io(struct work_struct *work)
+{
+       struct fc_rport *rport =
+               container_of(work, struct fc_rport, fail_io_work.work);
+       struct Scsi_Host *shost = rport_to_shost(rport);
+       struct fc_internal *i = to_fc_internal(shost->transportt);
+
+       if (rport->port_state != FC_PORTSTATE_BLOCKED)
+               return;
+
+       i->f->terminate_rport_io(rport);
+}
+
+/**
  * fc_scsi_scan_rport - called to perform a scsi scan on a remote port.
  *
- * @data:      remote port to be scanned.
+ * @work:      remote port to be scanned.
  **/
 static void
-fc_scsi_scan_rport(void *data)
+fc_scsi_scan_rport(struct work_struct *work)
 {
-       struct fc_rport *rport = (struct fc_rport *)data;
+       struct fc_rport *rport =
+               container_of(work, struct fc_rport, scan_work);
        struct Scsi_Host *shost = rport_to_shost(rport);
        unsigned long flags;