]> git.hungrycats.org Git - linux/commitdiff
scsi-reset-2.4.18.diff
authorJames Bottomley <jejb@mulgrave.(none)>
Fri, 1 Mar 2002 13:41:16 +0000 (07:41 -0600)
committerJames Bottomley <jejb@mulgrave.(none)>
Fri, 1 Mar 2002 13:41:16 +0000 (07:41 -0600)
SCSI reservation/reset handling

- Make both the old and the new error handlers respond correctly
  to reservation conflicts (i.e. return an I/O error).

- Add a scsi_reset_provider() function for use by the sg driver
  SCSI reset facility.

drivers/scsi/scsi.c
drivers/scsi/scsi.h
drivers/scsi/scsi_error.c
drivers/scsi/scsi_obsolete.c
drivers/scsi/scsi_syms.c

index aa7d5efb873c3b61d64c6e84a213ddb818d5f255..ac4ac575b42ae0180a8b6899bf5c7093ad9eca51 100644 (file)
@@ -156,7 +156,12 @@ void scsi_build_commandblocks(Scsi_Device * SDpnt);
  */
 extern void scsi_old_done(Scsi_Cmnd * SCpnt);
 extern void scsi_old_times_out(Scsi_Cmnd * SCpnt);
+extern int scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag);
 
+/* 
+ * Private interface into the new error handling code.
+ */
+extern int scsi_new_reset(Scsi_Cmnd *SCpnt, unsigned int flag);
 
 /*
  * Function:    scsi_initialize_queue()
@@ -2722,6 +2727,102 @@ void scsi_free_host_dev(Scsi_Device * SDpnt)
         kfree(SDpnt);
 }
 
+/*
+ * Function:   scsi_reset_provider_done_command
+ *
+ * Purpose:    Dummy done routine.
+ *
+ * Notes:      Some low level drivers will call scsi_done and end up here,
+ *             others won't bother.
+ *             We don't want the bogus command used for the bus/device
+ *             reset to find its way into the mid-layer so we intercept
+ *             it here.
+ */
+static void
+scsi_reset_provider_done_command(Scsi_Cmnd *SCpnt)
+{
+}
+
+/*
+ * Function:   scsi_reset_provider
+ *
+ * Purpose:    Send requested reset to a bus or device at any phase.
+ *
+ * Arguments:  device  - device to send reset to
+ *             flag - reset type (see scsi.h)
+ *
+ * Returns:    SUCCESS/FAILURE.
+ *
+ * Notes:      This is used by the SCSI Generic driver to provide
+ *             Bus/Device reset capability.
+ */
+int
+scsi_reset_provider(Scsi_Device *dev, int flag)
+{
+       Scsi_Cmnd SC, *SCpnt = &SC;
+       int rtn;
+
+       memset(&SCpnt->eh_timeout, 0, sizeof(SCpnt->eh_timeout));
+       SCpnt->host                     = dev->host;
+       SCpnt->device                   = dev;
+       SCpnt->target                   = dev->id;
+       SCpnt->lun                      = dev->lun;
+       SCpnt->channel                  = dev->channel;
+       SCpnt->request.rq_status        = RQ_SCSI_BUSY;
+       SCpnt->request.waiting          = NULL;
+       SCpnt->use_sg                   = 0;
+       SCpnt->old_use_sg               = 0;
+       SCpnt->old_cmd_len              = 0;
+       SCpnt->underflow                = 0;
+       SCpnt->transfersize             = 0;
+       SCpnt->resid                    = 0;
+       SCpnt->serial_number            = 0;
+       SCpnt->serial_number_at_timeout = 0;
+       SCpnt->host_scribble            = NULL;
+       SCpnt->next                     = NULL;
+       SCpnt->state                    = SCSI_STATE_INITIALIZING;
+       SCpnt->owner                    = SCSI_OWNER_MIDLEVEL;
+    
+       memset(&SCpnt->cmnd, '\0', sizeof(SCpnt->cmnd));
+    
+       SCpnt->scsi_done                = scsi_reset_provider_done_command;
+       SCpnt->done                     = NULL;
+       SCpnt->reset_chain              = NULL;
+        
+       SCpnt->buffer                   = NULL;
+       SCpnt->bufflen                  = 0;
+       SCpnt->request_buffer           = NULL;
+       SCpnt->request_bufflen          = 0;
+
+       SCpnt->internal_timeout         = NORMAL_TIMEOUT;
+       SCpnt->abort_reason             = DID_ABORT;
+
+       SCpnt->cmd_len                  = 0;
+
+       SCpnt->sc_data_direction        = SCSI_DATA_UNKNOWN;
+       SCpnt->sc_request               = NULL;
+       SCpnt->sc_magic                 = SCSI_CMND_MAGIC;
+
+       /*
+        * Sometimes the command can get back into the timer chain,
+        * so use the pid as an identifier.
+        */
+       SCpnt->pid                      = 0;
+
+       if (dev->host->hostt->use_new_eh_code) {
+               rtn = scsi_new_reset(SCpnt, flag);
+       } else {
+               unsigned long flags;
+
+               spin_lock_irqsave(&io_request_lock, flags);
+               rtn = scsi_old_reset(SCpnt, flag);
+               spin_unlock_irqrestore(&io_request_lock, flags);
+       }
+
+       scsi_delete_timer(SCpnt);
+       return rtn;
+}
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index 7b434522f3ea30154d1f46c94d8b3b6ceb21fa23..12c248df2530082f7b927ad7a22817bebff08cb2 100644 (file)
@@ -849,6 +849,16 @@ struct scsi_cmnd {
        current->state = TASK_RUNNING;  \
     }; }
 
