Merge git://git.kernel.org/pub/scm/linux/kernel/git/jejb/scsi-misc-2.6
[safe/jmp/linux-2.6] / drivers / scsi / scsi_transport_fc.c
index c6f70da..6531c91 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>
@@ -2384,6 +2385,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 +2424,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 +2979,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 +3045,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 +3054,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);
+
+       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;
+               /* 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 +3095,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 +3145,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.
@@ -3769,8 +3811,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)