diff options
Diffstat (limited to 'sys/dev/isp/isp_freebsd.c')
-rw-r--r-- | sys/dev/isp/isp_freebsd.c | 209 |
1 files changed, 169 insertions, 40 deletions
diff --git a/sys/dev/isp/isp_freebsd.c b/sys/dev/isp/isp_freebsd.c index 2f371d2..0d8f7dc 100644 --- a/sys/dev/isp/isp_freebsd.c +++ b/sys/dev/isp/isp_freebsd.c @@ -195,11 +195,13 @@ 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 cam_status isp_target_putback_atio(struct ispsoftc *, union ccb *); +static timeout_t isp_refire_putback_atio; 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 void isp_handle_platform_ctio_part2(struct ispsoftc *, union ccb *); static __inline int is_lun_enabled(struct ispsoftc *isp, lun_id_t lun) @@ -403,7 +405,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, s; + int bus; lun_id_t lun; target_id_t tgt; @@ -697,6 +699,7 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) u_int32_t *hp, save_handle; u_int16_t iptr, optr; + if (isp_getrqentry(isp, &iptr, &optr, &qe)) { xpt_print_path(ccb->ccb_h.path); printf("Request Queue Overflow in isp_target_start_ctio\n"); @@ -709,8 +712,8 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) */ if (IS_FC(isp)) { + struct ccb_accept_tio *atiop; ct2_entry_t *cto = qe; - u_int16_t *ssptr = NULL; cto->ct_header.rqs_entry_type = RQSTYPE_CTIO2; cto->ct_header.rqs_entry_count = 1; @@ -718,15 +721,29 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) if (isp->isp_maxluns <= 16) { cto->ct_lun = ccb->ccb_h.target_lun; } + /* + * Start with a residual based on what the original datalength + * was supposed to be. Basically, we ignore what CAM has set + * for residuals. The data transfer routines will knock off + * the residual for each byte actually moved- and also will + * be responsible for setting the underrun flag. + */ + /* HACK! HACK! */ + if ((atiop = ccb->ccb_h.periph_priv.entries[1].ptr) != NULL) { + cto->ct_resid = atiop->ccb_h.spriv_field0; + } + + /* + * We always have to use the tag_id- it has the RX_ID + * for this exchage. + */ cto->ct_rxid = cso->tag_id; - cto->ct_flags = CT2_CCINCR; if (cso->dxfer_len == 0) { cto->ct_flags |= CT2_FLAG_MODE1 | CT2_NO_DATA; - KASSERT(ccb->ccb_h.flags & CAM_SEND_STATUS, - ("a CTIO with no data and no status?")); - cto->ct_flags |= CT2_SENDSTATUS; - ssptr = &cto->rsp.m1.ct_scsi_status; - *ssptr = cso->scsi_status; + if (ccb->ccb_h.flags & CAM_SEND_STATUS) { + cto->ct_flags |= CT2_SENDSTATUS; + cto->rsp.m1.ct_scsi_status = cso->scsi_status; + } if ((ccb->ccb_h.flags & CAM_SEND_SENSE) != 0) { int m = min(cso->sense_len, MAXRESPLEN); bcopy(&cso->sense_data, cto->rsp.m1.ct_resp, m); @@ -741,24 +758,19 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) cto->ct_flags |= CT2_DATA_OUT; } if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { - ssptr = &cto->rsp.m0.ct_scsi_status; cto->ct_flags |= CT2_SENDSTATUS; cto->rsp.m0.ct_scsi_status = cso->scsi_status; } + /* + * If we're sending data and status back together, + * we can't also send back sense data as well. + */ ccb->ccb_h.flags &= ~CAM_SEND_SENSE; } - if (ssptr && cso->resid) { - cto->ct_resid = cso->resid; - if (cso->resid < 0) - *ssptr |= CT2_DATA_OVER; - else - *ssptr |= CT2_DATA_UNDER; - } - if (isp_tdebug > 1 && ssptr && - (cso->scsi_status != SCSI_STATUS_OK || cso->resid)) { + if (isp_tdebug > 1 && (cto->ct_flags & CAM_SEND_STATUS)) { printf("%s:CTIO2 RX_ID 0x%x SCSI STATUS 0x%x " - "resid %d\n", isp->isp_name, cto->ct_rxid, - cso->scsi_status, cso->resid); + "datalength %u\n", isp->isp_name, cto->ct_rxid, + cso->scsi_status, cto->ct_resid); } hp = &cto->ct_reserved; } else { @@ -769,23 +781,30 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) cto->ct_iid = cso->init_id; cto->ct_tgt = ccb->ccb_h.target_id; cto->ct_lun = ccb->ccb_h.target_lun; - cto->ct_tag_type = cso->tag_action; - cto->ct_tag_val = cso->tag_id; - cto->ct_flags = CT_CCINCR; - if (cso->dxfer_len) { + if (cso->tag_id && cso->tag_action) { + /* + * We don't specify a tag type for regular SCSI. + * Just the tag value and set the flag. + */ + cto->ct_tag_val = cso->tag_id; + cto->ct_flags |= CT_TQAE; + } + if (ccb->ccb_h.flags & CAM_DIS_DISCONNECT) { + cto->ct_flags |= CT_NODISC; + } + if (cso->dxfer_len == 0) { cto->ct_flags |= CT_NO_DATA; } else if ((cso->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN) { cto->ct_flags |= CT_DATA_IN; } else { cto->ct_flags |= CT_DATA_OUT; } - if ((ccb->ccb_h.flags & CAM_SEND_STATUS) != 0) { + if (ccb->ccb_h.flags & CAM_SEND_STATUS) { cto->ct_flags |= CT_SENDSTATUS; cto->ct_scsi_status = cso->scsi_status; cto->ct_resid = cso->resid; } - if (isp_tdebug > 1 && - (cso->scsi_status != SCSI_STATUS_OK || cso->resid)) { + if (isp_tdebug > 1 && (cto->ct_flags & CAM_SEND_STATUS)) { printf("%s:CTIO SCSI STATUS 0x%x resid %d\n", isp->isp_name, cso->scsi_status, cso->resid); } @@ -827,6 +846,65 @@ isp_target_start_ctio(struct ispsoftc *isp, union ccb *ccb) } } +static cam_status +isp_target_putback_atio(struct ispsoftc *isp, union ccb *ccb) +{ + void *qe; + struct ccb_accept_tio *atiop; + u_int16_t iptr, optr; + + if (isp_getrqentry(isp, &iptr, &optr, &qe)) { + xpt_print_path(ccb->ccb_h.path); + printf("Request Queue Overflow in isp_target_putback_atio\n"); + return (CAM_RESRC_UNAVAIL); + } + bzero(qe, QENTRY_LEN); + atiop = (struct ccb_accept_tio *) ccb; + if (IS_FC(isp)) { + at2_entry_t *at = qe; + at->at_header.rqs_entry_type = RQSTYPE_ATIO2; + at->at_header.rqs_entry_count = 1; + if (isp->isp_maxluns > 16) { + at->at_scclun = (uint16_t) atiop->ccb_h.target_lun; + } else { + at->at_lun = (uint8_t) atiop->ccb_h.target_lun; + } + at->at_status = CT_OK; + at->at_rxid = atiop->tag_id; + ISP_SWIZ_ATIO2(isp, qe, qe); + } else { + at_entry_t *at = qe; + at->at_header.rqs_entry_type = RQSTYPE_ATIO; + at->at_header.rqs_entry_count = 1; + at->at_iid = atiop->init_id; + at->at_tgt = atiop->ccb_h.target_id; + at->at_lun = atiop->ccb_h.target_lun; + at->at_status = CT_OK; + if (atiop->ccb_h.status & CAM_TAG_ACTION_VALID) { + at->at_tag_type = atiop->tag_action; + } + at->at_tag_val = atiop->tag_id; + ISP_SWIZ_ATIO(isp, qe, qe); + } + ISP_TDQE(isp, "isp_target_putback_atio", (int) optr, qe); + MemoryBarrier(); + ISP_ADD_REQUEST(isp, iptr); + return (CAM_REQ_CMP); +} + +static void +isp_refire_putback_atio(void *arg) +{ + union ccb *ccb = arg; + int s = splcam(); + if (isp_target_putback_atio(XS_ISP(ccb), ccb) != CAM_REQ_CMP) { + (void) timeout(isp_refire_putback_atio, ccb, 10); + } else { + isp_handle_platform_ctio_part2(XS_ISP(ccb), ccb); + } + splx(s); +} + /* * Handle ATIO stuff that the generic code can't. * This means handling CDBs. @@ -913,24 +991,33 @@ isp_handle_platform_atio(struct ispsoftc *isp, at_entry_t *aep) 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->ccb_h.flags = CAM_DIS_DISCONNECT; + } else { + atiop->ccb_h.flags = 0; } + if (status & QLTM_SVALID) { + size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data)); + atiop->sense_len = amt; + MEMCPY(&atiop->sense_data, aep->at_sense, amt); + } else { + atiop->sense_len = 0; + } 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; + atiop->tag_id = aep->at_tag_val; 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 > 1) { - printf("%s:ATIO CDB=0x%x iid%d->lun%d tag 0x%x ttype 0x%x\n", + printf("%s:ATIO CDB=0x%x iid%d->lun%d tag 0x%x ttype 0x%x %s", isp->isp_name, aep->at_cdb[0] & 0xff, aep->at_iid, - aep->at_lun, aep->at_tag_val & 0xff, aep->at_tag_type); + aep->at_lun, aep->at_tag_val & 0xff, aep->at_tag_type, + (aep->at_flags & AT_NODISC)? "nondisc\n" : "\n"); } rls_lun_statep(isp, tptr); return (0); @@ -1027,11 +1114,20 @@ isp_handle_platform_atio2(struct ispsoftc *isp, at2_entry_t *aep) 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 = lun; } + if (aep->at_status & QLTM_SVALID) { + size_t amt = imin(QLTM_SENSELEN, sizeof (atiop->sense_data)); + atiop->sense_len = amt; + MEMCPY(&atiop->sense_data, aep->at_sense, amt); + } else { + atiop->sense_len = 0; + } + atiop->init_id = aep->at_iid; atiop->cdb_len = ATIO2_CDBLEN; MEMCPY(atiop->cdb_io.cdb_bytes, aep->at_cdb, ATIO2_CDBLEN); @@ -1056,21 +1152,28 @@ isp_handle_platform_atio2(struct ispsoftc *isp, at2_entry_t *aep) if (atiop->tag_action != 0) { atiop->ccb_h.status |= CAM_TAG_ACTION_VALID; } + + /* + * Preserve overall command datalength in private field. + */ + atiop->ccb_h.spriv_field0 = aep->at_datalen; + xpt_done((union ccb*)atiop); if (isp_tdebug > 1) { - printf("%s:ATIO2 RX_ID 0x%x CDB=0x%x iid%d->lun%d tattr 0x%x\n", + printf("%s:ATIO2 RX_ID 0x%x CDB=0x%x iid%d->lun%d tattr 0x%x " + "datalen %u\n", isp->isp_name, aep->at_rxid & 0xffff, aep->at_cdb[0] & 0xff, - aep->at_iid, lun, aep->at_taskflags); + aep->at_iid, lun, aep->at_taskflags, aep->at_datalen); } rls_lun_statep(isp, tptr); return (0); } static int -isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) +isp_handle_platform_ctio(struct ispsoftc *isp, void *arg) { union ccb *ccb; - int sentstatus, ok; + int sentstatus, ok, notify_cam; /* * CTIO and CTIO2 are close enough.... @@ -1084,7 +1187,7 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) ct2_entry_t *ct = arg; sentstatus = ct->ct_flags & CT2_SENDSTATUS; ok = (ct->ct_status & ~QLTM_SVALID) == CT_OK; - if (ok && ccb->ccb_h.flags & CAM_SEND_SENSE) { + if (ok && (ccb->ccb_h.flags & CAM_SEND_SENSE)) { ccb->ccb_h.status |= CAM_SENT_SENSE; } if (isp_tdebug > 1) { @@ -1093,6 +1196,7 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) ct->ct_status, ct->ct_flags, (ccb->ccb_h.status & CAM_SENT_SENSE) != 0); } + notify_cam = ct->ct_header.rqs_seqno; } else { ct_entry_t *ct = arg; sentstatus = ct->ct_flags & CT_SENDSTATUS; @@ -1102,6 +1206,7 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) isp->isp_name, ct->ct_tag_val, ct->ct_status, ct->ct_flags); } + notify_cam = ct->ct_header.rqs_seqno; } /* @@ -1112,7 +1217,8 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) * * 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. + * pass information along. The exception is that we clear + * the notion of handling a non-disconnecting command here. */ if (sentstatus) { @@ -1126,7 +1232,26 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) } } + if (notify_cam == 0) { + if (isp_tdebug > 1) { + printf("%s:Intermediate CTIO done\n", isp->isp_name); + } + return (0); + } + if (isp_tdebug > 1) { + printf("%s:Final CTIO done\n", isp->isp_name); + } + if (isp_target_putback_atio(isp, ccb) != CAM_REQ_CMP) { + (void) timeout(isp_refire_putback_atio, ccb, 10); + } else { + isp_handle_platform_ctio_part2(isp, ccb); + } + return (0); +} +static void +isp_handle_platform_ctio_part2(struct ispsoftc *isp, union ccb *ccb) +{ if ((ccb->ccb_h.status & CAM_STATUS_MASK) == CAM_REQ_INPROG) { ccb->ccb_h.status |= CAM_REQ_CMP; } @@ -1148,7 +1273,6 @@ isp_handle_platform_ctio(struct ispsoftc *isp, void * arg) } } xpt_done(ccb); - return (0); } #endif @@ -1427,8 +1551,13 @@ isp_action(struct cam_sim *sim, union ccb *ccb) xpt_done(ccb); break; } + ccb->ccb_h.sim_priv.entries[0].field = 0; + ccb->ccb_h.sim_priv.entries[1].ptr = isp; ISP_LOCK(isp); if (ccb->ccb_h.func_code == XPT_ACCEPT_TARGET_IO) { +#if 0 + (void) isp_target_putback_atio(isp, ccb); +#endif SLIST_INSERT_HEAD(&tptr->atios, &ccb->ccb_h, sim_links.sle); } else { |