+/*
+ * old style reset request from external source
+ * (private to sg.c and scsi_error.c, supplied by scsi_obsolete.c)
+ */
+#define SCSI_TRY_RESET_DEVICE  1
+#define SCSI_TRY_RESET_BUS     2
+#define SCSI_TRY_RESET_HOST    3
+
+extern int scsi_reset_provider(Scsi_Device *, int);
+
 #endif
 
 /*
index 91a1e0258d813add3a2e0a71b4c7c6c63f6cd7fb..25f7b1130fbb732a05b7f2742e326c0d7bc089a2 100644 (file)
@@ -984,15 +984,24 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt)
        case DID_SOFT_ERROR:
                goto maybe_retry;
 
+       case DID_ERROR:
+               if (msg_byte(SCpnt->result) == COMMAND_COMPLETE &&
+                   status_byte(SCpnt->result) == RESERVATION_CONFLICT)
+                       /*
+                        * execute reservation conflict processing code
+                        * lower down
+                        */
+                       break;
+               /* FALLTHROUGH */
+
        case DID_BUS_BUSY:
        case DID_PARITY:
-       case DID_ERROR:
                goto maybe_retry;
        case DID_TIME_OUT:
                /*
-                  * When we scan the bus, we get timeout messages for
-                  * these commands if there is no device available.
-                  * Other hosts report DID_NO_CONNECT for the same thing.
+                * When we scan the bus, we get timeout messages for
+                * these commands if there is no device available.
+                * Other hosts report DID_NO_CONNECT for the same thing.
                 */
                if ((SCpnt->cmnd[0] == TEST_UNIT_READY ||
                     SCpnt->cmnd[0] == INQUIRY)) {
@@ -1053,8 +1062,13 @@ int scsi_decide_disposition(Scsi_Cmnd * SCpnt)
                 */
                return SUCCESS;
        case BUSY:
-       case RESERVATION_CONFLICT:
                goto maybe_retry;
+
+       case RESERVATION_CONFLICT:
+               printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", 
+                      SCpnt->host->host_no, SCpnt->channel,
+                      SCpnt->device->id, SCpnt->device->lun);
+               return SUCCESS; /* causes immediate I/O error */
        default:
                return FAILED;
        }
@@ -1959,6 +1973,45 @@ void scsi_error_handler(void *data)
                up(host->eh_notify);
 }
 
