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/ioccom.h>
50 #include <sys/queue.h>
51 #include <sys/kthread.h>
52 #include <sys/syslog.h>
55 #include <sys/eventhandler.h>
56 #include <sys/mutex.h>
57 #include <sys/mutex2.h>
59 #include <bus/cam/cam.h>
60 #include <bus/cam/cam_ccb.h>
61 #include <bus/cam/cam_sim.h>
62 #include <bus/cam/cam_xpt_sim.h>
63 #include <bus/cam/cam_periph.h>
65 #include <dev/disk/iscsi/initiator/iscsi.h>
66 #include <dev/disk/iscsi/initiator/iscsivar.h>
69 _async(isc_session_t
*sp
, pduq_t
*pq
)
75 pdu_free(sp
->isc
, pq
);
79 _reject(isc_session_t
*sp
, pduq_t
*pq
)
87 pdu
= mtod(pq
->mp
, pdu_t
*);
88 itt
= pdu
->ipdu
.bhs
.itt
;
89 reject
= &pq
->pdu
.ipdu
.reject
;
90 sdebug(2, "itt=%x reason=0x%x", ntohl(itt
), reject
->reason
);
91 opq
= i_search_hld(sp
, itt
, 0);
93 iscsi_reject(sp
, opq
, pq
);
95 switch(pq
->pdu
.ipdu
.bhs
.opcode
) {
96 case ISCSI_LOGOUT_CMD
: // XXX: wasabi does this - can't figure out why
97 sdebug(2, "ISCSI_LOGOUT_CMD ...");
100 xdebug("%d] we lost something itt=%x",
101 sp
->sid
, ntohl(pq
->pdu
.ipdu
.bhs
.itt
));
104 pdu_free(sp
->isc
, pq
);
108 _r2t(isc_session_t
*sp
, pduq_t
*pq
)
113 opq
= i_search_hld(sp
, pq
->pdu
.ipdu
.bhs
.itt
, 1);
115 iscsi_r2t(sp
, opq
, pq
);
118 r2t_t
*r2t
= &pq
->pdu
.ipdu
.r2t
;
120 xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
121 sp
->sid
, ntohl(pq
->pdu
.ipdu
.bhs
.itt
),
122 ntohl(r2t
->r2tSN
), ntohl(r2t
->bo
), ntohl(r2t
->ddtl
));
124 pdu_free(sp
->isc
, pq
);
128 _scsi_rsp(isc_session_t
*sp
, pduq_t
*pq
)
133 opq
= i_search_hld(sp
, pq
->pdu
.ipdu
.bhs
.itt
, 0);
134 debug(5, "itt=%x pq=%p opq=%p", ntohl(pq
->pdu
.ipdu
.bhs
.itt
), pq
, opq
);
136 iscsi_done(sp
, opq
, pq
);
138 xdebug("%d] we lost something itt=%x",
139 sp
->sid
, ntohl(pq
->pdu
.ipdu
.bhs
.itt
));
140 pdu_free(sp
->isc
, pq
);
144 _read_data(isc_session_t
*sp
, pduq_t
*pq
)
149 opq
= i_search_hld(sp
, pq
->pdu
.ipdu
.bhs
.itt
, 1);
151 if(scsi_decap(sp
, opq
, pq
) != 1) {
152 i_remove_hld(sp
, opq
); // done
153 pdu_free(sp
->isc
, opq
);
157 xdebug("%d] we lost something itt=%x",
158 sp
->sid
, ntohl(pq
->pdu
.ipdu
.bhs
.itt
));
159 pdu_free(sp
->isc
, pq
);
163 | the jury is not back with a veredict, user or kernel
166 _nop_out(isc_session_t
*sp
)
173 sdebug(4, "cws=%d", sp
->cws
);
176 | only send a nop if window is closed.
178 if((pq
= pdu_alloc(sp
->isc
, M_NOWAIT
)) == NULL
)
179 // I guess we ran out of resources
181 nop_out
= &pq
->pdu
.ipdu
.nop_out
;
182 nop_out
->opcode
= ISCSI_NOP_OUT
;
183 nop_out
->itt
= htonl(sp
->sn
.itt
);
187 if(isc_qout(sp
, pq
) != 0) {
189 pdu_free(sp
->isc
, pq
);
195 _nop_in(isc_session_t
*sp
, pduq_t
*pq
)
197 pdu_t
*pp
= &pq
->pdu
;
198 nop_in_t
*nop_in
= &pp
->ipdu
.nop_in
;
199 bhs_t
*bhs
= &pp
->ipdu
.bhs
;
203 sdebug(5, "itt=%x ttt=%x", htonl(nop_in
->itt
), htonl(nop_in
->ttt
));
204 if(nop_in
->itt
== -1) {
205 if(pp
->ds_len
!= 0) {
207 | according to RFC 3720 this should be zero
210 xdebug("%d] dslen not zero", sp
->sid
);
212 if(nop_in
->ttt
!= -1) {
215 | target wants a nop_out
217 bhs
->opcode
= ISCSI_NOP_OUT
;
221 | we are reusing the pdu, so bhs->ttt == nop_in->ttt;
222 | and need to zero out 'Reserved'
225 nop_out
= &pp
->ipdu
.nop_out
;
226 nop_out
->sn
.maxcmd
= 0;
227 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 iscsi_lock_ex(&sp
->io_mtx
);
323 sp
->flags
|= ISC_OQNOTEMPTY
;
324 if(sp
->flags
& ISC_OWAITING
)
326 iscsi_unlock_ex(&sp
->io_mtx
);
331 | called when a fullPhase is restarted
334 ism_restart(isc_session_t
*sp
)
338 sdebug(2, "restart ...");
339 lastcmd
= iscsi_requeue(sp
);
341 if(lastcmd
!= sp
->sn
.cmd
) {
342 sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd
, sp
->sn
.cmd
);
343 sp
->sn
.cmd
= lastcmd
;
346 iscsi_lock_ex(&sp
->io_mtx
);
347 if(sp
->flags
& ISC_OWAITING
) {
350 iscsi_unlock_ex(&sp
->io_mtx
);
352 sdebug(2, "restarted lastcmd=0x%x", lastcmd
);
356 ism_fullfeature(struct cdev
*dev
, int flag
)
358 isc_session_t
*sp
= (isc_session_t
*)dev
->si_drv2
;
361 sdebug(2, "flag=%d", flag
);
366 sp
->flags
&= ~ISC_FFPHASE
;
369 error
= ic_fullfeature(dev
);
379 ism_recv(isc_session_t
*sp
, pduq_t
*pq
)
386 bhs
= &pq
->pdu
.ipdu
.bhs
;
387 statSN
= ntohl(bhs
->OpcodeSpecificFields
[1]);
391 | this code is only for debugging.
395 if((sp
->flags
& ISC_STALLED
) == 0) {
396 sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
397 sn
->maxCmd
, sn
->expCmd
, bhs
->opcode
, sn
->cmd
, sp
->cws
);
398 sp
->flags
|= ISC_STALLED
;
400 if(sp
->flags
& ISC_STALLED
) {
401 sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
402 sn
->maxCmd
, sn
->expCmd
, bhs
->opcode
, sn
->cmd
, sp
->cws
);
403 sp
->flags
&= ~ISC_STALLED
;;
410 if(sp
->sn
.expCmd
!= sn
->cmd
) {
411 sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
412 sn
->expCmd
, sn
->cmd
);
415 sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
416 bhs
->opcode
, ntohl(bhs
->itt
), statSN
, sp
->sn
.maxCmd
);
418 switch(bhs
->opcode
) {
419 case ISCSI_READ_DATA
: {
420 data_in_t
*cmd
= &pq
->pdu
.ipdu
.data_in
;
427 if(statSN
> (sp
->sn
.stat
+ 1)) {
428 sdebug(1, "we lost some rec=0x%x exp=0x%x",
429 statSN
, sp
->sn
.stat
);
430 // XXX: must do some error recovery here.
432 sp
->sn
.stat
= statSN
;
435 switch(bhs
->opcode
) {
436 case ISCSI_LOGIN_RSP
:
438 case ISCSI_LOGOUT_RSP
:
439 i_nqueue_rsp(sp
, pq
);
441 sdebug(3, "wakeup rsp");
444 case ISCSI_NOP_IN
: _nop_in(sp
, pq
); break;
445 case ISCSI_SCSI_RSP
: _scsi_rsp(sp
, pq
); break;
446 case ISCSI_READ_DATA
: _read_data(sp
, pq
); break;
447 case ISCSI_R2T
: _r2t(sp
, pq
); break;
448 case ISCSI_REJECT
: _reject(sp
, pq
); break;
449 case ISCSI_ASYNC
: _async(sp
, pq
); break;
453 sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
454 bhs
->opcode
, ntohl(bhs
->itt
));
460 | go through the out queues looking for work
461 | if either nothing to do, or window is closed
465 proc_out(isc_session_t
*sp
)
475 while(sp
->flags
& ISC_LINK_UP
) {
479 | check if there is outstanding work in:
480 | 1- the Immediate queue
482 | 3- the cmd queue, only if the command window allows it.
484 which
= BIT(0) | BIT(1);
485 if(SNA_GT(sn
->cmd
, sn
->maxCmd
) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
488 sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which
, sn
->maxCmd
, sn
->cmd
);
490 if((pq
= i_dqueue_snd(sp
, which
)) == NULL
)
492 sdebug(4, "pq=%p", pq
);
496 switch(bhs
->opcode
) {
499 bhs
->itt
= htonl(sn
->itt
);
501 case ISCSI_LOGIN_CMD
:
503 case ISCSI_LOGOUT_CMD
:
507 bhs
->CmdSN
= htonl(sn
->cmd
);
511 case ISCSI_WRITE_DATA
:
512 bhs
->ExpStSN
= htonl(sn
->stat
);
516 // XXX: can this happen?
517 xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
519 sn
->cmd
, sn
->expCmd
, sn
->maxCmd
, sn
->expStat
, sn
->itt
);
523 sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
525 sn
->cmd
, sn
->expCmd
, sn
->maxCmd
, sn
->expStat
, sn
->itt
);
528 i_nqueue_hld(sp
, pq
);
530 if((error
= isc_sendPDU(sp
, pq
)) == 0) {
533 pdu_free(sp
->isc
, pq
);
536 xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
537 error
, ndone
, bhs
->opcode
, pq
->ccb
, ntohl(bhs
->itt
));
539 i_remove_hld(sp
, pq
);
542 sp
->flags
&= ~ISC_LINK_UP
;
546 i_rqueue_pdu(sp
, pq
);
551 xdebug("back to cam");
552 pq
->ccb
->ccb_h
.status
|= CAM_REQUEUE_REQ
; // some better error?
553 XPT_DONE(sp
->isc
, pq
->ccb
);
554 pdu_free(sp
->isc
, pq
);
557 xdebug("we lost it!");
565 | survives link breakdowns.
570 isc_session_t
*sp
= (isc_session_t
*)vp
;
575 sdebug(3, "started sp->flags=%x", sp
->flags
);
577 if((sp
->flags
& ISC_HOLD
) == 0) {
578 error
= proc_out(sp
);
580 sdebug(3, "error=%d", error
);
583 iscsi_lock_ex(&sp
->io_mtx
);
584 if((sp
->flags
& ISC_LINK_UP
) == 0) {
588 if((sp
->flags
& (ISC_OQNOTEMPTY
| ISC_SM_RUN
)) == ISC_SM_RUN
) {
589 sp
->flags
|= ISC_OWAITING
;
590 if(issleep(&sp
->flags
, &sp
->io_mtx
, 0, "iscproc", hz
*30) == EWOULDBLOCK
) {
591 if(sp
->flags
& ISC_CON_RUNNING
)
594 sp
->flags
&= ~ISC_OWAITING
;
596 sp
->flags
&= ~ISC_OQNOTEMPTY
;
597 iscsi_unlock_ex(&sp
->io_mtx
);
598 } while(sp
->flags
& ISC_SM_RUN
);
600 sp
->flags
&= ~ISC_SM_RUNNING
;
601 sdebug(3, "dropped ISC_SM_RUNNING");
605 debug(3, "terminated sp=%p sp->sid=%d", sp
, sp
->sid
);
612 isc_dump_options(SYSCTL_HANDLER_ARGS
)
618 sp
= (isc_session_t
*)arg1
;
620 ksprintf(bp
, "targetname='%s'", sp
->opt
.targetName
);
622 ksprintf(bp
, " targetname='%s'", sp
->opt
.targetAddress
);
623 error
= SYSCTL_OUT(req
, buf
, strlen(buf
));
629 isc_dump_stats(SYSCTL_HANDLER_ARGS
)
632 struct isc_softc
*sc
;
636 sp
= (isc_session_t
*)arg1
;
641 ksnprintf(bp
, n
, "recv=%d sent=%d", sp
->stats
.nrecv
, sp
->stats
.nsent
);
644 ksnprintf(bp
, n
, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
645 sp
->flags
, sc
->npdu_alloc
, sc
->npdu_max
);
648 ksnprintf(bp
, n
, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
649 sp
->cws
, sp
->sn
.cmd
, sp
->sn
.expCmd
, sp
->sn
.maxCmd
, sp
->sn
.stat
, sp
->sn
.itt
);
650 error
= SYSCTL_OUT(req
, buf
, strlen(buf
));
655 isc_sysctl_targetName(SYSCTL_HANDLER_ARGS
)
661 ksnprintf(buf
, sizeof(buf
), "%s", *cp
);
662 error
= SYSCTL_OUT(req
, buf
, strlen(buf
));
666 isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS
)
672 ksnprintf(buf
, sizeof(buf
), "%s", *cp
);
673 error
= SYSCTL_OUT(req
, buf
, strlen(buf
));
677 isc_add_sysctls(isc_session_t
*sp
)
680 sdebug(6, "sid=%d %s", sp
->sid
, sp
->dev
->si_name
);
682 sysctl_ctx_init(&sp
->clist
);
683 sp
->oid
= SYSCTL_ADD_NODE(&sp
->clist
,
684 SYSCTL_CHILDREN(sp
->isc
->oid
),
686 sp
->dev
->si_name
+5, // iscsi0
690 SYSCTL_ADD_PROC(&sp
->clist
,
691 SYSCTL_CHILDREN(sp
->oid
),
695 (void *)&sp
->opt
.targetName
, 0,
696 isc_sysctl_targetName
, "A", "target name");
698 SYSCTL_ADD_PROC(&sp
->clist
,
699 SYSCTL_CHILDREN(sp
->oid
),
703 (void *)&sp
->opt
.targetAddress
, 0,
704 isc_sysctl_targetAddress
, "A", "target address");
706 SYSCTL_ADD_PROC(&sp
->clist
,
707 SYSCTL_CHILDREN(sp
->oid
),
712 isc_dump_stats
, "A", "statistics");
714 SYSCTL_ADD_INT(&sp
->clist
,
715 SYSCTL_CHILDREN(sp
->oid
),
719 &sp
->douio
, 0, "enable uio on read");
723 ism_stop(isc_session_t
*sp
)
725 struct isc_softc
*sc
= sp
->isc
;
729 sdebug(2, "terminating");
731 | first stop the receiver
733 isc_stop_receiver(sp
);
736 | now stop the xmitter
738 sp
->flags
&= ~ISC_SM_RUN
;
739 while(sp
->flags
& ISC_SM_RUNNING
) {
740 sdebug(2, "waiting for ism to stop");
742 tsleep(sp
, 0, "-", hz
);
744 sdebug(2, "ism stopped");
745 sp
->flags
&= ~ISC_FFPHASE
;
749 (void)i_pdu_flush(sp
);
751 ic_lost_target(sp
, sp
->sid
);
753 lockmgr(&sc
->lock
, LK_EXCLUSIVE
);
754 TAILQ_REMOVE(&sc
->isc_sess
, sp
, sp_link
);
756 lockmgr(&sc
->lock
, LK_RELEASE
);
764 mtx_uninit(&sp
->rsp_mtx
);
765 mtx_uninit(&sp
->rsv_mtx
);
766 mtx_uninit(&sp
->hld_mtx
);
767 mtx_uninit(&sp
->snd_mtx
);
768 mtx_uninit(&sp
->io_mtx
);
771 sc
->sessions
[sp
->sid
] = NULL
;
773 if(sysctl_ctx_free(&sp
->clist
))
774 xdebug("sysctl_ctx_free failed");
780 ism_start(isc_session_t
*sp
)
784 | now is a good time to do some initialization
786 TAILQ_INIT(&sp
->rsp
);
787 TAILQ_INIT(&sp
->rsv
);
788 TAILQ_INIT(&sp
->csnd
);
789 TAILQ_INIT(&sp
->isnd
);
790 TAILQ_INIT(&sp
->wsnd
);
791 TAILQ_INIT(&sp
->hld
);
793 mtx_init(&sp
->rsv_mtx
);
794 mtx_init(&sp
->rsp_mtx
);
795 mtx_init(&sp
->snd_mtx
);
796 mtx_init(&sp
->hld_mtx
);
798 mtx_init(&sp
->io_mtx
);
802 sp
->flags
|= ISC_SM_RUN
;
803 sp
->flags
|= ISC_SM_RUNNING
;
805 debug(4, "starting ism_proc: sp->sid=%d", sp
->sid
);
806 return kthread_create(ism_proc
, sp
, &sp
->stp
, "ism_%d", sp
->sid
);