X-Git-Url: http://ftp.safe.ca/?a=blobdiff_plain;f=drivers%2Fscsi%2Fscsi_error.c;h=1b0060b791e8dc6fe0b1dd4ace0a1e3f3ea96c7e;hb=473e28563fbb038515d4616546297483d3727c02;hp=895c9452be4ca65cc3aebf78ee8bd9f319618c15;hpb=17fa53da1239b8712c5cebbd72a74c713b6c2db9;p=safe%2Fjmp%2Flinux-2.6 diff --git a/drivers/scsi/scsi_error.c b/drivers/scsi/scsi_error.c index 895c945..1b0060b 100644 --- a/drivers/scsi/scsi_error.c +++ b/drivers/scsi/scsi_error.c @@ -18,26 +18,27 @@ #include #include #include -#include #include +#include #include #include #include #include #include +#include #include #include #include +#include #include #include -#include #include "scsi_priv.h" #include "scsi_logging.h" +#include "scsi_transport_api.h" #define SENSE_TIMEOUT (10*HZ) -#define START_UNIT_TIMEOUT (30*HZ) /* * These should *probably* be handled by the host itself. @@ -50,135 +51,95 @@ void scsi_eh_wakeup(struct Scsi_Host *shost) { if (shost->host_busy == shost->host_failed) { - up(shost->eh_wait); + wake_up_process(shost->ehandler); SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread\n")); } } /** + * 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. * @eh_flag: optional SCSI_EH flag. * * Return value: * 0 on failure. - **/ + */ int scsi_eh_scmd_add(struct scsi_cmnd *scmd, int eh_flag) { struct Scsi_Host *shost = scmd->device->host; unsigned long flags; + int ret = 0; - if (shost->eh_wait == NULL) + if (!shost->ehandler) return 0; spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RECOVERY)) + if (scsi_host_set_state(shost, SHOST_CANCEL_RECOVERY)) + goto out_unlock; + ret = 1; scmd->eh_eflags |= eh_flag; list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); - scsi_host_set_state(shost, SHOST_RECOVERY); shost->host_failed++; scsi_eh_wakeup(shost); + out_unlock: spin_unlock_irqrestore(shost->host_lock, flags); - return 1; -} - -/** - * scsi_add_timer - Start timeout timer for a single scsi command. - * @scmd: scsi command that is about to start running. - * @timeout: amount of time to allow this command to run. - * @complete: timeout function to call if timer isn't canceled. - * - * Notes: - * This should be turned into an inline function. Each scsi command - * has its own timer, and as it is added to the queue, we set up the - * timer. When the command completes, we cancel the timer. - **/ -void scsi_add_timer(struct scsi_cmnd *scmd, int timeout, - void (*complete)(struct scsi_cmnd *)) -{ - - /* - * If the clock was already running for this command, then - * first delete the timer. The timer handling code gets rather - * confused if we don't do this. - */ - if (scmd->eh_timeout.function) - del_timer(&scmd->eh_timeout); - - scmd->eh_timeout.data = (unsigned long)scmd; - scmd->eh_timeout.expires = jiffies + timeout; - scmd->eh_timeout.function = (void (*)(unsigned long)) complete; - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p, time:" - " %d, (%p)\n", __FUNCTION__, - scmd, timeout, complete)); - - add_timer(&scmd->eh_timeout); -} - -/** - * scsi_delete_timer - Delete/cancel timer for a given function. - * @scmd: Cmd that we are canceling timer for - * - * Notes: - * This should be turned into an inline function. - * - * Return value: - * 1 if we were able to detach the timer. 0 if we blew it, and the - * timer function has already started to run. - **/ -int scsi_delete_timer(struct scsi_cmnd *scmd) -{ - int rtn; - - rtn = del_timer(&scmd->eh_timeout); - - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: scmd: %p," - " rtn: %d\n", __FUNCTION__, - scmd, rtn)); - - scmd->eh_timeout.data = (unsigned long)NULL; - scmd->eh_timeout.function = NULL; - - return rtn; + return ret; } /** * scsi_times_out - Timeout function for normal scsi commands. - * @scmd: Cmd that is timing out. + * @req: request that is timing out. * * Notes: * We do not need to lock this. There is the potential for a race * only in that the normal completion handling might run, but if the * normal completion function determines that the timer has already * fired, then it mustn't do anything. - **/ -void scsi_times_out(struct scsi_cmnd *scmd) + */ +enum blk_eh_timer_return scsi_times_out(struct request *req) { + struct scsi_cmnd *scmd = req->special; + enum blk_eh_timer_return rtn = BLK_EH_NOT_HANDLED; + 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 (scmd->device->host->transportt->eh_timed_out) + rtn = scmd->device->host->transportt->eh_timed_out(scmd); + else if (scmd->device->host->hostt->eh_timed_out) + rtn = scmd->device->host->hostt->eh_timed_out(scmd); - 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__); + if (unlikely(rtn == BLK_EH_NOT_HANDLED && + !scsi_eh_scmd_add(scmd, SCSI_EH_CANCEL_CMD))) { + scmd->result |= DID_TIME_OUT << 16; + rtn = BLK_EH_HANDLED; } + + return rtn; } /** @@ -191,17 +152,16 @@ void scsi_times_out(struct scsi_cmnd *scmd) * * Return value: * 0 when dev was taken offline by error recovery. 1 OK to proceed. - **/ + */ int scsi_block_when_processing_errors(struct scsi_device *sdev) { int online; - wait_event(sdev->host->host_wait, (sdev->host->shost_state != - SHOST_RECOVERY)); + wait_event(sdev->host->host_wait, !scsi_host_in_recovery(sdev->host)); online = scsi_device_online(sdev); - SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__, + SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __func__, online)); return online; @@ -213,7 +173,7 @@ EXPORT_SYMBOL(scsi_block_when_processing_errors); * scsi_eh_prt_fail_stats - Log info on failures. * @shost: scsi host being recovered. * @work_q: Queue of scsi cmds to process. - **/ + */ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, struct list_head *work_q) { @@ -237,11 +197,10 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, if (cmd_cancel || cmd_failed) { SCSI_LOG_ERROR_RECOVERY(3, - printk("%s: %d:%d:%d:%d cmds failed: %d," - " cancel: %d\n", - __FUNCTION__, shost->host_no, - sdev->channel, sdev->id, sdev->lun, - cmd_failed, cmd_cancel)); + sdev_printk(KERN_INFO, sdev, + "%s: cmds failed: %d, cancel: %d\n", + __func__, cmd_failed, + cmd_cancel)); cmd_cancel = 0; cmd_failed = 0; ++devices_failed; @@ -264,9 +223,10 @@ static inline void scsi_eh_prt_fail_stats(struct Scsi_Host *shost, * 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) { + struct scsi_device *sdev = scmd->device; struct scsi_sense_hdr sshdr; if (! scsi_command_normalize_sense(scmd, &sshdr)) @@ -275,6 +235,16 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) if (scsi_sense_is_deferred(&sshdr)) return NEEDS_RETRY; + if (sdev->scsi_dh_data && sdev->scsi_dh_data->scsi_dh && + sdev->scsi_dh_data->scsi_dh->check_sense) { + int rc; + + rc = sdev->scsi_dh_data->scsi_dh->check_sense(sdev, &sshdr); + if (rc != SCSI_RETURN_NOT_HANDLED) + return rc; + /* handler does not care. Drop down to default handling */ + } + /* * Previous logic looked for FILEMARK, EOM or ILI which are * mainly associated with tapes and returned SUCCESS. @@ -302,6 +272,9 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) return /* soft_error */ SUCCESS; case ABORTED_COMMAND: + if (sshdr.asc == 0x10) /* DIF */ + return SUCCESS; + return NEEDS_RETRY; case NOT_READY: case UNIT_ATTENTION: @@ -337,11 +310,16 @@ 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: if (scmd->device->retry_hwerror) - return NEEDS_RETRY; + return ADD_TO_MLQUEUE; else return SUCCESS; @@ -362,7 +340,7 @@ static int scsi_check_sense(struct scsi_cmnd *scmd) * queued during error recovery. the main difference here is that we * don't allow for the possibility of retries here, and we are a lot * more restrictive about what we consider acceptable. - **/ + */ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) { /* @@ -404,9 +382,13 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) * who knows? FIXME(eric) */ return SUCCESS; + case RESERVATION_CONFLICT: + /* + * let issuer deal with this, it could be just fine + */ + return SUCCESS; case BUSY: case QUEUE_FULL: - case RESERVATION_CONFLICT: default: return FAILED; } @@ -414,138 +396,348 @@ static int scsi_eh_completed_normally(struct scsi_cmnd *scmd) } /** - * scsi_eh_times_out - timeout function for error handling. - * @scmd: Cmd that is timing out. + * scsi_eh_done - Completion function for error handling. + * @scmd: Cmd that is done. + */ +static void scsi_eh_done(struct scsi_cmnd *scmd) +{ + struct completion *eh_action; + + SCSI_LOG_ERROR_RECOVERY(3, + printk("%s scmd: %p result: %x\n", + __func__, scmd, scmd->result)); + + eh_action = scmd->device->host->eh_action; + if (eh_action) + complete(eh_action); +} + +/** + * 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", + __func__)); + + 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", + __func__)); + + 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; +} + +static void __scsi_report_device_reset(struct scsi_device *sdev, void *data) +{ + sdev->was_reset = 1; + sdev->expecting_cc_ua = 1; +} + +/** + * scsi_try_target_reset - Ask host to perform a target reset + * @scmd: SCSI cmd used to send a target reset * * Notes: - * During error handling, the kernel thread will be sleeping waiting - * for some action to complete on the device. our only job is to - * record that it timed out, and to wake up the thread. - **/ -static void scsi_eh_times_out(struct scsi_cmnd *scmd) + * 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_target_reset(struct scsi_cmnd *scmd) { - scmd->eh_eflags |= SCSI_EH_REC_TIMEOUT; - SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__, - scmd)); + unsigned long flags; + int rtn; + + if (!scmd->device->host->hostt->eh_target_reset_handler) + return FAILED; + + rtn = scmd->device->host->hostt->eh_target_reset_handler(scmd); + if (rtn == SUCCESS) { + spin_lock_irqsave(scmd->device->host->host_lock, flags); + __starget_for_each_device(scsi_target(scmd->device), NULL, + __scsi_report_device_reset); + spin_unlock_irqrestore(scmd->device->host->host_lock, flags); + } - up(scmd->device->host->eh_action); + return rtn; } /** - * scsi_eh_done - Completion function for error handling. - * @scmd: Cmd that is done. - **/ -static void scsi_eh_done(struct scsi_cmnd *scmd) + * 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) { - /* - * if the timeout handler is already running, then just set the - * flag which says we finished late, and return. we have no - * way of stopping the timeout handler from running, so we must - * always defer to it. - */ - if (del_timer(&scmd->eh_timeout)) { - scmd->request->rq_status = RQ_SCSI_DONE; + int rtn; - SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n", - __FUNCTION__, scmd, scmd->result)); + if (!scmd->device->host->hostt->eh_device_reset_handler) + return FAILED; - up(scmd->device->host->eh_action); - } + rtn = scmd->device->host->hostt->eh_device_reset_handler(scmd); + if (rtn == SUCCESS) + __scsi_report_device_reset(scmd->device, NULL); + 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_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_to_abort_cmd - Ask host to abort a running command. + * @scmd: SCSI cmd to abort from Lower Level. * * Notes: - * The initialization of the structures is quite a bit different in - * this case, and furthermore, there is a different completion handler - * vs scsi_dispatch_cmd. - * Return value: - * SUCCESS or FAILED or NEEDS_RETRY - **/ -static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) + * 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_target_reset(scmd) != SUCCESS) + if (scsi_try_bus_reset(scmd) != SUCCESS) + scsi_try_host_reset(scmd); +} + +/** + * scsi_eh_prep_cmnd - Save a scsi command info as part of error recory + * @scmd: SCSI command structure to hijack + * @ses: structure to save restore information + * @cmnd: CDB to send. Can be NULL if no new cmnd is needed + * @cmnd_size: size in bytes of @cmnd (must be <= BLK_MAX_CDB) + * @sense_bytes: size of sense data to copy. or 0 (if != 0 @cmnd is ignored) + * + * This function is used to save a scsi command information before re-execution + * as part of the error recovery process. If @sense_bytes is 0 the command + * sent must be one that does not transfer any data. If @sense_bytes != 0 + * @cmnd is ignored and this functions sets up a REQUEST_SENSE command + * and cmnd buffers to read @sense_bytes into @scmd->sense_buffer. + */ +void scsi_eh_prep_cmnd(struct scsi_cmnd *scmd, struct scsi_eh_save *ses, + unsigned char *cmnd, int cmnd_size, unsigned sense_bytes) { struct scsi_device *sdev = scmd->device; - struct Scsi_Host *shost = sdev->host; - DECLARE_MUTEX_LOCKED(sem); - unsigned long flags; - int rtn = SUCCESS; /* - * we will use a queued command if possible, otherwise we will - * emulate the queuing and calling of completion function ourselves. + * 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. */ - if (sdev->scsi_level <= SCSI_2) + ses->cmd_len = scmd->cmd_len; + ses->cmnd = scmd->cmnd; + ses->data_direction = scmd->sc_data_direction; + ses->sdb = scmd->sdb; + ses->next_rq = scmd->request->next_rq; + ses->result = scmd->result; + ses->underflow = scmd->underflow; + ses->prot_op = scmd->prot_op; + + scmd->prot_op = SCSI_PROT_NORMAL; + scmd->cmnd = ses->eh_cmnd; + memset(scmd->cmnd, 0, BLK_MAX_CDB); + memset(&scmd->sdb, 0, sizeof(scmd->sdb)); + scmd->request->next_rq = NULL; + + if (sense_bytes) { + scmd->sdb.length = min_t(unsigned, SCSI_SENSE_BUFFERSIZE, + sense_bytes); + sg_init_one(&ses->sense_sgl, scmd->sense_buffer, + scmd->sdb.length); + scmd->sdb.table.sgl = &ses->sense_sgl; + scmd->sc_data_direction = DMA_FROM_DEVICE; + scmd->sdb.table.nents = 1; + scmd->cmnd[0] = REQUEST_SENSE; + scmd->cmnd[4] = scmd->sdb.length; + scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); + } else { + scmd->sc_data_direction = DMA_NONE; + if (cmnd) { + BUG_ON(cmnd_size > BLK_MAX_CDB); + memcpy(scmd->cmnd, cmnd, cmnd_size); + scmd->cmd_len = COMMAND_SIZE(scmd->cmnd[0]); + } + } + + scmd->underflow = 0; + + if (sdev->scsi_level <= SCSI_2 && sdev->scsi_level != SCSI_UNKNOWN) scmd->cmnd[1] = (scmd->cmnd[1] & 0x1f) | (sdev->lun << 5 & 0xe0); - scsi_add_timer(scmd, timeout, scsi_eh_times_out); + /* + * Zero the sense buffer. The scsi spec mandates that any + * untransferred sense data should be interpreted as being zero. + */ + memset(scmd->sense_buffer, 0, SCSI_SENSE_BUFFERSIZE); +} +EXPORT_SYMBOL(scsi_eh_prep_cmnd); +/** + * scsi_eh_restore_cmnd - Restore a scsi command info as part of error recory + * @scmd: SCSI command structure to restore + * @ses: saved information from a coresponding call to scsi_eh_prep_cmnd + * + * Undo any damage done by above scsi_eh_prep_cmnd(). + */ +void scsi_eh_restore_cmnd(struct scsi_cmnd* scmd, struct scsi_eh_save *ses) +{ /* - * set up the semaphore so we wait for the command to complete. + * Restore original data */ - shost->eh_action = &sem; - scmd->request->rq_status = RQ_SCSI_BUSY; + scmd->cmd_len = ses->cmd_len; + scmd->cmnd = ses->cmnd; + scmd->sc_data_direction = ses->data_direction; + scmd->sdb = ses->sdb; + scmd->request->next_rq = ses->next_rq; + scmd->result = ses->result; + scmd->underflow = ses->underflow; + scmd->prot_op = ses->prot_op; +} +EXPORT_SYMBOL(scsi_eh_restore_cmnd); + +/** + * 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 + * @sense_bytes: size of sense data to copy or 0 + * + * This function is used to send a scsi command down to a target device + * as part of the error recovery process. See also scsi_eh_prep_cmnd() above. + * + * 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, unsigned sense_bytes) +{ + struct scsi_device *sdev = scmd->device; + struct Scsi_Host *shost = sdev->host; + DECLARE_COMPLETION_ONSTACK(done); + unsigned long timeleft; + unsigned long flags; + struct scsi_eh_save ses; + int rtn; + + scsi_eh_prep_cmnd(scmd, &ses, cmnd, cmnd_size, sense_bytes); + shost->eh_action = &done; spin_lock_irqsave(shost->host_lock, flags); scsi_log_send(scmd); shost->hostt->queuecommand(scmd, scsi_eh_done); spin_unlock_irqrestore(shost->host_lock, flags); - down(&sem); - scsi_log_completion(scmd, SUCCESS); + timeleft = wait_for_completion_timeout(&done, timeout); shost->eh_action = NULL; - /* - * see if timeout. if so, tell the host to forget about it. - * in other words, we don't want a callback any more. - */ - if (scmd->eh_eflags & SCSI_EH_REC_TIMEOUT) { - scmd->eh_eflags &= ~SCSI_EH_REC_TIMEOUT; - - /* - * as far as the low level driver is - * concerned, this command is still active, so - * we must give the low level driver a chance - * to abort it. (db) - * - * 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); - - scmd->request->rq_status = RQ_SCSI_DONE; - rtn = FAILED; - } + scsi_log_completion(scmd, SUCCESS); - SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd: %p, rtn:%x\n", - __FUNCTION__, scmd, rtn)); + SCSI_LOG_ERROR_RECOVERY(3, + printk("%s: scmd: %p, timeleft: %ld\n", + __func__, scmd, timeleft)); /* - * now examine the actual status codes to see whether the command - * actually did complete normally. + * If there is time left scsi_eh_done got called, and we will + * examine the actual status codes to see whether the command + * actually did complete normally, else tell the host to forget + * about this command. */ - if (rtn == SUCCESS) { + if (timeleft) { rtn = scsi_eh_completed_normally(scmd); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scsi_eh_completed_normally %x\n", - __FUNCTION__, rtn)); + __func__, rtn)); + switch (rtn) { case SUCCESS: case NEEDS_RETRY: case FAILED: break; + case ADD_TO_MLQUEUE: + rtn = NEEDS_RETRY; + break; default: rtn = FAILED; break; } + } else { + scsi_abort_eh_cmnd(scmd); + rtn = FAILED; } + scsi_eh_restore_cmnd(scmd, &ses); return rtn; } @@ -557,60 +749,10 @@ static int scsi_send_eh_cmnd(struct scsi_cmnd *scmd, int timeout) * Some hosts automatically obtain this information, others require * that we obtain it on our own. This function will *not* return until * the command either times out, or it completes. - **/ + */ static int scsi_request_sense(struct scsi_cmnd *scmd) { - static unsigned char generic_sense[6] = - {REQUEST_SENSE, 0, 0, 0, 252, 0}; - unsigned char *scsi_result; - int saved_result; - int rtn; - - memcpy(scmd->cmnd, generic_sense, sizeof(generic_sense)); - - 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; + return scsi_send_eh_cmnd(scmd, NULL, 0, SENSE_TIMEOUT, ~0); } /** @@ -621,28 +763,22 @@ static int scsi_request_sense(struct scsi_cmnd *scmd) * Notes: * We don't want to use the normal command completion while we are are * still handling errors - it may cause other commands to be queued, - * and that would disturb what we are doing. thus we really want to + * and that would disturb what we are doing. Thus we really want to * keep a list of pending commands for final completion, and once we * are ready to leave error handling we handle completion for real. - **/ -static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, - struct list_head *done_q) + */ +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. * @work_q: Queue of commands to process. - * @done_q: Queue of proccessed commands.. + * @done_q: Queue of processed commands. * * Description: * See if we need to request sense information. if so, then get it @@ -650,7 +786,7 @@ 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 + * not automatically request sense information, we end up shutting * it down before we request it. * * All drivers should request sense information internally these days, @@ -658,9 +794,9 @@ static void scsi_eh_finish_cmd(struct scsi_cmnd *scmd, * * 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; @@ -670,10 +806,9 @@ static int scsi_eh_get_sense(struct list_head *work_q, SCSI_SENSE_VALID(scmd)) continue; - SCSI_LOG_ERROR_RECOVERY(2, printk("%s: requesting sense" - " for id: %d\n", - current->comm, - scmd->device->id)); + SCSI_LOG_ERROR_RECOVERY(2, scmd_printk(KERN_INFO, scmd, + "%s: requesting sense\n", + current->comm)); rtn = scsi_request_sense(scmd); if (rtn != SUCCESS) continue; @@ -703,98 +838,50 @@ 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. - * @scmd: Scsi cmd to send TUR + * @scmd: &scsi_cmnd to send TUR * * Return value: * 0 - Device is ready. 1 - Device NOT ready. - **/ + */ 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: - 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; + rtn = scsi_send_eh_cmnd(scmd, tur_command, 6, SENSE_TIMEOUT, 0); - /* - * 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; - else if (rtn == NEEDS_RETRY) { + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd %p rtn %x\n", + __func__, scmd, rtn)); + + switch (rtn) { + case NEEDS_RETRY: if (retry_cnt--) goto retry_tur; + /*FALLTHRU*/ + case SUCCESS: return 0; + default: + return 1; } - return 1; } /** - * scsi_eh_abort_cmds - abort canceled commands. - * @shost: scsi host being recovered. - * @eh_done_q: list_head for processed commands. + * scsi_eh_abort_cmds - abort pending commands. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. * * Decription: * Try and see whether or not it makes sense to try and abort the - * running command. this only works out to be the case if we have one - * command that has timed out. if the command simply failed, it makes + * running command. This only works out to be the case if we have one + * command that has timed out. If the command simply failed, it makes * no sense to try and abort the command, since as far as the shost * adapter is concerned, it isn't running. - **/ + */ static int scsi_eh_abort_cmds(struct list_head *work_q, struct list_head *done_q) { @@ -827,91 +914,39 @@ static int scsi_eh_abort_cmds(struct list_head *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 + * @scmd: &scsi_cmnd to send START_UNIT * * Return value: * 0 - Device is ready. 1 - Device NOT ready. - **/ + */ 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) - return 1; - - 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; + if (scmd->device->allow_restart) { + int i, rtn = NEEDS_RETRY; - rtn = scsi_send_eh_cmnd(scmd, START_UNIT_TIMEOUT); + for (i = 0; rtn == NEEDS_RETRY && i < 2; i++) + rtn = scsi_send_eh_cmnd(scmd, stu_command, 6, scmd->device->request_queue->rq_timeout, 0); - /* - * 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; + if (rtn == SUCCESS) + return 0; + } - /* - * 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; } /** * scsi_eh_stu - send START_UNIT if needed - * @shost: scsi host being recovered. - * @eh_done_q: list_head for processed commands. + * @shost: &scsi host being recovered. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. * * Notes: * If commands are failing due to not ready, initializing command required, * try revalidating the device, which will end up sending a start unit. - **/ + */ static int scsi_eh_stu(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) @@ -957,14 +992,15 @@ static int scsi_eh_stu(struct Scsi_Host *shost, /** * scsi_eh_bus_device_reset - send bdr if needed * @shost: scsi host being recovered. - * @eh_done_q: list_head for processed commands. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. * * Notes: - * Try a bus device reset. still, look to see whether we have multiple + * Try a bus device reset. Still, look to see whether we have multiple * devices that are jammed or not - if we have multiple devices, it * makes no sense to try bus_device_reset - we really would need to try * a bus_reset instead. - **/ + */ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) @@ -1011,66 +1047,72 @@ static int scsi_eh_bus_device_reset(struct Scsi_Host *shost, } /** - * 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->device->channel); - 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) + * scsi_eh_target_reset - send target reset if needed + * @shost: scsi host being recovered. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. + * + * Notes: + * Try a target reset. + */ +static int scsi_eh_target_reset(struct Scsi_Host *shost, + struct list_head *work_q, + struct list_head *done_q) { - unsigned long flags; + struct scsi_cmnd *scmd, *tgtr_scmd, *next; + unsigned int id = 0; 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); + do { + tgtr_scmd = NULL; + list_for_each_entry(scmd, work_q, eh_entry) { + if (id == scmd_id(scmd)) { + tgtr_scmd = scmd; + break; + } + } + if (!tgtr_scmd) { + /* not one exactly equal; find the next highest */ + list_for_each_entry(scmd, work_q, eh_entry) { + if (scmd_id(scmd) > id && + (!tgtr_scmd || + scmd_id(tgtr_scmd) > scmd_id(scmd))) + tgtr_scmd = scmd; + } + } + if (!tgtr_scmd) + /* no more commands, that's it */ + break; - 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->device->channel); - spin_unlock_irqrestore(scmd->device->host->host_lock, flags); - } + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Sending target reset " + "to target %d\n", + current->comm, id)); + rtn = scsi_try_target_reset(tgtr_scmd); + if (rtn == SUCCESS) { + list_for_each_entry_safe(scmd, next, work_q, eh_entry) { + if (id == scmd_id(scmd)) + if (!scsi_device_online(scmd->device) || + !scsi_eh_tur(tgtr_scmd)) + scsi_eh_finish_cmd(scmd, + done_q); + } + } else + SCSI_LOG_ERROR_RECOVERY(3, printk("%s: Target reset" + " failed target: " + "%d\n", + current->comm, id)); + id++; + } while(id != 0); - return rtn; + return list_empty(work_q); } /** * scsi_eh_bus_reset - send a bus reset - * @shost: scsi host being recovered. - * @eh_done_q: list_head for processed commands. - **/ + * @shost: &scsi host being recovered. + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. + */ static int scsi_eh_bus_reset(struct Scsi_Host *shost, struct list_head *work_q, struct list_head *done_q) @@ -1089,7 +1131,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, for (channel = 0; channel <= shost->max_channel; channel++) { chan_scmd = NULL; list_for_each_entry(scmd, work_q, eh_entry) { - if (channel == scmd->device->channel) { + if (channel == scmd_channel(scmd)) { chan_scmd = scmd; break; /* @@ -1107,7 +1149,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, rtn = scsi_try_bus_reset(chan_scmd); if (rtn == SUCCESS) { list_for_each_entry_safe(scmd, next, work_q, eh_entry) { - if (channel == scmd->device->channel) + if (channel == scmd_channel(scmd)) if (!scsi_device_online(scmd->device) || !scsi_eh_tur(scmd)) scsi_eh_finish_cmd(scmd, @@ -1127,7 +1169,7 @@ static int scsi_eh_bus_reset(struct Scsi_Host *shost, * scsi_eh_host_reset - send a host reset * @work_q: list_head for processed commands. * @done_q: list_head for processed commands. - **/ + */ static int scsi_eh_host_reset(struct list_head *work_q, struct list_head *done_q) { @@ -1162,21 +1204,15 @@ static int scsi_eh_host_reset(struct list_head *work_q, * scsi_eh_offline_sdevs - offline scsi devices that fail to recover * @work_q: list_head for processed commands. * @done_q: list_head for processed commands. - * - **/ + */ static void scsi_eh_offline_sdevs(struct list_head *work_q, struct list_head *done_q) { struct scsi_cmnd *scmd, *next; list_for_each_entry_safe(scmd, next, work_q, eh_entry) { - printk(KERN_INFO "scsi: Device offlined - not" - " ready after error recovery: host" - " %d channel %d id %d lun %d\n", - scmd->device->host->host_no, - scmd->device->channel, - scmd->device->id, - scmd->device->lun); + sdev_printk(KERN_INFO, scmd->device, "Device offlined - " + "not ready after error recovery\n"); scsi_device_set_state(scmd->device, SDEV_OFFLINE); if (scmd->eh_eflags & SCSI_EH_CANCEL_CMD) { /* @@ -1189,6 +1225,40 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q, } /** + * scsi_noretry_cmd - determinte if command should be failed fast + * @scmd: SCSI cmd to examine. + */ +int scsi_noretry_cmd(struct scsi_cmnd *scmd) +{ + switch (host_byte(scmd->result)) { + case DID_OK: + break; + case DID_BUS_BUSY: + return blk_failfast_transport(scmd->request); + case DID_PARITY: + return blk_failfast_dev(scmd->request); + case DID_ERROR: + if (msg_byte(scmd->result) == COMMAND_COMPLETE && + status_byte(scmd->result) == RESERVATION_CONFLICT) + return 0; + /* fall through */ + case DID_SOFT_ERROR: + return blk_failfast_driver(scmd->request); + } + + switch (status_byte(scmd->result)) { + case CHECK_CONDITION: + /* + * assume caller has checked sense and determinted + * the check condition was retryable. + */ + return blk_failfast_dev(scmd->request); + } + + return 0; +} + +/** * scsi_decide_disposition - Disposition a cmd on return from LLD. * @scmd: SCSI cmd to examine. * @@ -1201,7 +1271,7 @@ static void scsi_eh_offline_sdevs(struct list_head *work_q, * is woken. In cases where the error code indicates an error that * doesn't require the error handler read (i.e. we don't need to * abort/reset), this function should return SUCCESS. - **/ + */ int scsi_decide_disposition(struct scsi_cmnd *scmd) { int rtn; @@ -1213,7 +1283,7 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) if (!scsi_device_online(scmd->device)) { SCSI_LOG_ERROR_RECOVERY(5, printk("%s: device offline - report" " as SUCCESS\n", - __FUNCTION__)); + __func__)); return SUCCESS; } @@ -1260,7 +1330,21 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) case DID_REQUEUE: return ADD_TO_MLQUEUE; - + case DID_TRANSPORT_DISRUPTED: + /* + * LLD/transport was disrupted during processing of the IO. + * The transport class is now blocked/blocking, + * and the transport will decide what to do with the IO + * based on its timers and recovery capablilities if + * there are enough retries. + */ + goto maybe_retry; + case DID_TRANSPORT_FAILFAST: + /* + * The transport decided to failfast the IO (most likely + * the fast io fail tmo fired), so send IO directly upwards. + */ + return SUCCESS; case DID_ERROR: if (msg_byte(scmd->result) == COMMAND_COMPLETE && status_byte(scmd->result) == RESERVATION_CONFLICT) @@ -1317,8 +1401,9 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) return ADD_TO_MLQUEUE; case GOOD: case COMMAND_TERMINATED: - case TASK_ABORTED: return SUCCESS; + case TASK_ABORTED: + goto maybe_retry; case CHECK_CONDITION: rtn = scsi_check_sense(scmd); if (rtn == NEEDS_RETRY) @@ -1338,10 +1423,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) return SUCCESS; case RESERVATION_CONFLICT: - 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); + sdev_printk(KERN_INFO, scmd->device, + "reservation conflict\n"); return SUCCESS; /* causes immediate i/o error */ default: return FAILED; @@ -1354,8 +1437,8 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) * the request was not marked fast fail. Note that above, * even if the request is marked fast fail, we still requeue * for queue congestion conditions (QUEUE_FULL or BUSY) */ - if ((++scmd->retries) < scmd->allowed - && !blk_noretry_request(scmd->request)) { + if ((++scmd->retries) <= scmd->allowed + && !scsi_noretry_cmd(scmd)) { return NEEDS_RETRY; } else { /* @@ -1365,70 +1448,48 @@ int scsi_decide_disposition(struct scsi_cmnd *scmd) } } -/** - * scsi_eh_lock_done - done function for eh door lock request - * @scmd: SCSI command block for the door lock request - * - * Notes: - * We completed the asynchronous door lock request, and it has either - * locked the door or failed. We must free the command structures - * associated with this request. - **/ -static void scsi_eh_lock_done(struct scsi_cmnd *scmd) +static void eh_lock_door_done(struct request *req, int uptodate) { - struct scsi_request *sreq = scmd->sc_request; - - scsi_release_request(sreq); + __blk_put_request(req->q, req); } - /** * scsi_eh_lock_door - Prevent medium removal for the specified device * @sdev: SCSI device to prevent medium removal * * Locking: - * We must be called from process context; scsi_allocate_request() - * may sleep. + * We must be called from process context. * * Notes: * We queue up an asynchronous "ALLOW MEDIUM REMOVAL" request on the * head of the devices request queue, and continue. - * - * Bugs: - * scsi_allocate_request() may sleep waiting for existing requests to - * be processed. However, since we haven't kicked off any request - * processing for this host, this may deadlock. - * - * If scsi_allocate_request() fails for what ever reason, we - * completely forget to lock the door. - **/ + */ static void scsi_eh_lock_door(struct scsi_device *sdev) { - struct scsi_request *sreq = scsi_allocate_request(sdev, GFP_KERNEL); + struct request *req; - if (unlikely(!sreq)) { - printk(KERN_ERR "%s: request allocate failed," - "prevent media removal cmd not sent\n", __FUNCTION__); - return; - } + /* + * blk_get_request with GFP_KERNEL (__GFP_WAIT) sleeps until a + * request becomes available + */ + req = blk_get_request(sdev->request_queue, READ, GFP_KERNEL); - sreq->sr_cmnd[0] = ALLOW_MEDIUM_REMOVAL; - sreq->sr_cmnd[1] = 0; - sreq->sr_cmnd[2] = 0; - sreq->sr_cmnd[3] = 0; - sreq->sr_cmnd[4] = SCSI_REMOVAL_PREVENT; - sreq->sr_cmnd[5] = 0; - sreq->sr_data_direction = DMA_NONE; - sreq->sr_bufflen = 0; - sreq->sr_buffer = NULL; - sreq->sr_allowed = 5; - sreq->sr_done = scsi_eh_lock_done; - sreq->sr_timeout_per_command = 10 * HZ; - sreq->sr_cmd_len = COMMAND_SIZE(sreq->sr_cmnd[0]); - - scsi_insert_special_req(sreq, 1); -} + req->cmd[0] = ALLOW_MEDIUM_REMOVAL; + req->cmd[1] = 0; + req->cmd[2] = 0; + req->cmd[3] = 0; + req->cmd[4] = SCSI_REMOVAL_PREVENT; + req->cmd[5] = 0; + + req->cmd_len = COMMAND_SIZE(req->cmd[0]); + req->cmd_type = REQ_TYPE_BLOCK_PC; + req->cmd_flags |= REQ_QUIET; + req->timeout = 10 * HZ; + req->retries = 5; + + blk_execute_rq_nowait(req->q, NULL, req, 1, eh_lock_door_done); +} /** * scsi_restart_operations - restart io operations to the specified host. @@ -1437,10 +1498,11 @@ static void scsi_eh_lock_door(struct scsi_device *sdev) * Notes: * When we entered the error handler, we blocked all further i/o to * this device. we need to 'reverse' this process. - **/ + */ static void scsi_restart_operations(struct Scsi_Host *shost) { struct scsi_device *sdev; + unsigned long flags; /* * If the door was locked, we need to insert a door lock request @@ -1458,9 +1520,13 @@ static void scsi_restart_operations(struct Scsi_Host *shost) * ioctls to queued block devices. */ SCSI_LOG_ERROR_RECOVERY(3, printk("%s: waking up host to restart\n", - __FUNCTION__)); + __func__)); - scsi_host_set_state(shost, SHOST_RUNNING); + spin_lock_irqsave(shost->host_lock, flags); + if (scsi_host_set_state(shost, SHOST_RUNNING)) + if (scsi_host_set_state(shost, SHOST_CANCEL)) + BUG_ON(scsi_host_set_state(shost, SHOST_DEL)); + spin_unlock_irqrestore(shost->host_lock, flags); wake_up(&shost->host_wait); @@ -1476,34 +1542,36 @@ static void scsi_restart_operations(struct Scsi_Host *shost) /** * scsi_eh_ready_devs - check device ready state and recover if not. * @shost: host to be recovered. - * @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) + * @work_q: &list_head for pending commands. + * @done_q: &list_head for processed commands. + */ +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)) - if (!scsi_eh_bus_reset(shost, work_q, done_q)) - if (!scsi_eh_host_reset(work_q, done_q)) - scsi_eh_offline_sdevs(work_q, done_q); + if (!scsi_eh_target_reset(shost, work_q, done_q)) + if (!scsi_eh_bus_reset(shost, work_q, done_q)) + 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. * @done_q: list_head of processed commands. - * - **/ -static void scsi_eh_flush_done_q(struct list_head *done_q) + */ +void scsi_eh_flush_done_q(struct list_head *done_q) { struct scsi_cmnd *scmd, *next; list_for_each_entry_safe(scmd, next, done_q, eh_entry) { list_del_init(&scmd->eh_entry); if (scsi_device_online(scmd->device) && - !blk_noretry_request(scmd->request) && - (++scmd->retries < scmd->allowed)) { + !scsi_noretry_cmd(scmd) && + (++scmd->retries <= scmd->allowed)) { SCSI_LOG_ERROR_RECOVERY(3, printk("%s: flush" " retry cmd: %p\n", current->comm, @@ -1524,6 +1592,7 @@ static 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. @@ -1547,7 +1616,7 @@ static void scsi_eh_flush_done_q(struct list_head *done_q) * scsi_finish_cmd() called for it. we do all of the retry stuff * here, so when we restart the host after we return it should have an * empty queue. - **/ + */ static void scsi_unjam_host(struct Scsi_Host *shost) { unsigned long flags; @@ -1568,72 +1637,50 @@ static void scsi_unjam_host(struct Scsi_Host *shost) } /** - * scsi_error_handler - Handle errors/timeouts of SCSI cmds. + * scsi_error_handler - SCSI error handler thread * @data: Host for which we are running. * * Notes: - * This is always run in the context of a kernel thread. The idea is - * that we start this thing up when the kernel starts up (one per host - * that we detect), and it immediately goes to sleep and waits for some - * event (i.e. failure). When this takes place, we have the job of - * trying to unjam the bus and restarting things. - **/ + * This is the main error handling loop. This is run as a kernel thread + * for every SCSI host and handles all error handling activity. + */ int scsi_error_handler(void *data) { - struct Scsi_Host *shost = (struct Scsi_Host *) data; - int rtn; - DECLARE_MUTEX_LOCKED(sem); - - current->flags |= PF_NOFREEZE; - shost->eh_wait = &sem; + struct Scsi_Host *shost = data; /* - * Wake up the thread that created us. + * We use TASK_INTERRUPTIBLE so that the thread is not + * counted against the load average as a running process. + * We never actually get interrupted because kthread_run + * disables signal delivery for the created thread. */ - SCSI_LOG_ERROR_RECOVERY(3, printk("Wake up parent of" - " scsi_eh_%d\n",shost->host_no)); - - while (1) { - /* - * If we get a signal, it means we are supposed to go - * away and die. This typically happens if the user is - * trying to unload a module. - */ - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" - " scsi_eh_%d" - " sleeping\n",shost->host_no)); - - /* - * Note - we always use down_interruptible with the semaphore - * even if the module was loaded as part of the kernel. The - * reason is that down() will cause this thread to be counted - * in the load average as a running process, and down - * interruptible doesn't. Given that we need to allow this - * thread to die if the driver was loaded as a module, using - * semaphores isn't unreasonable. - */ - down_interruptible(&sem); - if (kthread_should_stop()) - break; - - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler" - " scsi_eh_%d waking" - " up\n",shost->host_no)); + set_current_state(TASK_INTERRUPTIBLE); + while (!kthread_should_stop()) { + if ((shost->host_failed == 0 && shost->host_eh_scheduled == 0) || + shost->host_failed != shost->host_busy) { + SCSI_LOG_ERROR_RECOVERY(1, + printk("Error handler scsi_eh_%d sleeping\n", + shost->host_no)); + schedule(); + set_current_state(TASK_INTERRUPTIBLE); + continue; + } - shost->eh_active = 1; + __set_current_state(TASK_RUNNING); + SCSI_LOG_ERROR_RECOVERY(1, + printk("Error handler scsi_eh_%d waking up\n", + shost->host_no)); /* * We have a host that is failing for some reason. Figure out * 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->hostt->eh_strategy_handler) - rtn = shost->hostt->eh_strategy_handler(shost); + if (shost->transportt->eh_strategy_handler) + shost->transportt->eh_strategy_handler(shost); else scsi_unjam_host(shost); - shost->eh_active = 0; - /* * Note - if the above fails completely, the action is to take * individual devices offline and flush the queue of any @@ -1642,16 +1689,13 @@ int scsi_error_handler(void *data) * which are still online. */ scsi_restart_operations(shost); - + set_current_state(TASK_INTERRUPTIBLE); } + __set_current_state(TASK_RUNNING); - SCSI_LOG_ERROR_RECOVERY(1, printk("Error handler scsi_eh_%d" - " exiting\n",shost->host_no)); - - /* - * Make sure that nobody tries to wake us up again. - */ - shost->eh_wait = NULL; + SCSI_LOG_ERROR_RECOVERY(1, + printk("Error handler scsi_eh_%d exiting\n", shost->host_no)); + shost->ehandler = NULL; return 0; } @@ -1681,10 +1725,8 @@ void scsi_report_bus_reset(struct Scsi_Host *shost, int channel) struct scsi_device *sdev; __shost_for_each_device(sdev, shost) { - if (channel == sdev->channel) { - sdev->was_reset = 1; - sdev->expecting_cc_ua = 1; - } + if (channel == sdev_channel(sdev)) + __scsi_report_device_reset(sdev, NULL); } } EXPORT_SYMBOL(scsi_report_bus_reset); @@ -1716,11 +1758,9 @@ void scsi_report_device_reset(struct Scsi_Host *shost, int channel, int target) struct scsi_device *sdev; __shost_for_each_device(sdev, shost) { - if (channel == sdev->channel && - target == sdev->id) { - sdev->was_reset = 1; - sdev->expecting_cc_ua = 1; - } + if (channel == sdev_channel(sdev) && + target == sdev_id(sdev)) + __scsi_report_device_reset(sdev, NULL); } } EXPORT_SYMBOL(scsi_report_device_reset); @@ -1747,35 +1787,26 @@ 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; + blk_rq_init(NULL, &req); 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->cmnd = req.cmd; + 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; + memset(&scmd->sdb, 0, sizeof(scmd->sdb)); 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); - - /* - * Sometimes the command can get back into the timer chain, - * so use the pid as an identifier. - */ - 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: @@ -1783,6 +1814,11 @@ scsi_reset_provider(struct scsi_device *dev, int flag) if (rtn == SUCCESS) break; /* FALLTHROUGH */ + case SCSI_TRY_RESET_TARGET: + rtn = scsi_try_target_reset(scmd); + if (rtn == SUCCESS) + break; + /* FALLTHROUGH */ case SCSI_TRY_RESET_BUS: rtn = scsi_try_bus_reset(scmd); if (rtn == SUCCESS) @@ -1795,6 +1831,22 @@ 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", + __func__)); + + wake_up(&shost->host_wait); + + scsi_run_host_queues(shost); + scsi_next_command(scmd); return rtn; } @@ -1818,7 +1870,7 @@ EXPORT_SYMBOL(scsi_reset_provider); * * 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) { @@ -1864,26 +1916,16 @@ 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) { return scsi_normalize_sense(cmd->sense_buffer, - sizeof(cmd->sense_buffer), sshdr); + SCSI_SENSE_BUFFERSIZE, sshdr); } EXPORT_SYMBOL(scsi_command_normalize_sense); /** - * scsi_sense_desc_find - search for a given descriptor type in - * descriptor sense data format. - * + * 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 @@ -1894,7 +1936,7 @@ EXPORT_SYMBOL(scsi_command_normalize_sense); * * 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) { @@ -1922,9 +1964,7 @@ const u8 * scsi_sense_desc_find(const u8 * sense_buffer, int sb_len, EXPORT_SYMBOL(scsi_sense_desc_find); /** - * scsi_get_sense_info_fld - attempts to get information field from - * sense data (either fixed or descriptor format) - * + * scsi_get_sense_info_fld - 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 @@ -1932,7 +1972,7 @@ EXPORT_SYMBOL(scsi_sense_desc_find); * * 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) { @@ -1972,3 +2012,31 @@ int scsi_get_sense_info_fld(const u8 * sense_buffer, int sb_len, } } EXPORT_SYMBOL(scsi_get_sense_info_fld); + +/** + * scsi_build_sense_buffer - build sense data in a buffer + * @desc: Sense format (non zero == descriptor format, + * 0 == fixed format) + * @buf: Where to build sense data + * @key: Sense key + * @asc: Additional sense code + * @ascq: Additional sense code qualifier + * + **/ +void scsi_build_sense_buffer(int desc, u8 *buf, u8 key, u8 asc, u8 ascq) +{ + if (desc) { + buf[0] = 0x72; /* descriptor, current */ + buf[1] = key; + buf[2] = asc; + buf[3] = ascq; + buf[7] = 0; + } else { + buf[0] = 0x70; /* fixed, current */ + buf[2] = key; + buf[7] = 0xa; + buf[12] = asc; + buf[13] = ascq; + } +} +EXPORT_SYMBOL(scsi_build_sense_buffer);