📄 isp_linux.c
字号:
f = (Scsi_Cmnd *) b->host_scribble; if (f == Cmnd) { b->host_scribble = f->host_scribble; if (isp->isp_osinfo.wqtail == Cmnd) isp->isp_osinfo.wqtail = b; break; } b = f; } } if (f) { f->host_scribble = NULL; isp->isp_osinfo.wqcnt -= 1; } return (f);}static INLINE voidisplinux_runwaitq(struct ispsoftc *isp){ Scsi_Cmnd *f; while ((f = isp_remove_from_waitq(isp->isp_osinfo.wqnext)) != NULL) { int result = isp_start(f); /* * Restart the timer for this command if it is queued or completing. */ if (result == CMD_QUEUED || result == CMD_COMPLETE) { int ntime = f->timeout_per_command; if (isp_xtime) ntime *= isp_xtime; scsi_add_timer(f, ntime, f->done); } if (result == CMD_QUEUED) { if (isp->isp_osinfo.hiwater < isp->isp_nactive) isp->isp_osinfo.hiwater = isp->isp_nactive; continue; } /* * If we cannot start a command on a fibre channel card, it means * that loop state isn't ready for us to do so. Activate the FC * thread to rediscover loop and fabric residency- but not if * we consider the loop to be dead. If the loop is considered dead, * we wait until a PDB Changed after a Loop UP activates the FC * thread. */ if (result == CMD_RQLATER && IS_FC(isp) && isp->isp_deadloop == 0) { SEND_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, 0); } /* * Put the command back on the wait queue. Don't change any * timer parameters for it because they were established * when we originally put the command on the waitq in the first * place. */ if (result == CMD_EAGAIN || result == CMD_RQLATER) { isplinux_insert_head_waitq(isp, f); break; } if (result == CMD_COMPLETE) { isp_done(f); } else { panic("isplinux_runwaitq: result %d", result); } }}static INLINE voidisplinux_flushwaitq(struct ispsoftc *isp){ Scsi_Cmnd *Cmnd, *Ncmnd; if ((Cmnd = isp->isp_osinfo.wqnext) == NULL) { return; } isp->isp_osinfo.wqnext = isp->isp_osinfo.wqtail = NULL; isp->isp_osinfo.wqcnt = 0; ISP_UNLK_SOFTC(isp); do { Ncmnd = (Scsi_Cmnd *) Cmnd->host_scribble; Cmnd->host_scribble = NULL; XS_INITERR(Cmnd); XS_SETERR(Cmnd, DID_NO_CONNECT); /* * Add back a timer else scsi_done drops this on the floor. */ scsi_add_timer(Cmnd, Cmnd->timeout_per_command, Cmnd->done); ISP_LOCK_SCSI_DONE(isp); (*Cmnd->scsi_done)(Cmnd); ISP_UNLK_SCSI_DONE(isp); } while ((Cmnd = Ncmnd) != NULL); ISP_LOCK_SOFTC(isp);}static INLINE Scsi_Cmnd *isplinux_remove_from_doneq(Scsi_Cmnd *Cmnd){ Scsi_Cmnd *f; struct ispsoftc *isp; if (Cmnd == NULL) return (NULL); isp = (struct ispsoftc *) Cmnd->host->hostdata; if (isp->isp_osinfo.dqnext == NULL) return (NULL); if ((f = isp->isp_osinfo.dqnext) == Cmnd) { isp->isp_osinfo.dqnext = (Scsi_Cmnd *) Cmnd->host_scribble; } else { Scsi_Cmnd *b = f; while (f) { f = (Scsi_Cmnd *) b->host_scribble; if (f == Cmnd) { b->host_scribble = f->host_scribble; if (isp->isp_osinfo.dqtail == Cmnd) isp->isp_osinfo.dqtail = b; break; } b = f; } } if (f) { f->host_scribble = NULL; } return (f);}intisplinux_queuecommand(Scsi_Cmnd *Cmnd, void (*donecmd)(Scsi_Cmnd *)){ struct Scsi_Host *host = Cmnd->host; struct ispsoftc *isp = (struct ispsoftc *) (host->hostdata); int result; Cmnd->scsi_done = donecmd; Cmnd->sense_buffer[0] = 0; if (isp_xtime) { Cmnd->timeout *= isp_xtime; } ISP_DRIVER_ENTRY_LOCK(isp); ISP_LOCK_SOFTC(isp); /* * First off, see whether we need to (re)init the HBA. * If we need to and fail to, pretend that this was a selection timeout. */ if (isp->isp_state != ISP_RUNSTATE) { if (isp->isp_role != ISP_ROLE_NONE) { isplinux_reinit(isp); } if (isp->isp_state != ISP_RUNSTATE) { ISP_UNLK_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); XS_INITERR(Cmnd); XS_SETERR(Cmnd, DID_NO_CONNECT); donecmd(Cmnd); return (0); } } /* * Next see if we have any stored up commands to run. If so, run them. * If we get back from this with commands still ready to run, put the * current command at the tail of waiting commands to be run later. */ isplinux_runwaitq(isp); if (isp->isp_osinfo.wqnext) { isplinux_append_to_waitq(isp, Cmnd); ISP_UNLK_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); return (0); } /* * Finally, try and run this command. */ result = isp_start(Cmnd); if (result == CMD_QUEUED) { if (isp->isp_osinfo.hiwater < isp->isp_nactive) isp->isp_osinfo.hiwater = isp->isp_nactive; result = 0; if (isp_xtime) { scsi_delete_timer(Cmnd); scsi_add_timer(Cmnd, isp_xtime * Cmnd->timeout_per_command, Cmnd->done); } } else if (result == CMD_EAGAIN) { /* * We ran out of request queue space (or could not * get DMA resources). Tell the upper layer to try * later. */ result = 1; } else if (result == CMD_RQLATER) { /* * Temporarily hold off on this one. * Typically this means for fibre channel * that the loop is down or we're processing * some other change (e.g., fabric membership * change) */ isplinux_append_to_waitq(isp, Cmnd); if (IS_FC(isp) && isp->isp_deadloop == 0) { SEND_THREAD_EVENT(isp, ISP_THREAD_FC_RESCAN, 0); } result = 0; } else if (result == CMD_COMPLETE) { result = -1; } else { panic("unknown return code %d from isp_start", result); } ISP_UNLK_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); if (result == -1) { Cmnd->result &= ~0xff; Cmnd->result |= Cmnd->SCp.Status; Cmnd->host_scribble = NULL; (*Cmnd->scsi_done)(Cmnd); result = 0; } return (result);}static INLINE void isplinux_scsi_probe_done(Scsi_Cmnd *);static INLINE voidisplinux_scsi_probe_done(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp = (struct ispsoftc *) Cmnd->host->hostdata; /* * If we haven't seen this target yet, check the command result. If * it was an inquiry and it succeeded okay, then we can update our * notions about this target's capabilities. * * If the command did *not* succeed, we also update our notions about * this target's capabilities (pessimistically) - it's probably not there. * All of this so we can know when we're done so we stop wasting cycles * seeing whether we can enable sync mode or not. */ if (isp->isp_psco[Cmnd->channel][Cmnd->target] == 0) { int i, b; caddr_t iqd; sdparam *sdp = (sdparam *) isp->isp_param; sdp += Cmnd->channel; if (Cmnd->cmnd[0] == 0x12 && host_byte(Cmnd->result) == DID_OK) { if (Cmnd->use_sg == 0) { iqd = (caddr_t) Cmnd->buffer; } else { iqd = ((struct scatterlist *) Cmnd->request_buffer)->address; } sdp->isp_devparam[Cmnd->target].goal_flags &= ~(DPARM_TQING|DPARM_SYNC|DPARM_WIDE); if (iqd[7] & 0x2) { sdp->isp_devparam[Cmnd->target].goal_flags |= DPARM_TQING; } if (iqd[7] & 0x10) { sdp->isp_devparam[Cmnd->target].goal_flags |= DPARM_SYNC; } if (iqd[7] & 0x20) { sdp->isp_devparam[Cmnd->target].goal_flags |= DPARM_WIDE; } sdp->isp_devparam[Cmnd->target].dev_update = 1; isp->isp_psco[Cmnd->channel][Cmnd->target] = 1; } else if (host_byte(Cmnd->result) != DID_OK) { isp->isp_psco[Cmnd->channel][Cmnd->target] = 1; } isp->isp_dutydone = 1; for (b = 0; b < (IS_DUALBUS(isp)?2 : 1) && isp->isp_dutydone; b++) { for (i = 0; i < MAX_TARGETS; i++) { if (i != sdp->isp_initiator_id) { if (isp->isp_psco[b][i] == 0) { isp->isp_dutydone = 0; break; } } } } /* * Have we scanned all busses and all targets? You only get * one chance (per reset) to see what devices on this bus have * to offer. */ if (isp->isp_dutydone) { for (b = 0; b < (IS_DUALBUS(isp)?2 : 1) && isp->isp_dutydone; b++) { for (i = 0; i < MAX_TARGETS; i++) { isp->isp_psco[b][i] = 0; } isp->isp_update |= (1 << b); } } }}voidisp_done(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp = (struct ispsoftc *) (Cmnd->host->hostdata); if (IS_SCSI(isp) && isp->isp_dutydone == 0) { isplinux_scsi_probe_done(Cmnd); } Cmnd->result &= ~0xff; Cmnd->result |= Cmnd->SCp.Status; if (Cmnd->SCp.Status != GOOD) { isp_prt(isp, ISP_LOGDEBUG0, "%d.%d.%d: cmd finishes with status 0x%x", XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd), Cmnd->SCp.Status); } /* * If we had a way handling residuals, this is where we'd do it */ /* * Queue command on completion queue. */ if (isp->isp_osinfo.dqnext == NULL) { isp->isp_osinfo.dqnext = Cmnd; } else { isp->isp_osinfo.dqtail->host_scribble = (unsigned char *) Cmnd; } isp->isp_osinfo.dqtail = Cmnd; Cmnd->host_scribble = NULL;}/* * Error handling routines */intisplinux_abort(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp; u_int16_t handle; if (Cmnd == NULL || Cmnd->host == NULL) { return (FAILED); } isp = (struct ispsoftc *) Cmnd->host->hostdata; if (Cmnd->serial_number != Cmnd->serial_number_at_timeout) { isp_prt(isp, ISP_LOGWARN, "isplinux_abort: serial number mismatch"); return (FAILED); } ISP_DRIVER_ENTRY_LOCK(isp); ISP_LOCKU_SOFTC(isp); handle = isp_find_handle(isp, Cmnd); if (handle == 0) { int wqfnd = 0; Scsi_Cmnd *NewCmnd = isp_remove_from_waitq(Cmnd); if (NewCmnd == NULL) { NewCmnd = isplinux_remove_from_doneq(Cmnd); wqfnd++; } ISP_UNLKU_SOFTC(isp); isp_prt(isp, ISP_LOGINFO, "isplinux_abort: found %d:%p for non-running cmd for %d.%d.%d", wqfnd, NewCmnd, XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd)); if (NewCmnd == NULL) { ISP_DRIVER_EXIT_LOCK(isp); return (FAILED); } } else { if (isp_control(isp, ISPCTL_ABORT_CMD, Cmnd)) { ISP_UNLKU_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); return (FAILED); } if (isp->isp_nactive > 0) isp->isp_nactive--; isp_destroy_handle(isp, handle); ISP_UNLKU_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); isp_prt(isp, ISP_LOGINFO, "isplinux_abort: aborted running cmd (handle 0x%x) for %d.%d.%d", handle, XS_CHANNEL(Cmnd), XS_TGT(Cmnd), XS_LUN(Cmnd)); } Cmnd->result = DID_ABORT << 16; (*Cmnd->scsi_done)(Cmnd); return (SUCCESS);}/* * XXX: What does the midlayer expect for commands in process? * XXX: Are we supposed to clean up dead commands ourselves? */intisplinux_bdr(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp; int arg; if (Cmnd == NULL || Cmnd->host == NULL) { return (FAILED); } isp = (struct ispsoftc *) Cmnd->host->hostdata; arg = Cmnd->channel << 16 | Cmnd->target; ISP_DRIVER_ENTRY_LOCK(isp); ISP_LOCKU_SOFTC(isp); arg = isp_control(isp, ISPCTL_RESET_DEV, &arg); ISP_UNLKU_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); isp_prt(isp, ISP_LOGINFO, "Bus Device Reset %succesfully sent to %d.%d.%d", arg == 0? "s" : "uns", Cmnd->channel, Cmnd->target, Cmnd->lun); return ((arg == 0)? SUCCESS : FAILED);}/* * XXX: What does the midlayer expect for commands in process? */intisplinux_sreset(Scsi_Cmnd *Cmnd){ struct ispsoftc *isp; int arg; if (Cmnd == NULL || Cmnd->host == NULL) return (FAILED); isp = (struct ispsoftc *) Cmnd->host->hostdata; arg = Cmnd->channel; ISP_DRIVER_ENTRY_LOCK(isp); ISP_LOCKU_SOFTC(isp); arg = isp_control(isp, ISPCTL_RESET_BUS, &arg); ISP_UNLKU_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); isp_prt(isp, ISP_LOGINFO, "SCSI Bus Reset on Channel %d %succesful", Cmnd->channel, arg == 0? "s" : "uns"); return ((arg == 0)? SUCCESS : FAILED);}/* * We call completion on any commands owned here- * except the one we were called with. */intisplinux_hreset(Scsi_Cmnd *Cmnd){ Scsi_Cmnd *tmp, *dq, *wq, *xqf, *xql; struct ispsoftc *isp; u_int16_t handle; if (Cmnd == NULL || Cmnd->host == NULL) return (FAILED); isp = (struct ispsoftc *) Cmnd->host->hostdata; isp_prt(isp, ISP_LOGINFO, "Resetting Host Adapter"); ISP_DRIVER_ENTRY_LOCK(isp); ISP_LOCKU_SOFTC(isp); /* * Save pending, running, and completed commands. */ xql = xqf = NULL; for (handle = 1; handle <= isp->isp_maxcmds; handle++) { tmp = isp_find_xs(isp, handle); if (tmp == NULL) { continue; } isp_destroy_handle(isp, handle); tmp->host_scribble = NULL; if (xqf) { xql->host_scribble = (unsigned char *) tmp; } else { xqf = xql = tmp; } xql = tmp; } dq = isp->isp_osinfo.dqnext; isp->isp_osinfo.dqnext = NULL; wq = isp->isp_osinfo.wqnext; isp->isp_osinfo.wqnext = NULL; isp->isp_nactive = 0; isplinux_reinit(isp); ISP_UNLKU_SOFTC(isp); ISP_DRIVER_EXIT_LOCK(isp); /* * Call completion on the detritus, skipping the one we were called with. */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -