[SCSI] qla2xxx: Support for asynchronous TM and Marker IOCBs.
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_fc.c
index f436e03..0681378 100644 (file)
@@ -27,6 +27,8 @@
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -474,7 +476,8 @@ MODULE_PARM_DESC(dev_loss_tmo,
                 "Maximum number of seconds that the FC transport should"
                 " insulate the loss of a remote port. Once this value is"
                 " exceeded, the scsi target is removed. Value should be"
-                " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT.");
+                " between 1 and SCSI_DEVICE_BLOCK_MAX_TIMEOUT if"
+                " fast_io_fail_tmo is not set.");
 
 /*
  * Netlink Infrastructure
@@ -648,11 +651,22 @@ static __init int fc_transport_init(void)
                return error;
        error = transport_class_register(&fc_vport_class);
        if (error)
-               return error;
+               goto unreg_host_class;
        error = transport_class_register(&fc_rport_class);
        if (error)
-               return error;
-       return transport_class_register(&fc_transport_class);
+               goto unreg_vport_class;
+       error = transport_class_register(&fc_transport_class);
+       if (error)
+               goto unreg_rport_class;
+       return 0;
+
+unreg_rport_class:
+       transport_class_unregister(&fc_rport_class);
+unreg_vport_class:
+       transport_class_unregister(&fc_vport_class);
+unreg_host_class:
+       transport_class_unregister(&fc_host_class);
+       return error;
 }
 
 static void __exit fc_transport_exit(void)
@@ -820,7 +834,7 @@ static ssize_t
 store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       int val;
+       unsigned long val;
        struct fc_rport *rport = transport_class_to_rport(dev);
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
@@ -830,9 +844,23 @@ store_fc_rport_dev_loss_tmo(struct device *dev, struct device_attribute *attr,
            (rport->port_state == FC_PORTSTATE_NOTPRESENT))
                return -EBUSY;
        val = simple_strtoul(buf, &cp, 0);
-       if ((*cp && (*cp != '\n')) ||
-           (val < 0) || (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+       if ((*cp && (*cp != '\n')) || (val < 0))
+               return -EINVAL;
+
+       /*
+        * Check for overflow; dev_loss_tmo is u32
+        */
+       if (val > UINT_MAX)
+               return -EINVAL;
+
+       /*
+        * If fast_io_fail is off we have to cap
+        * dev_loss_tmo at SCSI_DEVICE_BLOCK_MAX_TIMEOUT
+        */
+       if (rport->fast_io_fail_tmo == -1 &&
+           val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT)
                return -EINVAL;
+
        i->f->set_rport_dev_loss_tmo(rport, val);
        return count;
 }
@@ -913,9 +941,16 @@ store_fc_rport_fast_io_fail_tmo(struct device *dev,
                rport->fast_io_fail_tmo = -1;
        else {
                val = simple_strtoul(buf, &cp, 0);
-               if ((*cp && (*cp != '\n')) ||
-                   (val < 0) || (val >= rport->dev_loss_tmo))
+               if ((*cp && (*cp != '\n')) || (val < 0))
                        return -EINVAL;
+               /*
+                * Cap fast_io_fail by dev_loss_tmo or
+                * SCSI_DEVICE_BLOCK_MAX_TIMEOUT.
+                */
+               if ((val >= rport->dev_loss_tmo) ||
+                   (val > SCSI_DEVICE_BLOCK_MAX_TIMEOUT))
+                       return -EINVAL;
+
                rport->fast_io_fail_tmo = val;
        }
        return count;
@@ -1204,6 +1239,15 @@ store_fc_vport_delete(struct device *dev, struct device_attribute *attr,
 {
        struct fc_vport *vport = transport_class_to_vport(dev);
        struct Scsi_Host *shost = vport_to_shost(vport);
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING)) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               return -EBUSY;
+       }
+       vport->flags |= FC_VPORT_DELETING;
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        fc_queue_work(shost, &vport->vport_delete_work);
        return count;
