📄 scsi_error.c
字号:
/* * 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. * */#define __NO_VERSION__#include <linux/module.h>#include <linux/sched.h>#include <linux/timer.h>#include <linux/string.h>#include <linux/malloc.h>#include <linux/ioport.h>#include <linux/kernel.h>#include <linux/stat.h>#include <linux/blk.h>#include <linux/interrupt.h>#include <linux/delay.h>#include <linux/smp_lock.h>#define __KERNEL_SYSCALLS__#include <linux/unistd.h>#include <asm/system.h>#include <asm/irq.h>#include <asm/dma.h>#include "scsi.h"#include "hosts.h"#include "constants.h"#define SHUTDOWN_SIGS (sigmask(SIGKILL)|sigmask(SIGINT)|sigmask(SIGTERM))#ifdef DEBUG #define SENSE_TIMEOUT SCSI_TIMEOUT #define ABORT_TIMEOUT SCSI_TIMEOUT #define RESET_TIMEOUT SCSI_TIMEOUT#else #define SENSE_TIMEOUT (10*HZ) #define RESET_TIMEOUT (2*HZ) #define ABORT_TIMEOUT (15*HZ)#endif#define STATIC/* * These should *probably* be handled by the host itself. * Since it is allowed to sleep, it probably should. */#define BUS_RESET_SETTLE_TIME 5*HZ#define HOST_RESET_SETTLE_TIME 10*HZstatic const char RCSid[] = "$Header: /mnt/ide/home/eric/CVSROOT/linux/drivers/scsi/scsi_error.c,v 1.10 1997/12/08 04:50:35 eric Exp $";STATIC int scsi_check_sense (Scsi_Cmnd * SCpnt);STATIC int scsi_request_sense(Scsi_Cmnd *);STATIC void scsi_send_eh_cmnd (Scsi_Cmnd * SCpnt, int timeout);STATIC int scsi_try_to_abort_command(Scsi_Cmnd *, int);STATIC int scsi_test_unit_ready(Scsi_Cmnd *);STATIC int scsi_try_bus_device_reset(Scsi_Cmnd *, int timeout);STATIC int scsi_try_bus_reset(Scsi_Cmnd *);STATIC int scsi_try_host_reset(Scsi_Cmnd *);STATIC int scsi_unit_is_ready(Scsi_Cmnd *);STATIC void scsi_eh_action_done(Scsi_Cmnd *, int);STATIC int scsi_eh_retry_command(Scsi_Cmnd *);STATIC int scsi_eh_completed_normally(Scsi_Cmnd * SCpnt);STATIC void scsi_restart_operations(struct Scsi_Host *);STATIC void scsi_eh_finish_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt);/* * Function: scsi_add_timer() * * Purpose: Start timeout timer for a single scsi command. * * Arguments: SCset - 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. * * Returns: Nothing * * Notes: This should be turned into an inline function. * * More Notes: Each scsi command has it's own timer, and as it is added to * the queue, we set up the timer. When the command completes, * we cancel the timer. Pretty simple, really, especially * compared to the old way of handling this crap. */voidscsi_add_timer(Scsi_Cmnd * SCset, int timeout, void (*complete)(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( SCset->eh_timeout.function != NULL ) { del_timer(&SCset->eh_timeout); } SCset->eh_timeout.data = (unsigned long) SCset; SCset->eh_timeout.expires = jiffies + timeout; SCset->eh_timeout.function = (void (*)(unsigned long))complete; SCSI_LOG_ERROR_RECOVERY(5,printk("Adding timer for command %p at %d (%p)\n", SCset, timeout, complete)); add_timer(&SCset->eh_timeout);}/* * Function: scsi_delete_timer() * * Purpose: Delete/cancel timer for a given function. * * Arguments: SCset - command that we are canceling timer for. * * Returns: Amount of time remaining before command would have timed out. * * Notes: This should be turned into an inline function. */intscsi_delete_timer(Scsi_Cmnd * SCset){ int rtn; rtn = jiffies - SCset->eh_timeout.expires; del_timer(&SCset->eh_timeout); SCSI_LOG_ERROR_RECOVERY(5,printk("Clearing timer for command %p\n", SCset)); SCset->eh_timeout.data = (unsigned long) NULL; SCset->eh_timeout.function = NULL; return rtn;}/* * Function: scsi_times_out() * * Purpose: Timeout function for normal scsi commands.. * * Arguments: SCpnt - command that is timing out. * * Returns: Nothing. * * Notes: */static void do_scsi_times_out (Scsi_Cmnd * SCpnt){ /* * Notify the low-level code that this operation failed and we are * reposessing the command. */#ifdef ERIC_neverdef /* * FIXME(eric) * Allow the host adapter to push a queue ordering tag * out to the bus to force the command in question to complete. * If the host wants to do this, then we just restart the timer * for the command. Before we really do this, some real thought * as to the optimum way to handle this should be done. We *do* * need to force ordering every so often to ensure that all requests * do eventually complete, but I am not sure if this is the best way * to actually go about it. * * Better yet, force a sync here, but don't block since we are in an * interrupt. */ if( SCpnt->host->hostt->eh_ordered_queue_tag ) { if( (*SCpnt->host->hostt->eh_ordered_queue_tag)(SCpnt)) { scsi_add_timer(SCpnt, SCpnt->internal_timeout, scsi_times_out); return; } } /* * FIXME(eric) - add a second special interface to handle this * case. Ideally that interface can also be used to request * a queu */ if (SCpnt->host->can_queue) { SCpnt->host->hostt->queuecommand (SCpnt, NULL); }#endif /* Set the serial_number_at_timeout to the current serial_number */ SCpnt->serial_number_at_timeout = SCpnt->serial_number; SCpnt->eh_state = FAILED; SCpnt->state = SCSI_STATE_TIMEOUT; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCpnt->host->in_recovery = 1; SCpnt->host->host_failed++; SCSI_LOG_TIMEOUT(3,printk("Command timed out active=%d busy=%d failed=%d\n", atomic_read(&SCpnt->host->host_active), SCpnt->host->host_busy, SCpnt->host->host_failed)); /* * If the host is having troubles, then look to see if this was the last * command that might have failed. If so, wake up the error handler. */ if( SCpnt->host->host_busy == SCpnt->host->host_failed ) { up(SCpnt->host->eh_wait); }}void scsi_times_out (Scsi_Cmnd * SCpnt){ unsigned long flags; spin_lock_irqsave(&io_request_lock, flags); do_scsi_times_out(SCpnt); spin_unlock_irqrestore(&io_request_lock, flags);}/* * Function scsi_block_when_processing_errors * * Purpose: Prevent more commands from being queued while error recovery * is taking place. * * Arguments: SDpnt - device on which we are performing recovery. * * Returns: FALSE The device was taken offline by error recovery. * TRUE OK to proceed. * * Notes: We block until the host is out of error recovery, and then * check to see whether the host or the device is offline. */int scsi_block_when_processing_errors(Scsi_Device * SDpnt){ SCSI_SLEEP( &SDpnt->host->host_wait, SDpnt->host->in_recovery); SCSI_LOG_ERROR_RECOVERY(5,printk("Open returning %d\n", SDpnt->online)); return SDpnt->online;}/* * Function: scsi_eh_times_out() * * Purpose: Timeout function for error handling. * * Arguments: SCpnt - command that is timing out. * * Returns: Nothing. * * 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. */STATICvoid scsi_eh_times_out (Scsi_Cmnd * SCpnt){ unsigned long flags; int rtn = FAILED; spin_lock_irqsave(&io_request_lock, flags); SCpnt->eh_state = SCSI_STATE_TIMEOUT; SCpnt->owner = SCSI_OWNER_LOWLEVEL; /* * 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) */ if (SCpnt->host->hostt->eh_abort_handler) rtn = SCpnt->host->hostt->eh_abort_handler(SCpnt); SCpnt->request.rq_status = RQ_SCSI_DONE; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCSI_LOG_ERROR_RECOVERY(5,printk("In scsi_eh_times_out %p\n", SCpnt)); if (SCpnt->host->eh_action != NULL) up(SCpnt->host->eh_action); else printk("Missing scsi error handler thread\n"); spin_unlock_irqrestore(&io_request_lock, flags);}/* * Function: scsi_eh_done() * * Purpose: Completion function for error handling. * * Arguments: SCpnt - command that is timing out. * * Returns: Nothing. * * 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 the action completed, and to wake up the * thread. */STATICvoid scsi_eh_done (Scsi_Cmnd * SCpnt){ SCpnt->request.rq_status = RQ_SCSI_DONE; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCpnt->eh_state = SUCCESS; SCSI_LOG_ERROR_RECOVERY(5,printk("In eh_done %p result:%x\n", SCpnt, SCpnt->result)); if (SCpnt->host->eh_action != NULL) up(SCpnt->host->eh_action);}/* * Function: scsi_eh_action_done() * * Purpose: Completion function for error handling. * * Arguments: SCpnt - command that is timing out. * answer - boolean that indicates whether operation succeeded. * * Returns: Nothing. * * Notes: This callback is only used for abort and reset operations. */STATICvoid scsi_eh_action_done (Scsi_Cmnd * SCpnt, int answer){ SCpnt->request.rq_status = RQ_SCSI_DONE; SCpnt->owner = SCSI_OWNER_ERROR_HANDLER; SCpnt->eh_state = (answer ? SUCCESS : FAILED); if (SCpnt->host->eh_action != NULL) up(SCpnt->host->eh_action);}/* * Function: scsi_sense_valid() * * Purpose: Determine whether a host has automatically obtained sense * information or not. If we have it, then give a recommendation * as to what we should do next. */intscsi_sense_valid(Scsi_Cmnd * SCpnt){ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) { return FALSE; } return TRUE;}/* * Function: scsi_eh_retry_command() * * Purpose: Retry the original command * * Returns: SUCCESS - we were able to get the sense data. * FAILED - we were not able to get the sense data. * * Notes: This function will *NOT* return until the command either * times out, or it completes. */STATIC intscsi_eh_retry_command(Scsi_Cmnd * SCpnt){ memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd, sizeof(SCpnt->data_cmnd)); SCpnt->request_buffer = SCpnt->buffer; SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; scsi_send_eh_cmnd (SCpnt, SCpnt->timeout_per_command); /* * Hey, we are done. Let's look to see what happened. */ return SCpnt->eh_state;}/* * Function: scsi_request_sense() * * Purpose: Request sense data from a particular target. * * Returns: SUCCESS - we were able to get the sense data. * FAILED - we were not able to get the sense data. * * Notes: 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 intscsi_request_sense(Scsi_Cmnd * SCpnt){ static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0}; unsigned char scsi_result0[256], *scsi_result=NULL; memcpy ((void *) SCpnt->cmnd , (void *) generic_sense, sizeof(generic_sense)); SCpnt->cmnd[1] = SCpnt->lun << 5; scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) ? &scsi_result0[0] : scsi_init_malloc (512, GFP_ATOMIC|GFP_DMA); if (scsi_result == NULL) { printk("cannot allocate scsi_result in scsi_request_sense.\n"); return FAILED; } /* * Zero the sense buffer. Some host adapters automatically always request * sense, so it is not a good idea that SCpnt->request_buffer and * SCpnt->sense_buffer point to the same address (DB). * 0 is not a valid sense code. */ memset ((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); memset ((void *) scsi_result, 0, 256); SCpnt->request_buffer = scsi_result; SCpnt->request_bufflen = 256; SCpnt->use_sg = 0; SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]); scsi_send_eh_cmnd (SCpnt, SENSE_TIMEOUT); /* Last chance to have valid sense data */ if (!scsi_sense_valid(SCpnt)) memcpy((void *) SCpnt->sense_buffer, SCpnt->request_buffer, sizeof(SCpnt->sense_buffer)); if (scsi_result != &scsi_result0[0] && scsi_result != NULL) scsi_init_free (scsi_result, 512); /* * When we eventually call scsi_finish, we really wish to complete * the original request, so let's restore the original data. (DB) */ memcpy ((void *) SCpnt->cmnd, (void*) SCpnt->data_cmnd, sizeof(SCpnt->data_cmnd)); SCpnt->request_buffer = SCpnt->buffer; SCpnt->request_bufflen = SCpnt->bufflen; SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; /* * Hey, we are done. Let's look to see what happened. */ return SCpnt->eh_state;}/* * Function: scsi_test_unit_ready() * * Purpose: Run test unit ready command to see if the device is talking to us or not. * */STATIC intscsi_test_unit_ready(Scsi_Cmnd * SCpnt){ static unsigned char tur_command[6] = {TEST_UNIT_READY, 0,0,0,0,0}; unsigned char scsi_result0[256], *scsi_result=NULL; memcpy ((void *) SCpnt->cmnd , (void *) tur_command, sizeof(tur_command)); SCpnt->cmnd[1] = SCpnt->lun << 5; scsi_result = (!SCpnt->host->hostt->unchecked_isa_dma) ? &scsi_result0[0] : scsi_init_malloc (512, GFP_ATOMIC|GFP_DMA); if (scsi_result == NULL) { printk("cannot allocate scsi_result in scsi_test_unit_ready.\n"); return FAILED; } /* * Zero the sense buffer. Some host adapters automatically always request * sense, so it is not a good idea that SCpnt->request_buffer and * SCpnt->sense_buffer point to the same address (DB). * 0 is not a valid sense code. */ memset ((void *) SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer)); memset ((void *) scsi_result, 0, 256); SCpnt->request_buffer = scsi_result; SCpnt->request_bufflen = 256; SCpnt->use_sg = 0;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -