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

📄 scsi_error.c

📁 Linux内核源代码 为压缩文件 是<<Linux内核>>一书中的源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
	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] : kmalloc(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;	SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);	SCpnt->underflow = 0;	SCpnt->sc_data_direction = SCSI_DATA_NONE;	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;}/* * This would normally need to get the IO request lock, * but as it doesn't actually touch anything that needs * to be locked we can avoid the lock here.. */STATICvoid scsi_sleep_done(struct semaphore *sem){	if (sem != NULL) {		up(sem);	}}void scsi_sleep(int timeout){	DECLARE_MUTEX_LOCKED(sem);	struct timer_list timer;	init_timer(&timer);	timer.data = (unsigned long) &sem;	timer.expires = jiffies + timeout;	timer.function = (void (*)(unsigned long)) scsi_sleep_done;	SCSI_LOG_ERROR_RECOVERY(5, printk("Sleeping for timer tics %d\n", timeout));	add_timer(&timer);	down(&sem);	del_timer(&timer);}/* * Function:  scsi_send_eh_cmnd * * Purpose:     Send a command out to a device as part of error recovery. * * Notes:       The initialization of the structures is quite a bit different *              in this case, and furthermore, there is a different completion *              handler. */STATIC void scsi_send_eh_cmnd(Scsi_Cmnd * SCpnt, int timeout){	unsigned long flags;	struct Scsi_Host *host;	ASSERT_LOCK(&io_request_lock, 0);	host = SCpnt->host;      retry:	/*	 * We will use a queued command if possible, otherwise we will emulate the	 * queuing and calling of completion function ourselves.	 */	SCpnt->owner = SCSI_OWNER_LOWLEVEL;	if (host->can_queue) {		DECLARE_MUTEX_LOCKED(sem);		SCpnt->eh_state = SCSI_STATE_QUEUED;		scsi_add_timer(SCpnt, timeout, scsi_eh_times_out);		/*		 * Set up the semaphore so we wait for the command to complete.		 */		SCpnt->host->eh_action = &sem;		SCpnt->request.rq_status = RQ_SCSI_BUSY;		spin_lock_irqsave(&io_request_lock, flags);		host->hostt->queuecommand(SCpnt, scsi_eh_done);		spin_unlock_irqrestore(&io_request_lock, flags);		down(&sem);		SCpnt->host->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 (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) 			 *			 * 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.			 */			spin_lock_irqsave(&io_request_lock, flags);			if (SCpnt->host->hostt->eh_abort_handler)				SCpnt->host->hostt->eh_abort_handler(SCpnt);			spin_unlock_irqrestore(&io_request_lock, flags);						SCpnt->request.rq_status = RQ_SCSI_DONE;			SCpnt->owner = SCSI_OWNER_ERROR_HANDLER;						SCpnt->eh_state = FAILED;		}		SCSI_LOG_ERROR_RECOVERY(5, printk("send_eh_cmnd: %p eh_state:%x\n",						SCpnt, SCpnt->eh_state));	} else {		int temp;		/*		 * We damn well had better never use this code.  There is no timeout		 * protection here, since we would end up waiting in the actual low		 * level driver, we don't know how to wake it up.		 */		spin_lock_irqsave(&io_request_lock, flags);		temp = host->hostt->command(SCpnt);		spin_unlock_irqrestore(&io_request_lock, flags);		SCpnt->result = temp;		if (scsi_eh_completed_normally(SCpnt)) {			SCpnt->eh_state = SUCCESS;		} else {			SCpnt->eh_state = FAILED;		}	}	/*	 * Now examine the actual status codes to see whether the command actually	 * did complete normally.	 */	if (SCpnt->eh_state == SUCCESS) {		switch (scsi_eh_completed_normally(SCpnt)) {		case SUCCESS:			SCpnt->eh_state = SUCCESS;			break;		case NEEDS_RETRY:			goto retry;		case FAILED:		default:			SCpnt->eh_state = FAILED;			break;		}	} else {		SCpnt->eh_state = FAILED;	}}/* * Function:  scsi_unit_is_ready() * * Purpose:     Called after TEST_UNIT_READY is run, to test to see if *              the unit responded in a way that indicates it is ready. */STATIC int scsi_unit_is_ready(Scsi_Cmnd * SCpnt){	if (SCpnt->result) {		if (((driver_byte(SCpnt->result) & DRIVER_SENSE) ||		     (status_byte(SCpnt->result) & CHECK_CONDITION)) &&		    ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {			if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&			    ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&			    ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST)) {				return 0;			}		}	}	return 1;}/* * Function:    scsi_eh_finish_command * * Purpose:     Handle a command that we are finished with WRT error handling. * * Arguments:   SClist - pointer to list into which we are putting completed commands. *              SCpnt  - command that is completing * * 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 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_command(Scsi_Cmnd ** SClist, Scsi_Cmnd * SCpnt){	SCpnt->state = SCSI_STATE_BHQUEUE;	SCpnt->bh_next = *SClist;	/*	 * Set this back so that the upper level can correctly free up	 * things.	 */	SCpnt->use_sg = SCpnt->old_use_sg;	SCpnt->sc_data_direction = SCpnt->sc_old_data_direction;	SCpnt->underflow = SCpnt->old_underflow;	*SClist = SCpnt;}/* * Function:  scsi_try_to_abort_command * * Purpose:     Ask host adapter to abort a running command. * * Returns:     FAILED          Operation failed or not supported. *              SUCCESS         Succeeded. * * 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. * * Notes:       It may be possible to combine this with all of the reset *              handling to eliminate a lot of code duplication.  I don't *              know what makes more sense at the moment - this is just a *              prototype. */STATIC int scsi_try_to_abort_command(Scsi_Cmnd * SCpnt, int timeout){	int rtn;	unsigned long flags;	SCpnt->eh_state = FAILED;	/* Until we come up with something better */	if (SCpnt->host->hostt->eh_abort_handler == NULL) {		return FAILED;	}	/* 	 * scsi_done was called just after the command timed out and before	 * we had a chance to process it. (DB)	 */	if (SCpnt->serial_number == 0)		return SUCCESS;	SCpnt->owner = SCSI_OWNER_LOWLEVEL;	spin_lock_irqsave(&io_request_lock, flags);	rtn = SCpnt->host->hostt->eh_abort_handler(SCpnt);	spin_unlock_irqrestore(&io_request_lock, flags);	return rtn;}/* * Function:  scsi_try_bus_device_reset * * Purpose:     Ask host adapter to perform a bus device reset for a given *              device. * * Returns:     FAILED          Operation failed or not supported. *              SUCCESS         Succeeded. * * 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(Scsi_Cmnd * SCpnt, int timeout){	unsigned long flags;	int rtn;	SCpnt->eh_state = FAILED;	/* Until we come up with something better */	if (SCpnt->host->hostt->eh_device_reset_handler == NULL) {		return FAILED;	}	SCpnt->owner = SCSI_OWNER_LOWLEVEL;	spin_lock_irqsave(&io_request_lock, flags);	rtn = SCpnt->host->hostt->eh_device_reset_handler(SCpnt);	spin_unlock_irqrestore(&io_request_lock, flags);	if (rtn == SUCCESS)		SCpnt->eh_state = SUCCESS;	return SCpnt->eh_state;}/* * Function:  scsi_try_bus_reset * * Purpose:     Ask host adapter to perform a bus reset for a host. * * Returns:     FAILED          Operation failed or not supported. *              SUCCESS         Succeeded. * * Notes:        */STATIC int scsi_try_bus_reset(Scsi_Cmnd * SCpnt){	unsigned long flags;	int rtn;	SCpnt->eh_state = FAILED;	/* Until we come up with something better */	SCpnt->owner = SCSI_OWNER_LOWLEVEL;	SCpnt->serial_number_at_timeout = SCpnt->serial_number;	if (SCpnt->host->hostt->eh_bus_reset_handler == NULL) {		return FAILED;	}	spin_lock_irqsave(&io_request_lock, flags);	rtn = SCpnt->host->hostt->eh_bus_reset_handler(SCpnt);	spin_unlock_irqrestore(&io_request_lock, flags);	if (rtn == SUCCESS)		SCpnt->eh_state = SUCCESS;	/*	 * If we had a successful bus reset, mark the command blocks to expect	 * a condition code of unit attention.	 */	scsi_sleep(BUS_RESET_SETTLE_TIME);	if (SCpnt->eh_state == SUCCESS) {		Scsi_Device *SDloop;		for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) {			if (SCpnt->channel == SDloop->channel) {				SDloop->was_reset = 1;				SDloop->expecting_cc_ua = 1;			}		}	}	return SCpnt->eh_state;}/* * Function:  scsi_try_host_reset * * Purpose:     Ask host adapter to reset itself, and the bus. * * Returns:     FAILED          Operation failed or not supported. *              SUCCESS         Succeeded. * * Notes: */STATIC int scsi_try_host_reset(Scsi_Cmnd * SCpnt){	unsigned long flags;	int rtn;	SCpnt->eh_state = FAILED;	/* Until we come up with something better */	SCpnt->owner = SCSI_OWNER_LOWLEVEL;	SCpnt->serial_number_at_timeout = SCpnt->serial_number;	if (SCpnt->host->hostt->eh_host_reset_handler == NULL) {		return FAILED;	}	spin_lock_irqsave(&io_request_lock, flags);	rtn = SCpnt->host->hostt->eh_host_reset_handler(SCpnt);	spin_unlock_irqrestore(&io_request_lock, flags);	if (rtn == SUCCESS)		SCpnt->eh_state = SUCCESS;	/*	 * If we had a successful host reset, mark the command blocks to expect	 * a condition code of unit attention.	 */	scsi_sleep(HOST_RESET_SETTLE_TIME);	if (SCpnt->eh_state == SUCCESS) {		Scsi_Device *SDloop;		for (SDloop = SCpnt->host->host_queue; SDloop; SDloop = SDloop->next) {			SDloop->was_reset = 1;			SDloop->expecting_cc_ua = 1;		}	}	return SCpnt->eh_state;}/* * Function:  scsi_decide_disposition * * Purpose:     Examine a command block that has come back from the low-level *              and figure out what to do next. * * Returns:     SUCCESS         - pass on to upper level. *              FAILED          - pass on to error handler thread. *              RETRY           - command should be retried. *              SOFTERR         - command succeeded, but we need to log *                                a soft error. * * Notes:       This is *ONLY* called when we are examining the status *              after sending out the actual data command.  Any commands *              that are queued for error recovery (i.e. TEST_UNIT_READY) *              do *NOT* come through here. * *              NOTE - When this routine returns FAILED, it means the error *              handler thread is woken.  In cases where the error code *              indicates an error that doesn't require the error handler *              thread (i.e. we don't need to abort/reset), then this function *              should return SUCCESS. */int scsi_decide_disposition(Scsi_Cmnd * SCpnt){	int rtn;	/*	 * If the device is offline, then we clearly just pass the result back	 * up to the top level.	 */	if (SCpnt->device->online == FALSE) {		SCSI_LOG_ERROR_RECOVERY(5, printk("scsi_error.c: device offline - report as SUCCESS\n"));		return SUCCESS;	}	/*	 * First check the host byte, to see if there is anything in there	 * that would indicate what we need to do.	 */	switch (host_byte(SCpnt->result)) {	case DID_PASSTHROUGH:		/*		 * No matter what, pass this through to the upper layer.		 * Nuke this special code so that it looks like we are saying		 * DID_OK.		 */		SCpnt->result &= 0xff00ffff;		return SUCCESS;	case DID_OK:		/*		 * Looks good.  Drop through, and check the next byte.		 */		break;	case DID_NO_CONNECT:	case DID_BAD_TARGET:	case DID_ABORT:		/*		 * Note - this means that we just report the status back to the		 * top level driver, not that we actually think that it indicates		 * success.		 */		return SUCCESS;		/*		 * When the low level driver returns DID_SOFT_ERROR,		 * it is responsible for keeping an internal retry counter 		 * in order to avoid endless loops (DB)		 *		 * Actually this is a bug in this function here.  We should

⌨️ 快捷键说明

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