linux 2.6.16.38 w/ vs2.0.3-rc1
[linux-2.6.git] / drivers / scsi / scsi_error.c
index 3d355d0..ff82ccf 100644 (file)
 #include <linux/delay.h>
 
 #include <scsi/scsi.h>
-#include <scsi/scsi_cmnd.h>
 #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>
 
 #include "scsi_priv.h"
 #include "scsi_logging.h"
@@ -57,28 +56,6 @@ void scsi_eh_wakeup(struct Scsi_Host *shost)
        }
 }
 
-/**
- * scsi_schedule_eh - schedule EH for SCSI host
- * @shost:     SCSI host to invoke error handling on.
- *
- * Schedule SCSI EH without scmd.
- **/
-void scsi_schedule_eh(struct Scsi_Host *shost)
-{
-       unsigned long flags;
-
-       spin_lock_irqsave(shost->host_lock, flags);
-
-       if (scsi_host_set_state(shost, SHOST_RECOVERY) == 0 ||
-           scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY) == 0) {
-               shost->host_eh_scheduled++;
-               scsi_eh_wakeup(shost);
-       }
-
-       spin_unlock_irqrestore(shost->host_lock, flags);
-}
-EXPORT_SYMBOL_GPL(scsi_schedule_eh);
-
 /**
  * scsi_eh_scmd_add - add scsi cmd to error handling.
  * @scmd:      scmd to run eh on.
@@ -186,12 +163,16 @@ void scsi_times_out(struct scsi_cmnd *scmd)
 {
        scsi_log_completion(scmd, TIMEOUT_ERROR);
 
-       if (scmd->device->host->transportt->eh_timed_out)
-               switch (scmd->device->host->transportt->eh_timed_out(scmd)) {
+       if (scmd->device->host->hostt->eh_timed_out)
+               switch (scmd->device->host->hostt->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;
@@ -460,72 +441,21 @@ static void scsi_eh_done(struct scsi_cmnd *scmd)
  * Return value:
  *    SUCCESS or FAILED or NEEDS_RETRY
  **/
-static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
-                            int cmnd_size, int timeout, int copy_sense)
+static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout)
 {
        struct scsi_device *sdev = scmd->device;
        struct Scsi_Host *shost = sdev->host;
-       int old_result = scmd->result;
-       DECLARE_COMPLETION_ONSTACK(done);
+       DECLARE_COMPLETION(done);
        unsigned long timeleft;
        unsigned long flags;
-       unsigned char old_cmnd[MAX_COMMAND_SIZE];
-       enum dma_data_direction old_data_direction;
-       unsigned short old_use_sg;
-       unsigned char old_cmd_len;
-       unsigned old_bufflen;
-       void *old_buffer;
        int rtn;
 
-       /*
-        * We need saved copies of a number of fields - this is because
-        * error handling may need to overwrite these with different values
-        * to run different commands, and once error handling is complete,
-        * we will need to restore these values prior to running the actual
-        * command.
-        */
-       old_buffer = scmd->request_buffer;
-       old_bufflen = scmd->request_bufflen;
-       memcpy(old_cmnd, scmd->cmnd, sizeof(scmd->cmnd));
-       old_data_direction = scmd->sc_data_direction;
-       old_cmd_len = scmd->cmd_len;
-       old_use_sg = scmd->use_sg;
-
-       memset(scmd->cmnd, 0, sizeof(scmd->cmnd));
-       memcpy(scmd->cmnd, cmnd, cmnd_size);
-
-       if (copy_sense) {
-               int 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)
-                       return FAILED;
-       } else {
-               scmd->request_buffer = NULL;
-               scmd->request_bufflen = 0;
-               scmd->sc_data_direction = DMA_NONE;
-       }
-
-       scmd->underflow = 0;
-       scmd->use_sg = 0;
-       scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
-
        if (sdev->scsi_level <= SCSI_2)
                scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) |
                        (sdev->lun << 5 & 0xe0);
 
-       /*
-        * Zero the sense buffer.  The scsi spec mandates that any
-        * untransferred sense data should be interpreted as being zero.
-        */
-       memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
-
        shost->eh_action = &done;
+       scmd->request->rq_status = RQ_SCSI_BUSY;
 
        spin_lock_irqsave(shost->host_lock, flags);
        scsi_log_send(scmd);
@@ -534,6 +464,7 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
 
        timeleft = wait_for_completion_timeout(&done, timeout);
 
+       scmd->request->rq_status = RQ_SCSI_DONE;
        shost->eh_action = NULL;
 
        scsi_log_completion(scmd, SUCCESS);
@@ -574,29 +505,6 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
                rtn = FAILED;
        }
 
-
-       /*
-        * Last chance to have valid sense data.
-        */
-       if (copy_sense) {
-               if (!SCSI_SENSE_VALID(scmd)) {
-                       memcpy(scmd->sense_buffer, scmd->request_buffer,
-                              sizeof(scmd->sense_buffer));
-               }
-               kfree(scmd->request_buffer);
-       }
-
-
-       /*
-        * Restore original data
-        */
-       scmd->request_buffer = old_buffer;
-       scmd->request_bufflen = old_bufflen;
-       memcpy(scmd->cmnd, old_cmnd, sizeof(scmd->cmnd));
-       scmd->sc_data_direction = old_data_direction;
-       scmd->cmd_len = old_cmd_len;
-       scmd->use_sg = old_use_sg;
-       scmd->result = old_result;
        return rtn;
 }
 
@@ -612,9 +520,56 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, unsigned char *cmnd,
 static int scsi_request_sense(struct scsi_cmnd *scmd)
 {
        static unsigned char generic_sense[6] =
-               {REQUEST_SENSE, 0, 0, 0, 252, 0};
+       {REQUEST_SENSE, 0, 0, 0, 252, 0};
+       unsigned char *scsi_result;
+       int saved_result;
+       int rtn;
+
+       memcpy(scmd->cmnd, generic_sense, sizeof(generic_sense));
 
-       return scsi_send_eh_cmnd(scmd, generic_sense, 6, SENSE_TIMEOUT, 1);
+       scsi_result = kmalloc(252, GFP_ATOMIC | ((scmd->device->host->hostt->unchecked_isa_dma) ? __GFP_DMA : 0));
+
+
+       if (unlikely(!scsi_result)) {
+               printk(KERN_ERR "%s: cannot allocate scsi_result.\n",
+                      __FUNCTION__);
+               return FAILED;
+       }
+
+       /*
+        * zero the sense buffer.  some host adapters automatically always
+        * request sense, so it is not a good idea that
+        * scmd->request_buffer and scmd->sense_buffer point to the same
+        * address (db).  0 is not a valid sense code. 
+        */
+       memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+       memset(scsi_result, 0, 252);
+
+       saved_result = scmd->result;
+       scmd->request_buffer = scsi_result;
+       scmd->request_bufflen = 252;
+       scmd->use_sg = 0;
+       scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+       scmd->sc_data_direction = DMA_FROM_DEVICE;
+       scmd->underflow = 0;
+
+       rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
+
+       /* last chance to have valid sense data */
+       if(!SCSI_SENSE_VALID(scmd)) {
+               memcpy(scmd->sense_buffer, scmd->request_buffer,
+                      sizeof(scmd->sense_buffer));
+       }
+
+       kfree(scsi_result);
+
+       /*
+        * when we eventually call scsi_finish, we really wish to complete
+        * the original request, so let's restore the original data. (db)
+        */
+       scsi_setup_cmd_retry(scmd);
+       scmd->result = saved_result;
+       return rtn;
 }
 
 /**
@@ -629,13 +584,19 @@ static int scsi_request_sense(struct scsi_cmnd *scmd)
  *    keep a list of pending commands for final completion, and once we
  *    are ready to leave error handling we handle completion for real.
  **/
-void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, struct list_head *done_q)
+static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd,
+                              struct list_head *done_q)
 {
        scmd->device->host->host_failed--;
        scmd->eh_eflags = 0;
+
+       /*
+        * set this back so that the upper level can correctly free up
+        * things.
+        */
+       scsi_setup_cmd_retry(scmd);
        list_move_tail(&scmd->eh_entry, done_q);
 }
-EXPORT_SYMBOL(scsi_eh_finish_cmd);
 
 /**
  * scsi_eh_get_sense - Get device sense data.
@@ -737,23 +698,47 @@ static int scsi_eh_tur(struct scsi_cmnd *scmd)
 {
        static unsigned char tur_command[6] = {TEST_UNIT_READY, 0, 0, 0, 0, 0};
        int retry_cnt = 1, rtn;
+       int saved_result;
 
 retry_tur:
-       rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, SENSE_TIMEOUT, 0);
+       memcpy(scmd->cmnd, tur_command, sizeof(tur_command));
+
+       /*
+        * zero the sense buffer.  the scsi spec mandates that any
+        * untransferred sense data should be interpreted as being zero.
+        */
+       memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
+
+       saved_result = scmd->result;
+       scmd->request_buffer = NULL;
+       scmd->request_bufflen = 0;
+       scmd->use_sg = 0;
+       scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+       scmd->underflow = 0;
+       scmd->sc_data_direction = DMA_NONE;
+
+       rtn = scsi_send_eh_cmnd(scmd, SENSE_TIMEOUT);
+
+       /*
+        * when we eventually call scsi_finish, we really wish to complete
+        * the original request, so let's restore the original data. (db)
+        */
+       scsi_setup_cmd_retry(scmd);
+       scmd->result = saved_result;
 
+       /*
+        * hey, we are done.  let's look to see what happened.
+        */
        SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n",
                __FUNCTION__, scmd, rtn));
-
-       switch (rtn) {
-       case NEEDS_RETRY:
+       if (rtn == SUCCESS)
+               return 0;
+       else if (rtn == NEEDS_RETRY) {
                if (retry_cnt--)
                        goto retry_tur;
-               /*FALLTHRU*/
-       case SUCCESS:
                return 0;
-       default:
-               return 1;
        }
+       return 1;
 }
 
 /**
@@ -835,16 +820,44 @@ static int scsi_try_bus_device_reset(struct scsi_cmnd *scmd)
 static int scsi_eh_try_stu(struct scsi_cmnd *scmd)
 {
        static unsigned char stu_command[6] = {START_STOP, 0, 0, 0, 1, 0};
+       int rtn;
+       int saved_result;
 
-       if (scmd->device->allow_restart) {
-               int rtn;
+       if (!scmd->device->allow_restart)
+               return 1;
 
-               rtn = scsi_send_eh_cmnd(scmd, stu_command, 6,
-                                       START_UNIT_TIMEOUT, 0);
-               if (rtn == SUCCESS)
-                       return 0;
-       }
+       memcpy(scmd->cmnd, stu_command, sizeof(stu_command));
+
+       /*
+        * zero the sense buffer.  the scsi spec mandates that any
+        * untransferred sense data should be interpreted as being zero.
+        */
+       memset(scmd->sense_buffer, 0, sizeof(scmd->sense_buffer));
 
+       saved_result = scmd->result;
+       scmd->request_buffer = NULL;
+       scmd->request_bufflen = 0;
+       scmd->use_sg = 0;
+       scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]);
+       scmd->underflow = 0;
+       scmd->sc_data_direction = DMA_NONE;
+
+       rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT);
+
+       /*
+        * when we eventually call scsi_finish, we really wish to complete
+        * the original request, so let's restore the original data. (db)
+        */
+       scsi_setup_cmd_retry(scmd);
+       scmd->result = saved_result;
+
+       /*
+        * hey, we are done.  let's look to see what happened.
+        */
+       SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n",
+               __FUNCTION__, scmd, rtn));
+       if (rtn == SUCCESS)
+               return 0;
        return 1;
 }
 
@@ -1412,7 +1425,7 @@ static void scsi_eh_ready_devs(struct Scsi_Host *shost,
  * @done_q:    list_head of processed commands.
  *
  **/
-void scsi_eh_flush_done_q(struct list_head *done_q)
+static void scsi_eh_flush_done_q(struct list_head *done_q)
 {
        struct scsi_cmnd *scmd, *next;
 
@@ -1441,7 +1454,6 @@ void scsi_eh_flush_done_q(struct list_head *done_q)
                }
        }
 }
