2 * Copyright (c) 2005-2008 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
26 * $FreeBSD: src/sys/dev/iscsi/initiator/isc_sm.c,v 1.3 2008/11/25 07:17:11 scottl Exp $
29 | iSCSI - Session Manager
30 | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $
33 #include "opt_iscsi_initiator.h"
35 #include <sys/param.h>
36 #include <sys/kernel.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/ctype.h>
41 #include <sys/errno.h>
42 #include <sys/sysctl.h>
45 #include <sys/socketvar.h>
46 #include <sys/socket.h>
47 #include <sys/protosw.h>
49 #include <sys/queue.h>
50 #include <sys/kthread.h>
51 #include <sys/syslog.h>
54 #include <sys/eventhandler.h>
56 #include <sys/thread2.h>
57 #include <sys/mutex2.h>
58 #include <sys/mplock2.h>
60 #include <bus/cam/cam.h>
61 #include <bus/cam/cam_ccb.h>
62 #include <bus/cam/cam_sim.h>
63 #include <bus/cam/cam_xpt_sim.h>
64 #include <bus/cam/cam_periph.h>
66 #include <dev/disk/iscsi/initiator/iscsi.h>
67 #include <dev/disk/iscsi/initiator/iscsivar.h>
70 _async(isc_session_t *sp, pduq_t *pq)
76 pdu_free(sp->isc, pq);
80 _reject(isc_session_t *sp, pduq_t *pq)
88 pdu = mtod(pq->mp, pdu_t *);
89 itt = pdu->ipdu.bhs.itt;
90 reject = &pq->pdu.ipdu.reject;
91 sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
92 opq = i_search_hld(sp, itt, 0);
94 iscsi_reject(sp, opq, pq);
96 switch(pq->pdu.ipdu.bhs.opcode) {
97 case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
98 sdebug(2, "ISCSI_LOGOUT_CMD ...");
101 xdebug("%d] we lost something itt=%x",
102 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
105 pdu_free(sp->isc, pq);
109 _r2t(isc_session_t *sp, pduq_t *pq)
114 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
116 iscsi_r2t(sp, opq, pq);
119 r2t_t *r2t = &pq->pdu.ipdu.r2t;
121 xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
122 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
123 ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
125 pdu_free(sp->isc, pq);
129 _scsi_rsp(isc_session_t *sp, pduq_t *pq)
134 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
135 debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
137 iscsi_done(sp, opq, pq);
139 xdebug("%d] we lost something itt=%x",
140 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
141 pdu_free(sp->isc, pq);
145 _read_data(isc_session_t *sp, pduq_t *pq)
150 opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
152 if(scsi_decap(sp, opq, pq) != 1) {
153 i_remove_hld(sp, opq); // done
154 pdu_free(sp->isc, opq);
158 xdebug("%d] we lost something itt=%x",
159 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
160 pdu_free(sp->isc, pq);
164 | the jury is not back with a veredict, user or kernel
167 _nop_out(isc_session_t *sp)
174 sdebug(4, "cws=%d", sp->cws);
177 | only send a nop if window is closed.
179 if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
180 // I guess we ran out of resources
182 nop_out = &pq->pdu.ipdu.nop_out;
183 nop_out->opcode = ISCSI_NOP_OUT;
184 nop_out->itt = htonl(sp->sn.itt);
188 if(isc_qout(sp, pq) != 0) {
190 pdu_free(sp->isc, pq);
196 _nop_in(isc_session_t *sp, pduq_t *pq)
198 pdu_t *pp = &pq->pdu;
199 nop_in_t *nop_in = &pp->ipdu.nop_in;
200 bhs_t *bhs = &pp->ipdu.bhs;
204 sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
205 if(nop_in->itt == -1) {
206 if(pp->ds_len != 0) {
208 | according to RFC 3720 this should be zero
211 xdebug("%d] dslen not zero", sp->sid);
213 if(nop_in->ttt != -1) {
216 | target wants a nop_out
218 bhs->opcode = ISCSI_NOP_OUT;
222 | we are reusing the pdu, so bhs->ttt == nop_in->ttt;
223 | and need to zero out 'Reserved'
226 nop_out = &pp->ipdu.nop_out;
227 nop_out->sn.maxcmd = 0;
228 memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
229 (void)isc_qout(sp, pq); //XXX: should check return?
233 // just making noise?
234 // see 10.9.1: target does not want and answer.
238 if(nop_in->ttt == -1) {
240 | it is an answer to a nop_in from us
242 if(nop_in->itt != -1) {
245 if(sp->flags & ISC_WAIT4PING) {
246 i_nqueue_rsp(sp, pq);
256 pdu_free(sp->isc, pq);
261 i_prepPDU(isc_session_t *sp, pduq_t *pq)
264 pdu_t *pp = &pq->pdu;
265 bhs_t *bhp = &pp->ipdu.bhs;
270 bhp->AHSLength = pp->ahs_len / 4;
277 #if BYTE_ORDER == LITTLE_ENDIAN
278 bhp->DSLength = ((n & 0x00ff0000) >> 16)
280 | ((n & 0x000000ff) << 16);
293 len -= sizeof(bhs_t);
294 if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
295 xdebug("%d] pdu len=%zd > %d",
296 sp->sid, len, sp->opt.maxBurstLength);
297 // XXX: when this happens it used to hang ...
304 isc_qout(isc_session_t *sp, pduq_t *pq)
310 if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
313 if(pq->pdu.ipdu.bhs.I)
314 i_nqueue_isnd(sp, pq);
316 if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
317 i_nqueue_wsnd(sp, pq);
319 i_nqueue_csnd(sp, pq);
321 sdebug(5, "enqued: pq=%p", pq);
323 iscsi_lock_ex(&sp->io_mtx);
324 sp->flags |= ISC_OQNOTEMPTY;
325 if(sp->flags & ISC_OWAITING)
327 iscsi_unlock_ex(&sp->io_mtx);
332 | called when a fullPhase is restarted
335 ism_restart(isc_session_t *sp)
339 sdebug(2, "restart ...");
340 lastcmd = iscsi_requeue(sp);
342 if(lastcmd != sp->sn.cmd) {
343 sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
344 sp->sn.cmd = lastcmd;
347 iscsi_lock_ex(&sp->io_mtx);
348 if(sp->flags & ISC_OWAITING) {
351 iscsi_unlock_ex(&sp->io_mtx);
353 sdebug(2, "restarted lastcmd=0x%x", lastcmd);
357 ism_fullfeature(struct cdev *dev, int flag)
359 isc_session_t *sp = (isc_session_t *)dev->si_drv2;
362 sdebug(2, "flag=%d", flag);
367 sp->flags &= ~ISC_FFPHASE;
370 error = ic_fullfeature(dev);
380 ism_recv(isc_session_t *sp, pduq_t *pq)
387 bhs = &pq->pdu.ipdu.bhs;
388 statSN = ntohl(bhs->OpcodeSpecificFields[1]);
392 | this code is only for debugging.
396 if((sp->flags & ISC_STALLED) == 0) {
397 sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
398 sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
399 sp->flags |= ISC_STALLED;
401 if(sp->flags & ISC_STALLED) {
402 sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
403 sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
404 sp->flags &= ~ISC_STALLED;
411 if(sp->sn.expCmd != sn->cmd) {
412 sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
413 sn->expCmd, sn->cmd);
416 sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
417 bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
419 switch(bhs->opcode) {
420 case ISCSI_READ_DATA: {
421 data_in_t *cmd = &pq->pdu.ipdu.data_in;
428 if(statSN > (sp->sn.stat + 1)) {
429 sdebug(1, "we lost some rec=0x%x exp=0x%x",
430 statSN, sp->sn.stat);
431 // XXX: must do some error recovery here.
433 sp->sn.stat = statSN;
436 switch(bhs->opcode) {
437 case ISCSI_LOGIN_RSP:
439 case ISCSI_LOGOUT_RSP:
440 i_nqueue_rsp(sp, pq);
442 sdebug(3, "wakeup rsp");
445 case ISCSI_NOP_IN: _nop_in(sp, pq); break;
446 case ISCSI_SCSI_RSP: _scsi_rsp(sp, pq); break;
447 case ISCSI_READ_DATA: _read_data(sp, pq); break;
448 case ISCSI_R2T: _r2t(sp, pq); break;
449 case ISCSI_REJECT: _reject(sp, pq); break;
450 case ISCSI_ASYNC: _async(sp, pq); break;
454 sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
455 bhs->opcode, ntohl(bhs->itt));
461 | go through the out queues looking for work
462 | if either nothing to do, or window is closed
466 proc_out(isc_session_t *sp)
476 while(sp->flags & ISC_LINK_UP) {
480 | check if there is outstanding work in:
481 | 1- the Immediate queue
483 | 3- the cmd queue, only if the command window allows it.
485 which = BIT(0) | BIT(1);
486 if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
489 sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd);
491 if((pq = i_dqueue_snd(sp, which)) == NULL)
493 sdebug(4, "pq=%p", pq);
497 switch(bhs->opcode) {
500 bhs->itt = htonl(sn->itt);
502 case ISCSI_LOGIN_CMD:
504 case ISCSI_LOGOUT_CMD:
508 bhs->CmdSN = htonl(sn->cmd);
512 case ISCSI_WRITE_DATA:
513 bhs->ExpStSN = htonl(sn->stat);
517 // XXX: can this happen?
518 xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
520 sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
524 sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
526 sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
529 i_nqueue_hld(sp, pq);
531 if((error = isc_sendPDU(sp, pq)) == 0) {
534 pdu_free(sp->isc, pq);
537 xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
538 error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt));
540 i_remove_hld(sp, pq);
543 sp->flags &= ~ISC_LINK_UP;
547 i_rqueue_pdu(sp, pq);
552 xdebug("back to cam");
553 pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error?
554 XPT_DONE(sp->isc, pq->ccb);
555 pdu_free(sp->isc, pq);
558 xdebug("we lost it!");
566 | survives link breakdowns.
571 isc_session_t *sp = (isc_session_t *)vp;
577 sdebug(3, "started sp->flags=%x", sp->flags);
579 if((sp->flags & ISC_HOLD) == 0) {
580 error = proc_out(sp);
582 sdebug(3, "error=%d", error);
585 iscsi_lock_ex(&sp->io_mtx);
586 if((sp->flags & ISC_LINK_UP) == 0) {
590 if((sp->flags & (ISC_OQNOTEMPTY | ISC_SM_RUN)) == ISC_SM_RUN) {
591 sp->flags |= ISC_OWAITING;
592 if(issleep(&sp->flags, &sp->io_mtx, 0, "iscproc", hz*30) == EWOULDBLOCK) {
593 if(sp->flags & ISC_CON_RUNNING)
596 sp->flags &= ~ISC_OWAITING;
598 sp->flags &= ~ISC_OQNOTEMPTY;
599 iscsi_unlock_ex(&sp->io_mtx);
600 } while(sp->flags & ISC_SM_RUN);
602 sp->flags &= ~ISC_SM_RUNNING;
603 sdebug(3, "dropped ISC_SM_RUNNING");
607 debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid);
614 isc_dump_options(SYSCTL_HANDLER_ARGS)
620 sp = (isc_session_t *)arg1;
622 ksprintf(bp, "targetname='%s'", sp->opt.targetName);
624 ksprintf(bp, " targetname='%s'", sp->opt.targetAddress);
625 error = SYSCTL_OUT(req, buf, strlen(buf));
631 isc_dump_stats(SYSCTL_HANDLER_ARGS)
634 struct isc_softc *sc;
638 sp = (isc_session_t *)arg1;
643 ksnprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
646 ksnprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
647 sp->flags, sc->npdu_alloc, sc->npdu_max);
650 ksnprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
651 sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
652 error = SYSCTL_OUT(req, buf, strlen(buf));
657 isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
663 ksnprintf(buf, sizeof(buf), "%s", *cp);
664 error = SYSCTL_OUT(req, buf, strlen(buf));
668 isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
674 ksnprintf(buf, sizeof(buf), "%s", *cp);
675 error = SYSCTL_OUT(req, buf, strlen(buf));
679 isc_add_sysctls(isc_session_t *sp)
682 sdebug(6, "sid=%d %s", sp->sid, sp->dev->si_name);
684 sysctl_ctx_init(&sp->clist);
685 sp->oid = SYSCTL_ADD_NODE(&sp->clist,
686 SYSCTL_CHILDREN(sp->isc->oid),
688 sp->dev->si_name+5, // iscsi0
692 SYSCTL_ADD_PROC(&sp->clist,
693 SYSCTL_CHILDREN(sp->oid),
697 (void *)&sp->opt.targetName, 0,
698 isc_sysctl_targetName, "A", "target name");
700 SYSCTL_ADD_PROC(&sp->clist,
701 SYSCTL_CHILDREN(sp->oid),
705 (void *)&sp->opt.targetAddress, 0,
706 isc_sysctl_targetAddress, "A", "target address");
708 SYSCTL_ADD_PROC(&sp->clist,
709 SYSCTL_CHILDREN(sp->oid),
714 isc_dump_stats, "A", "statistics");
716 SYSCTL_ADD_INT(&sp->clist,
717 SYSCTL_CHILDREN(sp->oid),
721 &sp->douio, 0, "enable uio on read");
725 ism_stop(isc_session_t *sp)
727 struct isc_softc *sc = sp->isc;
731 sdebug(2, "terminating");
733 | first stop the receiver
735 isc_stop_receiver(sp);
738 | now stop the xmitter
740 sp->flags &= ~ISC_SM_RUN;
741 while(sp->flags & ISC_SM_RUNNING) {
742 sdebug(2, "waiting for ism to stop");
744 tsleep(sp, 0, "-", hz);
746 sdebug(2, "ism stopped");
747 sp->flags &= ~ISC_FFPHASE;
751 (void)i_pdu_flush(sp);
753 ic_lost_target(sp, sp->sid);
755 lockmgr(&sc->lock, LK_EXCLUSIVE);
756 TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
758 lockmgr(&sc->lock, LK_RELEASE);
766 mtx_uninit(&sp->rsp_mtx);
767 mtx_uninit(&sp->rsv_mtx);
768 mtx_uninit(&sp->hld_mtx);
769 mtx_uninit(&sp->snd_mtx);
770 mtx_uninit(&sp->io_mtx);
773 sc->sessions[sp->sid] = NULL;
775 if(sysctl_ctx_free(&sp->clist))
776 xdebug("sysctl_ctx_free failed");
782 ism_start(isc_session_t *sp)
786 | now is a good time to do some initialization
788 TAILQ_INIT(&sp->rsp);
789 TAILQ_INIT(&sp->rsv);
790 TAILQ_INIT(&sp->csnd);
791 TAILQ_INIT(&sp->isnd);
792 TAILQ_INIT(&sp->wsnd);
793 TAILQ_INIT(&sp->hld);
795 mtx_init(&sp->rsv_mtx, "irsv");
796 mtx_init(&sp->rsp_mtx, "irsp");
797 mtx_init(&sp->snd_mtx, "isnd");
798 mtx_init(&sp->hld_mtx, "ihld");
800 mtx_init(&sp->io_mtx, "isio");
804 sp->flags |= ISC_SM_RUN;
805 sp->flags |= ISC_SM_RUNNING;
807 debug(4, "starting ism_proc: sp->sid=%d", sp->sid);
808 return kthread_create(ism_proc, sp, &sp->stp, "ism_%d", sp->sid);