]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/scsi/scsi_error.c
[SCSI] Fix sense key MEDIUM ERROR processing and retry
[linux-2.6-omap-h63xx.git] / drivers / scsi / scsi_error.c
index 3d355d054612bd0ec6a30cda4af65ff8aacd6001..2dce06a58c08292ab42dc001b8f84aac4605d600 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,18 @@ 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_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
@@ -469,6 +483,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
        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 +510,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)
@@ -583,7 +603,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 +677,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,6 +720,7 @@ static int scsi_eh_get_sense(struct list_head *work_q,
 
        return list_empty(work_q);
 }
+EXPORT_SYMBOL_GPL(scsi_eh_get_sense);
 
 /**
  * scsi_try_to_abort_cmd - Ask host to abort a running command.
@@ -1396,9 +1417,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 +1427,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.