Merge master.kernel.org:/pub/scm/linux/kernel/git/kyle/parisc-2.6
[cascardo/linux.git] / drivers / scsi / scsi_error.c
index a8ed5a2..b8edcf5 100644 (file)
@@ -359,6 +359,11 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
                return SUCCESS;
 
        case MEDIUM_ERROR:
+               if (sshdr.asc == 0x11 || /* UNRECOVERED READ ERR */
+                   sshdr.asc == 0x13 || /* AMNF DATA FIELD */
+                   sshdr.asc == 0x14) { /* RECORD NOT FOUND */
+                       return SUCCESS;
+               }
                return NEEDS_RETRY;
 
        case HARDWARE_ERROR:
@@ -453,9 +458,140 @@ static void scsi_eh_done(struct scsi_cmnd *scmd)
 }
 
 /**
- * scsi_send_eh_cmnd  - send a cmd to a device as part of error recovery.
- * @scmd:      SCSI Cmd to send.
- * @timeout:   Timeout for cmd.
+ * scsi_try_host_reset - ask host adapter to reset itself
+ * @scmd:      SCSI cmd to send hsot reset.
+ **/
+static int scsi_try_host_reset(struct scsi_cmnd *scmd)
+{
+       unsigned long flags;
+       int rtn;
+
+       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
+                                         __FUNCTION__));
+
+       if (!scmd->device->host->hostt->eh_host_reset_handler)
+               return FAILED;
+
+       rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
+
+       if (rtn == SUCCESS) {
+               if (!scmd->device->host->hostt->skip_settle_delay)
+                       ssleep(HOST_RESET_SETTLE_TIME);
+               spin_lock_irqsave(scmd->device->host->host_lock, flags);
+               scsi_report_bus_reset(scmd->device->host,
+                                     scmd_channel(scmd));
+               spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+       }
+
+       return rtn;
+}
+
+/**
+ * scsi_try_bus_reset - ask host to perform a bus reset
+ * @scmd:      SCSI cmd to send bus reset.
+ **/
+static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
+{
+       unsigned long flags;
+       int rtn;
+
+       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
+                                         __FUNCTION__));
+
+       if (!scmd->device->host->hostt->eh_bus_reset_handler)
+               return FAILED;
+
+       rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
+
+       if (rtn == SUCCESS) {
+               if (!scmd->device->host->hostt->skip_settle_delay)
+                       ssleep(BUS_RESET_SETTLE_TIME);
+               spin_lock_irqsave(scmd->device->host->host_lock, flags);
+               scsi_report_bus_reset(scmd->device->host,
+                                     scmd_channel(scmd));
+               spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
+       }
+
+       return rtn;
+}
+
+/**
+ * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
+ * @scmd:      SCSI cmd used to send BDR
+ *
+ * Notes:
+ *    There is no timeout for this operation.  if this operation is
+ *    unreliable for a given host, then the host itself needs to put a
+ *    timer on it, and set the host back to a consistent state prior to
+ *    returning.
+ **/
+static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
+{
+       int rtn;
+
+       if (!scmd->device->host->hostt->eh_device_reset_handler)
+               return FAILED;
+
+       rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
+       if (rtn == SUCCESS) {
+               scmd->device->was_reset = 1;
+               scmd->device->expecting_cc_ua = 1;
+       }
+
+       return rtn;
+}
+
+static int __scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
+{
+       if (!scmd->device->host->hostt->eh_abort_handler)
+               return FAILED;
+
+       return scmd->device->host->hostt->eh_abort_handler(scmd);
+}
+
+/**
+ * scsi_try_to_abort_cmd - Ask host to abort a running command.
+ * @scmd:      SCSI cmd to abort from Lower Level.
+ *
+ * Notes:
+ *    This function will not return until the user's completion function
+ *    has been called.  there is no timeout on this operation.  if the
+ *    author of the low-level driver wishes this operation to be timed,
+ *    they can provide this facility themselves.  helper functions in
+ *    scsi_error.c can be supplied to make this easier to do.
+ **/
+static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
+{
+       /*
+        * scsi_done was called just after the command timed out and before
+        * we had a chance to process it. (db)
+        */
+       if (scmd->serial_number == 0)
+               return SUCCESS;
+       return __scsi_try_to_abort_cmd(scmd);
+}
+
+static void scsi_abort_eh_cmnd(struct scsi_cmnd *scmd)
+{
+       if (__scsi_try_to_abort_cmd(scmd) != SUCCESS)
+               if (scsi_try_bus_device_reset(scmd) != SUCCESS)
+                       if (scsi_try_bus_reset(scmd) != SUCCESS)
+                               scsi_try_host_reset(scmd);
+}
+
+/**
+ * scsi_send_eh_cmnd  - submit a scsi command as part of error recory
+ * @scmd:       SCSI command structure to hijack
+ * @cmnd:       CDB to send
+ * @cmnd_size:  size in bytes of @cmnd
+ * @timeout:    timeout for this request
+ * @copy_sense: request sense data if set to 1
+ *
+ * This function is used to send a scsi command down to a target device
+ * as part of the error recovery process.  If @copy_sense is 0 the command
+ * sent must be one that does not transfer any data.  If @copy_sense is 1
+ * the command must be REQUEST_SENSE and this functions copies out the
+ * sense buffer it got into @scmd->sense_buffer.
  *
  * Return value:
  *    SUCCESS or FAILED or NEEDS_RETRY
@@ -466,9 +602,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
        struct scsi_device *sdev = scmd->device;
        struct Scsi_Host *shost = sdev->host;
        int old_result = scmd->result;
-       DECLARE_COMPLETION(done);
+       DECLARE_COMPLETION_ONSTACK(done);
        unsigned long timeleft;
        unsigned long flags;
+       struct scatterlist sgl;
        unsigned char old_cmnd[MAX_COMMAND_SIZE];
        enum dma_data_direction old_data_direction;
        unsigned short old_use_sg;
@@ -495,24 +632,29 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
        memcpy(scmd->cmnd, cmnd, cmnd_size);
 
        if (copy_sense) {
-               int gfp_mask = GFP_ATOMIC;
+               gfp_t gfp_mask = GFP_ATOMIC;
 
                if (shost->hostt->unchecked_isa_dma)
                        gfp_mask |= __GFP_DMA;
 
-               scmd->sc_data_direction = DMA_FROM_DEVICE;
-               scmd->request_bufflen = 252;
-               scmd->request_buffer = kzalloc(scmd->request_bufflen, gfp_mask);
-               if (!scmd->request_buffer)
+               sgl.page = alloc_page(gfp_mask);
+               if (!sgl.page)
                        return FAILED;
+               sgl.offset = 0;
+               sgl.length = 252;
+
+               scmd->sc_data_direction = DMA_FROM_DEVICE;
+               scmd->request_bufflen = sgl.length;
+               scmd->request_buffer = &sgl;
+               scmd->use_sg = 1;
        } else {
                scmd->request_buffer = NULL;
                scmd->request_bufflen = 0;
                scmd->sc_data_direction = DMA_NONE;
+               scmd->use_sg = 0;
        }
 
        scmd->underflow = 0;
-       scmd->use_sg = 0;
        scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
 
        if (sdev->scsi_level <= SCSI_2)
@@ -564,13 +706,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
                        break;
                }
        } else {
-               /*
-                * FIXME(eric) - we are not tracking whether we could
-                * abort a timed out command or not.  not sure how
-                * we should treat them differently anyways.
-                */
-               if (shost->hostt->eh_abort_handler)
-                       shost->hostt->eh_abort_handler(scmd);
+               scsi_abort_eh_cmnd(scmd);
                rtn = FAILED;
        }
 
@@ -583,7 +719,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
                        memcpy(scmd->sense_buffer, scmd->request_buffer,
                               sizeof(scmd->sense_buffer));
                }
-               kfree(scmd->request_buffer);
+               __free_page(sgl.page);
        }
 
 
@@ -657,8 +793,8 @@ EXPORT_SYMBOL(scsi_eh_finish_cmd);
  *    XXX: Long term this code should go away, but that needs an audit of
  *         all LLDDs first.
  **/
-static int scsi_eh_get_sense(struct list_head *work_q,
-                            struct list_head *done_q)
+int scsi_eh_get_sense(struct list_head *work_q,
+                     struct list_head *done_q)
 {
        struct scsi_cmnd *scmd, *next;
        int rtn;
@@ -700,31 +836,7 @@ static int scsi_eh_get_sense(struct list_head *work_q,
 
        return list_empty(work_q);
 }
-
-/**
- * scsi_try_to_abort_cmd - Ask host to abort a running command.
- * @scmd:      SCSI cmd to abort from Lower Level.
- *
- * Notes:
- *    This function will not return until the user's completion function
- *    has been called.  there is no timeout on this operation.  if the
- *    author of the low-level driver wishes this operation to be timed,
- *    they can provide this facility themselves.  helper functions in
- *    scsi_error.c can be supplied to make this easier to do.
- **/
-static int scsi_try_to_abort_cmd(struct scsi_cmnd *scmd)
-{
-       if (!scmd->device->host->hostt->eh_abort_handler)
-               return FAILED;
-
-       /*
-        * scsi_done was called just after the command timed out and before
-        * we had a chance to process it. (db)
-        */
-       if (scmd->serial_number == 0)
-               return SUCCESS;
-       return scmd->device->host->hostt->eh_abort_handler(scmd);
-}
+EXPORT_SYMBOL_GPL(scsi_eh_get_sense);
 
 /**
  * scsi_eh_tur - Send TUR to device.
@@ -799,32 +911,6 @@ static int scsi_eh_abort_cmds(struct list_head *work_q,
        return list_empty(work_q);
 }
 
-/**
- * scsi_try_bus_device_reset - Ask host to perform a BDR on a dev
- * @scmd:      SCSI cmd used to send BDR       
- *
- * Notes:
- *    There is no timeout for this operation.  if this operation is
- *    unreliable for a given host, then the host itself needs to put a
- *    timer on it, and set the host back to a consistent state prior to
- *    returning.
- **/
-static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
-{
-       int rtn;
-
-       if (!scmd->device->host->hostt->eh_device_reset_handler)
-               return FAILED;
-
-       rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd);
-       if (rtn == SUCCESS) {
-               scmd->device->was_reset = 1;
-               scmd->device->expecting_cc_ua = 1;
-       }
-
-       return rtn;
-}
-
 /**
  * scsi_eh_try_stu - Send START_UNIT to device.
  * @scmd:      Scsi cmd to send START_UNIT
@@ -955,64 +1041,6 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost,
        return list_empty(work_q);
 }
 
-/**
- * scsi_try_bus_reset - ask host to perform a bus reset
- * @scmd:      SCSI cmd to send bus reset.
- **/
-static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
-{
-       unsigned long flags;
-       int rtn;
-
-       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Bus RST\n",
-                                         __FUNCTION__));
-
-       if (!scmd->device->host->hostt->eh_bus_reset_handler)
-               return FAILED;
-
-       rtn = scmd->device->host->hostt->eh_bus_reset_handler(scmd);
-
-       if (rtn == SUCCESS) {
-               if (!scmd->device->host->hostt->skip_settle_delay)
-                       ssleep(BUS_RESET_SETTLE_TIME);
-               spin_lock_irqsave(scmd->device->host->host_lock, flags);
-               scsi_report_bus_reset(scmd->device->host,
-                                     scmd_channel(scmd));
-               spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
-       }
-
-       return rtn;
-}
-
-/**
- * scsi_try_host_reset - ask host adapter to reset itself
- * @scmd:      SCSI cmd to send hsot reset.
- **/
-static int scsi_try_host_reset(struct scsi_cmnd *scmd)
-{
-       unsigned long flags;
-       int rtn;
-
-       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Snd Host RST\n",
-                                         __FUNCTION__));
-
-       if (!scmd->device->host->hostt->eh_host_reset_handler)
-               return FAILED;
-
-       rtn = scmd->device->host->hostt->eh_host_reset_handler(scmd);
-
-       if (rtn == SUCCESS) {
-               if (!scmd->device->host->hostt->skip_settle_delay)
-                       ssleep(HOST_RESET_SETTLE_TIME);
-               spin_lock_irqsave(scmd->device->host->host_lock, flags);
-               scsi_report_bus_reset(scmd->device->host,
-                                     scmd_channel(scmd));
-               spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
-       }
-
-       return rtn;
-}
-
 /**
  * scsi_eh_bus_reset - send a bus reset 
  * @shost:     scsi host being recovered.
@@ -1396,9 +1424,9 @@ static void scsi_restart_operations(struct Scsi_Host *shost)
  * @eh_done_q: list_head for processed commands.
  *
  **/
-static void scsi_eh_ready_devs(struct Scsi_Host *shost,
-                              struct list_head *work_q,
-                              struct list_head *done_q)
+void scsi_eh_ready_devs(struct Scsi_Host *shost,
+                       struct list_head *work_q,
+                       struct list_head *done_q)
 {
        if (!scsi_eh_stu(shost, work_q, done_q))
                if (!scsi_eh_bus_device_reset(shost, work_q, done_q))
@@ -1406,6 +1434,7 @@ static void scsi_eh_ready_devs(struct Scsi_Host *shost,
                                if (!scsi_eh_host_reset(work_q, done_q))
                                        scsi_eh_offline_sdevs(work_q, done_q);
 }
+EXPORT_SYMBOL_GPL(scsi_eh_ready_devs);
 
 /**
  * scsi_eh_flush_done_q - finish processed commands or retry them.