@@ -1793,6 +1837,9 @@ store_fc_host_vport_delete(struct device *dev, struct device_attribute *attr,
        list_for_each_entry(vport, &fc_host->vports, peers) {
                if ((vport->channel == 0) &&
                    (vport->port_name == wwpn) && (vport->node_name == wwnn)) {
+                       if (vport->flags & (FC_VPORT_DEL | FC_VPORT_CREATING))
+                               break;
+                       vport->flags |= FC_VPORT_DELETING;
                        match = 1;
                        break;
                }
@@ -2824,7 +2871,7 @@ void
 fc_remote_port_delete(struct fc_rport  *rport)
 {
        struct Scsi_Host *shost = rport_to_shost(rport);
-       int timeout = rport->dev_loss_tmo;
+       unsigned long timeout = rport->dev_loss_tmo;
        unsigned long flags;
 
        /*
@@ -3144,6 +3191,41 @@ fc_scsi_scan_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
 }
 
+/**
+ * fc_block_scsi_eh - Block SCSI eh thread for blocked fc_rport
+ * @cmnd: SCSI command that scsi_eh is trying to recover
+ *
+ * This routine can be called from a FC LLD scsi_eh callback. It
+ * blocks the scsi_eh thread until the fc_rport leaves the
+ * FC_PORTSTATE_BLOCKED, or the fast_io_fail_tmo fires. This is
+ * necessary to avoid the scsi_eh failing recovery actions for blocked
+ * rports which would lead to offlined SCSI devices.
+ *
+ * Returns: 0 if the fc_rport left the state FC_PORTSTATE_BLOCKED.
+ *         FAST_IO_FAIL if the fast_io_fail_tmo fired, this should be
+ *         passed back to scsi_eh.
+ */
+int fc_block_scsi_eh(struct scsi_cmnd *cmnd)
+{
+       struct Scsi_Host *shost = cmnd->device->host;
+       struct fc_rport *rport = starget_to_rport(scsi_target(cmnd->device));
+       unsigned long flags;
+
+       spin_lock_irqsave(shost->host_lock, flags);
+       while (rport->port_state == FC_PORTSTATE_BLOCKED &&
+              !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               msleep(1000);
+               spin_lock_irqsave(shost->host_lock, flags);
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT)
+               return FAST_IO_FAIL;
+
+       return 0;
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
 
 /**
  * fc_vport_setup - allocates and creates a FC virtual port.
@@ -3317,18 +3399,6 @@ fc_vport_terminate(struct fc_vport *vport)
        unsigned long flags;
        int stat;
 
-       spin_lock_irqsave(shost->host_lock, flags);
-       if (vport->flags & FC_VPORT_CREATING) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               return -EBUSY;
-       }
-       if (vport->flags & (FC_VPORT_DEL)) {
-               spin_unlock_irqrestore(shost->host_lock, flags);
-               return -EALREADY;
-       }
-       vport->flags |= FC_VPORT_DELETING;
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
        if (i->f->vport_delete)
                stat = i->f->vport_delete(vport);
        else
@@ -3490,7 +3560,10 @@ fc_bsg_job_timeout(struct request *req)
        if (!done && i->f->bsg_timeout) {
                /* call LLDD to abort the i/o as it has timed out */
                err = i->f->bsg_timeout(job);
-               if (err)
+               if (err == -EAGAIN) {
+                       job->ref_cnt--;
+                       return BLK_EH_RESET_TIMER;
+               } else if (err)
                        printk(KERN_ERR "ERROR: FC BSG request timeout - LLD "
                                "abort failed with status %d\n", err);
        }
@@ -3672,6 +3745,7 @@ fc_bsg_host_dispatch(struct request_queue *q, struct Scsi_Host *shost,
 fail_host_msg:
        /* return the errno failure code as the only status */
        BUG_ON(job->reply_len < sizeof(uint32_t));
+       job->reply->reply_payload_rcv_len = 0;
        job->reply->result = ret;
        job->reply_len = sizeof(uint32_t);
        fc_bsg_jobdone(job);
@@ -3757,6 +3831,7 @@ check_bidi:
 fail_rport_msg:
        /* return the errno failure code as the only status */
        BUG_ON(job->reply_len < sizeof(uint32_t));
+       job->reply->reply_payload_rcv_len = 0;
        job->reply->result = ret;
        job->reply_len = sizeof(uint32_t);
        fc_bsg_jobdone(job);
@@ -3783,8 +3858,9 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
                return;
 
        while (!blk_queue_plugged(q)) {
-               if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED))
-                               break;
+               if (rport && (rport->port_state == FC_PORTSTATE_BLOCKED) &&
+                   !(rport->flags & FC_RPORT_FAST_FAIL_TIMEDOUT))
+                       break;
 
                req = blk_fetch_request(q);
                if (!req)
@@ -3793,7 +3869,7 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
                if (rport && (rport->port_state != FC_PORTSTATE_ONLINE)) {
                        req->errors = -ENXIO;
                        spin_unlock_irq(q->queue_lock);
-                       blk_end_request(req, -ENXIO, blk_rq_bytes(req));
+                       blk_end_request_all(req, -ENXIO);
                        spin_lock_irq(q->queue_lock);
                        continue;
                }
@@ -3803,7 +3879,7 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
                ret = fc_req_to_bsgjob(shost, rport, req);
                if (ret) {
                        req->errors = ret;
-                       blk_end_request(req, ret, blk_rq_bytes(req));
+                       blk_end_request_all(req, ret);
                        spin_lock_irq(q->queue_lock);
                        continue;
                }
@@ -3813,6 +3889,7 @@ fc_bsg_request_handler(struct request_queue *q, struct Scsi_Host *shost,
                /* check if we have the msgcode value at least */
                if (job->request_len < sizeof(uint32_t)) {
                        BUG_ON(job->reply_len < sizeof(uint32_t));
+                       job->reply->reply_payload_rcv_len = 0;
                        job->reply->result = -ENOMSG;
                        job->reply_len = sizeof(uint32_t);
                        fc_bsg_jobdone(job);