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

📄 scsi_error.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 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"/* * We must always allow SHUTDOWN_SIGS.  Even if we are not a module, * the host drivers that we are using may be loaded as modules, and * when we unload these,  we need to ensure that the error handler thread * can be shut down. * * Note - when we unload a module, we send a SIGHUP.  We mustn't * enable SIGTERM, as this is how the init shuts things down when you * go to single-user mode.  For that matter, init also sends SIGKILL, * so we mustn't enable that one either.  We use SIGHUP instead.  Other * options would be SIGPWR, I suppose. */#define SHUTDOWN_SIGS	(sigmask(SIGHUP))#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. */void scsi_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;	SCset->done_late = 0;	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:     1 if we were able to detach the timer.  0 if we *              blew it, and the timer function has already started *              to run. * * Notes:       This should be turned into an inline function. */int scsi_delete_timer(Scsi_Cmnd * SCset){	int rtn;	rtn = del_timer(&SCset->eh_timeout);	SCSI_LOG_ERROR_RECOVERY(5, printk("Clearing timer for command %p %d\n", SCset, rtn));	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:       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(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->eh_wait == NULL ) {		panic("Error handler thread not present at %p %p %s %d", 		      SCpnt, SCpnt->host, __FILE__, __LINE__);	}	if (SCpnt->host->host_busy == SCpnt->host->host_failed) {		up(SCpnt->host->eh_wait);	}}/* * 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){	SCpnt->eh_state = SCSI_STATE_TIMEOUT;	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");}/* * 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){	int     rtn;	/*	 * 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.	 */	rtn = del_timer(&SCpnt->eh_timeout);	if (!rtn) {		SCpnt->done_late = 1;		return;	}	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. */int scsi_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 int scsi_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;	SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;	SCpnt->underflow = SCpnt->old_underflow;	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 int scsi_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;	ASSERT_LOCK(&io_request_lock, 0);	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] : kmalloc(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]);	SCpnt->sc_data_direction = SCSI_DATA_READ;	SCpnt->underflow = 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)		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)	 */	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;	SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;	SCpnt->underflow = SCpnt->old_underflow;	/*	 * 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 int scsi_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;

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -