diff options
Diffstat (limited to 'sys/dev/isp/isp_freebsd.c')
-rw-r--r-- | sys/dev/isp/isp_freebsd.c | 486 |
1 files changed, 376 insertions, 110 deletions
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index 2a11c54..008da7b 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -26,17 +26,43 @@ * SUCH DAMAGE. */ #include <dev/isp/isp_freebsd.h> +#include <sys/unistd.h> +#include <sys/kthread.h> #include <machine/stdarg.h> /* for use by isp_prt below */ +#include <sys/conf.h> +#include <sys/ioccom.h> +#include <dev/isp/isp_ioctl.h> +static d_ioctl_t ispioctl; static void isp_intr_enable(void *); static void isp_cam_async(void *, u_int32_t, struct cam_path *, void *); static void isp_poll(struct cam_sim *); +#if 0 static void isp_relsim(void *); +#endif static timeout_t isp_watchdog; +static void isp_kthread(void *); static void isp_action(struct cam_sim *, union ccb *); +#define ISP_CDEV_MAJOR 248 +static struct cdevsw isp_cdevsw = { + /* open */ nullopen, + /* close */ nullclose, + /* read */ noread, + /* write */ nowrite, + /* ioctl */ ispioctl, + /* poll */ nopoll, + /* mmap */ nommap, + /* strategy */ nostrategy, + /* name */ "isp", + /* maj */ ISP_CDEV_MAJOR, + /* dump */ nodump, + /* psize */ nopsize, + /* flags */ D_TAPE, +}; + static struct ispsoftc *isplist = NULL; void @@ -91,6 +117,7 @@ isp_attach(struct ispsoftc *isp) CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) { xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); return; } @@ -102,6 +129,23 @@ isp_attach(struct ispsoftc *isp) xpt_action((union ccb *)&csa); isp->isp_sim = sim; isp->isp_path = path; + /* + * Create a kernel thread for fibre channel instances. We + * don't have dual channel FC cards. + */ + if (IS_FC(isp)) { + cv_init(&isp->isp_osinfo.kthread_cv, "isp_kthread_cv"); + if (kthread_create(isp_kthread, isp, &isp->isp_osinfo.kproc, + RFHIGHPID, "%s: fc_thrd", + device_get_nameunit(isp->isp_dev))) { + isp_prt(isp, ISP_LOGERR, "could not create kthread"); + xpt_bus_deregister(cam_sim_path(sim)); + cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); + return; + } + } + /* * If we have a second channel, construct SIM entry for that. @@ -113,12 +157,14 @@ isp_attach(struct ispsoftc *isp) xpt_bus_deregister(cam_sim_path(isp->isp_sim)); xpt_free_path(isp->isp_path); cam_simq_free(devq); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); return; } if (xpt_bus_register(sim, secondary) != CAM_SUCCESS) { xpt_bus_deregister(cam_sim_path(isp->isp_sim)); xpt_free_path(isp->isp_path); cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); return; } @@ -128,6 +174,7 @@ isp_attach(struct ispsoftc *isp) xpt_free_path(isp->isp_path); xpt_bus_deregister(cam_sim_path(sim)); cam_sim_free(sim, TRUE); + config_intrhook_disestablish(&isp->isp_osinfo.ehook); return; } @@ -140,6 +187,13 @@ isp_attach(struct ispsoftc *isp) isp->isp_sim2 = sim; isp->isp_path2 = path; } + + /* + * Create device nodes + */ + (void) make_dev(&isp_cdevsw, device_get_unit(isp->isp_dev), UID_ROOT, + GID_OPERATOR, 0600, "%s", device_get_nameunit(isp->isp_dev)); + if (isp->isp_role != ISP_ROLE_NONE) { isp->isp_state = ISP_RUNSTATE; ENABLE_INTS(isp); @@ -153,6 +207,89 @@ isp_attach(struct ispsoftc *isp) } tmp->isp_osinfo.next = isp; } + +} + +static int +ispioctl(dev_t dev, u_long cmd, caddr_t addr, int flags, struct proc *p) +{ + struct ispsoftc *isp; + int retval = ENOTTY; + + isp = isplist; + while (isp) { + if (minor(dev) == device_get_unit(isp->isp_dev)) { + break; + } + isp = isp->isp_osinfo.next; + } + if (isp == NULL) + return (ENXIO); + + switch (cmd) { + case ISP_SDBLEV: + { + int olddblev = isp->isp_dblev; + isp->isp_dblev = *(int *)addr; + *(int *)addr = olddblev; + retval = 0; + break; + } + case ISP_RESETHBA: + ISP_LOCK(isp); + isp_reinit(isp); + ISP_UNLOCK(isp); + retval = 0; + break; + case ISP_FC_RESCAN: + if (IS_FC(isp)) { + ISP_LOCK(isp); + if (isp_fc_runstate(isp, 5 * 1000000)) { + retval = EIO; + } else { + retval = 0; + } + ISP_UNLOCK(isp); + } + break; + case ISP_FC_LIP: + if (IS_FC(isp)) { + ISP_LOCK(isp); + if (isp_control(isp, ISPCTL_SEND_LIP, 0)) { + retval = EIO; + } else { + retval = 0; + } + ISP_UNLOCK(isp); + } + break; + case ISP_FC_GETDINFO: + { + struct isp_fc_device *ifc = (struct isp_fc_device *) addr; + struct lportdb *lp; + + if (ifc->loopid < 0 || ifc->loopid >= MAX_FC_TARG) { + retval = EINVAL; + break; + } + ISP_LOCK(isp); + lp = &FCPARAM(isp)->portdb[ifc->loopid]; + if (lp->valid) { + ifc->loopid = lp->loopid; + ifc->portid = lp->portid; + ifc->node_wwn = lp->node_wwn; + ifc->port_wwn = lp->port_wwn; + retval = 0; + } else { + retval = ENODEV; + } + ISP_UNLOCK(isp); + break; + } + default: + break; + } + return (retval); } static void @@ -198,7 +335,6 @@ static __inline int is_lun_enabled(struct ispsoftc *isp, int bus, lun_id_t lun) { tstate_t *tptr; - ISP_LOCK(isp); tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; if (tptr == NULL) { ISP_UNLOCK(isp); @@ -210,7 +346,6 @@ is_lun_enabled(struct ispsoftc *isp, int bus, lun_id_t lun) return (1); } } while ((tptr = tptr->next) != NULL); - ISP_UNLOCK(isp); return (0); } @@ -238,28 +373,23 @@ get_lun_statep(struct ispsoftc *isp, int bus, lun_id_t lun) { tstate_t *tptr; - ISP_LOCK(isp); if (lun == CAM_LUN_WILDCARD) { tptr = &isp->isp_osinfo.tsdflt[bus]; tptr->hold++; - ISP_UNLOCK(isp); return (tptr); } else { tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(isp, bus, lun)]; } if (tptr == NULL) { - ISP_UNLOCK(isp); return (NULL); } do { if (tptr->lun == lun && tptr->bus == bus) { tptr->hold++; - ISP_UNLOCK(isp); return (tptr); } } while ((tptr = tptr->next) != NULL); - ISP_UNLOCK(isp); return (tptr); } @@ -273,28 +403,23 @@ rls_lun_statep(struct ispsoftc *isp, tstate_t *tptr) static __inline int isp_psema_sig_rqe(struct ispsoftc *isp) { - ISP_LOCK(isp); while (isp->isp_osinfo.tmflags & TM_BUSY) { isp->isp_osinfo.tmflags |= TM_WANTED; if (tsleep(&isp->isp_osinfo.tmflags, PRIBIO|PCATCH, "i0", 0)) { - ISP_UNLOCK(isp); return (-1); } isp->isp_osinfo.tmflags |= TM_BUSY; } - ISP_UNLOCK(isp); return (0); } static __inline int isp_cv_wait_timed_rqe(struct ispsoftc *isp, int timo) { - ISP_LOCK(isp); if (tsleep(&isp->isp_osinfo.rstatus, PRIBIO, "qt1", timo)) { ISP_UNLOCK(isp); return (-1); } - ISP_UNLOCK(isp); return (0); } @@ -308,13 +433,11 @@ isp_cv_signal_rqe(struct ispsoftc *isp, int status) static __inline void isp_vsema_rqe(struct ispsoftc *isp) { - ISP_LOCK(isp); if (isp->isp_osinfo.tmflags & TM_WANTED) { isp->isp_osinfo.tmflags &= ~TM_WANTED; wakeup(&isp->isp_osinfo.tmflags); } isp->isp_osinfo.tmflags &= ~TM_BUSY; - ISP_UNLOCK(isp); } static cam_status @@ -351,7 +474,6 @@ create_lun_state(struct ispsoftc *isp, int bus, new->hold = 1; hfx = LUN_HASH_FUNC(isp, new->bus, new->lun); - ISP_LOCK(isp); tptr = isp->isp_osinfo.lun_hash[hfx]; if (tptr == NULL) { isp->isp_osinfo.lun_hash[hfx] = new; @@ -360,7 +482,6 @@ create_lun_state(struct ispsoftc *isp, int bus, tptr = tptr->next; tptr->next = new; } - ISP_UNLOCK(isp); *rslt = new; return (CAM_REQ_CMP); } @@ -372,14 +493,11 @@ destroy_lun_state(struct ispsoftc *isp, tstate_t *tptr) tstate_t *lw, *pw; hfx = LUN_HASH_FUNC(isp, tptr->bus, tptr->lun); - ISP_LOCK(isp); if (tptr->hold) { - ISP_UNLOCK(isp); return; } pw = isp->isp_osinfo.lun_hash[hfx]; if (pw == NULL) { - ISP_UNLOCK(isp); return; } else if (pw->lun == tptr->lun && pw->bus == tptr->bus) { isp->isp_osinfo.lun_hash[hfx] = pw->next; @@ -400,9 +518,11 @@ destroy_lun_state(struct ispsoftc *isp, tstate_t *tptr) } } free(tptr, M_DEVBUF); - ISP_UNLOCK(isp); } +/* + * we enter with our locks held. + */ static void isp_en_lun(struct ispsoftc *isp, union ccb *ccb) { @@ -410,7 +530,7 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) struct ccb_en_lun *cel = &ccb->cel; tstate_t *tptr; u_int16_t rstat; - int bus, frozen = 0; + int bus, cmd, frozen = 0; lun_id_t lun; target_id_t tgt; @@ -461,9 +581,8 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) xpt_freeze_simq(isp->isp_sim, 1); isp->isp_osinfo.drain = 1; while (isp->isp_osinfo.drain) { - (void) msleep(&isp->isp_osinfo.drain, - &isp->isp_osinfo.lock, PRIBIO, - "ispdrain", 10 * hz); + (void) msleep(&isp->isp_osinfo.drain, &isp->isp_lock, + PRIBIO, "ispdrain", 10 * hz); } ISP_UNLOCK(isp); } @@ -479,17 +598,18 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) fcparam *fcp = isp->isp_param; int rv; - ISP_LOCK(isp); rv = isp_fc_runstate(isp, 2 * 1000000); - ISP_UNLOCK(isp); if (fcp->isp_fwstate != FW_READY || fcp->isp_loopstate != LOOP_READY) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "could not get a good port database read"); ccb->ccb_h.status = CAM_REQ_CMP_ERR; - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } } @@ -506,8 +626,11 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) if (cel->enable) { if (isp->isp_osinfo.tmflags & (1 << bus)) { ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } ccb->ccb_h.status = @@ -516,56 +639,68 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) xpt_path_target_id(ccb->ccb_h.path), xpt_path_lun_id(ccb->ccb_h.path)); if (ccb->ccb_h.status != CAM_REQ_CMP) { - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } SLIST_INIT(&tptr->atios); SLIST_INIT(&tptr->inots); av |= ENABLE_TARGET_FLAG; - ISP_LOCK(isp); av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); if (av) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; xpt_free_path(tptr->owner); - ISP_UNLOCK(isp); - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } isp->isp_osinfo.tmflags |= (1 << bus); - ISP_UNLOCK(isp); } else { if ((isp->isp_osinfo.tmflags & (1 << bus)) == 0) { ccb->ccb_h.status = CAM_LUN_INVALID; - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } if (are_any_luns_enabled(isp, bus)) { ccb->ccb_h.status = CAM_SCSI_BUSY; - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } - ISP_LOCK(isp); av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); if (av) { ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; - ISP_UNLOCK(isp); - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } isp->isp_osinfo.tmflags &= ~(1 << bus); - ISP_UNLOCK(isp); ccb->ccb_h.status = CAM_REQ_CMP; } xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGINFO, "Target Mode %sabled on channel %d", (cel->enable) ? "en" : "dis", bus); - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } return; } @@ -573,8 +708,11 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) * We can move along now... */ - if (frozen) + if (frozen) { + ISPLOCK_2_CAMLOCK(isp); xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } if (cel->enable) { @@ -599,11 +737,24 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) return; } - ISP_LOCK(isp); if (cel->enable) { u_int32_t seq = isp->isp_osinfo.rollinfo++; + int c, n, ulun = lun; + + cmd = RQSTYPE_ENABLE_LUN; + c = DFLT_CMND_CNT; + n = DFLT_INOT_CNT; + if (IS_FC(isp) && lun != 0) { + cmd = RQSTYPE_MODIFY_LUN; + n = 0; + /* + * For SCC firmware, we only deal with setting + * (enabling or modifying) lun 0. + */ + ulun = 0; + } rstat = LUN_ERR; - if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) { + if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, "isp_lun_cmd failed"); goto out; @@ -611,23 +762,35 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) if (isp_cv_wait_timed_rqe(isp, 30 * hz)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, - "wait for ENABLE LUN timed out"); + "wait for ENABLE/MODIFY LUN timed out"); goto out; } rstat = isp->isp_osinfo.rstatus; if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, - "ENABLE LUN returned 0x%x", rstat); + "ENABLE/MODIFY LUN returned 0x%x", rstat); goto out; } } else { + int c, n, ulun = lun; u_int32_t seq; - seq = isp->isp_osinfo.rollinfo++; rstat = LUN_ERR; + seq = isp->isp_osinfo.rollinfo++; + cmd = -RQSTYPE_MODIFY_LUN; - if (isp_lun_cmd(isp, -RQSTYPE_MODIFY_LUN, bus, tgt, lun, seq)) { + c = DFLT_CMND_CNT; + n = DFLT_INOT_CNT; + if (IS_FC(isp) && lun != 0) { + n = 0; + /* + * For SCC firmware, we only deal with setting + * (enabling or modifying) lun 0. + */ + ulun = 0; + } + if (isp_lun_cmd(isp, cmd, bus, tgt, ulun, c, n, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed"); goto out; @@ -645,10 +808,15 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) "MODIFY LUN returned 0x%x", rstat); goto out; } - rstat = LUN_ERR; + if (IS_FC(isp) && lun) { + goto out; + } + seq = isp->isp_osinfo.rollinfo++; - if (isp_lun_cmd(isp, -RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) { + rstat = LUN_ERR; + cmd = -RQSTYPE_ENABLE_LUN; + if (isp_lun_cmd(isp, cmd, bus, tgt, lun, 0, 0, seq)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, "isp_lun_cmd failed"); goto out; @@ -656,20 +824,19 @@ isp_en_lun(struct ispsoftc *isp, union ccb *ccb) if (isp_cv_wait_timed_rqe(isp, 30 * hz)) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGERR, - "wait for ENABLE LUN timed out"); + "wait for DISABLE LUN timed out"); goto out; } rstat = isp->isp_osinfo.rstatus; if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); isp_prt(isp, ISP_LOGWARN, - "ENABLE LUN returned 0x%x", rstat); + "DISABLE LUN returned 0x%x", rstat); goto out; } } out: isp_vsema_rqe(isp); - ISP_UNLOCK(isp); if (rstat != LUN_OK) { xpt_print_path(ccb->ccb_h.path); @@ -867,7 +1034,7 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) cto->ct_timeout = 10; hp = &cto->ct_syshandle; ccb->ccb_h.flags &= ~CAM_SEND_SENSE; - if (cto->ct_flags & CT_SENDSTATUS) + if (cto->ct_flags & CT_SENDSTATUS) cto->ct_flags |= CT_CCINCR; } @@ -1005,9 +1172,7 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) * If QLTM_SVALID is set, the firware has recommended Sense Data. * * If the DISCONNECTS DISABLED bit is set in the flags field, - * we're still connected on the SCSI bus - i.e. the initiator - * did not set DiscPriv in the identify message. We don't care - * about this so it's ignored. + * we're still connected on the SCSI bus. */ status = aep->at_status; if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { @@ -1021,15 +1186,13 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) return (0); } if ((status & ~QLTM_SVALID) != AT_CDB) { - isp_prt(isp, - ISP_LOGWARN, "bogus atio (0x%x) leaked to platform", + isp_prt(isp, ISP_LOGWARN, "bad atio (0x%x) leaked to platform", status); isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); return (0); } - bus = aep->at_iid >> 7; - aep->at_iid &= 0x7f; + bus = GET_BUS_VAL(aep->at_iid); tptr = get_lun_statep(isp, bus, aep->at_lun); if (tptr == NULL) { tptr = get_lun_statep(isp, bus, CAM_LUN_WILDCARD); @@ -1062,7 +1225,7 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) xpt_print_path(tptr->owner); isp_prt(isp, ISP_LOGWARN, "no ATIOS for lun %d from initiator %d on channel %d", - aep->at_lun, aep->at_iid, bus); + aep->at_lun, GET_IID_VAL(aep->at_iid), bus); rls_lun_statep(isp, tptr); if (aep->at_flags & AT_TQAE) isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); @@ -1089,7 +1252,7 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) atiop->sense_len = 0; } - atiop->init_id = aep->at_iid & 0x7f; + atiop->init_id = GET_IID_VAL(aep->at_iid); atiop->cdb_len = aep->at_cdblen; MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, aep->at_cdblen); atiop->ccb_h.status = CAM_CDB_RECVD; @@ -1104,10 +1267,11 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) } xpt_done((union ccb*)atiop); isp_prt(isp, ISP_LOGTDEBUG1, - "ATIO[%x] CDB=0x%x iid%d->lun%d tag 0x%x ttype 0x%x %s", - aep->at_handle, aep->at_cdb[0] & 0xff, aep->at_iid, aep->at_lun, - aep->at_tag_val & 0xff, aep->at_tag_type, - (aep->at_flags & AT_NODISC)? "nondisc" : "disconnecting"); + "ATIO[%x] CDB=0x%x bus %d iid%d->lun%d tag 0x%x ttype 0x%x %s", + aep->at_handle, aep->at_cdb[0] & 0xff, GET_BUS_VAL(aep->at_iid), + GET_IID_VAL(aep->at_iid), aep->at_lun, aep->at_tag_val & 0xff, + aep->at_tag_type, (aep->at_flags & AT_NODISC)? + "nondisc" : "disconnecting"); rls_lun_statep(isp, tptr); return (0); } @@ -1280,8 +1444,9 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void *arg) (ccb->ccb_h.status & CAM_SENT_SENSE) != 0, sentstatus? "FIN" : "MID"); notify_cam = ct->ct_header.rqs_seqno & 0x1; - if (ct->ct_flags & CT2_DATAMASK) + if ((ct->ct_flags & CT2_DATAMASK) != CT2_NO_DATA) { resid = ct->ct_resid; + } } else { ct_entry_t *ct = arg; sentstatus = ct->ct_flags & CT_SENDSTATUS; @@ -1302,7 +1467,7 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void *arg) * the auto-replenish feature for CTIOs. */ notify_cam = ct->ct_header.rqs_seqno & 0x1; - if (ct->ct_status & QLTM_SVALID) { + if (ct->ct_status & QLTM_SVALID) { char *sp = (char *)ct; sp += CTIO_SENSE_OFFSET; ccb->csio.sense_len = @@ -1310,8 +1475,9 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void *arg) MEMCPY(&ccb->csio.sense_data, sp, ccb->csio.sense_len); ccb->ccb_h.status |= CAM_AUTOSNS_VALID; } - if (ct->ct_flags & CT_DATAMASK) + if ((ct->ct_flags & CT_DATAMASK) != CT_NO_DATA) { resid = ct->ct_resid; + } } ccb->csio.resid += resid; @@ -1397,6 +1563,7 @@ isp_poll(struct cam_sim *sim) ISP_UNLOCK(isp); } +#if 0 static void isp_relsim(void *arg) { @@ -1412,6 +1579,7 @@ isp_relsim(void *arg) } ISP_UNLOCK(isp); } +#endif static void isp_watchdog(void *arg) @@ -1497,6 +1665,61 @@ isp_watchdog(void *arg) ISP_UNLOCK(isp); } +#ifdef ISP_SMPLOCK +static void +isp_kthread(void *arg) +{ + int wasfrozen; + struct ispsoftc *isp = arg; + + mtx_lock(&isp->isp_lock); + for (;;) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread checking FC state"); + while (isp_fc_runstate(isp, 2 * 1000000) != 0) { +#if 0 + msleep(&lbolt, &isp->isp_lock, + PRIBIO, "isp_fcthrd", 0); +#else + msleep(isp_kthread, &isp->isp_lock, + PRIBIO, "isp_fcthrd", hz); +#endif + } + wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN; + isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN; + if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread up release simq"); + ISPLOCK_2_CAMLOCK(isp); + xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } + cv_wait(&isp->isp_osinfo.kthread_cv, &isp->isp_lock); + } +} +#else +static void +isp_kthread(void *arg) +{ + int wasfrozen; + struct ispsoftc *isp = arg; + + mtx_lock(&Giant); + for (;;) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread checking FC state"); + while (isp_fc_runstate(isp, 2 * 1000000) != 0) { + tsleep(isp_kthread, PRIBIO, "isp_fcthrd", hz); + } + wasfrozen = isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN; + isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN; + if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, "kthread up release simq"); + ISPLOCK_2_CAMLOCK(isp); + xpt_release_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } + tsleep(&isp->isp_osinfo.kthread_cv, PRIBIO, "isp_fc_worker", 0); + } +} +#endif static void isp_action(struct cam_sim *sim, union ccb *ccb) { @@ -1511,7 +1734,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) ccb->ccb_h.sim_priv.entries[1].ptr = isp; if (isp->isp_state != ISP_RUNSTATE && ccb->ccb_h.func_code == XPT_SCSI_IO) { - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); isp_init(isp); if (isp->isp_state != ISP_INITSTATE) { ISP_UNLOCK(isp); @@ -1524,10 +1747,11 @@ isp_action(struct cam_sim *sim, union ccb *ccb) return; } isp->isp_state = ISP_RUNSTATE; - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); } isp_prt(isp, ISP_LOGDEBUG2, "isp_action code %x", ccb->ccb_h.func_code); + switch (ccb->ccb_h.func_code) { case XPT_SCSI_IO: /* Execute the requested I/O operation */ /* @@ -1555,9 +1779,8 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } #endif ((struct ccb_scsiio *) ccb)->scsi_status = SCSI_STATUS_OK; - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); error = isp_start((XS_T *) ccb); - ISP_UNLOCK(isp); switch (error) { case CMD_QUEUED: ccb->ccb_h.status |= CAM_SIM_QUEUED; @@ -1578,14 +1801,27 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } else { callout_handle_init(&ccb->ccb_h.timeout_ch); } + ISPLOCK_2_CAMLOCK(isp); break; case CMD_RQLATER: +#ifdef ISP_SMPLOCK + cv_signal(&isp->isp_osinfo.kthread_cv); +#else + wakeup(&isp->isp_osinfo.kthread_cv); +#endif if (isp->isp_osinfo.simqfrozen == 0) { isp_prt(isp, ISP_LOGDEBUG2, "RQLATER freeze simq"); +#if 0 isp->isp_osinfo.simqfrozen |= SIMQFRZ_TIMED; timeout(isp_relsim, isp, 500); +#else + isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; +#endif + ISPLOCK_2_CAMLOCK(isp); xpt_freeze_simq(sim, 1); + } else { + ISPLOCK_2_CAMLOCK(isp); } XS_SETERR(ccb, CAM_REQUEUE_REQ); xpt_done(ccb); @@ -1598,12 +1834,12 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } isp->isp_osinfo.simqfrozen |= SIMQFRZ_RESOURCE; XS_SETERR(ccb, CAM_REQUEUE_REQ); + ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; case CMD_COMPLETE: - ISP_LOCK(isp); isp_done((struct ccb_scsiio *) ccb); - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); break; default: isp_prt(isp, ISP_LOGERR, @@ -1611,12 +1847,15 @@ isp_action(struct cam_sim *sim, union ccb *ccb) error, __LINE__, __FILE__); XS_SETERR(ccb, CAM_REQ_CMP_ERR); xpt_done(ccb); + ISPLOCK_2_CAMLOCK(isp); } break; #ifdef ISP_TARGET_MODE case XPT_EN_LUN: /* Enable LUN as a target */ + CAMLOCK_2_ISPLOCK(isp); isp_en_lun(isp, ccb); + ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; @@ -1633,7 +1872,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } ccb->ccb_h.sim_priv.entries[0].field = 0; ccb->ccb_h.sim_priv.entries[1].ptr = isp; - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, sim_links.sle); @@ -1641,14 +1880,14 @@ isp_action(struct cam_sim *sim, union ccb *ccb) SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, sim_links.sle); } - ISP_UNLOCK(isp); rls_lun_statep(isp, tptr); ccb->ccb_h.status = CAM_REQ_INPROG; + ISPLOCK_2_CAMLOCK(isp); break; } case XPT_CONT_TARGET_IO: { - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); ccb->ccb_h.status = isp_target_start_ctio(isp, ccb); if (ccb->ccb_h.status != CAM_REQ_INPROG) { if (isp->isp_osinfo.simqfrozen == 0) { @@ -1659,11 +1898,12 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } isp->isp_osinfo.simqfrozen |= SIMQFRZ_RESOURCE; XS_SETERR(ccb, CAM_REQUEUE_REQ); + ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); } else { + ISPLOCK_2_CAMLOCK(isp); ccb->ccb_h.status |= CAM_SIM_QUEUED; } - ISP_UNLOCK(isp); break; } #endif @@ -1673,9 +1913,9 @@ isp_action(struct cam_sim *sim, union ccb *ccb) tgt = ccb->ccb_h.target_id; tgt |= (bus << 16); - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); error = isp_control(isp, ISPCTL_RESET_DEV, &tgt); - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); if (error) { ccb->ccb_h.status = CAM_REQ_CMP_ERR; } else { @@ -1686,6 +1926,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) case XPT_ABORT: /* Abort the specified CCB */ { union ccb *accb = ccb->cab.abort_ccb; + CAMLOCK_2_ISPLOCK(isp); switch (accb->ccb_h.func_code) { #ifdef ISP_TARGET_MODE case XPT_ACCEPT_TARGET_IO: @@ -1698,9 +1939,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) break; #endif case XPT_SCSI_IO: - ISP_LOCK(isp); error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); - ISP_UNLOCK(isp); if (error) { ccb->ccb_h.status = CAM_UA_ABORT; } else { @@ -1711,6 +1950,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) ccb->ccb_h.status = CAM_REQ_INVALID; break; } + ISPLOCK_2_CAMLOCK(isp); xpt_done(ccb); break; } @@ -1718,7 +1958,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) cts = &ccb->cts; tgt = cts->ccb_h.target_id; - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); if (IS_SCSI(isp)) { sdparam *sdp = isp->isp_param; u_int16_t *dptr; @@ -1795,7 +2035,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) sdp->isp_devparam[tgt].dev_update = 1; isp->isp_update |= (1 << bus); } - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); ccb->ccb_h.status = CAM_REQ_CMP; xpt_done(ccb); break; @@ -1823,14 +2063,13 @@ isp_action(struct cam_sim *sim, union ccb *ccb) u_int16_t dval, pval, oval; int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); + CAMLOCK_2_ISPLOCK(isp); sdp += bus; if (cts->flags & CCB_TRANS_CURRENT_SETTINGS) { - ISP_LOCK(isp); sdp->isp_devparam[tgt].dev_refresh = 1; isp->isp_update |= (1 << bus); (void) isp_control(isp, ISPCTL_UPDATE_PARAMS, NULL); - ISP_UNLOCK(isp); dval = sdp->isp_devparam[tgt].cur_dflags; oval = sdp->isp_devparam[tgt].cur_offset; pval = sdp->isp_devparam[tgt].cur_period; @@ -1840,7 +2079,6 @@ isp_action(struct cam_sim *sim, union ccb *ccb) pval = sdp->isp_devparam[tgt].sync_period; } - ISP_LOCK(isp); cts->flags &= ~(CCB_TRANS_DISC_ENB|CCB_TRANS_TAG_ENB); if (dval & DPARM_DISC) { @@ -1864,7 +2102,7 @@ isp_action(struct cam_sim *sim, union ccb *ccb) CCB_TRANS_SYNC_RATE_VALID | CCB_TRANS_SYNC_OFFSET_VALID; } - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); isp_prt(isp, ISP_LOGDEBUG0, "%d.%d get %s period 0x%x offset 0x%x flags 0x%x", bus, tgt, (cts->flags & CCB_TRANS_CURRENT_SETTINGS)? @@ -1905,9 +2143,9 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } case XPT_RESET_BUS: /* Reset the specified bus */ bus = cam_sim_bus(sim); - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); error = isp_control(isp, ISPCTL_RESET_BUS, &bus); - ISP_UNLOCK(isp); + ISPLOCK_2_CAMLOCK(isp); if (error) ccb->ccb_h.status = CAM_REQ_CMP_ERR; else { @@ -2050,15 +2288,9 @@ isp_done(struct ccb_scsiio *sccb) "finished command on borrowed time"); } XS_CMD_S_CLEAR(sccb); - ISP_UNLOCK(isp); -#ifdef ISP_SMPLOCK - mtx_lock(&Giant); - xpt_done((union ccb *) sccb); - mtx_unlock(&Giant); -#else + ISPLOCK_2_CAMLOCK(isp); xpt_done((union ccb *) sccb); -#endif - ISP_LOCK(isp); + CAMLOCK_2_ISPLOCK(isp); } } @@ -2109,7 +2341,9 @@ isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg) "NEW_TGT_PARAMS bus %d tgt %d period %x offset %x flags %x", bus, tgt, neg.sync_period, neg.sync_offset, flags); xpt_setup_ccb(&neg.ccb_h, tmppath, 1); + ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_TRANSFER_NEG, tmppath, &neg); + CAMLOCK_2_ISPLOCK(isp); xpt_free_path(tmppath); break; } @@ -2118,33 +2352,60 @@ isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg) isp_prt(isp, ISP_LOGINFO, "SCSI bus reset on bus %d detected", bus); if (bus > 0 && isp->isp_path2) { + ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_BUS_RESET, isp->isp_path2, NULL); + CAMLOCK_2_ISPLOCK(isp); } else if (isp->isp_path) { + ISPLOCK_2_CAMLOCK(isp); xpt_async(AC_BUS_RESET, isp->isp_path, NULL); + CAMLOCK_2_ISPLOCK(isp); } break; + case ISPASYNC_LIP: + if (isp->isp_path) { + if (isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, "LIP freeze simq"); + ISPLOCK_2_CAMLOCK(isp); + xpt_freeze_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } + isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; + } + isp_prt(isp, ISP_LOGINFO, "LIP Received"); + break; + case ISPASYNC_LOOP_RESET: + if (isp->isp_path) { + if (isp->isp_osinfo.simqfrozen == 0) { + isp_prt(isp, ISP_LOGDEBUG0, + "Loop Reset freeze simq"); + ISPLOCK_2_CAMLOCK(isp); + xpt_freeze_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); + } + isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; + } + isp_prt(isp, ISP_LOGINFO, "Loop Reset Received"); + break; case ISPASYNC_LOOP_DOWN: if (isp->isp_path) { if (isp->isp_osinfo.simqfrozen == 0) { - isp_prt(isp, ISP_LOGDEBUG2, + isp_prt(isp, ISP_LOGDEBUG0, "loop down freeze simq"); + ISPLOCK_2_CAMLOCK(isp); xpt_freeze_simq(isp->isp_sim, 1); + CAMLOCK_2_ISPLOCK(isp); } isp->isp_osinfo.simqfrozen |= SIMQFRZ_LOOPDOWN; } isp_prt(isp, ISP_LOGINFO, "Loop DOWN"); break; case ISPASYNC_LOOP_UP: - if (isp->isp_path) { - int wasfrozen = - isp->isp_osinfo.simqfrozen & SIMQFRZ_LOOPDOWN; - isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_LOOPDOWN; - if (wasfrozen && isp->isp_osinfo.simqfrozen == 0) { - xpt_release_simq(isp->isp_sim, 1); - isp_prt(isp, ISP_LOGDEBUG2, - "loop up release simq"); - } - } + /* + * Now we just note that Loop has come up. We don't + * actually do anything because we're waiting for a + * Change Notify before activating the FC cleanup + * thread to look at the state of the loop again. + */ isp_prt(isp, ISP_LOGINFO, "Loop UP"); break; case ISPASYNC_PROMENADE: @@ -2175,6 +2436,11 @@ isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg) isp_prt(isp, ISP_LOGINFO, "Name Server Database Changed"); } +#ifdef ISP_SMPLOCK + cv_signal(&isp->isp_osinfo.kthread_cv); +#else + wakeup(&isp->isp_osinfo.kthread_cv); +#endif break; case ISPASYNC_FABRIC_DEV: { |