-EXPORT_SYMBOL(scsi_eh_flush_done_q);
 
 /**
  * scsi_unjam_host - Attempt to fix a host which has a cmd that failed.
@@ -1507,7 +1519,7 @@ int scsi_error_handler(void *data)
         */
        set_current_state(TASK_INTERRUPTIBLE);
        while (!kthread_should_stop()) {
-               if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) ||
+               if (shost->host_failed == 0 ||
                    shost->host_failed != shost->host_busy) {
                        SCSI_LOG_ERROR_RECOVERY(1,
                                printk("Error handler scsi_eh_%d sleeping\n",
@@ -1527,8 +1539,8 @@ int scsi_error_handler(void *data)
                 * what we need to do to get it up and online again (if we can).
                 * If we fail, we end up taking the thing offline.
                 */
-               if (shost->transportt->eh_strategy_handler)
-                       shost->transportt->eh_strategy_handler(shost);
+               if (shost->hostt->eh_strategy_handler) 
+                       shost->hostt->eh_strategy_handler(shost);
                else
                        scsi_unjam_host(shost);
 
@@ -1642,24 +1654,27 @@ int
 scsi_reset_provider(struct scsi_device *dev, int flag)
 {
        struct scsi_cmnd *scmd = scsi_get_command(dev, GFP_KERNEL);
-       struct Scsi_Host *shost = dev->host;
        struct request req;
-       unsigned long flags;
        int rtn;
 
        scmd->request = &req;
        memset(&scmd->eh_timeout, 0, sizeof(scmd->eh_timeout));
+       scmd->request->rq_status        = RQ_SCSI_BUSY;
 
        memset(&scmd->cmnd, '\0', sizeof(scmd->cmnd));
     
        scmd->scsi_done         = scsi_reset_provider_done_command;
        scmd->done                      = NULL;
+       scmd->buffer                    = NULL;
+       scmd->bufflen                   = 0;
        scmd->request_buffer            = NULL;
        scmd->request_bufflen           = 0;
 
        scmd->cmd_len                   = 0;
 
        scmd->sc_data_direction         = DMA_BIDIRECTIONAL;
+       scmd->sc_request                = NULL;
+       scmd->sc_magic                  = SCSI_CMND_MAGIC;
 
        init_timer(&scmd->eh_timeout);
 
@@ -1669,10 +1684,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
         */
        scmd->pid                       = 0;
 
-       spin_lock_irqsave(shost->host_lock, flags);
-       shost->tmf_in_progress = 1;
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
        switch (flag) {
        case SCSI_TRY_RESET_DEVICE:
                rtn = scsi_try_bus_device_reset(scmd);
@@ -1691,22 +1702,6 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
                rtn = FAILED;
        }
 
-       spin_lock_irqsave(shost->host_lock, flags);
-       shost->tmf_in_progress = 0;
-       spin_unlock_irqrestore(shost->host_lock, flags);
-
-       /*
-        * be sure to wake up anyone who was sleeping or had their queue
-        * suspended while we performed the TMF.
-        */
-       SCSI_LOG_ERROR_RECOVERY(3,
-               printk("%s: waking up host to restart after TMF\n",
-               __FUNCTION__));
-
-       wake_up(&shost->host_wait);
-
-       scsi_run_host_queues(shost);
-
        scsi_next_command(scmd);
        return rtn;
 }
@@ -1776,6 +1771,14 @@ int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
 }
 EXPORT_SYMBOL(scsi_normalize_sense);
 
+int scsi_request_normalize_sense(struct scsi_request *sreq,
+                                struct scsi_sense_hdr *sshdr)
+{
+       return scsi_normalize_sense(sreq->sr_sense_buffer,
+                       sizeof(sreq->sr_sense_buffer), sshdr);
+}
+EXPORT_SYMBOL(scsi_request_normalize_sense);
+
 int scsi_command_normalize_sense(struct scsi_cmnd *cmd,
                                 struct scsi_sense_hdr *sshdr)
 {