Merge branch 'for-rmk/samsung6' of git://git.fluff.org/bjdooks/linux into devel-stable
[linux-2.6.git] / drivers / scsi / scsi_transport_fc.c
index 751f239..653f22a 100644 (file)
@@ -27,6 +27,7 @@
  */
 #include <linux/module.h>
 #include <linux/init.h>
+#include <linux/delay.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
@@ -291,7 +292,7 @@ static void fc_scsi_scan_rport(struct work_struct *work);
 #define FC_STARGET_NUM_ATTRS   3
 #define FC_RPORT_NUM_ATTRS     10
 #define FC_VPORT_NUM_ATTRS     9
-#define FC_HOST_NUM_ATTRS      21
+#define FC_HOST_NUM_ATTRS      22
 
 struct fc_internal {
        struct scsi_transport_template t;
@@ -648,11 +649,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)
@@ -2384,6 +2396,7 @@ fc_rport_final_delete(struct work_struct *work)
        struct Scsi_Host *shost = rport_to_shost(rport);
        struct fc_internal *i = to_fc_internal(shost->transportt);
        unsigned long flags;
+       int do_callback = 0;
 
        /*
         * if a scan is pending, flush the SCSI Host work_q so that
@@ -2422,8 +2435,15 @@ fc_rport_final_delete(struct work_struct *work)
         * Avoid this call if we already called it when we preserved the
         * rport for the binding.
         */
+       spin_lock_irqsave(shost->host_lock, flags);
        if (!(rport->flags & FC_RPORT_DEVLOSS_CALLBK_DONE) &&
-           (i->f->dev_loss_tmo_callbk))
+           (i->f->dev_loss_tmo_callbk)) {
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               do_callback = 1;
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+
+       if (do_callback)
                i->f->dev_loss_tmo_callbk(rport);
 
        fc_bsg_remove(rport->rqst_q);
@@ -2970,6 +2990,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
        struct fc_internal *i = to_fc_internal(shost->transportt);
        struct fc_host_attrs *fc_host = shost_to_fc_host(shost);
        unsigned long flags;
+       int do_callback = 0;
 
        spin_lock_irqsave(shost->host_lock, flags);
 
@@ -3035,7 +3056,6 @@ fc_timeout_deleted_rport(struct work_struct *work)
        rport->roles = FC_PORT_ROLE_UNKNOWN;
        rport->port_state = FC_PORTSTATE_NOTPRESENT;
        rport->flags &= ~FC_RPORT_FAST_FAIL_TIMEDOUT;
-       rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
 
        /*
         * Pre-emptively kill I/O rather than waiting for the work queue
@@ -3045,32 +3065,40 @@ fc_timeout_deleted_rport(struct work_struct *work)
        spin_unlock_irqrestore(shost->host_lock, flags);
        fc_terminate_rport_io(rport);
 
-       BUG_ON(rport->port_state != FC_PORTSTATE_NOTPRESENT);
+       spin_lock_irqsave(shost->host_lock, flags);
 
-       /* remove the identifiers that aren't used in the consisting binding */
-       switch (fc_host->tgtid_bind_type) {
-       case FC_TGTID_BIND_BY_WWPN:
-               rport->node_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_WWNN:
-               rport->port_name = -1;
-               rport->port_id = -1;
-               break;
-       case FC_TGTID_BIND_BY_ID:
-               rport->node_name = -1;
-               rport->port_name = -1;
-               break;
-       case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
-               break;
+       if (rport->port_state == FC_PORTSTATE_NOTPRESENT) {     /* still missing */
+
+               /* remove the identifiers that aren't used in the consisting binding */
+               switch (fc_host->tgtid_bind_type) {
+               case FC_TGTID_BIND_BY_WWPN:
+                       rport->node_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_WWNN:
+                       rport->port_name = -1;
+                       rport->port_id = -1;
+                       break;
+               case FC_TGTID_BIND_BY_ID:
+                       rport->node_name = -1;
+                       rport->port_name = -1;
+                       break;
+               case FC_TGTID_BIND_NONE:        /* to keep compiler happy */
+                       break;
+               }
+
+               /*
+                * As this only occurs if the remote port (scsi target)
+                * went away and didn't come back - we'll remove
+                * all attached scsi devices.
+                */
+               rport->flags |= FC_RPORT_DEVLOSS_CALLBK_DONE;
+               fc_queue_work(shost, &rport->stgt_delete_work);
+
+               do_callback = 1;
        }
 
-       /*
-        * As this only occurs if the remote port (scsi target)
-        * went away and didn't come back - we'll remove
-        * all attached scsi devices.
-        */
-       fc_queue_work(shost, &rport->stgt_delete_work);
+       spin_unlock_irqrestore(shost->host_lock, flags);
 
        /*
         * Notify the driver that the rport is now dead. The LLDD will
@@ -3078,7 +3106,7 @@ fc_timeout_deleted_rport(struct work_struct *work)
         *
         * Note: we set the CALLBK_DONE flag above to correspond
         */
-       if (i->f->dev_loss_tmo_callbk)
+       if (do_callback && i->f->dev_loss_tmo_callbk)
                i->f->dev_loss_tmo_callbk(rport);
 }
 
@@ -3128,6 +3156,31 @@ 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. This is necessary to avoid the scsi_eh
+ * failing recovery actions for blocked rports which would lead to
+ * offlined SCSI devices.
+ */
+void 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) {
+               spin_unlock_irqrestore(shost->host_lock, flags);
+               msleep(1000);
+               spin_lock_irqsave(shost->host_lock, flags);
+       }
+       spin_unlock_irqrestore(shost->host_lock, flags);
+}
+EXPORT_SYMBOL(fc_block_scsi_eh);
 
 /**
  * fc_vport_setup - allocates and creates a FC virtual port.
@@ -3474,7 +3527,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);
        }
@@ -3586,6 +3642,7 @@ enum fc_dispatch_result {
 
 /**
  * fc_bsg_host_dispatch - process fc host bsg requests and dispatch to LLDD
+ * @q:         fc host request queue
  * @shost:     scsi host rport attached to
  * @job:       bsg job to be processed
  */
@@ -3655,6 +3712,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);
@@ -3693,6 +3751,7 @@ fc_bsg_goose_queue(struct fc_rport *rport)
 
 /**
  * fc_bsg_rport_dispatch - process rport bsg requests and dispatch to LLDD
+ * @q:         rport request queue
  * @shost:     scsi host rport attached to
  * @rport:     rport request destined to
  * @job:       bsg job to be processed
@@ -3739,6 +3798,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);
@@ -3765,8 +3825,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)
@@ -3795,6 +3856,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);