2 * Copyright (c) 2005-2007 Daniel Braniss <danny@cs.huji.ac.il>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 | iSCSI - Session Manager
29 | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $
32 #include <sys/cdefs.h>
33 __FBSDID("$FreeBSD$");
35 #include "opt_iscsi_initiator.h"
37 #include <sys/param.h>
38 #include <sys/kernel.h>
40 #include <sys/systm.h>
41 #include <sys/malloc.h>
42 #include <sys/ctype.h>
43 #include <sys/errno.h>
44 #include <sys/sysctl.h>
47 #include <sys/socketvar.h>
48 #include <sys/socket.h>
49 #include <sys/protosw.h>
51 #include <sys/ioccom.h>
52 #include <sys/queue.h>
53 #include <sys/kthread.h>
54 #include <sys/syslog.h>
59 #include <cam/cam_ccb.h>
60 #include <cam/cam_sim.h>
61 #include <cam/cam_xpt_sim.h>
62 #include <cam/cam_periph.h>
64 #include <dev/iscsi/initiator/iscsi.h>
65 #include <dev/iscsi/initiator/iscsivar.h>
68 _async(isc_session_t *sp, pduq_t *pq)
74 pdu_free(sp->isc, pq);
78 _reject(isc_session_t *sp, pduq_t *pq)
86 pdu = mtod(pq->mp, pdu_t *);
87 itt = pdu->ipdu.bhs.itt;
88 reject = &pq->pdu.ipdu.reject;
89 sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
90 opq = i_search_hld(sp, itt, 0);
92 iscsi_reject(sp, opq, pq);
94 switch(pq->pdu.ipdu.bhs.opcode) {
95 case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
96 sdebug(2, "ISCSI_LOGOUT_CMD ...");
99 xdebug("%d] we lost something itt=%x",
100 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
103 pdu_free(sp->isc, pq);
107 _r2t(isc_session_t *sp, pduq_t *pq)
112 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
114 iscsi_r2t(sp, opq, pq);
117 r2t_t *r2t = &pq->pdu.ipdu.r2t;
119 xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
120 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
121 ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
123 pdu_free(sp->isc, pq);
127 _scsi_rsp(isc_session_t *sp, pduq_t *pq)
132 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
133 debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
135 iscsi_done(sp, opq, pq);
137 xdebug("%d] we lost something itt=%x",
138 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
139 pdu_free(sp->isc, pq);
143 _read_data(isc_session_t *sp, pduq_t *pq)
148 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
150 if(scsi_decap(sp, opq, pq) != 1) {
151 i_remove_hld(sp, opq); // done
152 pdu_free(sp->isc, opq);
156 xdebug("%d] we lost something itt=%x",
157 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
158 pdu_free(sp->isc, pq);
162 | the jury is not back with a veredict, user or kernel
165 _nop_out(isc_session_t *sp)
172 sdebug(4, "cws=%d", sp->cws);
175 | only send a nop if window is closed.
177 if((pq = pdu_alloc(sp->isc, 0)) == NULL)
178 // I guess we ran out of resources
180 nop_out = &pq->pdu.ipdu.nop_out;
181 nop_out->opcode = ISCSI_NOP_OUT;
182 nop_out->itt = htonl(sp->sn.itt);
186 if(isc_qout(sp, pq) != 0) {
188 pdu_free(sp->isc, pq);
194 _nop_in(isc_session_t *sp, pduq_t *pq)
196 pdu_t *pp = &pq->pdu;
197 nop_in_t *nop_in = &pp->ipdu.nop_in;
198 bhs_t *bhs = &pp->ipdu.bhs;
202 sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
203 if(nop_in->itt == -1) {
204 if(pp->ds_len != 0) {
206 | according to RFC 3720 this should be zero
209 xdebug("%d] dslen not zero", sp->sid);
211 if(nop_in->ttt != -1) {
214 | target wants a nop_out
216 bhs->opcode = ISCSI_NOP_OUT;
220 | we are reusing the pdu, so bhs->ttt == nop_in->ttt;
221 | and need to zero out 'Reserved'
224 nop_out = &pp->ipdu.nop_out;
225 nop_out->sn.maxcmd = 0;
226 memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
228 (void)isc_qout(sp, pq); //XXX: should check return?
232 // just making noise?
233 // see 10.9.1: target does not want and answer.
237 if(nop_in->ttt == -1) {
239 | it is an answer to a nop_in from us
241 if(nop_in->itt != -1) {
244 if(sp->flags & ISC_WAIT4PING) {
245 i_nqueue_rsp(sp, pq);
255 pdu_free(sp->isc, pq);
260 i_prepPDU(isc_session_t *sp, pduq_t *pq)
263 pdu_t *pp = &pq->pdu;
264 bhs_t *bhp = &pp->ipdu.bhs;
269 bhp->AHSLength = pp->ahs_len / 4;
276 #if BYTE_ORDER == LITTLE_ENDIAN
277 bhp->DSLength = ((n & 0x00ff0000) >> 16)
279 | ((n & 0x000000ff) << 16);
292 len -= sizeof(bhs_t);
293 if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
294 xdebug("%d] pdu len=%zd > %d",
295 sp->sid, len, sp->opt.maxBurstLength);
296 // XXX: when this happens it used to hang ...
303 isc_qout(isc_session_t *sp, pduq_t *pq)
309 if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
312 if(pq->pdu.ipdu.bhs.I)
313 i_nqueue_isnd(sp, pq);
315 if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
316 i_nqueue_wsnd(sp, pq);
318 i_nqueue_csnd(sp, pq);
320 sdebug(5, "enqued: pq=%p", pq);
322 if(sp->flags & ISC_OWAITING) {
323 mtx_lock(&sp->io_mtx); // XXX
325 mtx_unlock(&sp->io_mtx); // XXX
333 | called when a fullPhase is restarted
336 ism_restart(isc_session_t *sp)
340 sdebug(2, "restart ...");
341 sp->flags |= ISC_SM_HOLD;
342 lastcmd = iscsi_requeue(sp);
344 if(lastcmd != sp->sn.cmd) {
345 sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
346 sp->sn.cmd = lastcmd;
349 sp->flags &= ~ISC_SM_HOLD;
354 ism_fullfeature(struct cdev *dev, int flag)
356 isc_session_t *sp = (isc_session_t *)dev->si_drv2;
359 sdebug(2, "flag=%d", flag);
364 sp->flags &= ~ISC_FFPHASE;
367 error = ic_fullfeature(dev);
370 error = ism_restart(sp);
377 ism_recv(isc_session_t *sp, pduq_t *pq)
384 bhs = &pq->pdu.ipdu.bhs;
385 statSN = ntohl(bhs->OpcodeSpecificFields[1]);
389 | this code is only for debugging.
393 if((sp->flags & ISC_STALLED) == 0) {
394 sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
395 sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
396 sp->flags |= ISC_STALLED;
398 if(sp->flags & ISC_STALLED) {
399 sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
400 sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
401 sp->flags &= ~ISC_STALLED;;
408 if(sp->sn.expCmd != sn->cmd) {
409 sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
410 sn->expCmd, sn->cmd);
413 sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
414 bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
416 switch(bhs->opcode) {
417 case ISCSI_READ_DATA: {
418 data_in_t *cmd = &pq->pdu.ipdu.data_in;
425 if(statSN > (sp->sn.stat + 1)) {
426 sdebug(1, "we lost some rec=0x%x exp=0x%x",
427 statSN, sp->sn.stat);
428 // XXX: must do some error recovery here.
430 sp->sn.stat = statSN;
433 switch(bhs->opcode) {
434 case ISCSI_LOGIN_RSP:
436 case ISCSI_LOGOUT_RSP:
437 i_nqueue_rsp(sp, pq);
439 sdebug(3, "wakeup rsp");
442 case ISCSI_NOP_IN: _nop_in(sp, pq); break;
443 case ISCSI_SCSI_RSP: _scsi_rsp(sp, pq); break;
444 case ISCSI_READ_DATA: _read_data(sp, pq); break;
445 case ISCSI_R2T: _r2t(sp, pq); break;
446 case ISCSI_REJECT: _reject(sp, pq); break;
447 case ISCSI_ASYNC: _async(sp, pq); break;
451 sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
452 bhs->opcode, ntohl(bhs->itt));
458 proc_out(isc_session_t *sp)
462 int error, ndone = 0;
472 | check if there is outstanding work in:
473 | 1- the Inmediate queue
475 | 3- the cmd queue, only if the command window allows it.
477 which = BIT(0) | BIT(1);
478 if(SNA_GT(sn->cmd, sn->maxCmd) == 0)
481 if((pq = i_dqueue_snd(sp, which)) == NULL)
486 switch(bhs->opcode) {
489 bhs->itt = htonl(sn->itt);
491 case ISCSI_LOGIN_CMD:
493 case ISCSI_LOGOUT_CMD:
497 bhs->CmdSN = htonl(sn->cmd);
501 case ISCSI_WRITE_DATA:
502 bhs->ExpStSN = htonl(sn->stat);
506 // XXX: can this happen?
507 xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
509 sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
513 sdebug(5, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
515 sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
518 i_nqueue_hld(sp, pq);
520 if((error = isc_sendPDU(sp, pq)) == 0)
523 xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
524 error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt));
526 // XXX: better do some error recovery ...
531 i_remove_hld(sp, pq);
532 pq->ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; // some better error?
537 // how do we pass back an error?
541 if(pq->ccb == NULL || error)
542 pdu_free(sp->isc, pq);
548 | survives link breakdowns.
553 isc_session_t *sp = (isc_session_t *)vp;
557 sdebug(3, "started");
559 sp->flags |= ISC_SM_RUNNING;
561 if(sp->flags & ISC_SM_HOLD)
564 odone = proc_out(sp);
565 sdebug(7, "odone=%d", odone);
567 mtx_lock(&sp->io_mtx);
569 sp->flags |= ISC_OWAITING;
571 if((msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK)
572 && (sp->flags & ISC_CON_RUNNING))
575 sp->flags &= ~ISC_OWAITING;
577 mtx_unlock(&sp->io_mtx);
579 } while(sp->flags & ISC_SM_RUN);
581 sp->flags &= ~ISC_SM_RUNNING;
583 #if __FreeBSD_version >= 700000
584 destroy_dev(sp->dev);
587 sdebug(3, "terminated");
595 isc_dump_options(SYSCTL_HANDLER_ARGS)
601 sp = (isc_session_t *)arg1;
603 sprintf(bp, "targetname='%s'", sp->opt.targetName);
605 sprintf(bp, " targetname='%s'", sp->opt.targetAddress);
606 error = SYSCTL_OUT(req, buf, strlen(buf));
612 isc_dump_stats(SYSCTL_HANDLER_ARGS)
615 struct isc_softc *sc;
619 sp = (isc_session_t *)arg1;
624 snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
627 snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
628 sp->flags, sc->npdu_alloc, sc->npdu_max);
631 snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
632 sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
633 error = SYSCTL_OUT(req, buf, strlen(buf));
638 isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
644 snprintf(buf, sizeof(buf), "%s", *cp);
645 error = SYSCTL_OUT(req, buf, strlen(buf));
650 isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
656 snprintf(buf, sizeof(buf), "%s", *cp);
657 error = SYSCTL_OUT(req, buf, strlen(buf));
662 isc_add_sysctls(isc_session_t *sp)
665 sdebug(6, "sid=%d %s", sp->sid, sp->dev->si_name);
667 sysctl_ctx_init(&sp->clist);
668 sp->oid = SYSCTL_ADD_NODE(&sp->clist,
669 SYSCTL_CHILDREN(sp->isc->oid),
671 sp->dev->si_name+5, // iscsi0
675 SYSCTL_ADD_PROC(&sp->clist,
676 SYSCTL_CHILDREN(sp->oid),
680 (void *)&sp->opt.targetName, 0,
681 isc_sysctl_targetName, "A", "target name");
683 SYSCTL_ADD_PROC(&sp->clist,
684 SYSCTL_CHILDREN(sp->oid),
688 (void *)&sp->opt.targetAddress, 0,
689 isc_sysctl_targetAddress, "A", "target address");
691 SYSCTL_ADD_PROC(&sp->clist,
692 SYSCTL_CHILDREN(sp->oid),
697 isc_dump_stats, "A", "statistics");
701 ism_stop(isc_session_t *sp)
703 struct isc_softc *sc = sp->isc;
707 sdebug(2, "terminating");
709 | first stop the receiver
711 isc_stop_receiver(sp);
713 | now stop the xmitter
716 sp->flags &= ~ISC_SM_RUN;
717 while(n-- && (sp->flags & ISC_SM_RUNNING)) {
718 sdebug(2, "n=%d", n);
720 tsleep(sp, PRIBIO, "-", 5*hz);
722 sdebug(2, "final n=%d", n);
723 sp->flags &= ~ISC_FFPHASE;
727 (void)i_pdu_flush(sp);
729 ic_lost_target(sp, sp->sid);
732 TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
734 mtx_unlock(&sc->mtx);
736 #if __FreeBSD_version < 700000
737 destroy_dev(sp->dev);
740 mtx_destroy(&sp->rsp_mtx);
741 mtx_destroy(&sp->rsv_mtx);
742 mtx_destroy(&sp->hld_mtx);
743 mtx_destroy(&sp->snd_mtx);
744 mtx_destroy(&sp->io_mtx);
747 sc->sessions[sp->sid] = NULL;
749 if(sysctl_ctx_free(&sp->clist))
750 xdebug("sysctl_ctx_free failed");
756 ism_start(isc_session_t *sp)
760 | now is a good time to do some initialization
762 TAILQ_INIT(&sp->rsp);
763 TAILQ_INIT(&sp->rsv);
764 TAILQ_INIT(&sp->csnd);
765 TAILQ_INIT(&sp->isnd);
766 TAILQ_INIT(&sp->wsnd);
767 TAILQ_INIT(&sp->hld);
769 mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF);
770 mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF);
771 mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF);
772 mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF);
774 mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_SPIN);
775 mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_SPIN);
776 mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_SPIN);
777 mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_SPIN);
779 mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF);
783 sp->flags |= ISC_SM_RUN;
785 return kproc_create(ism_proc, sp, &sp->stp, 0, 0, "ism_%d", sp->sid);