Index: head/sys/dev/iscsi_initiator/isc_sm.c =================================================================== --- head/sys/dev/iscsi_initiator/isc_sm.c (revision 358495) +++ head/sys/dev/iscsi_initiator/isc_sm.c (revision 358496) @@ -1,750 +1,750 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2010 Daniel Braniss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* | iSCSI - Session Manager | $Id: isc_sm.c 743 2009-08-08 10:54:53Z danny $ */ #include __FBSDID("$FreeBSD$"); #include "opt_iscsi_initiator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static void _async(isc_session_t *sp, pduq_t *pq) { debug_called(8); iscsi_async(sp, pq); pdu_free(sp->isc, pq); } static void _reject(isc_session_t *sp, pduq_t *pq) { pduq_t *opq; pdu_t *pdu; reject_t *reject; int itt; debug_called(8); pdu = mtod(pq->mp, pdu_t *); itt = pdu->ipdu.bhs.itt; reject = &pq->pdu.ipdu.reject; sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason); opq = i_search_hld(sp, itt, 0); if(opq != NULL) iscsi_reject(sp, opq, pq); else { switch(pq->pdu.ipdu.bhs.opcode) { case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why sdebug(2, "ISCSI_LOGOUT_CMD ..."); break; default: xdebug("%d] we lost something itt=%x", sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); } } pdu_free(sp->isc, pq); } static void _r2t(isc_session_t *sp, pduq_t *pq) { pduq_t *opq; debug_called(8); opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); if(opq != NULL) { iscsi_r2t(sp, opq, pq); } else { r2t_t *r2t = &pq->pdu.ipdu.r2t; xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x", sp->sid, ntohl(pq->pdu.ipdu.bhs.itt), ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl)); } pdu_free(sp->isc, pq); } static void _scsi_rsp(isc_session_t *sp, pduq_t *pq) { pduq_t *opq; debug_called(8); opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0); debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq); if(opq != NULL) { iscsi_done(sp, opq, pq); i_acked_hld(sp, &pq->pdu); } else xdebug("%d] we lost something itt=%x", sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); pdu_free(sp->isc, pq); } static void _read_data(isc_session_t *sp, pduq_t *pq) { pduq_t *opq; debug_called(8); opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1); if(opq != NULL) { if(scsi_decap(sp, opq, pq) != 1) { i_remove_hld(sp, opq); // done pdu_free(sp->isc, opq); } } else xdebug("%d] we lost something itt=%x", sp->sid, ntohl(pq->pdu.ipdu.bhs.itt)); pdu_free(sp->isc, pq); } /* | this is a kludge, | the jury is not back with a veredict, user or kernel */ static void _nop_out(isc_session_t *sp) { pduq_t *pq; nop_out_t *nop_out; debug_called(8); sdebug(4, "cws=%d", sp->cws); if(sp->cws == 0) { /* | only send a nop if window is closed. */ if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) // I guess we ran out of resources return; nop_out = &pq->pdu.ipdu.nop_out; nop_out->opcode = ISCSI_NOP_OUT; nop_out->itt = htonl(sp->sn.itt); nop_out->ttt = -1; nop_out->I = 1; nop_out->F = 1; if(isc_qout(sp, pq) != 0) { sdebug(1, "failed"); pdu_free(sp->isc, pq); } } } static void _nop_in(isc_session_t *sp, pduq_t *pq) { pdu_t *pp = &pq->pdu; nop_in_t *nop_in = &pp->ipdu.nop_in; bhs_t *bhs = &pp->ipdu.bhs; debug_called(8); sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt)); if(nop_in->itt == -1) { if(pp->ds_len != 0) { /* | according to RFC 3720 this should be zero | what to do if not? */ xdebug("%d] dslen not zero", sp->sid); } if(nop_in->ttt != -1) { nop_out_t *nop_out; /* | target wants a nop_out */ bhs->opcode = ISCSI_NOP_OUT; bhs->I = 1; bhs->F = 1; /* | we are reusing the pdu, so bhs->ttt == nop_in->ttt; | and need to zero out 'Reserved' | small cludge here. */ nop_out = &pp->ipdu.nop_out; nop_out->sn.maxcmd = 0; memset(nop_out->mbz, 0, sizeof(nop_out->mbz)); (void)isc_qout(sp, pq); //XXX: should check return? return; } //else { // just making noise? // see 10.9.1: target does not want and answer. //} } else if(nop_in->ttt == -1) { /* | it is an answer to a nop_in from us */ if(nop_in->itt != -1) { #ifdef ISC_WAIT4PING // XXX: MUTEX please if(sp->flags & ISC_WAIT4PING) { i_nqueue_rsp(sp, pq); wakeup(&sp->rsp); return; } #endif } } /* | drop it */ pdu_free(sp->isc, pq); return; } int i_prepPDU(isc_session_t *sp, pduq_t *pq) { size_t len, n; pdu_t *pp = &pq->pdu; bhs_t *bhp = &pp->ipdu.bhs; len = sizeof(bhs_t); if(pp->ahs_len) { len += pp->ahs_len; bhp->AHSLength = pp->ahs_len / 4; } if(ISOK2DIG(sp->hdrDigest, pp)) len += 4; if(pp->ds_len) { n = pp->ds_len; len += n; #if BYTE_ORDER == LITTLE_ENDIAN bhp->DSLength = ((n & 0x00ff0000) >> 16) | (n & 0x0000ff00) | ((n & 0x000000ff) << 16); #else bhp->DSLength = n; #endif if(len & 03) { n = 4 - (len & 03); len += n; } if(ISOK2DIG(sp->dataDigest, pp)) len += 4; } pq->len = len; len -= sizeof(bhs_t); if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) { xdebug("%d] pdu len=%zd > %d", sp->sid, len, sp->opt.maxBurstLength); // XXX: when this happens it used to hang ... return E2BIG; } return 0; } int isc_qout(isc_session_t *sp, pduq_t *pq) { int error = 0; debug_called(8); if(pq->len == 0 && (error = i_prepPDU(sp, pq))) return error; if(pq->pdu.ipdu.bhs.I) i_nqueue_isnd(sp, pq); else if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA) i_nqueue_wsnd(sp, pq); else i_nqueue_csnd(sp, pq); sdebug(5, "enqued: pq=%p", pq); mtx_lock(&sp->io_mtx); sp->flags |= ISC_OQNOTEMPTY; if(sp->flags & ISC_OWAITING) wakeup(&sp->flags); mtx_unlock(&sp->io_mtx); return error; } /* | called when a fullPhase is restarted */ void ism_restart(isc_session_t *sp) { int lastcmd; sdebug(2, "restart ..."); lastcmd = iscsi_requeue(sp); #if 0 if(lastcmd != sp->sn.cmd) { sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd); sp->sn.cmd = lastcmd; } #endif mtx_lock(&sp->io_mtx); if(sp->flags & ISC_OWAITING) { wakeup(&sp->flags); } mtx_unlock(&sp->io_mtx); sdebug(2, "restarted sn.cmd=0x%x lastcmd=0x%x", sp->sn.cmd, lastcmd); } void ism_recv(isc_session_t *sp, pduq_t *pq) { bhs_t *bhs; int statSN; debug_called(8); bhs = &pq->pdu.ipdu.bhs; statSN = ntohl(bhs->OpcodeSpecificFields[1]); #ifdef notyet if(sp->sn.expCmd != sn->cmd) { sdebug(1, "we lost something ... exp=0x%x cmd=0x%x", sn->expCmd, sn->cmd); } #endif sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x", bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd); switch(bhs->opcode) { case ISCSI_READ_DATA: { data_in_t *cmd = &pq->pdu.ipdu.data_in; if(cmd->S == 0) break; } default: if(statSN > (sp->sn.stat + 1)) { sdebug(1, "we lost some rec=0x%x exp=0x%x", statSN, sp->sn.stat); // XXX: must do some error recovery here. } sp->sn.stat = statSN; } switch(bhs->opcode) { case ISCSI_LOGIN_RSP: case ISCSI_TEXT_RSP: case ISCSI_LOGOUT_RSP: i_nqueue_rsp(sp, pq); wakeup(&sp->rsp); sdebug(3, "wakeup rsp"); break; case ISCSI_NOP_IN: _nop_in(sp, pq); break; case ISCSI_SCSI_RSP: _scsi_rsp(sp, pq); break; case ISCSI_READ_DATA: _read_data(sp, pq); break; case ISCSI_R2T: _r2t(sp, pq); break; case ISCSI_REJECT: _reject(sp, pq); break; case ISCSI_ASYNC: _async(sp, pq); break; case ISCSI_TASK_RSP: default: sdebug(1, "opcode=0x%x itt=0x%x not implemented yet", bhs->opcode, ntohl(bhs->itt)); break; } } /* | go through the out queues looking for work | if either nothing to do, or window is closed | return. */ static int proc_out(isc_session_t *sp) { sn_t *sn = &sp->sn; pduq_t *pq; int error, which; debug_called(8); error = 0; while(sp->flags & ISC_LINK_UP) { pdu_t *pp; bhs_t *bhs; /* | check if there is outstanding work in: | 1- the Immediate queue | 2- the R2T queue | 3- the cmd queue, only if the command window allows it. */ which = BIT(0) | BIT(1); if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0 which |= BIT(2); sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd); if((pq = i_dqueue_snd(sp, which)) == NULL) break; sdebug(4, "pq=%p", pq); pp = &pq->pdu; bhs = &pp->ipdu.bhs; switch(bhs->opcode) { case ISCSI_SCSI_CMD: sn->itt++; bhs->itt = htonl(sn->itt); case ISCSI_LOGIN_CMD: case ISCSI_TEXT_CMD: case ISCSI_LOGOUT_CMD: case ISCSI_SNACK: case ISCSI_NOP_OUT: case ISCSI_TASK_CMD: bhs->CmdSN = htonl(sn->cmd); if(bhs->I == 0) sn->cmd++; case ISCSI_WRITE_DATA: bhs->ExpStSN = htonl(sn->stat + 1); break; default: // XXX: can this happen? xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", bhs->opcode, sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); // XXX: and now? } sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)", bhs->opcode, sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt); if(bhs->opcode != ISCSI_NOP_OUT) /* | enqued till ack is received | note: sosend(...) does not mean the packet left | the host so that freeing resources has to wait */ i_nqueue_hld(sp, pq); error = isc_sendPDU(sp, pq); if(bhs->opcode == ISCSI_NOP_OUT) pdu_free(sp->isc, pq); if(error) { xdebug("error=%d opcode=0x%x ccb=%p itt=%x", error, bhs->opcode, pq->ccb, ntohl(bhs->itt)); i_remove_hld(sp, pq); switch(error) { case EPIPE: sp->flags &= ~ISC_LINK_UP; case EAGAIN: xdebug("requed"); i_rqueue_pdu(sp, pq); break; default: if(pq->ccb) { xdebug("back to cam"); pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error? - xpt_done(sp, pq->ccb); + xpt_done(pq->ccb); pdu_free(sp->isc, pq); } else xdebug("we lost it!"); } } } return error; } /* | survives link breakdowns. */ static void ism_out(void *vp) { isc_session_t *sp = (isc_session_t *)vp; int error; debug_called(8); sp->flags |= ISC_SM_RUNNING; sdebug(3, "started sp->flags=%x", sp->flags); do { if((sp->flags & ISC_HOLD) == 0) { error = proc_out(sp); if(error) { sdebug(3, "error=%d", error); } } mtx_lock(&sp->io_mtx); if((sp->flags & ISC_LINK_UP) == 0) { sdebug(3, "ISC_LINK_UP==0, sp->flags=%x ", sp->flags); if(sp->soc != NULL) sdebug(3, "so_state=%x", sp->soc->so_state); wakeup(&sp->soc); } if(!(sp->flags & ISC_OQNOTEMPTY)) { sp->flags |= ISC_OWAITING; if(msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) { if(sp->flags & ISC_CON_RUNNING) _nop_out(sp); } sp->flags &= ~ISC_OWAITING; } sp->flags &= ~ISC_OQNOTEMPTY; mtx_unlock(&sp->io_mtx); } while(sp->flags & ISC_SM_RUN); sp->flags &= ~ISC_SM_RUNNING; sdebug(3, "dropped ISC_SM_RUNNING"); wakeup(&sp->soc); wakeup(sp); // XXX: do we need this one? destroy_dev(sp->dev); debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid); kproc_exit(0); } #if 0 static int isc_dump_options(SYSCTL_HANDLER_ARGS) { int error; isc_session_t *sp; struct sbuf sb; sbuf_new_for_sysctl(&sb, NULL, 128, req); sp = (isc_session_t *)arg1; sbuf_printf(&sb, "targetname='%s'", sp->opt.targetName); sbuf_printf(&sb, " targetaddress='%s'", sp->opt.targetAddress); error = sbuf_finish(&sb); sbuf_delete(&sb); return error; } #endif static int isc_dump_stats(SYSCTL_HANDLER_ARGS) { isc_session_t *sp; struct isc_softc *sc; int error; struct sbuf sb; sp = (isc_session_t *)arg1; sc = sp->isc; sbuf_new_for_sysctl(&sb, NULL, 128, req); sbuf_printf(&sb, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent); sbuf_printf(&sb, " flags=0x%08x pdus-alloc=%d pdus-max=%d", sp->flags, sc->npdu_alloc, sc->npdu_max); sbuf_printf(&sb, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x", sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt); error = sbuf_finish(&sb); sbuf_delete(&sb); return error; } static void isc_add_sysctls(isc_session_t *sp) { debug_called(8); sdebug(6, "sid=%d %s", sp->sid, devtoname(sp->dev)); sysctl_ctx_init(&sp->clist); sp->oid = SYSCTL_ADD_NODE(&sp->clist, SYSCTL_CHILDREN(sp->isc->oid), OID_AUTO, devtoname(sp->dev) + 5, // iscsi0 CTLFLAG_RD | CTLFLAG_MPSAFE, 0, "initiator"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "targetname", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, (void *)&sp->opt.targetName, 0, sysctl_handle_string, "A", "target name"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "targeaddress", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, (void *)&sp->opt.targetAddress, 0, sysctl_handle_string, "A", "target address"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "stats", CTLTYPE_STRING | CTLFLAG_RD | CTLFLAG_NEEDGIANT, (void *)sp, 0, isc_dump_stats, "A", "statistics"); SYSCTL_ADD_INT(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "douio", CTLFLAG_RW, &sp->douio, 0, "enable uio on read"); } void ism_stop(isc_session_t *sp) { struct isc_softc *sc = sp->isc; int n; debug_called(8); sdebug(2, "terminating"); /* | first stop the receiver */ isc_stop_receiver(sp); /* | now stop the xmitter */ n = 5; sp->flags &= ~ISC_SM_RUN; while(n-- && (sp->flags & ISC_SM_RUNNING)) { sdebug(2, "n=%d", n); wakeup(&sp->flags); tsleep(sp, PRIBIO, "-", 5*hz); } sdebug(2, "final n=%d", n); sp->flags &= ~ISC_FFPHASE; iscsi_cleanup(sp); (void)i_pdu_flush(sp); ic_destroy(sp); sx_xlock(&sc->unit_sx); free_unr(sc->unit, sp->sid); sx_xunlock(&sc->unit_sx); mtx_lock(&sc->isc_mtx); TAILQ_REMOVE(&sc->isc_sess, sp, sp_link); sc->nsess--; mtx_unlock(&sc->isc_mtx); mtx_destroy(&sp->rsp_mtx); mtx_destroy(&sp->rsv_mtx); mtx_destroy(&sp->hld_mtx); mtx_destroy(&sp->snd_mtx); mtx_destroy(&sp->io_mtx); i_freeopt(&sp->opt); if(sysctl_ctx_free(&sp->clist)) xdebug("sysctl_ctx_free failed"); free(sp, M_ISCSI); } int ism_start(isc_session_t *sp) { debug_called(8); /* | now is a good time to do some initialization */ TAILQ_INIT(&sp->rsp); TAILQ_INIT(&sp->rsv); TAILQ_INIT(&sp->csnd); TAILQ_INIT(&sp->isnd); TAILQ_INIT(&sp->wsnd); TAILQ_INIT(&sp->hld); mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF); mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF); mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF); mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF); mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF); isc_add_sysctls(sp); sp->flags |= ISC_SM_RUN; debug(4, "starting ism_proc: sp->sid=%d", sp->sid); return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); -a} +} Index: head/sys/dev/iscsi_initiator/iscsi_subr.c =================================================================== --- head/sys/dev/iscsi_initiator/iscsi_subr.c (revision 358495) +++ head/sys/dev/iscsi_initiator/iscsi_subr.c (revision 358496) @@ -1,603 +1,603 @@ /*- * SPDX-License-Identifier: BSD-2-Clause-FreeBSD * * Copyright (c) 2005-2010 Daniel Braniss * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * */ /* | $Id: iscsi_subr.c 743 2009-08-08 10:54:53Z danny $ */ #include __FBSDID("$FreeBSD$"); #include "opt_iscsi_initiator.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include /* | Interface to the SCSI layer */ void iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq) { union ccb *ccb = opq->ccb; struct ccb_scsiio *csio = &ccb->csio; pdu_t *opp = &opq->pdu; bhs_t *bhp = &opp->ipdu.bhs; r2t_t *r2t = &pq->pdu.ipdu.r2t; pduq_t *wpq; int error; debug_called(8); sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); switch(bhp->opcode) { case ISCSI_SCSI_CMD: if(opp->ipdu.scsi_req.W) { data_out_t *cmd; u_int ddtl = ntohl(r2t->ddtl); u_int edtl = ntohl(opp->ipdu.scsi_req.edtlen); u_int bleft, bs, dsn, bo; caddr_t bp = csio->data_ptr; bo = ntohl(r2t->bo); bp += MIN(bo, edtl - ddtl); bleft = ddtl; if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl); else bs = ddtl; dsn = 0; sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x", edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength); while(bleft > 0) { wpq = pdu_alloc(sp->isc, M_NOWAIT); // testing ... if(wpq == NULL) { sdebug(3, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt), ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W); sdebug(1, "npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc); while((wpq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { sdebug(2, "waiting..."); pause("isc_r2t", 5*hz); } } cmd = &wpq->pdu.ipdu.data_out; cmd->opcode = ISCSI_WRITE_DATA; cmd->lun[0] = r2t->lun[0]; cmd->lun[1] = r2t->lun[1]; cmd->ttt = r2t->ttt; cmd->itt = r2t->itt; cmd->dsn = htonl(dsn); cmd->bo = htonl(bo); cmd->F = (bs < bleft)? 0: 1; // is this the last one? bs = MIN(bs, bleft); wpq->pdu.ds_len = bs; wpq->pdu.ds_addr = bp; error = isc_qout(sp, wpq); sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error); if(error) break; bo += bs; bp += bs; bleft -= bs; dsn++; } } break; default: // XXX: should not happen ... xdebug("huh? opcode=0x%x", bhp->opcode); } } static int getSenseData(u_int status, union ccb *ccb, pduq_t *pq) { pdu_t *pp = &pq->pdu; struct ccb_scsiio *scsi = (struct ccb_scsiio *)ccb; struct scsi_sense_data *sense = &scsi->sense_data; struct mbuf *m = pq->mp; scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; caddr_t bp; int sense_len, mustfree = 0; int error_code, sense_key, asc, ascq; bp = mtod(pq->mp, caddr_t); if((sense_len = scsi_2btoul(bp)) == 0) return 0; debug(4, "sense_len=%d", sense_len); /* | according to the specs, the sense data cannot | be larger than 252 ... */ if(sense_len > m->m_len) { bp = malloc(sense_len, M_ISCSI, M_WAITOK); debug(3, "calling i_mbufcopy(len=%d)", sense_len); i_mbufcopy(pq->mp, bp, sense_len); mustfree++; } scsi->scsi_status = status; bcopy(bp+2, sense, min(sense_len, scsi->sense_len)); scsi->sense_resid = 0; if(cmd->flag & (BIT(1)|BIT(2))) scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt); scsi_extract_sense_len(sense, scsi->sense_len - scsi->sense_resid, &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1); debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x", sense_len, ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid, pp->ds_len, error_code, sense_key); if(mustfree) free(bp, M_ISCSI); return 1; } /* | Some information is from SAM draft. */ static void _scsi_done(isc_session_t *sp, u_int response, u_int status, union ccb *ccb, pduq_t *pq) { struct ccb_hdr *ccb_h = &ccb->ccb_h; debug_called(8); if(status || response) { sdebug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq); if(pq != NULL) sdebug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len); } ccb_h->status = 0; switch(response) { case 0: // Command Completed at Target switch(status) { case 0: // Good, all is ok ccb_h->status = CAM_REQ_CMP; break; case 0x02: // Check Condition if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq)) ccb_h->status |= CAM_AUTOSNS_VALID; case 0x14: // Intermediate-Condition Met case 0x10: // Intermediate case 0x04: // Condition Met ccb_h->status |= CAM_SCSI_STATUS_ERROR; break; case 0x08: ccb_h->status = CAM_BUSY; break; case 0x18: // Reservation Conflict case 0x28: // Task Set Full ccb_h->status = CAM_REQUEUE_REQ; break; default: //case 0x22: // Command Terminated //case 0x30: // ACA Active //case 0x40: // Task Aborted ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; } break; default: if((response >= 0x80) && (response <= 0xFF)) { // Vendor specific ... } case 1: // target failure ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED; break; } sdebug(5, "ccb_h->status=%x", ccb_h->status); - xpt_done(sp, ccb); + xpt_done(ccb); } /* | returns the lowest cmdseq that was not acked */ int iscsi_requeue(isc_session_t *sp) { pduq_t *pq; u_int i, n, last; debug_called(8); i = last = 0; sp->flags |= ISC_HOLD; while((pq = i_dqueue_hld(sp)) != NULL) { i++; if(pq->ccb != NULL) { _scsi_done(sp, 0, 0x28, pq->ccb, NULL); n = ntohl(pq->pdu.ipdu.bhs.CmdSN); if(last==0 || (last > n)) last = n; sdebug(2, "last=%x n=%x", last, n); } pdu_free(sp->isc, pq); } sp->flags &= ~ISC_HOLD; return i? last: sp->sn.cmd; } int i_pdu_flush(isc_session_t *sp) { int n = 0; pduq_t *pq; debug_called(8); while((pq = i_dqueue_rsp(sp)) != NULL) { pdu_free(sp->isc, pq); n++; } while((pq = i_dqueue_rsv(sp)) != NULL) { pdu_free(sp->isc, pq); n++; } while((pq = i_dqueue_snd(sp, -1)) != NULL) { pdu_free(sp->isc, pq); n++; } while((pq = i_dqueue_hld(sp)) != NULL) { pdu_free(sp->isc, pq); n++; } while((pq = i_dqueue_wsnd(sp)) != NULL) { pdu_free(sp->isc, pq); n++; } if(n != 0) xdebug("%d pdus recovered, should have been ZERO!", n); return n; } /* | called from ism_destroy. */ void iscsi_cleanup(isc_session_t *sp) { pduq_t *pq, *pqtmp; debug_called(8); TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, pqtmp) { sdebug(3, "hld pq=%p", pq); if(pq->ccb) _scsi_done(sp, 1, 0x40, pq->ccb, NULL); TAILQ_REMOVE(&sp->hld, pq, pq_link); if(pq->buf) { free(pq->buf, M_ISCSIBUF); pq->buf = NULL; } pdu_free(sp->isc, pq); } while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) { sdebug(3, "pq=%p", pq); if(pq->ccb) _scsi_done(sp, 1, 0x40, pq->ccb, NULL); if(pq->buf) { free(pq->buf, M_ISCSIBUF); pq->buf = NULL; } pdu_free(sp->isc, pq); } wakeup(&sp->rsp); } void iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq) { pdu_t *pp = &pq->pdu; scsi_rsp_t *cmd = &pp->ipdu.scsi_rsp; debug_called(8); _scsi_done(sp, cmd->response, cmd->status, opq->ccb, pq); pdu_free(sp->isc, opq); } // see RFC 3720, 10.9.1 page 146 /* | NOTE: | the call to isc_stop_receiver is a kludge, | instead, it should be handled by the userland controller, | but that means that there should be a better way, other than | sending a signal. Somehow, this packet should be supplied to | the userland via read. */ void iscsi_async(isc_session_t *sp, pduq_t *pq) { pdu_t *pp = &pq->pdu; async_t *cmd = &pp->ipdu.async; debug_called(8); sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode); switch(cmd->asyncEvent) { case 0: // check status ... break; case 1: // target request logout isc_stop_receiver(sp); // XXX: temporary solution break; case 2: // target indicates it wants to drop connection isc_stop_receiver(sp); // XXX: temporary solution break; case 3: // target indicates it will drop all connections. isc_stop_receiver(sp); // XXX: temporary solution break; case 4: // target request parameter negotiation break; default: break; } } void iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq) { union ccb *ccb = opq->ccb; //reject_t *reject = &pq->pdu.ipdu.reject; debug_called(8); //XXX: check RFC 10.17.1 (page 176) ccb->ccb_h.status = CAM_REQ_ABORTED; - xpt_done(sp, ccb); + xpt_done(ccb); pdu_free(sp->isc, opq); } /* | deal with lun */ static int dwl(isc_session_t *sp, int lun, u_char *lp) { debug_called(8); sdebug(4, "lun=%d", lun); /* | mapping LUN to iSCSI LUN | check the SAM-2 specs | hint: maxLUNS is a small number, cam's LUN is 32bits | iSCSI is 64bits, scsi is ? */ // XXX: check if this will pass the endian test if(lun < 256) { lp[0] = 0; lp[1] = lun; } else if(lun < 16384) { lp[0] = (1 << 5) | ((lun >> 8) & 0x3f); lp[1] = lun & 0xff; } else { xdebug("lun %d: is unsupported!", lun); return -1; } return 0; } /* | encapsulate the scsi command and */ int scsi_encap(struct cam_sim *sim, union ccb *ccb) { isc_session_t *sp = cam_sim_softc(sim); struct ccb_scsiio *csio = &ccb->csio; struct ccb_hdr *ccb_h = &ccb->ccb_h; pduq_t *pq; scsi_req_t *cmd; debug_called(8); debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0); sp = ccb_h->spriv_ptr0; if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { debug(2, "ccb->sp=%p", ccb_h->spriv_ptr0); sdebug(1, "pdu_alloc failed sc->npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc); while((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) { sdebug(2, "waiting..."); pause("isc_encap", 5*hz); } } cmd = &pq->pdu.ipdu.scsi_req; cmd->opcode = ISCSI_SCSI_CMD; cmd->F = 1; #if 0 // this breaks at least Isilon's iscsi target. /* | map tag option, default is UNTAGGED */ switch(csio->tag_action) { case MSG_SIMPLE_Q_TAG: cmd->attr = iSCSI_TASK_SIMPLE; break; case MSG_HEAD_OF_Q_TAG: cmd->attr = iSCSI_TASK_HOFQ; break; case MSG_ORDERED_Q_TAG: cmd->attr = iSCSI_TASK_ORDER; break; case MSG_ACA_TASK: cmd->attr = iSCSI_TASK_ACA; break; } #else cmd->attr = iSCSI_TASK_SIMPLE; #endif dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun); if((ccb_h->flags & CAM_CDB_POINTER) != 0) { if((ccb_h->flags & CAM_CDB_PHYS) == 0) { if(csio->cdb_len > 16) { sdebug(3, "oversize cdb %d > 16", csio->cdb_len); goto invalid; } } else { sdebug(3, "not phys"); goto invalid; } } if(csio->cdb_len > sizeof(cmd->cdb)) xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb)); memcpy(cmd->cdb, ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes, csio->cdb_len); cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT; cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN; cmd->edtlen = htonl(csio->dxfer_len); pq->ccb = ccb; /* | place it in the out queue */ if(isc_qout(sp, pq) == 0) return 1; invalid: ccb->ccb_h.status = CAM_REQ_INVALID; pdu_free(sp->isc, pq); return 0; } int scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq) { union ccb *ccb = opq->ccb; struct ccb_scsiio *csio = &ccb->csio; pdu_t *opp = &opq->pdu; bhs_t *bhp = &opp->ipdu.bhs; debug_called(8); sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d", pq, opq, bhp->opcode, pq->pdu.ds_len); if(ccb == NULL) { sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq, bhp->opcode, pq->pdu.ds_len); xdebug("%d] ccb == NULL!", sp->sid); return 0; } if(pq->pdu.ds_len != 0) { switch(bhp->opcode) { case ISCSI_SCSI_CMD: { scsi_req_t *cmd = &opp->ipdu.scsi_req; sdebug(5, "itt=0x%x opcode=%x R=%d", ntohl(pq->pdu.ipdu.bhs.itt), pq->pdu.ipdu.bhs.opcode, cmd->R); switch(pq->pdu.ipdu.bhs.opcode) { case ISCSI_READ_DATA: // SCSI Data in { caddr_t bp = NULL; // = mtod(pq->mp, caddr_t); data_in_t *rcmd = &pq->pdu.ipdu.data_in; if(cmd->R) { sdebug(5, "copy to=%p from=%p l1=%d l2=%d mp@%p", csio->data_ptr, bp? mtod(pq->mp, caddr_t): 0, ntohl(cmd->edtlen), pq->pdu.ds_len, pq->mp); if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) { int offset, len = pq->pdu.ds_len; if(pq->mp != NULL) { caddr_t dp; offset = ntohl(rcmd->bo); dp = csio->data_ptr + offset; i_mbufcopy(pq->mp, dp, len); } } else { xdebug("edtlen=%d < ds_len=%d", ntohl(cmd->edtlen), pq->pdu.ds_len); } } if(rcmd->S) { /* | contains also the SCSI Status */ _scsi_done(sp, 0, rcmd->status, opq->ccb, NULL); return 0; } else return 1; } break; } } default: sdebug(3, "opcode=%02x", bhp->opcode); break; } } /* | XXX: error ... */ return 1; }