diff options
-rw-r--r-- | sys/dev/isp/isp_freebsd.c | 1096 |
1 files changed, 1079 insertions, 17 deletions
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index ba45c9e..83d3835 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -33,12 +33,14 @@ * SUCH DAMAGE. */ #include <dev/isp/isp_freebsd.h> +#include <sys/malloc.h> static void isp_cam_async(void *, u_int32_t, struct cam_path *, void *); static void isp_poll(struct cam_sim *); -static void isp_action(struct cam_sim *, union ccb *); static void isp_relsim(void *); +static void isp_action(struct cam_sim *, union ccb *); +static struct ispsoftc *isplist = NULL; /* #define ISP_LUN0_ONLY 1 */ #ifdef DEBUG int isp_debug = 2; @@ -139,12 +141,959 @@ isp_attach(struct ispsoftc *isp) isp->isp_sim2 = sim; isp->isp_path2 = path; } - if (isp->isp_state == ISP_INITSTATE) { - isp->isp_state = ISP_RUNSTATE; + isp->isp_state = ISP_RUNSTATE; + if (isplist == NULL) { + isplist = isp; + } else { + struct ispsoftc *tmp = isplist; + while (tmp->isp_osinfo.next) { + tmp = tmp->isp_osinfo.next; + } + tmp->isp_osinfo.next = isp; + } +} + + +/* + * Put the target mode functions here, because some are inlines + */ + +#ifdef ISP_TARGET_MODE +#include "targbh.h" + +static __inline int is_lun_enabled(struct ispsoftc *, lun_id_t); +static __inline int are_any_luns_enabled(struct ispsoftc *); +static __inline tstate_t *get_lun_statep(struct ispsoftc *, lun_id_t); +static __inline void rls_lun_statep(struct ispsoftc *, tstate_t *); +static __inline int isp_psema_sig_rqe(struct ispsoftc *); +static __inline int isp_cv_wait_timed_rqe(struct ispsoftc *, int); +static __inline void isp_cv_signal_rqe(struct ispsoftc *, int); +static __inline void isp_vsema_rqe(struct ispsoftc *); +static cam_status +create_lun_state(struct ispsoftc *, struct cam_path *, tstate_t **); +static void destroy_lun_state(struct ispsoftc *, tstate_t *); +static void isp_en_lun(struct ispsoftc *, union ccb *); +static cam_status isp_abort_tgt_ccb(struct ispsoftc *, union ccb *); +static cam_status isp_target_start_ctio(struct ispsoftc *, union ccb *); + + +static int isp_handle_platform_atio(struct ispsoftc *, at_entry_t *); +static int isp_handle_platform_atio2(struct ispsoftc *, at2_entry_t *); +static int isp_handle_platform_ctio(struct ispsoftc *, void *); + +static __inline int +is_lun_enabled(struct ispsoftc *isp, lun_id_t lun) +{ + tstate_t *tptr; + int s = splsoftcam(); + if ((tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)]) == NULL) { + splx(s); + return (0); + } + do { + if (tptr->lun == (lun_id_t) lun) { + splx(s); + return (1); + } + } while ((tptr = tptr->next) != NULL); + splx(s); + return (0); +} + +static __inline int +are_any_luns_enabled(struct ispsoftc *isp) +{ + int i; + for (i = 0; i < LUN_HASH_SIZE; i++) { + if (isp->isp_osinfo.lun_hash[i]) { + return (1); + } + } + return (0); +} + +static __inline tstate_t * +get_lun_statep(struct ispsoftc *isp, lun_id_t lun) +{ + tstate_t *tptr; + int s; + + s = splsoftcam(); + if (lun == CAM_LUN_WILDCARD) { + tptr = &isp->isp_osinfo.tsdflt; + tptr->hold++; + splx(s); + return (tptr); + } else { + tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)]; + } + if (tptr == NULL) { + splx(s); + return (NULL); + } + + do { + if (tptr->lun == lun) { + tptr->hold++; + splx(s); + return (tptr); + } + } while ((tptr = tptr->next) != NULL); + splx(s); + return (tptr); +} + +static __inline void +rls_lun_statep(struct ispsoftc *isp, tstate_t *tptr) +{ + if (tptr->hold) + tptr->hold--; +} + +static __inline int +isp_psema_sig_rqe(struct ispsoftc *isp) +{ + int s = splcam(); + while (isp->isp_osinfo.tmflags & TM_BUSY) { + isp->isp_osinfo.tmflags |= TM_WANTED; + if (tsleep(&isp->isp_osinfo.tmflags, PRIBIO|PCATCH, "i0", 0)) { + splx(s); + return (-1); + } + isp->isp_osinfo.tmflags |= TM_BUSY; + } + splx(s); + return (0); +} + +static __inline int +isp_cv_wait_timed_rqe(struct ispsoftc *isp, int timo) +{ + int s = splcam(); + if (tsleep(&isp->isp_osinfo.rstatus, PRIBIO, "qt1", timo)) { + splx(s); + return (-1); } + splx(s); + return (0); +} + +static __inline void +isp_cv_signal_rqe(struct ispsoftc *isp, int status) +{ + isp->isp_osinfo.rstatus = status; + wakeup(&isp->isp_osinfo.rstatus); +} + +static __inline void +isp_vsema_rqe(struct ispsoftc *isp) +{ + int s = splcam(); + if (isp->isp_osinfo.tmflags & TM_WANTED) { + isp->isp_osinfo.tmflags &= ~TM_WANTED; + wakeup(&isp->isp_osinfo.tmflags); + } + isp->isp_osinfo.tmflags &= ~TM_BUSY; + splx(s); +} + +static cam_status +create_lun_state(struct ispsoftc *isp, struct cam_path *path, tstate_t **rslt) +{ + int s; + cam_status status; + lun_id_t lun; + tstate_t *tptr, *new; + + lun = xpt_path_lun_id(path); + if (lun < 0) { + return (CAM_LUN_INVALID); + } + if (is_lun_enabled(isp, lun)) { + return (CAM_LUN_ALRDY_ENA); + } + new = (tstate_t *) malloc(sizeof (tstate_t), M_DEVBUF, M_NOWAIT); + if (new == NULL) { + return (CAM_RESRC_UNAVAIL); + } + bzero(new, sizeof (tstate_t)); + + status = xpt_create_path(&new->owner, NULL, xpt_path_path_id(path), + xpt_path_target_id(path), xpt_path_lun_id(path)); + if (status != CAM_REQ_CMP) { + free(new, M_DEVBUF); + return (status); + } + new->lun = lun; + SLIST_INIT(&new->atios); + SLIST_INIT(&new->inots); + new->hold = 1; + + s = splsoftcam(); + if ((tptr = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)]) == NULL) { + isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(lun)] = new; + } else { + while (tptr->next) + tptr = tptr->next; + tptr->next = new; + } + splx(s); + *rslt = new; + return (CAM_REQ_CMP); +} + +static __inline void +destroy_lun_state(struct ispsoftc *isp, tstate_t *tptr) +{ + tstate_t *lw, *pw; + int s; + + s = splsoftcam(); + if (tptr->hold) { + splx(s); + return; + } + pw = isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(tptr->lun)]; + if (pw == NULL) { + splx(s); + return; + } else if (pw->lun == tptr->lun) { + isp->isp_osinfo.lun_hash[LUN_HASH_FUNC(tptr->lun)] = pw->next; + } else { + lw = pw; + pw = lw->next; + while (pw) { + if (pw->lun == tptr->lun) { + lw->next = pw->next; + break; + } + lw = pw; + pw = pw->next; + } + if (pw == NULL) { + splx(s); + return; + } + } + free(tptr, M_DEVBUF); + splx(s); } static void +isp_en_lun(struct ispsoftc *isp, union ccb *ccb) +{ + const char *lfmt = "Lun now %sabled for target mode\n"; + struct ccb_en_lun *cel = &ccb->cel; + tstate_t *tptr; + u_int16_t rstat; + int bus, s; + lun_id_t lun; + target_id_t tgt; + + + bus = XS_CHANNEL(ccb); + tgt = ccb->ccb_h.target_id; + lun = ccb->ccb_h.target_lun; + + /* + * First, check to see if we're enabling on fibre channel + * and don't yet have a notion of who the heck we are (no + * loop yet). We do this by + */ + if (IS_FC(isp) && cel->enable && + (isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) == 0) { + int rv; + fcparam *fcp = isp->isp_param; + + s = splcam(); + rv = isp_control(isp, ISPCTL_FCLINK_TEST, NULL); + (void) splx(s); + if (rv || fcp->isp_fwstate != FW_READY) { + xpt_print_path(ccb->ccb_h.path); + printf("link status not good yet\n"); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + return; + } + s = splcam(); + rv = isp_control(isp, ISPCTL_PDB_SYNC, NULL); + (void) splx(s); + if (rv || fcp->isp_loopstate != LOOP_READY) { + xpt_print_path(ccb->ccb_h.path); + printf("could not get a good port database\n"); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + return; + } + } + + + /* + * Next check to see whether this is a target/lun wildcard action. + * + * If so, we enable/disable target mode but don't do any lun enabling. + */ + if (lun == CAM_LUN_WILDCARD && tgt == CAM_TARGET_WILDCARD) { + int av; + tptr = &isp->isp_osinfo.tsdflt; + if (cel->enable) { + if (isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) { + ccb->ccb_h.status = CAM_LUN_ALRDY_ENA; + return; + } + ccb->ccb_h.status = + xpt_create_path(&tptr->owner, NULL, + xpt_path_path_id(ccb->ccb_h.path), + xpt_path_target_id(ccb->ccb_h.path), + xpt_path_lun_id(ccb->ccb_h.path)); + if (ccb->ccb_h.status != CAM_REQ_CMP) { + return; + } + SLIST_INIT(&tptr->atios); + SLIST_INIT(&tptr->inots); + av = 1; + s = splcam(); + av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); + if (av) { + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + xpt_free_path(tptr->owner); + splx(s); + return; + } + isp->isp_osinfo.tmflags |= TM_TMODE_ENABLED; + splx(s); + } else { + if ((isp->isp_osinfo.tmflags & TM_TMODE_ENABLED) == 0) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; + } + if (are_any_luns_enabled(isp)) { + ccb->ccb_h.status = CAM_SCSI_BUSY; + return; + } + av = 0; + s = splcam(); + av = isp_control(isp, ISPCTL_TOGGLE_TMODE, &av); + if (av) { + ccb->ccb_h.status = CAM_FUNC_NOTAVAIL; + splx(s); + return; + } + isp->isp_osinfo.tmflags &= ~TM_TMODE_ENABLED; + splx(s); + ccb->ccb_h.status = CAM_REQ_CMP; + } + xpt_print_path(ccb->ccb_h.path); + printf(lfmt, (cel->enable) ? "en" : "dis"); + return; + } + + /* + * Do some sanity checking first. + */ + + if (IS_SCSI(isp)) { + if (lun < 0 || lun >= 32) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; + } + if (tgt != CAM_TARGET_WILDCARD && + tgt != ((sdparam *) isp->isp_param)->isp_initiator_id) { + ccb->ccb_h.status = CAM_TID_INVALID; + return; + } + } else { +#ifdef ISP2100_SCCLUN + if (lun < 0 || lun >= 65536) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; +#else + if (lun < 0 || lun >= 16) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; + } +#endif + if (tgt != CAM_TARGET_WILDCARD && + tgt != ((fcparam *) isp->isp_param)->isp_loopid) { + ccb->ccb_h.status = CAM_TID_INVALID; + return; + } + } + + + if (cel->enable) { + ccb->ccb_h.status = + create_lun_state(isp, ccb->ccb_h.path, &tptr); + if (ccb->ccb_h.status != CAM_REQ_CMP) { + return; + } + } else { + tptr = get_lun_statep(isp, lun); + if (tptr == NULL) { + ccb->ccb_h.status = CAM_LUN_INVALID; + return; + } + } + + if (isp_psema_sig_rqe(isp)) { + rls_lun_statep(isp, tptr); + if (cel->enable) + destroy_lun_state(isp, tptr); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + return; + } + + s = splcam(); + if (cel->enable) { + u_int32_t seq = isp->isp_osinfo.rollinfo++; + rstat = LUN_ERR; + if (isp_lun_cmd(isp, RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) { + xpt_print_path(ccb->ccb_h.path); + printf("isp_lun_cmd failed\n"); + goto out; + } + if (isp_cv_wait_timed_rqe(isp, 30 * hz)) { + xpt_print_path(ccb->ccb_h.path); + printf("wait for ENABLE LUN timed out\n"); + goto out; + } + rstat = isp->isp_osinfo.rstatus; + if (rstat != LUN_OK) { + xpt_print_path(ccb->ccb_h.path); + printf("ENABLE LUN returned 0x%x\n", rstat); + goto out; + } + } else { + u_int32_t seq; + + seq = isp->isp_osinfo.rollinfo++; + rstat = LUN_ERR; + + if (isp_lun_cmd(isp, -RQSTYPE_MODIFY_LUN, bus, tgt, lun, seq)) { + xpt_print_path(ccb->ccb_h.path); + printf("isp_lun_cmd failed\n"); + goto out; + } + if (isp_cv_wait_timed_rqe(isp, 30 * hz)) { + xpt_print_path(ccb->ccb_h.path); + printf("wait for MODIFY LUN timed out\n"); + goto out; + } + rstat = isp->isp_osinfo.rstatus; + if (rstat != LUN_OK) { + xpt_print_path(ccb->ccb_h.path); + printf("MODIFY LUN returned 0x%x\n", rstat); + goto out; + } + rstat = LUN_ERR; + seq = isp->isp_osinfo.rollinfo++; + + if (isp_lun_cmd(isp, -RQSTYPE_ENABLE_LUN, bus, tgt, lun, seq)) { + xpt_print_path(ccb->ccb_h.path); + printf("isp_lun_cmd failed\n"); + goto out; + } + if (isp_cv_wait_timed_rqe(isp, 30 * hz)) { + xpt_print_path(ccb->ccb_h.path); + printf("wait for ENABLE LUN timed out\n"); + goto out; + } + rstat = isp->isp_osinfo.rstatus; + if (rstat != LUN_OK) { + xpt_print_path(ccb->ccb_h.path); + printf("ENABLE LUN returned 0x%x\n", rstat); + goto out; + } + } +out: + isp_vsema_rqe(isp); + splx(s); + + if (rstat != LUN_OK) { + xpt_print_path(ccb->ccb_h.path); + printf("lun %sable failed\n", (cel->enable) ? "en" : "dis"); + ccb->ccb_h.status = CAM_REQ_CMP_ERR; + rls_lun_statep(isp, tptr); + if (cel->enable) + destroy_lun_state(isp, tptr); + } else { + xpt_print_path(ccb->ccb_h.path); + printf(lfmt, (cel->enable) ? "en" : "dis"); + rls_lun_statep(isp, tptr); + if (cel->enable == 0) { + destroy_lun_state(isp, tptr); + } + ccb->ccb_h.status = CAM_REQ_CMP; + } +} + +static cam_status +isp_abort_tgt_ccb(struct ispsoftc *isp, union ccb *ccb) +{ + tstate_t *tptr; + struct ccb_hdr_slist *lp; + struct ccb_hdr *curelm; + int found; + union ccb *accb = ccb->cab.abort_ccb; + + if (accb->ccb_h.target_id != CAM_TARGET_WILDCARD) { + if (IS_FC(isp) && (accb->ccb_h.target_id != + ((fcparam *) isp->isp_param)->isp_loopid)) { + return (CAM_PATH_INVALID); + } else if (IS_SCSI(isp) && (accb->ccb_h.target_id != + ((sdparam *) isp->isp_param)->isp_initiator_id)) { + return (CAM_PATH_INVALID); + } + } + tptr = get_lun_statep(isp, accb->ccb_h.target_lun); + if (tptr == NULL) { + return (CAM_PATH_INVALID); + } + if (accb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { + lp = &tptr->atios; + } else if (accb->ccb_h.func_code == XPT_IMMED_NOTIFY) { + lp = &tptr->inots; + } else { + rls_lun_statep(isp, tptr); + return (CAM_UA_ABORT); + } + curelm = SLIST_FIRST(lp); + found = 0; + if (curelm == &accb->ccb_h) { + found = 1; + SLIST_REMOVE_HEAD(lp, sim_links.sle); + } else { + while(curelm != NULL) { + struct ccb_hdr *nextelm; + + nextelm = SLIST_NEXT(curelm, sim_links.sle); + if (nextelm == &accb->ccb_h) { + found = 1; + SLIST_NEXT(curelm, sim_links.sle) = + SLIST_NEXT(nextelm, sim_links.sle); + break; + } + curelm = nextelm; + } + } + rls_lun_statep(isp, tptr); + if (found) { + accb->ccb_h.status = CAM_REQ_ABORTED; + return (CAM_REQ_CMP); + } + return(CAM_PATH_INVALID); +} + +static cam_status +isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) +{ + void *qe; + u_int32_t *hp, save_handle; + u_int16_t iptr, optr; + + if (isp_getrqentry(isp, &iptr, &optr, &qe)) { + PRINTF("%s: Request Queue Overflow in isp_target_start_ctio\n", + isp->isp_name); + return (CAM_RESRC_UNAVAIL); + } + MEMZERO(qe, QENTRY_LEN); + + /* + * We're either moving data or completing a command here. + */ + + if (IS_FC(isp)) { + ct2_entry_t *cto = qe; + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; + cto->ct_header.rqs_entry_count = 1; + cto->ct_iid = ccb->csio.init_id; +#ifdef ISP2100_SCCLUN + cto->ct_scclun = ccb->ccb_h.target_lun; +#else + cto->ct_lun = ccb->ccb_h.target_lun; +#endif + cto->ct_rxid = ccb->csio.tag_id; + cto->ct_flags = CT2_FLAG_MODE0 | CT2_CCINCR; + if (ccb->csio.dxfer_len == 0) { + cto->ct_flags |= CT2_NO_DATA; + } + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { + cto->ct_flags |= CT2_SENDSTATUS; + cto->rsp.m0.ct_scsi_status = ccb->csio.scsi_status; + if (ccb->csio.resid) { +printf("resid %d\n", ccb->csio.resid); + cto->ct_resid = ccb->csio.resid; + if (ccb->csio.resid < 0) + cto->ct_flags |= CT2_DATA_OVER; + else + cto->ct_flags |= CT2_DATA_UNDER; + } + /* + * If we had Sense Data already, + * here's where we'd set it up. + */ + } + hp = &cto->ct_reserved; + } else { + ct_entry_t *cto = qe; + cto->ct_header.rqs_entry_type = RQSTYPE_CTIO; + cto->ct_header.rqs_entry_count = 1; + cto->ct_iid = ccb->csio.init_id; + cto->ct_tgt = ccb->ccb_h.target_id; + cto->ct_lun = ccb->ccb_h.target_lun; + cto->ct_tag_type = ccb->csio.tag_action; + cto->ct_tag_val = ccb->csio.tag_id; + cto->ct_flags = CT_CCINCR; + if (ccb->csio.dxfer_len) { + cto->ct_flags |= CT_NO_DATA; + } + if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { + cto->ct_flags |= CT_SENDSTATUS; + cto->ct_scsi_status = ccb->csio.scsi_status; + cto->ct_resid = ccb->csio.resid; + } + hp = &cto->ct_reserved; + } + + if (isp_save_xs(isp, (ISP_SCSI_XFER_T *)ccb, hp)) { + PRINTF("%s: No XFLIST pointers for isp_target_start_ctio\n", + isp->isp_name); + return (CAM_RESRC_UNAVAIL); + } + + + /* + * Call the dma setup routines for this entry (and any subsequent + * CTIOs) if there's data to move, and then tell the f/w it's got + * new things to play with. As with ispscsicmd's usage of DMA setup, + * any swizzling is done in the machine dependent layer. Because + * of this, we put the request onto the queue area first in native + * format. + */ + + save_handle = *hp; + switch (ISP_DMASETUP(isp, &ccb->csio, qe, &iptr, optr)) { + case CMD_QUEUED: + MemoryBarrier(); + ISP_ADD_REQUEST(isp, iptr); + return (CAM_REQ_INPROG); + + case CMD_EAGAIN: + ccb->ccb_h.status = CAM_RESRC_UNAVAIL; + isp_destroy_handle(isp, save_handle); + return (CAM_RESRC_UNAVAIL); + + default: + isp_destroy_handle(isp, save_handle); + return (ccb->ccb_h.spriv_field0); + } +} + + +/* + * Handle ATIO stuff that the generic code can't. + * This means handling CDBs. + */ + +static int +isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) +{ + tstate_t *tptr; + int status; + struct ccb_accept_tio *atiop; + + /* + * The firmware status (except for the QLTM_SVALID bit) + * indicates why this ATIO was sent to us. + * + * 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. + */ + status = aep->at_status; + if ((status & ~QLTM_SVALID) == AT_PHASE_ERROR) { + /* + * Bus Phase Sequence error. We should have sense data + * suggested by the f/w. I'm not sure quite yet what + * to do about this for CAM. + */ + printf("%s: PHASE ERROR\n", isp->isp_name); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + if ((status & ~QLTM_SVALID) != AT_CDB) { + printf("%s: bogus atio (0x%x) leaked to platform\n", + isp->isp_name, status); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + + tptr = get_lun_statep(isp, aep->at_lun); + if (tptr == NULL) { + tptr = get_lun_statep(isp, CAM_LUN_WILDCARD); + } + + if (tptr == NULL) { + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a BUSY status + * instead. This works out okay because the only + * time we should, in fact, get this, is in the + * case that somebody configured us without the + * blackhole driver, so they get what they deserve. + */ + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + + atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); + if (atiop == NULL) { + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a QUEUE FULL status + * instead. This works out okay because the only time we + * should, in fact, get this, is in the case that we've + * run out of ATIOS. + */ + xpt_print_path(tptr->owner); + printf("no ATIOS for lun %d from initiator %d\n", + aep->at_lun, aep->at_iid); + rls_lun_statep(isp, tptr); + if (aep->at_flags & AT_TQAE) + isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); + else + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); + if (tptr == &isp->isp_osinfo.tsdflt) { + atiop->ccb_h.target_id = aep->at_tgt; + atiop->ccb_h.target_lun = aep->at_lun; + } + if (aep->at_flags & AT_NODISC) { + xpt_print_path(tptr->owner); + printf("incoming command that cannot disconnect\n"); + } + + + atiop->init_id = 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; + if ((atiop->tag_action = aep->at_tag_type) != 0) { + atiop->tag_id = aep->at_tag_val; + atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; + } + xpt_done((union ccb*)atiop); + if (isp_tdebug) { + xpt_print_path(tptr->owner); + printf("CDB[0x%x] from %d for lun %d tag 0x%x tagtype 0x%x\n", + aep->at_cdb[0] & 0xff, aep->at_iid, aep->at_lun, + aep->at_tag_val & 0xff, aep->at_tag_type); + } + rls_lun_statep(isp, tptr); + return (0); +} + +static int +isp_handle_platform_atio2(struct ispsoftc *isp, at2_entry_t *aep) +{ + tstate_t *tptr; + struct ccb_accept_tio *atiop; + + /* + * The firmware status (except for the QLTM_SVALID bit) + * indicates why this ATIO was sent to us. + * + * If QLTM_SVALID is set, the firware has recommended Sense Data. + */ + if ((aep->at_status & ~QLTM_SVALID) != AT_CDB) { + printf("%s: bogus atio (0x%x) leaked to platform\n", + isp->isp_name, aep->at_status); + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + + tptr = get_lun_statep(isp, aep->at_lun); + if (tptr == NULL) { + tptr = get_lun_statep(isp, CAM_LUN_WILDCARD); + } + + if (tptr == NULL) { +#if 0 + /* XXX WE REALLY NEED A HARDWIRED SENSE/INQ CTIO TO USE XXX */ + u_int32_t ccode = SCSI_STATUS_CHECK | 0x100; +#if NTARGBH > 0 + /* Not Ready, Unit Not Self-Configured yet.... */ + ccode |= (SSD_KEY_NOT_READY << 8) | (0x3E << 24); +#else + /* Illegal Request, Unit Not Self-Configured yet.... */ + ccode |= (SSD_KEY_ILLEGAL_REQUEST << 8) | (0x25 << 24); +#endif +#else + u_int32_t ccode = SCSI_STATUS_BUSY; +#endif + + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a BUSY status + * instead. This works out okay because the only + * time we should, in fact, get this, is in the + * case that somebody configured us without the + * blackhole driver, so they get what they deserve. + */ + isp_endcmd(isp, aep, ccode, 0); + return (0); + } + + atiop = (struct ccb_accept_tio *) SLIST_FIRST(&tptr->atios); + if (atiop == NULL) { + /* + * Because we can't autofeed sense data back with + * a command for parallel SCSI, we can't give back + * a CHECK CONDITION. We'll give back a QUEUE FULL status + * instead. This works out okay because the only time we + * should, in fact, get this, is in the case that we've + * run out of ATIOS. + */ + xpt_print_path(tptr->owner); + printf("no ATIOS for lun %d from initiator %d\n", + aep->at_lun, aep->at_iid); + rls_lun_statep(isp, tptr); + if (aep->at_flags & AT_TQAE) + isp_endcmd(isp, aep, SCSI_STATUS_QUEUE_FULL, 0); + else + isp_endcmd(isp, aep, SCSI_STATUS_BUSY, 0); + return (0); + } + SLIST_REMOVE_HEAD(&tptr->atios, sim_links.sle); + if (tptr == &isp->isp_osinfo.tsdflt) { + atiop->ccb_h.target_id = + ((fcparam *)isp->isp_param)->isp_loopid; + atiop->ccb_h.target_lun = aep->at_lun; + } + atiop->init_id = aep->at_iid; + atiop->cdb_len = ATIO2_CDBLEN; + MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); + atiop->ccb_h.status = CAM_CDB_RECVD; + atiop->tag_id = aep->at_rxid; + switch (aep->at_taskflags & ATIO2_TC_ATTR_MASK) { + case ATIO2_TC_ATTR_SIMPLEQ: + atiop->tag_action = MSG_SIMPLE_Q_TAG; + break; + case ATIO2_TC_ATTR_HEADOFQ: + atiop->tag_action = MSG_HEAD_OF_Q_TAG; + break; + case ATIO2_TC_ATTR_ORDERED: + atiop->tag_action = MSG_ORDERED_Q_TAG; + break; + case ATIO2_TC_ATTR_ACAQ: /* ?? */ + case ATIO2_TC_ATTR_UNTAGGED: + default: + atiop->tag_action = 0; + break; + } + if (atiop->tag_action != 0) { + atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; + } + xpt_done((union ccb*)atiop); + if (isp_tdebug) { + xpt_print_path(tptr->owner); + printf("CDB[0x%x] from %d for lun %d RX_ID 0x%x tflags 0x%x\n", + aep->at_cdb[0] & 0xff, aep->at_iid, aep->at_lun, + aep->at_rxid & 0xffff, aep->at_taskflags); + } + rls_lun_statep(isp, tptr); + return (0); +} + +static int +isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) +{ + union ccb *ccb; + int sentstatus, ok; + + /* + * CTIO and CTIO2 are close enough.... + */ + + ccb = (union ccb *) isp_find_xs(isp, ((ct_entry_t *)arg)->ct_reserved); + KASSERT((ccb != NULL), ("null ccb in isp_handle_platform_ctio")); + isp_destroy_handle(isp, ((ct_entry_t *)arg)->ct_reserved); + + if (IS_FC(isp)) { + ct2_entry_t *ct = arg; + sentstatus = ct->ct_flags & CT2_SENDSTATUS; + ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; + if (isp_tdebug) { + printf("%s: CTIO2 RX_ID 0x%x sts 0x%x flg 0x%x done\n", + isp->isp_name, ct->ct_rxid, ct->ct_status, + ct->ct_flags); + } + } else { + ct_entry_t *ct = arg; + sentstatus = ct->ct_flags & CT_SENDSTATUS; + ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; + if (isp_tdebug) { + printf("%s: CTIO tag 0x%x sts 0x%x flg 0x%x done\n", + isp->isp_name, ct->ct_tag_val, ct->ct_status, + ct->ct_flags); + } + } + + /* + * We're here either because data transfers are done (and + * it's time to send a final status CTIO) or because the final + * status CTIO is done. We don't get called for all intermediate + * CTIOs that happen for a large data transfer. + * + * In any case, for this platform, the upper layers figure out + * what to do next, so all we do here is collect status and + * pass information along. + */ + + if (sentstatus) { + /* + * Data transfer done. See if all went okay. + */ + if (ok) { + ccb->csio.resid = 0; + } else { + ccb->csio.resid = ccb->csio.dxfer_len; + } + } + + + if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { + ccb->ccb_h.status |= CAM_REQ_CMP; + } + ccb->ccb_h.status &= ~CAM_SIM_QUEUED; + if (isp->isp_osinfo.simqfrozen & SIMQFRZ_RESOURCE) { + isp->isp_osinfo.simqfrozen &= ~SIMQFRZ_RESOURCE; + if (isp->isp_osinfo.simqfrozen == 0) { + if ((ccb->ccb_h.status & CAM_DEV_QFRZN) == 0) { + IDPRINTF(3, ("%s: isp_done -> relsimq\n", + isp->isp_name)); + ccb->ccb_h.status |= CAM_RELEASE_SIMQ; + } else { + IDPRINTF(3, ("%s: isp_done -> devq frozen\n", + isp->isp_name)); + } + } else { + IDPRINTF(3, ("%s: isp_done -> simqfrozen = %x\n", + isp->isp_name, isp->isp_osinfo.simqfrozen)); + } + } + xpt_done(ccb); + return (0); +} +#endif + +static void isp_cam_async(void *cbarg, u_int32_t code, struct cam_path *path, void *arg) { struct cam_sim *sim; @@ -312,16 +1261,64 @@ isp_action(struct cam_sim *sim, union ccb *ccb) } break; +#ifdef ISP_TARGET_MODE case XPT_EN_LUN: /* Enable LUN as a target */ - case XPT_TARGET_IO: /* Execute target I/O request */ - case XPT_ACCEPT_TARGET_IO: /* Accept Host Target Mode CDB */ - case XPT_CONT_TARGET_IO: /* Continue Host Target I/O Connection*/ - ccb->ccb_h.status = CAM_REQ_INVALID; + isp_en_lun(isp, ccb); xpt_done(ccb); break; + case XPT_NOTIFY_ACK: /* recycle notify ack */ +xpt_print_path(ccb->ccb_h.path); +printf("notify ack\n"); + case XPT_IMMED_NOTIFY: /* Add Immediate Notify Resource */ + case XPT_ACCEPT_TARGET_IO: /* Add Accept Target IO Resource */ + { + tstate_t *tptr = get_lun_statep(isp, ccb->ccb_h.target_lun); + if (tptr == NULL) { + ccb->ccb_h.status = CAM_LUN_INVALID; + xpt_done(ccb); + break; + } + s = splsoftcam(); + if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { + SLIST_INSERT_HEAD(&tptr->atios, + &ccb->ccb_h, sim_links.sle); + } else { + SLIST_INSERT_HEAD(&tptr->inots, &ccb->ccb_h, + sim_links.sle); + } + splx(s); + rls_lun_statep(isp, tptr); + ccb->ccb_h.status = CAM_REQ_INPROG; + break; + } + case XPT_CONT_TARGET_IO: + { + s = splcam(); + ccb->ccb_h.status = isp_target_start_ctio(isp, ccb); + if (ccb->ccb_h.status != CAM_REQ_INPROG) { + if (isp->isp_osinfo.simqfrozen == 0) { + xpt_freeze_simq(sim, 1); + xpt_print_path(ccb->ccb_h.path); + printf("XPT_CONT_TARGET_IO freeze simq\n"); + } + isp->isp_osinfo.simqfrozen |= SIMQFRZ_RESOURCE; + ccb->ccb_h.status &= ~CAM_STATUS_MASK; + ccb->ccb_h.status |= CAM_REQUEUE_REQ; + xpt_done(ccb); + } else { + ccb->ccb_h.status |= CAM_SIM_QUEUED; + } + splx(s); + break; + } +#endif case XPT_RESET_DEV: /* BDR the specified SCSI device */ - tgt = ccb->ccb_h.target_id; /* XXX: Which Bus? */ + + bus = cam_sim_bus(xpt_path_sim(ccb->ccb_h.path)); + tgt = ccb->ccb_h.target_id; + tgt |= (bus << 16); + s = splcam(); error = isp_control(isp, ISPCTL_RESET_DEV, &tgt); (void) splx(s); @@ -333,17 +1330,36 @@ isp_action(struct cam_sim *sim, union ccb *ccb) xpt_done(ccb); break; case XPT_ABORT: /* Abort the specified CCB */ - s = splcam(); - error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); - (void) splx(s); - if (error) { - ccb->ccb_h.status = CAM_REQ_CMP_ERR; - } else { - ccb->ccb_h.status = CAM_REQ_CMP; + { + union ccb *accb = ccb->cab.abort_ccb; + switch (accb->ccb_h.func_code) { +#ifdef ISP_TARGET_MODE + case XPT_ACCEPT_TARGET_IO: + case XPT_IMMED_NOTIFY: + ccb->ccb_h.status = isp_abort_tgt_ccb(isp, ccb); + break; + case XPT_CONT_TARGET_IO: + PRINTF("%s: cannot abort CTIOs yet\n", isp->isp_name); + ccb->ccb_h.status = CAM_UA_ABORT; + break; +#endif + case XPT_SCSI_IO: + s = splcam(); + error = isp_control(isp, ISPCTL_ABORT_CMD, ccb); + (void) splx(s); + if (error) { + ccb->ccb_h.status = CAM_UA_ABORT; + } else { + ccb->ccb_h.status = CAM_REQ_CMP; + } + break; + default: + ccb->ccb_h.status = CAM_REQ_INVALID; + break; } xpt_done(ccb); break; - + } case XPT_SET_TRAN_SETTINGS: /* Nexus Settings */ cts = &ccb->cts; @@ -352,7 +1368,8 @@ isp_action(struct cam_sim *sim, union ccb *ccb) if (IS_SCSI(isp)) { sdparam *sdp = isp->isp_param; u_int16_t *dptr; - int bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); + + bus = cam_sim_bus(xpt_path_sim(cts->ccb_h.path)); sdp += bus; #if 0 @@ -569,7 +1586,11 @@ isp_action(struct cam_sim *sim, union ccb *ccb) struct ccb_pathinq *cpi = &ccb->cpi; cpi->version_num = 1; +#ifdef ISP_TARGET_MODE + cpi->target_sprt = PIT_PROCESSOR | PIT_DISCONNECT | PIT_TERM_IO; +#else cpi->target_sprt = 0; +#endif cpi->hba_eng_cnt = 0; cpi->max_target = ISP_MAX_TARGETS(isp) - 1; cpi->max_lun = ISP_MAX_LUNS(isp) - 1; @@ -840,7 +1861,48 @@ isp_async(struct ispsoftc *isp, ispasync_t cmd, void *arg) break; } #endif +#ifdef ISP_TARGET_MODE + case ISPASYNC_TARGET_MESSAGE: + { + tmd_msg_t *mp = arg; + ITDEBUG(1, ("%s: bus %d iid %d tgt %d lun %d ttype %x tval %x" + " msg[0]=0x%x\n", isp->isp_name, mp->nt_bus, mp->nt_iid, + mp->nt_tgt, mp->nt_lun, mp->nt_tagtype, mp->nt_tagval, + mp->nt_msg[0])); + break; + } + case ISPASYNC_TARGET_EVENT: + { + tmd_event_t *ep = arg; + ITDEBUG(1, ("%s: bus %d event code 0x%x\n", isp->isp_name, + ep->ev_bus, ep->ev_event)); + break; + } + case ISPASYNC_TARGET_ACTION: + switch (((isphdr_t *)arg)->rqs_entry_type) { + default: + printf("%s: event 0x%x for unhandled target action\n", + isp->isp_name, ((isphdr_t *)arg)->rqs_entry_type); + break; + case RQSTYPE_ATIO: + rv = isp_handle_platform_atio(isp, (at_entry_t *) arg); + break; + case RQSTYPE_ATIO2: + rv = isp_handle_platform_atio2(isp, (at2_entry_t *)arg); + break; + case RQSTYPE_CTIO2: + case RQSTYPE_CTIO: + rv = isp_handle_platform_ctio(isp, arg); + break; + case RQSTYPE_ENABLE_LUN: + case RQSTYPE_MODIFY_LUN: + isp_cv_signal_rqe(isp, ((lun_entry_t *)arg)->le_status); + break; + } + break; +#endif default: + PRINTF("%s: unknown isp_async event %d\n", isp->isp_name, cmd); rv = -1; break; } |