Current Path : /sys/dev/iscsi/initiator/ |
FreeBSD hs32.drive.ne.jp 9.1-RELEASE FreeBSD 9.1-RELEASE #1: Wed Jan 14 12:18:08 JST 2015 root@hs32.drive.ne.jp:/sys/amd64/compile/hs32 amd64 |
Current File : //sys/dev/iscsi/initiator/isc_sm.c |
/*- * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il> * 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 <sys/cdefs.h> __FBSDID("$FreeBSD: release/9.1.0/sys/dev/iscsi/initiator/isc_sm.c 232405 2012-03-02 21:29:57Z ed $"); #include "opt_iscsi_initiator.h" #include <sys/param.h> #include <sys/kernel.h> #include <sys/conf.h> #include <sys/systm.h> #include <sys/malloc.h> #include <sys/ctype.h> #include <sys/errno.h> #include <sys/sysctl.h> #include <sys/file.h> #include <sys/uio.h> #include <sys/socketvar.h> #include <sys/socket.h> #include <sys/protosw.h> #include <sys/proc.h> #include <sys/ioccom.h> #include <sys/queue.h> #include <sys/kthread.h> #include <sys/syslog.h> #include <sys/mbuf.h> #include <sys/bus.h> #include <sys/sx.h> #include <cam/cam.h> #include <cam/cam_ccb.h> #include <cam/cam_sim.h> #include <cam/cam_xpt_sim.h> #include <cam/cam_periph.h> #include <dev/iscsi/initiator/iscsi.h> #include <dev/iscsi/initiator/iscsivar.h> 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); 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? #if __FreeBSD_version >= 700000 destroy_dev(sp->dev); #endif debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid); #if __FreeBSD_version >= 800000 kproc_exit(0); #else kthread_exit(0); #endif } #if 0 static int isc_dump_options(SYSCTL_HANDLER_ARGS) { int error; isc_session_t *sp; char buf[1024], *bp; sp = (isc_session_t *)arg1; bp = buf; sprintf(bp, "targetname='%s'", sp->opt.targetName); bp += strlen(bp); sprintf(bp, " targetname='%s'", sp->opt.targetAddress); error = SYSCTL_OUT(req, buf, strlen(buf)); return error; } #endif static int isc_dump_stats(SYSCTL_HANDLER_ARGS) { isc_session_t *sp; struct isc_softc *sc; char buf[1024], *bp; int error, n; sp = (isc_session_t *)arg1; sc = sp->isc; bp = buf; n = sizeof(buf); snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent); bp += strlen(bp); n -= strlen(bp); snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d", sp->flags, sc->npdu_alloc, sc->npdu_max); bp += strlen(bp); n -= strlen(bp); snprintf(bp, n, " 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 = SYSCTL_OUT(req, buf, strlen(buf)); return error; } static int isc_sysctl_targetName(SYSCTL_HANDLER_ARGS) { char buf[128], **cp; int error; cp = (char **)arg1; snprintf(buf, sizeof(buf), "%s", *cp); error = SYSCTL_OUT(req, buf, strlen(buf)); return error; } static int isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS) { char buf[128], **cp; int error; cp = (char **)arg1; snprintf(buf, sizeof(buf), "%s", *cp); error = SYSCTL_OUT(req, buf, strlen(buf)); 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, 0, "initiator"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "targetname", CTLTYPE_STRING | CTLFLAG_RD, (void *)&sp->opt.targetName, 0, isc_sysctl_targetName, "A", "target name"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "targeaddress", CTLTYPE_STRING | CTLFLAG_RD, (void *)&sp->opt.targetAddress, 0, isc_sysctl_targetAddress, "A", "target address"); SYSCTL_ADD_PROC(&sp->clist, SYSCTL_CHILDREN(sp->oid), OID_AUTO, "stats", CTLTYPE_STRING | CTLFLAG_RD, (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); #if __FreeBSD_version < 700000 destroy_dev(sp->dev); #endif 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); #if __FreeBSD_version >= 800000 return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); #else return kthread_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid); #endif }