📄 scsi.c
字号:
exit = DRIVER_SENSE | SUGGEST_RETRY; break; case SUGGEST_ABORT: status = FINISHED; exit = DRIVER_SENSE | SUGGEST_ABORT; break; case SUGGEST_SENSE: scsi_request_sense (SCpnt); status = PENDING; break; } } else { status=REDO; exit = SUGGEST_RETRY; } break; default : exit = (DRIVER_ERROR | SUGGEST_DIE); } switch (status) { case FINISHED: case PENDING: break; case MAYREDO:#ifdef DEBUG printk("In MAYREDO, allowing %d retries, have %d\n", SCpnt->allowed, SCpnt->retries);#endif if ((++SCpnt->retries) < SCpnt->allowed) { if ((SCpnt->retries >= (SCpnt->allowed >> 1)) && !(SCpnt->host->last_reset > 0 && jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD) && !(SCpnt->flags & WAS_RESET)) { printk("scsi%d channel %d : resetting for second half of retries.\n", SCpnt->host->host_no, SCpnt->channel); scsi_reset(SCpnt, SCSI_RESET_SYNCHRONOUS); break; } } else { status = FINISHED; break; } /* fall through to REDO */ case REDO: if (SCpnt->flags & WAS_SENSE) scsi_request_sense(SCpnt); else { 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; internal_cmnd (SCpnt); } break; default: INTERNAL_ERROR; } if (status == FINISHED) {#ifdef DEBUG printk("Calling done function - at address %p\n", SCpnt->done);#endif host->host_busy--; /* Indicate that we are free */ if (host->block && host->host_busy == 0) { host_active = NULL; /* For block devices "wake_up" is done in end_scsi_request */ if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR && MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) { struct Scsi_Host * next; for (next = host->block; next != host; next = next->block) wake_up(&next->host_wait); } } wake_up(&host->host_wait); SCpnt->result = result | ((exit & 0xff) << 24); SCpnt->use_sg = SCpnt->old_use_sg; SCpnt->cmd_len = SCpnt->old_cmd_len; SCpnt->done (SCpnt); } #undef FINISHED#undef REDO#undef MAYREDO#undef PENDING}/* * The scsi_abort function interfaces with the abort() function of the host * we are aborting, and causes the current command to not complete. The * caller should deal with any error messages or status returned on the * next call. * * This will not be called reentrantly for a given host. *//* * Since we're nice guys and specified that abort() and reset() * can be non-reentrant. The internal_timeout flags are used for * this. */int scsi_abort (Scsi_Cmnd * SCpnt, int why){ int oldto; unsigned long flags; struct Scsi_Host * host = SCpnt->host; while(1) { save_flags(flags); cli(); /* * Protect against races here. If the command is done, or we are * on a different command forget it. */ if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { restore_flags(flags); return 0; } if (SCpnt->internal_timeout & IN_ABORT) { restore_flags(flags); while (SCpnt->internal_timeout & IN_ABORT) barrier(); } else { SCpnt->internal_timeout |= IN_ABORT; oldto = update_timeout(SCpnt, ABORT_TIMEOUT); if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) { /* OK, this command must have died when we did the * reset. The device itself must have lied. */ printk("Stale command on %d %d:%d appears to have died when" " the bus was reset\n", SCpnt->channel, SCpnt->target, SCpnt->lun); } restore_flags(flags); if (!host->host_busy) { SCpnt->internal_timeout &= ~IN_ABORT; update_timeout(SCpnt, oldto); return 0; } printk("scsi : aborting command due to timeout : pid %lu, scsi%d," " channel %d, id %d, lun %d ", SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel, (int) SCpnt->target, (int) SCpnt->lun); print_command (SCpnt->cmnd); if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) return 0; SCpnt->abort_reason = why; switch(host->hostt->abort(SCpnt)) { /* We do not know how to abort. Try waiting another * time increment and see if this helps. Set the * WAS_TIMEDOUT flag set so we do not try this twice */ case SCSI_ABORT_BUSY: /* Tough call - returning 1 from * this is too severe */ case SCSI_ABORT_SNOOZE: if(why == DID_TIME_OUT) { save_flags(flags); cli(); SCpnt->internal_timeout &= ~IN_ABORT; if(SCpnt->flags & WAS_TIMEDOUT) { restore_flags(flags); return 1; /* Indicate we cannot handle this. * We drop down into the reset handler * and try again */ } else { SCpnt->flags |= WAS_TIMEDOUT; oldto = SCpnt->timeout_per_command; update_timeout(SCpnt, oldto); } restore_flags(flags); } return 0; case SCSI_ABORT_PENDING: if(why != DID_TIME_OUT) { save_flags(flags); cli(); update_timeout(SCpnt, oldto); restore_flags(flags); } return 0; case SCSI_ABORT_SUCCESS: /* We should have already aborted this one. No * need to adjust timeout */ SCpnt->internal_timeout &= ~IN_ABORT; return 0; case SCSI_ABORT_NOT_RUNNING: SCpnt->internal_timeout &= ~IN_ABORT; update_timeout(SCpnt, 0); return 0; case SCSI_ABORT_ERROR: default: SCpnt->internal_timeout &= ~IN_ABORT; return 1; } } }}/* Mark a single SCSI Device as having been reset. */static inline void scsi_mark_device_reset(Scsi_Device *Device){ Device->was_reset = 1; Device->expecting_cc_ua = 1;}/* Mark all SCSI Devices on a specific Host as having been reset. */void scsi_mark_host_reset(struct Scsi_Host *Host){ Scsi_Cmnd *SCpnt; for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next) scsi_mark_device_reset(SCpnt->device);}/* Mark all SCSI Devices on a specific Host Bus as having been reset. */void scsi_mark_bus_reset(struct Scsi_Host *Host, int channel){ Scsi_Cmnd *SCpnt; for (SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next) if (SCpnt->channel == channel) scsi_mark_device_reset(SCpnt->device);}int scsi_reset (Scsi_Cmnd * SCpnt, unsigned int reset_flags){ int temp; unsigned long flags; Scsi_Cmnd * SCpnt1; struct Scsi_Host * host = SCpnt->host; printk("SCSI bus is being reset for host %d channel %d.\n", host->host_no, SCpnt->channel); #if 0 /* * First of all, we need to make a recommendation to the low-level * driver as to whether a BUS_DEVICE_RESET should be performed, * or whether we should do a full BUS_RESET. There is no simple * algorithm here - we basically use a series of heuristics * to determine what we should do. */ SCpnt->host->suggest_bus_reset = FALSE; /* * First see if all of the active devices on the bus have * been jammed up so that we are attempting resets. If so, * then suggest a bus reset. Forcing a bus reset could * result in some race conditions, but no more than * you would usually get with timeouts. We will cross * that bridge when we come to it. * * This is actually a pretty bad idea, since a sequence of * commands will often timeout together and this will cause a * Bus Device Reset followed immediately by a SCSI Bus Reset. * If all of the active devices really are jammed up, the * Bus Device Reset will quickly timeout and scsi_times_out * will follow up with a SCSI Bus Reset anyway. */ SCpnt1 = host->host_queue; while(SCpnt1) { if( SCpnt1->request.rq_status != RQ_INACTIVE && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 ) break; SCpnt1 = SCpnt1->next; } if( SCpnt1 == NULL ) { reset_flags |= SCSI_RESET_SUGGEST_BUS_RESET; } /* * If the code that called us is suggesting a hard reset, then * definitely request it. This usually occurs because a * BUS_DEVICE_RESET times out. * * Passing reset_flags along takes care of this automatically. */ if( reset_flags & SCSI_RESET_SUGGEST_BUS_RESET ) { SCpnt->host->suggest_bus_reset = TRUE; }#endif while (1) { save_flags(flags); cli(); /* * Protect against races here. If the command is done, or we are * on a different command forget it. */ if (reset_flags & SCSI_RESET_ASYNCHRONOUS) if (SCpnt->serial_number != SCpnt->serial_number_at_timeout) { restore_flags(flags); return 0; } if (SCpnt->internal_timeout & IN_RESET) { restore_flags(flags); while (SCpnt->internal_timeout & IN_RESET) barrier(); } else { SCpnt->internal_timeout |= IN_RESET; update_timeout(SCpnt, RESET_TIMEOUT); if (host->host_busy) { restore_flags(flags); SCpnt1 = host->host_queue; while(SCpnt1) { if (SCpnt1->request.rq_status != RQ_INACTIVE) {#if 0 if (!(SCpnt1->flags & IS_RESETTING) && !(SCpnt1->internal_timeout & IN_ABORT)) scsi_abort(SCpnt1, DID_RESET);#endif SCpnt1->flags |= (WAS_RESET | IS_RESETTING); } SCpnt1 = SCpnt1->next; } host->last_reset = jiffies; temp = host->hostt->reset(SCpnt, reset_flags); /* This test allows the driver to introduce an additional bus settle time delay by setting last_reset up to 20 seconds in the future. In the normal case where the driver does not modify last_reset, it must be assumed that the actual bus reset occurred immediately prior to the return to this code, and so last_reset must be updated to the current time, so that the delay in internal_cmnd will guarantee at least a MIN_RESET_DELAY bus settle time. */ if ((host->last_reset < jiffies) || (host->last_reset > (jiffies + 20 * HZ))) host->last_reset = jiffies; } else { if (!host->block) host->host_busy++; restore_flags(flags); host->last_reset = jiffies; SCpnt->flags |= (WAS_RESET | IS_RESETTING); temp = host->hostt->reset(SCpnt, reset_flags); if ((host->last_reset < jiffies) || (host->last_reset > (jiffies + 20 * HZ))) host->last_reset = jiffies; if (!host->block) host->host_busy--; } #ifdef DEBUG printk("scsi reset function returned %d\n", temp);#endif /* * Now figure out what we need to do, based upon * what the low level driver said that it did. * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING, * or SCSI_RESET_WAKEUP, then the low level driver did a * bus device reset or bus reset, so we should go through * and mark one or all of the devices on that bus * as having been reset. */ switch(temp & SCSI_RESET_ACTION) { case SCSI_RESET_SUCCESS: if (temp & SCSI_RESET_HOST_RESET) scsi_mark_host_reset(host); else if (temp & SCSI_RESET_BUS_RESET) scsi_mark_bus_reset(host, SCpnt->channel); else scsi_mark_device_reset(SCpnt->device); save_flags(flags); cli(); SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); restore_flags(flags); return 0; case SCSI_RESET_PENDING: if (temp & SCSI_RESET_HOST_RESET) scsi_mark_host_reset(host); else if (temp & SCSI_RESET_BUS_RESET) scsi_mark_bus_reset(host, SCpnt->channel); else scsi_mark_device_reset(SCpnt->device); case SCSI_RESET_NOT_RUNNING: return 0; case SCSI_RESET_PUNT: SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); scsi_request_sense (SCpnt); return 0; case SCSI_RESET_WAKEUP: if (temp & SCSI_RESET_HOST_RESET) scsi_mark_host_reset(host); else if (temp & SCSI_RESET_BUS_RESET) scsi_mark_bus_reset(host, SCpnt->channel); else scsi_mark_device_reset(SCpnt->device); SCpnt->internal_timeout &= ~(IN_RESET|IN_RESET2|IN_RESET3); scsi_request_sense (SCpnt); /* * If a bus reset was performed, we * need to wake up each and every command * that was active on the bus or if it was a HBA * reset all active commands on all channels */ if( temp & SCSI_RESET_HOST_RESET ) { SCpnt1 = host->host_queue; while(SCpnt1) { if (SCpnt1->request.rq_status != RQ_INACTIVE && SCpnt1 != SCpnt)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -