⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 scsi_error.c

📁 基于组件方式开发操作系统的OSKIT源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
/* *  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 + -