[SCSI] FC transport : Avoid device offline cases by stalling aborts until device...
authorJames Smart <James.Smart@Emulex.Com>
Mon, 13 Mar 2006 13:28:57 +0000 (08:28 -0500)
committerJames Bottomley <jejb@mulgrave.il.steeleye.com>
Mon, 13 Mar 2006 14:58:58 +0000 (08:58 -0600)
This moves the eh_timed_out functionality from the scsi_host_template
to the transport_template. Given that this is now a transport function,
the EH_RESET_TIMER case no longer caps the timer reschedulings. The
transport guarantees that this is not an infinite condition.

Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
drivers/scsi/scsi_error.c
drivers/scsi/scsi_transport_fc.c
include/scsi/scsi_host.h
include/scsi/scsi_transport.h

index 5cc97b7..9cf0206 100644 (file)
@@ -29,6 +29,7 @@
 #include <scsi/scsi_dbg.h>
 #include <scsi/scsi_device.h>
 #include <scsi/scsi_eh.h>
+#include <scsi/scsi_transport.h>
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_ioctl.h>
 #include <scsi/scsi_request.h>
@@ -163,16 +164,12 @@ void scsi_times_out(struct scsi_cmnd *scmd)
 {
        scsi_log_completion(scmd, TIMEOUT_ERROR);
 
-       if (scmd->device->host->hostt->eh_timed_out)
-               switch (scmd->device->host->hostt->eh_timed_out(scmd)) {
+       if (scmd->device->host->transportt->eh_timed_out)
+               switch (scmd->device->host->transportt->eh_timed_out(scmd)) {
                case EH_HANDLED:
                        __scsi_done(scmd);
                        return;
                case EH_RESET_TIMER:
-                       /* This allows a single retry even of a command
-                        * with allowed == 0 */
-                       if (scmd->retries++ > scmd->allowed)
-                               break;
                        scsi_add_timer(scmd, scmd->timeout_per_command,
                                       scsi_times_out);
                        return;
index 56012b2..3c3baa9 100644 (file)
@@ -31,6 +31,7 @@
 #include <scsi/scsi_host.h>
 #include <scsi/scsi_transport.h>
 #include <scsi/scsi_transport_fc.h>
+#include <scsi/scsi_cmnd.h>
 #include "scsi_priv.h"
 
 /*
@@ -1090,6 +1091,40 @@ static int fc_rport_match(struct attribute_container *cont,
 }
 
 
+/**
+ * fc_timed_out - FC Transport I/O timeout intercept handler
+ *
+ * @scmd:      The SCSI command which timed out
+ *
+ * This routine protects against error handlers getting invoked while a
+ * rport is in a blocked state, typically due to a temporarily loss of
+ * connectivity. If the error handlers are allowed to proceed, requests
+ * to abort i/o, reset the target, etc will likely fail as there is no way
+ * to communicate with the device to perform the requested function. These
+ * failures may result in the midlayer taking the device offline, requiring
+ * manual intervention to restore operation.
+ *
+ * This routine, called whenever an i/o times out, validates the state of
+ * the underlying rport. If the rport is blocked, it returns
+ * EH_RESET_TIMER, which will continue to reschedule the timeout.
+ * Eventually, either the device will return, or devloss_tmo will fire,
+ * and when the timeout then fires, it will be handled normally.
+ * If the rport is not blocked, normal error handling continues.
+ *
+ * Notes:
+ *     This routine assumes no locks are held on entry.
+ **/
+static enum scsi_eh_timer_return
+fc_timed_out(struct scsi_cmnd *scmd)
+{
+       struct fc_rport *rport = starget_to_rport(scsi_target(scmd->device));
+
+       if (rport->port_state == FC_PORTSTATE_BLOCKED)
+               return EH_RESET_TIMER;
+
+       return EH_NOT_HANDLED;
+}
+
 /*
  * Must be called with shost->host_lock held
  */
@@ -1146,6 +1181,8 @@ fc_attach_transport(struct fc_function_template *ft)
        /* Transport uses the shost workq for scsi scanning */
        i->t.create_work_queue = 1;
 
+       i->t.eh_timed_out = fc_timed_out;
+
        i->t.user_scan = fc_user_scan;
        
        /*
index 8279929..a6cf3e5 100644 (file)
@@ -146,20 +146,6 @@ struct scsi_host_template {
        int (* eh_bus_reset_handler)(struct scsi_cmnd *);
        int (* eh_host_reset_handler)(struct scsi_cmnd *);
 
-       /*
-        * This is an optional routine to notify the host that the scsi
-        * timer just fired.  The returns tell the timer routine what to
-        * do about this:
-        *
-        * EH_HANDLED:          I fixed the error, please complete the command
-        * EH_RESET_TIMER:      I need more time, reset the timer and
-        *                      begin counting again
-        * EH_NOT_HANDLED       Begin normal error recovery
-        *
-        * Status: OPTIONAL
-        */
-       enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
-
        /*
         * Before the mid layer attempts to scan for a new device where none
         * currently exists, it will call this entry in your driver.  Should
index e7b1054..b3657f1 100644 (file)
@@ -48,6 +48,17 @@ struct scsi_transport_template {
         * True if the transport wants to use a host-based work-queue
         */
        unsigned int create_work_queue : 1;
+
+       /*
+        * This is an optional routine that allows the transport to become
+        * involved when a scsi io timer fires. The return value tells the
+        * timer routine how to finish the io timeout handling:
+        * EH_HANDLED:          I fixed the error, please complete the command
+        * EH_RESET_TIMER:      I need more time, reset the timer and
+        *                      begin counting again
+        * EH_NOT_HANDLED       Begin normal error recovery
+        */
+       enum scsi_eh_timer_return (* eh_timed_out)(struct scsi_cmnd *);
 };
 
 #define transport_class_to_shost(tc) \