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