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

📄 scsi_error.c

📁 基于组件方式开发操作系统的OSKIT源代码
💻 C
📖 第 1 页 / 共 4 页
字号:
  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;}/* * 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){    struct semaphore sem = MUTEX_LOCKED;    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);    spin_unlock_irq(&io_request_lock);    down(&sem);    spin_lock_irq(&io_request_lock);        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){    struct Scsi_Host * host;    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)    {        struct semaphore sem = MUTEX_LOCKED;        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;	host->hostt->queuecommand (SCpnt, scsi_eh_done);        spin_unlock_irq(&io_request_lock);        down(&sem);        spin_lock_irq(&io_request_lock);        SCpnt->host->eh_action = NULL;	del_timer(&SCpnt->eh_timeout);	/*	 * 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->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.	 */	temp = host->hostt->command (SCpnt);	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 intscsi_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 voidscsi_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;    *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 intscsi_try_to_abort_command(Scsi_Cmnd * SCpnt, int timeout){  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;  return SCpnt->host->hostt->eh_abort_handler(SCpnt);}/* * 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 intscsi_try_bus_device_reset(Scsi_Cmnd * SCpnt, int timeout){  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;      rtn = SCpnt->host->hostt->eh_device_reset_handler(SCpnt);  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 intscsi_try_bus_reset(Scsi_Cmnd * SCpnt){  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;    }  rtn = SCpnt->host->hostt->eh_bus_reset_handler(SCpnt);  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 intscsi_try_host_reset(Scsi_Cmnd * SCpnt){    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;    }        rtn = SCpnt->host->hostt->eh_host_reset_handler(SCpnt);    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)       */    case DID_SOFT_ERROR:      return NEEDS_RETRY;    case DID_BUS_BUSY:    case DID_PARITY:    case DID_ERROR:      goto maybe_retry;    case DID_TIME_OUT:      /*         * When we scan the bus, we get timeout messages for         * these commands if there is no device available.         * Other hosts report DID_NO_CONNECT for the same thing.         */        if( (SCpnt->cmnd[0] == TEST_UNIT_READY ||             SCpnt->cmnd[0] == INQUIRY) )        {            return SUCCESS;        }        else        {            return FAILED;        }    case DID_RESET:      /*       * In the normal case where we haven't initiated a reset, this is       * a failure.       */      if( SCpnt->flags & IS_RESETTING )	{	  SCpnt->flags &= ~IS_RESETTING;	  goto maybe_retry;	}      /*       * Examine the sense data to figure out how to proceed from here.       * If there is no sense data, we will be forced into the error       * handler thread, where we get to examine the thing in a lot more       * detail.       */      return scsi_check_sense (SCpnt);    default:      return FAILED;    }  /*   * Next, check the message byte.   */  if( msg_byte(SCpnt->result) != COMMAND_COMPLETE )    {      return FAILED;    }  /*   * Now, check the status byte to see if this indicates anything special.

⌨️ 快捷键说明

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