X-Git-Url: http://git.onelab.eu/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_error.c;h=eb337e474583d4dc70d7e5b68a2e1ef360a96ea9;hb=6a77f38946aaee1cd85eeec6cf4229b204c15071;hp=58ac2319a342c119f1aee7105bff88672f881457;hpb=5273a3df6485dc2ad6aa7ddd441b9a21970f003b;p=linux-2.6.git diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 58ac2319a..eb337e474 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -22,29 +22,28 @@ #include #include #include -#include -#include +#include -#include "scsi.h" -#include "hosts.h" +#include +#include +#include +#include +#include +#include +#include #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);