scsi_error.c
来自「Linux Kernel 2.6.9 for OMAP1710」· C语言 代码 · 共 1,846 行 · 第 1/4 页
C
1,846 行
/* * scsi_error.c Copyright (C) 1997 Eric Youngdale * * SCSI error/timeout handling * Initial versions: Eric Youngdale. Based upon conversations with * Leonard Zubkoff and David Miller at Linux Expo, * ideas originating from all over the place. * * Restructured scsi_unjam_host and associated functions. * September 04, 2002 Mike Anderson (andmike@us.ibm.com) * * Forward port of Russell King's (rmk@arm.linux.org.uk) changes and * minor cleanups. * September 30, 2002 Mike Anderson (andmike@us.ibm.com) */#include <linux/module.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/slab.h>#include <linux/kernel.h>#include <linux/interrupt.h>#include <linux/blkdev.h>#include <linux/delay.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"#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)#define HOST_RESET_SETTLE_TIME (10)/* called with shost->host_lock held */void scsi_eh_wakeup(struct Scsi_Host *shost){ if (shost->host_busy == shost->host_failed) { up(shost->eh_wait); SCSI_LOG_ERROR_RECOVERY(5, printk("Waking error handler thread\n")); }}/** * 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; if (shost->eh_wait == NULL) return 0; spin_lock_irqsave(shost->host_lock, flags); scsi_eh_eflags_set(scmd, eh_flag); /* * FIXME: Can we stop setting owner and state. */ scmd->owner = SCSI_OWNER_ERROR_HANDLER; scmd->state = SCSI_STATE_FAILED; /* * Set the serial_number_at_timeout to the current * serial_number */ scmd->serial_number_at_timeout = scmd->serial_number; list_add_tail(&scmd->eh_entry, &shost->eh_cmd_q); set_bit(SHOST_RECOVERY, &shost->shost_state); shost->host_failed++; scsi_eh_wakeup(shost); 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;}/** * scsi_times_out - Timeout function for normal scsi commands. * @scmd: Cmd 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){ 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__); }}/** * scsi_block_when_processing_errors - Prevent cmds from being queued. * @sdev: Device on which we are performing recovery. * * Description: * We block until the host is out of error recovery, and then check to * see whether the host or the device is offline. * * 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, (!test_bit(SHOST_RECOVERY, &sdev->host->shost_state))); online = scsi_device_online(sdev); SCSI_LOG_ERROR_RECOVERY(5, printk("%s: rtn: %d\n", __FUNCTION__, online)); return online;}#ifdef CONFIG_SCSI_LOGGING/** * 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){ struct scsi_cmnd *scmd; struct scsi_device *sdev; int total_failures = 0; int cmd_failed = 0; int cmd_cancel = 0; int devices_failed = 0; shost_for_each_device(sdev, shost) { list_for_each_entry(scmd, work_q, eh_entry) { if (scmd->device == sdev) { ++total_failures; if (scsi_eh_eflags_chk(scmd, SCSI_EH_CANCEL_CMD)) ++cmd_cancel; else ++cmd_failed; } } 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)); cmd_cancel = 0; cmd_failed = 0; ++devices_failed; } } SCSI_LOG_ERROR_RECOVERY(2, printk("Total of %d commands on %d" " devices require eh work\n", total_failures, devices_failed));}#endif/** * scsi_check_sense - Examine scsi cmd sense * @scmd: Cmd to have sense checked. * * Return value: * SUCCESS or FAILED or NEEDS_RETRY **/static int scsi_check_sense(struct scsi_cmnd *scmd){ if (!SCSI_SENSE_VALID(scmd)) return FAILED; if (scmd->sense_buffer[2] & 0xe0) return SUCCESS; switch (scmd->sense_buffer[2] & 0xf) { case NO_SENSE: return SUCCESS; case RECOVERED_ERROR: return /* soft_error */ SUCCESS; case ABORTED_COMMAND: return NEEDS_RETRY; case NOT_READY: case UNIT_ATTENTION: /* * if we are expecting a cc/ua because of a bus reset that we * performed, treat this just as a retry. otherwise this is * information that we should pass up to the upper-level driver * so that we can deal with it there. */ if (scmd->device->expecting_cc_ua) { scmd->device->expecting_cc_ua = 0; return NEEDS_RETRY; } /* * if the device is in the process of becoming ready, we * should retry. */ if ((scmd->sense_buffer[12] == 0x04) && (scmd->sense_buffer[13] == 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)) { return FAILED; } return SUCCESS; /* these three are not supported */ case COPY_ABORTED: case VOLUME_OVERFLOW: case MISCOMPARE: return SUCCESS; case MEDIUM_ERROR: return NEEDS_RETRY; case ILLEGAL_REQUEST: case BLANK_CHECK: case DATA_PROTECT: case HARDWARE_ERROR: default: return SUCCESS; }}/** * scsi_eh_completed_normally - Disposition a eh cmd on return from LLD. * @scmd: SCSI cmd to examine. * * Notes: * This is *only* called when we are examining the status of commands * 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){ /* * first check the host byte, to see if there is anything in there * that would indicate what we need to do. */ if (host_byte(scmd->result) == DID_RESET) { /* * rats. we are already in the error handler, so we now * get to try and figure out what to do next. if the sense * is valid, we have a pretty good idea of what to do. * if not, we mark it as FAILED. */ return scsi_check_sense(scmd); } if (host_byte(scmd->result) != DID_OK) return FAILED; /* * next, check the message byte. */ if (msg_byte(scmd->result) != COMMAND_COMPLETE) return FAILED; /* * now, check the status byte to see if this indicates * anything special. */ switch (status_byte(scmd->result)) { case GOOD: case COMMAND_TERMINATED: return SUCCESS; case CHECK_CONDITION: return scsi_check_sense(scmd); case CONDITION_GOOD: case INTERMEDIATE_GOOD: case INTERMEDIATE_C_GOOD: /* * who knows? FIXME(eric) */ return SUCCESS; case BUSY: case QUEUE_FULL: case RESERVATION_CONFLICT: default: return FAILED; } return FAILED;}/** * scsi_eh_times_out - timeout function for error handling. * @scmd: Cmd that is timing out. * * 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){ scsi_eh_eflags_set(scmd, SCSI_EH_REC_TIMEOUT); SCSI_LOG_ERROR_RECOVERY(3, printk("%s: scmd:%p\n", __FUNCTION__, scmd)); if (scmd->device->host->eh_action) up(scmd->device->host->eh_action);}/** * scsi_eh_done - Completion function for error handling. * @scmd: Cmd that is done. **/static void scsi_eh_done(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; scmd->owner = SCSI_OWNER_ERROR_HANDLER; SCSI_LOG_ERROR_RECOVERY(3, printk("%s scmd: %p result: %x\n", __FUNCTION__, scmd, scmd->result)); if (scmd->device->host->eh_action) up(scmd->device->host->eh_action); }}/** * scsi_send_eh_cmnd - send a cmd to a device as part of error recovery. * @scmd: SCSI Cmd to send. * @timeout: Timeout for cmd. * * 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){ struct Scsi_Host *host = scmd->device->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. */
⌨️ 快捷键说明
复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?