vserver 1.9.5.x5
[linux-2.6.git] / drivers / scsi / scsi_error.c
index 58ac231..eb337e4 100644 (file)
 #include <linux/kernel.h>
 #include <linux/interrupt.h>
 #include <linux/blkdev.h>
-#include <linux/smp_lock.h>
-#include <scsi/scsi_ioctl.h>
+#include <linux/delay.h>
 
-#include "scsi.h"
-#include "hosts.h"
+#include <scsi/scsi.h>
+#include <scsi/scsi_dbg.h>
+#include <scsi/scsi_device.h>
+#include <scsi/scsi_eh.h>
+#include <scsi/scsi_host.h>
+#include <scsi/scsi_ioctl.h>
+#include <scsi/scsi_request.h>
 
 #include "scsi_priv.h"
 #include "scsi_logging.h"
 
-#ifdef DEBUG
-#define SENSE_TIMEOUT SCSI_TIMEOUT
-#else
-#define SENSE_TIMEOUT (10*HZ)
-#endif
-
-#define START_UNIT_TIMEOUT (30*HZ)
+#define SENSE_TIMEOUT          (10*HZ)
+#define START_UNIT_TIMEOUT     (30*HZ)
 
 /*
  * These should *probably* be handled by the host itself.
  * Since it is allowed to sleep, it probably should.
  */
-#define BUS_RESET_SETTLE_TIME   10*HZ
-#define HOST_RESET_SETTLE_TIME  10*HZ
+#define BUS_RESET_SETTLE_TIME   (10)
+#define HOST_RESET_SETTLE_TIME  (10)
 
 /* called with shost->host_lock held */
 void scsi_eh_wakeup(struct Scsi_Host *shost)
@@ -126,6 +125,7 @@ void scsi_add_timer(struct scsi_cmnd *scmd, int timeout,
 
        add_timer(&scmd->eh_timeout);
 }
+EXPORT_SYMBOL(scsi_add_timer);
 
 /**
  * scsi_delete_timer - Delete/cancel timer for a given function.
@@ -153,6 +153,7 @@ int scsi_delete_timer(struct scsi_cmnd *scmd)
 
        return rtn;
 }
+EXPORT_SYMBOL(scsi_delete_timer);
 
 /**
  * scsi_times_out - Timeout function for normal scsi commands.
@@ -167,6 +168,24 @@ int scsi_delete_timer(struct scsi_cmnd *scmd)
 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)) {
+               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;
+               case EH_NOT_HANDLED:
+                       break;
+               }
+
        if (unlikely(!scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) {
                panic("Error handler thread not present at %p %p %s %d",
                      scmd, scmd->device->host, __FILE__, __LINE__);
@@ -197,6 +216,7 @@ int scsi_block_when_processing_errors(struct scsi_device *sdev)
 
        return online;
 }
+EXPORT_SYMBOL(scsi_block_when_processing_errors);
 
 #ifdef CONFIG_SCSI_LOGGING
 /**
@@ -251,16 +271,42 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost,
  *
  * Return value:
  *     SUCCESS or FAILED or NEEDS_RETRY
+ *
+ * Notes:
+ *     When a deferred error is detected the current command has
+ *     not been executed and needs retrying.
  **/
 static int scsi_check_sense(struct scsi_cmnd *scmd)
 {
-       if (!SCSI_SENSE_VALID(scmd))
-               return FAILED;
+       struct scsi_sense_hdr sshdr;
 
-       if (scmd->sense_buffer[2] & 0xe0)
-               return SUCCESS;
+       if (! scsi_command_normalize_sense(scmd, &sshdr))
+               return FAILED;  /* no valid sense data */
 
-       switch (scmd->sense_buffer[2] & 0xf) {
+       if (scsi_sense_is_deferred(&sshdr))
+               return NEEDS_RETRY;
+
+       /*
+        * Previous logic looked for FILEMARK, EOM or ILI which are
+        * mainly associated with tapes and returned SUCCESS.
+        */
+       if (sshdr.response_code == 0x70) {
+               /* fixed format */
+               if (scmd->sense_buffer[2] & 0xe0)
+                       return SUCCESS;
+       } else {
+               /*
+                * descriptor format: look for "stream commands sense data
+                * descriptor" (see SSC-3). Assume single sense data
+                * descriptor. Ignore ILI from SBC-2 READ LONG and WRITE LONG.
+                */
+               if ((sshdr.additional_length > 3) &&
+                   (scmd->sense_buffer[8] == 0x4) &&
+                   (scmd->sense_buffer[11] & 0xe0))
+                       return SUCCESS;
+       }
+
+       switch (sshdr.sense_key) {
        case NO_SENSE:
                return SUCCESS;
        case RECOVERED_ERROR:
@@ -284,19 +330,15 @@ static int scsi_check_sense(struct scsi_cmnd *scmd)
                 * if the device is in the process of becoming ready, we 
                 * should retry.
                 */
-               if ((scmd->sense_buffer[12] == 0x04) &&
-                       (scmd->sense_buffer[13] == 0x01)) {
+               if ((sshdr.asc == 0x04) && (sshdr.ascq == 0x01))
                        return NEEDS_RETRY;
-               }
                /*
                 * if the device is not started, we need to wake
                 * the error handler to start the motor
                 */
                if (scmd->device->allow_restart &&
-                   (scmd->sense_buffer[12] == 0x04) &&
-                   (scmd->sense_buffer[13] == 0x02)) {
+                   (sshdr.asc == 0x04) && (sshdr.ascq == 0x02))
                        return FAILED;
-               }
                return SUCCESS;
 
                /* these three are not supported */
@@ -626,15 +668,13 @@ static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd,
  * Notes:
  *    This has the unfortunate side effect that if a shost adapter does
  *    not automatically request sense information, that we end up shutting
- *    it down before we request it.  All shosts should be doing this
- *    anyways, so for now all I have to say is tough noogies if you end up
- *    in here.  On second thought, this is probably a good idea.  We
- *    *really* want to give authors an incentive to automatically request
- *    this.
+ *    it down before we request it.
  *
- *    In 2.5 this capability will be going away.
+ *    All drivers should request sense information internally these days,
+ *    so for now all I have to say is tough noogies if you end up in here.
  *
- *    Really?  --hch
+ *    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)
@@ -660,7 +700,7 @@ static int scsi_eh_get_sense(struct list_head *work_q,
                SCSI_LOG_ERROR_RECOVERY(3, printk("sense requested for %p"
                                                  " result %x\n", scmd,
                                                  scmd->result));
-               SCSI_LOG_ERROR_RECOVERY(3, print_sense("bh", scmd));
+               SCSI_LOG_ERROR_RECOVERY(3, scsi_print_sense("bh", scmd));
 
                rtn = scsi_decide_disposition(scmd);
 
@@ -1026,7 +1066,8 @@ static int scsi_try_bus_reset(struct scsi_cmnd *scmd)
        spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
 
        if (rtn == SUCCESS) {
-               scsi_sleep(BUS_RESET_SETTLE_TIME);
+               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->device->channel);
                spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
@@ -1057,7 +1098,8 @@ static int scsi_try_host_reset(struct scsi_cmnd *scmd)
        spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
 
        if (rtn == SUCCESS) {
-               scsi_sleep(HOST_RESET_SETTLE_TIME);
+               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->device->channel);
                spin_unlock_irqrestore(scmd->device->host->host_lock, flags);
@@ -1196,43 +1238,6 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q,
        return;
 }
 
-/**
- * scsi_sleep_done - timer function for scsi_sleep
- * @sem:       semphore to signal
- *
- **/
-static void scsi_sleep_done(unsigned long data)
-{
-       struct semaphore *sem = (struct semaphore *)data;
-
-       if (sem)
-               up(sem);
-}
-
-/**
- * scsi_sleep - sleep for specified timeout
- * @timeout:   timeout value
- *
- **/
-void scsi_sleep(int timeout)
-{
-       DECLARE_MUTEX_LOCKED(sem);
-       struct timer_list timer;
-
-       init_timer(&timer);
-       timer.data = (unsigned long)&sem;
-       timer.expires = jiffies + timeout;
-       timer.function = (void (*)(unsigned long))scsi_sleep_done;
-
-       SCSI_LOG_ERROR_RECOVERY(5, printk("sleeping for timer tics %d\n",
-                                         timeout));
-
-       add_timer(&timer);
-
-       down(&sem);
-       del_timer(&timer);
-}
-
 /**
  * scsi_decide_disposition - Disposition a cmd on return from LLD.
  * @scmd:      SCSI cmd to examine.
@@ -1378,7 +1383,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd)
                return SUCCESS;
 
        case RESERVATION_CONFLICT:
-               printk("scsi%d (%d,%d,%d) : reservation conflict\n",
+               printk(KERN_INFO "scsi: reservation conflict: host"
+                                " %d channel %d id %d lun %d\n",
                       scmd->device->host->host_no, scmd->device->channel,
                       scmd->device->id, scmd->device->lun);
                return SUCCESS; /* causes immediate i/o error */
@@ -1620,8 +1626,6 @@ int scsi_error_handler(void *data)
        int rtn;
        DECLARE_MUTEX_LOCKED(sem);
 
-       lock_kernel();
-
        /*
         *    Flush resources
         */
@@ -1633,8 +1637,6 @@ int scsi_error_handler(void *data)
        shost->eh_wait = &sem;
        shost->ehandler = current;
 
-       unlock_kernel();
-
        /*
         * Wake up the thread that created us.
         */
@@ -1753,6 +1755,7 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel)
                }
        }
 }
+EXPORT_SYMBOL(scsi_report_bus_reset);
 
 /*
  * Function:    scsi_report_device_reset()
@@ -1788,6 +1791,7 @@ void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target)
                }
        }
 }
+EXPORT_SYMBOL(scsi_report_device_reset);
 
 static void
 scsi_reset_provider_done_command(struct scsi_cmnd *scmd)
@@ -1867,3 +1871,173 @@ scsi_reset_provider(struct scsi_device *dev, int flag)
        scsi_next_command(scmd);
        return rtn;
 }
+EXPORT_SYMBOL(scsi_reset_provider);
+
+/**
+ * scsi_normalize_sense - normalize main elements from either fixed or
+ *                     descriptor sense data format into a common format.
+ *
+ * @sense_buffer:      byte array containing sense data returned by device
+ * @sb_len:            number of valid bytes in sense_buffer
+ * @sshdr:             pointer to instance of structure that common
+ *                     elements are written to.
+ *
+ * Notes:
+ *     The "main elements" from sense data are: response_code, sense_key,
+ *     asc, ascq and additional_length (only for descriptor format).
+ *
+ *     Typically this function can be called after a device has
+ *     responded to a SCSI command with the CHECK_CONDITION status.
+ *
+ * Return value:
+ *     1 if valid sense data information found, else 0;
+ **/
+int scsi_normalize_sense(const u8 *sense_buffer, int sb_len,
+                         struct scsi_sense_hdr *sshdr)
+{
+       if (!sense_buffer || !sb_len || (sense_buffer[0] & 0x70) != 0x70)
+               return 0;
+
+       memset(sshdr, 0, sizeof(struct scsi_sense_hdr));
+
+       sshdr->response_code = (sense_buffer[0] & 0x7f);
+       if (sshdr->response_code >= 0x72) {
+               /*
+                * descriptor format
+                */
+               if (sb_len > 1)
+                       sshdr->sense_key = (sense_buffer[1] & 0xf);
+               if (sb_len > 2)
+                       sshdr->asc = sense_buffer[2];
+               if (sb_len > 3)
+                       sshdr->ascq = sense_buffer[3];
+               if (sb_len > 7)
+                       sshdr->additional_length = sense_buffer[7];
+       } else {
+               /* 
+                * fixed format
+                */
+               if (sb_len > 2)
+                       sshdr->sense_key = (sense_buffer[2] & 0xf);
+               if (sb_len > 7) {
+                       sb_len = (sb_len < (sense_buffer[7] + 8)) ?
+                                        sb_len : (sense_buffer[7] + 8);
+                       if (sb_len > 12)
+                               sshdr->asc = sense_buffer[12];
+                       if (sb_len > 13)
+                               sshdr->ascq = sense_buffer[13];
+               }
+       }
+
+       return 1;
+}
+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)
+{
+       return scsi_normalize_sense(cmd->sense_buffer,
+                       sizeof(cmd->sense_buffer), sshdr);
+}
+EXPORT_SYMBOL(scsi_command_normalize_sense);
+
+/**
+ * scsi_sense_desc_find - search for a given descriptor type in
+ *                     descriptor sense data format.
+ *
+ * @sense_buffer:      byte array of descriptor format sense data
+ * @sb_len:            number of valid bytes in sense_buffer
+ * @desc_type:         value of descriptor type to find
+ *                     (e.g. 0 -> information)
+ *
+ * Notes:
+ *     only valid when sense data is in descriptor format
+ *
+ * Return value:
+ *     pointer to start of (first) descriptor if found else NULL
+ **/
+const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len,
+                               int desc_type)
+{
+       int add_sen_len, add_len, desc_len, k;
+       const u8 * descp;
+
+       if ((sb_len < 8) || (0 == (add_sen_len = sense_buffer[7])))
+               return NULL;
+       if ((sense_buffer[0] < 0x72) || (sense_buffer[0] > 0x73))
+               return NULL;
+       add_sen_len = (add_sen_len < (sb_len - 8)) ?
+                       add_sen_len : (sb_len - 8);
+       descp = &sense_buffer[8];
+       for (desc_len = 0, k = 0; k < add_sen_len; k += desc_len) {
+               descp += desc_len;
+               add_len = (k < (add_sen_len - 1)) ? descp[1]: -1;
+               desc_len = add_len + 2;
+               if (descp[0] == desc_type)
+                       return descp;
+               if (add_len < 0) // short descriptor ??
+                       break;
+       }
+       return NULL;
+}
+EXPORT_SYMBOL(scsi_sense_desc_find);
+
+/**
+ * scsi_get_sense_info_fld - attempts to get information field from
+ *                     sense data (either fixed or descriptor format)
+ *
+ * @sense_buffer:      byte array of sense data
+ * @sb_len:            number of valid bytes in sense_buffer
+ * @info_out:          pointer to 64 integer where 8 or 4 byte information
+ *                     field will be placed if found.
+ *
+ * Return value:
+ *     1 if information field found, 0 if not found.
+ **/
+int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len,
+                           u64 * info_out)
+{
+       int j;
+       const u8 * ucp;
+       u64 ull;
+
+       if (sb_len < 7)
+               return 0;
+       switch (sense_buffer[0] & 0x7f) {
+       case 0x70:
+       case 0x71:
+               if (sense_buffer[0] & 0x80) {
+                       *info_out = (sense_buffer[3] << 24) +
+                                   (sense_buffer[4] << 16) +
+                                   (sense_buffer[5] << 8) + sense_buffer[6];
+                       return 1;
+               } else
+                       return 0;
+       case 0x72:
+       case 0x73:
+               ucp = scsi_sense_desc_find(sense_buffer, sb_len,
+                                          0 /* info desc */);
+               if (ucp && (0xa == ucp[1])) {
+                       ull = 0;
+                       for (j = 0; j < 8; ++j) {
+                               if (j > 0)
+                                       ull <<= 8;
+                               ull |= ucp[4 + j];
+                       }
+                       *info_out = ull;
+                       return 1;
+               } else
+                       return 0;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL(scsi_get_sense_info_fld);