+/*
+ * Function:   scsi_new_reset
+ *
+ * Purpose:    Send requested reset to a bus or device at any phase.
+ *
+ * Arguments:  SCpnt   - command ptr to send reset with (usually a dummy)
+ *             flag - reset type (see scsi.h)
+ *
+ * Returns:    SUCCESS/FAILURE.
+ *
+ * Notes:      This is used by the SCSI Generic driver to provide
+ *             Bus/Device reset capability.
+ */
+int
+scsi_new_reset(Scsi_Cmnd *SCpnt, int flag)
+{
+       int rtn;
+
+       switch(flag) {
+       case SCSI_TRY_RESET_DEVICE:
+               rtn = scsi_try_bus_device_reset(SCpnt, 0);
+               if (rtn == SUCCESS)
+                       break;
+               /* FALLTHROUGH */
+       case SCSI_TRY_RESET_BUS:
+               rtn = scsi_try_bus_reset(SCpnt);
+               if (rtn == SUCCESS)
+                       break;
+               /* FALLTHROUGH */
+       case SCSI_TRY_RESET_HOST:
+               rtn = scsi_try_host_reset(SCpnt);
+               break;
+       default:
+               rtn = FAILED;
+       }
+
+       return rtn;
+}
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index 632548bc523620db8daf6af95597e9d49ccb8fd9..488bb26672d53168ba2aee6d3e9d47796c5a7c23 100644 (file)
@@ -503,11 +503,18 @@ void scsi_old_done(Scsi_Cmnd * SCpnt)
                                        break;
 
                                case RESERVATION_CONFLICT:
-                                       printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
-                                              " reset.\n", SCpnt->host->host_no, SCpnt->channel);
-                                       scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS);
-                                       status = REDO;
+                                       /*
+                                        * Most HAs will return an error for
+                                        * this, so usually reservation
+                                        * conflicts will  be processed under
+                                        * DID_ERROR code
+                                        */
+                                       printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", 
+                                              SCpnt->host->host_no, SCpnt->channel,
+                                              SCpnt->device->id, SCpnt->device->lun);
+                                       status = CMD_FINISHED; /* returns I/O error */
                                        break;
+                                        
                                default:
                                        printk("Internal error %s %d \n"
                                         "status byte = %d \n", __FILE__,
@@ -557,6 +564,14 @@ void scsi_old_done(Scsi_Cmnd * SCpnt)
                exit = (DRIVER_HARD | SUGGEST_ABORT);
                break;
        case DID_ERROR:
+               if (msg_byte(result) == COMMAND_COMPLETE &&
+                   status_byte(result) == RESERVATION_CONFLICT) {
+                       printk("scsi%d (%d,%d,%d) : RESERVATION CONFLICT\n", 
+                              SCpnt->host->host_no, SCpnt->channel,
+                              SCpnt->device->id, SCpnt->device->lun);
+                       status = CMD_FINISHED; /* returns I/O error */
+                       break;
+               }
                status = MAYREDO;
                exit = (DRIVER_HARD | SUGGEST_ABORT);
                break;
@@ -1099,6 +1114,34 @@ int update_timeout(Scsi_Cmnd * SCset, int timeout)
 }
 
 
+/*
+ * This function exports SCSI Bus, Device or Host reset capability
+ * and is for use with the SCSI generic driver.
+ */
+int
+scsi_old_reset(Scsi_Cmnd *SCpnt, unsigned int flag)
+{
+       unsigned int old_flags = SCSI_RESET_SYNCHRONOUS;
+
+       switch(flag) {
+       case SCSI_TRY_RESET_DEVICE:
+               /* no suggestion flags to add, device reset is default */
+               break;
+       case SCSI_TRY_RESET_BUS:
+               old_flags |= SCSI_RESET_SUGGEST_BUS_RESET;
+               break;
+       case SCSI_TRY_RESET_HOST:
+               old_flags |= SCSI_RESET_SUGGEST_HOST_RESET;
+               break;
+       default:
+               return FAILED;
+       }
+
+       if (scsi_reset(SCpnt, old_flags))
+               return FAILED;
+       return SUCCESS;
+}
+
 /*
  * Overrides for Emacs so that we follow Linus's tabbing style.
  * Emacs will notice this stuff at the end of the file and automatically
index bdf2142b851ccd21a64d564d4b76412b8123d9e4..8a25a456ae591738672ab5730d5cdfe7d58e25c8 100644 (file)
@@ -83,6 +83,11 @@ EXPORT_SYMBOL(scsi_end_request);
 EXPORT_SYMBOL(scsi_register_blocked_host);
 EXPORT_SYMBOL(scsi_deregister_blocked_host);
 
+/*
+ * This symbol is for the highlevel drivers (e.g. sg) only.
+ */
+EXPORT_SYMBOL(scsi_reset_provider);
+
 /*
  * These are here only while I debug the rest of the scsi stuff.
  */