📄 scsi_error.c
字号:
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 + -