1171568Sscottl/*-
2211095Sdes * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
3171568Sscottl * All rights reserved.
4171568Sscottl *
5171568Sscottl * Redistribution and use in source and binary forms, with or without
6171568Sscottl * modification, are permitted provided that the following conditions
7171568Sscottl * are met:
8171568Sscottl * 1. Redistributions of source code must retain the above copyright
9171568Sscottl *    notice, this list of conditions and the following disclaimer.
10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11171568Sscottl *    notice, this list of conditions and the following disclaimer in the
12171568Sscottl *    documentation and/or other materials provided with the distribution.
13171568Sscottl *
14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17171568Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24171568Sscottl * SUCH DAMAGE.
25171568Sscottl *
26171568Sscottl */
27171568Sscottl/*
28171568Sscottl | iSCSI - Session Manager
29211095Sdes | $Id: isc_sm.c 743 2009-08-08 10:54:53Z danny $
30171568Sscottl */
31171568Sscottl
32171568Sscottl#include <sys/cdefs.h>
33171568Sscottl__FBSDID("$FreeBSD$");
34171568Sscottl
35171568Sscottl#include "opt_iscsi_initiator.h"
36171568Sscottl
37171568Sscottl#include <sys/param.h>
38171568Sscottl#include <sys/kernel.h>
39171568Sscottl#include <sys/conf.h>
40171568Sscottl#include <sys/systm.h>
41171568Sscottl#include <sys/malloc.h>
42171568Sscottl#include <sys/ctype.h>
43171568Sscottl#include <sys/errno.h>
44171568Sscottl#include <sys/sysctl.h>
45171568Sscottl#include <sys/file.h>
46171568Sscottl#include <sys/uio.h>
47171568Sscottl#include <sys/socketvar.h>
48171568Sscottl#include <sys/socket.h>
49171568Sscottl#include <sys/protosw.h>
50171568Sscottl#include <sys/proc.h>
51171568Sscottl#include <sys/ioccom.h>
52171568Sscottl#include <sys/queue.h>
53171568Sscottl#include <sys/kthread.h>
54171568Sscottl#include <sys/syslog.h>
55171568Sscottl#include <sys/mbuf.h>
56171568Sscottl#include <sys/bus.h>
57211095Sdes#include <sys/sx.h>
58171568Sscottl
59171568Sscottl#include <cam/cam.h>
60171568Sscottl#include <cam/cam_ccb.h>
61171568Sscottl#include <cam/cam_sim.h>
62171568Sscottl#include <cam/cam_xpt_sim.h>
63171568Sscottl#include <cam/cam_periph.h>
64171568Sscottl
65254657Strasz#include <dev/iscsi_initiator/iscsi.h>
66254657Strasz#include <dev/iscsi_initiator/iscsivar.h>
67171568Sscottl
68171568Sscottlstatic void
69171568Sscottl_async(isc_session_t *sp, pduq_t *pq)
70171568Sscottl{
71171568Sscottl     debug_called(8);
72171568Sscottl
73171568Sscottl     iscsi_async(sp, pq);
74171568Sscottl
75171568Sscottl     pdu_free(sp->isc, pq);
76171568Sscottl}
77171568Sscottl
78171568Sscottlstatic void
79171568Sscottl_reject(isc_session_t *sp, pduq_t *pq)
80171568Sscottl{
81171568Sscottl     pduq_t	*opq;
82171568Sscottl     pdu_t	*pdu;
83171568Sscottl     reject_t	*reject;
84171568Sscottl     int	itt;
85171568Sscottl
86171568Sscottl     debug_called(8);
87171568Sscottl     pdu = mtod(pq->mp, pdu_t *);
88171568Sscottl     itt = pdu->ipdu.bhs.itt;
89171568Sscottl     reject = &pq->pdu.ipdu.reject;
90171568Sscottl     sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
91171568Sscottl     opq = i_search_hld(sp, itt, 0);
92171568Sscottl     if(opq != NULL)
93171568Sscottl	  iscsi_reject(sp, opq, pq);
94171568Sscottl     else {
95171568Sscottl	  switch(pq->pdu.ipdu.bhs.opcode) {
96171568Sscottl	  case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
97171568Sscottl	       sdebug(2, "ISCSI_LOGOUT_CMD ...");
98171568Sscottl	       break;
99171568Sscottl	  default:
100171568Sscottl	       xdebug("%d] we lost something itt=%x",
101171568Sscottl		      sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
102171568Sscottl	  }
103171568Sscottl     }
104171568Sscottl     pdu_free(sp->isc, pq);
105171568Sscottl}
106171568Sscottl
107171568Sscottlstatic void
108171568Sscottl_r2t(isc_session_t *sp, pduq_t *pq)
109171568Sscottl{
110171568Sscottl     pduq_t	*opq;
111171568Sscottl
112171568Sscottl     debug_called(8);
113171568Sscottl     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
114171568Sscottl     if(opq != NULL) {
115171568Sscottl	  iscsi_r2t(sp, opq, pq);
116171568Sscottl     }
117171568Sscottl     else {
118171568Sscottl	  r2t_t		*r2t = &pq->pdu.ipdu.r2t;
119171568Sscottl
120171568Sscottl	  xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
121171568Sscottl		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
122171568Sscottl		 ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
123171568Sscottl     }
124171568Sscottl     pdu_free(sp->isc, pq);
125171568Sscottl}
126171568Sscottl
127171568Sscottlstatic void
128171568Sscottl_scsi_rsp(isc_session_t *sp, pduq_t *pq)
129171568Sscottl{
130171568Sscottl     pduq_t	*opq;
131171568Sscottl
132171568Sscottl     debug_called(8);
133171568Sscottl     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
134171568Sscottl     debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
135211095Sdes     if(opq != NULL) {
136171568Sscottl	  iscsi_done(sp, opq, pq);
137211095Sdes	  i_acked_hld(sp, &pq->pdu);
138211095Sdes     }
139171568Sscottl     else
140171568Sscottl	  xdebug("%d] we lost something itt=%x",
141171568Sscottl		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
142171568Sscottl     pdu_free(sp->isc, pq);
143171568Sscottl}
144171568Sscottl
145171568Sscottlstatic void
146171568Sscottl_read_data(isc_session_t *sp, pduq_t *pq)
147171568Sscottl{
148171568Sscottl     pduq_t		*opq;
149171568Sscottl
150171568Sscottl     debug_called(8);
151171568Sscottl     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
152171568Sscottl     if(opq != NULL) {
153171568Sscottl	  if(scsi_decap(sp, opq, pq) != 1) {
154171568Sscottl	       i_remove_hld(sp, opq); // done
155171568Sscottl	       pdu_free(sp->isc, opq);
156171568Sscottl	  }
157171568Sscottl     }
158171568Sscottl     else
159171568Sscottl	  xdebug("%d] we lost something itt=%x",
160171568Sscottl		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
161171568Sscottl     pdu_free(sp->isc, pq);
162171568Sscottl}
163171568Sscottl/*
164171568Sscottl | this is a kludge,
165171568Sscottl | the jury is not back with a veredict, user or kernel
166171568Sscottl */
167171568Sscottlstatic void
168171568Sscottl_nop_out(isc_session_t *sp)
169171568Sscottl{
170171568Sscottl     pduq_t	*pq;
171171568Sscottl     nop_out_t	*nop_out;
172171568Sscottl
173171568Sscottl     debug_called(8);
174171568Sscottl
175171568Sscottl     sdebug(4, "cws=%d", sp->cws);
176171568Sscottl     if(sp->cws == 0) {
177171568Sscottl	  /*
178171568Sscottl	   | only send a nop if window is closed.
179171568Sscottl	   */
180185289Sscottl	  if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
181171568Sscottl	       // I guess we ran out of resources
182171568Sscottl	       return;
183171568Sscottl	  nop_out = &pq->pdu.ipdu.nop_out;
184171568Sscottl	  nop_out->opcode = ISCSI_NOP_OUT;
185171568Sscottl	  nop_out->itt = htonl(sp->sn.itt);
186171568Sscottl	  nop_out->ttt = -1;
187171568Sscottl	  nop_out->I = 1;
188171568Sscottl	  nop_out->F = 1;
189171568Sscottl	  if(isc_qout(sp, pq) != 0) {
190171568Sscottl	       sdebug(1, "failed");
191171568Sscottl	       pdu_free(sp->isc, pq);
192171568Sscottl	  }
193171568Sscottl     }
194171568Sscottl}
195171568Sscottl
196171568Sscottlstatic void
197171568Sscottl_nop_in(isc_session_t *sp, pduq_t *pq)
198171568Sscottl{
199171568Sscottl     pdu_t	*pp = &pq->pdu;
200171568Sscottl     nop_in_t	*nop_in = &pp->ipdu.nop_in;
201171568Sscottl     bhs_t	*bhs = &pp->ipdu.bhs;
202171568Sscottl
203171568Sscottl     debug_called(8);
204171568Sscottl
205171568Sscottl     sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
206171568Sscottl     if(nop_in->itt == -1) {
207171568Sscottl	  if(pp->ds_len != 0) {
208171568Sscottl	       /*
209171568Sscottl		| according to RFC 3720 this should be zero
210171568Sscottl		| what to do if not?
211171568Sscottl		*/
212171568Sscottl	       xdebug("%d] dslen not zero", sp->sid);
213171568Sscottl	  }
214171568Sscottl	  if(nop_in->ttt != -1) {
215171568Sscottl	       nop_out_t	*nop_out;
216171568Sscottl	       /*
217171568Sscottl		| target wants a nop_out
218171568Sscottl	        */
219171568Sscottl	       bhs->opcode = ISCSI_NOP_OUT;
220171568Sscottl	       bhs->I = 1;
221171568Sscottl	       bhs->F = 1;
222171568Sscottl	       /*
223171568Sscottl		| we are reusing the pdu, so bhs->ttt == nop_in->ttt;
224171568Sscottl		| and need to zero out 'Reserved'
225171568Sscottl		| small cludge here.
226171568Sscottl	        */
227171568Sscottl	       nop_out = &pp->ipdu.nop_out;
228171568Sscottl	       nop_out->sn.maxcmd = 0;
229171568Sscottl	       memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
230171568Sscottl	       (void)isc_qout(sp, pq); //XXX: should check return?
231171568Sscottl	       return;
232171568Sscottl	  }
233171568Sscottl	  //else {
234171568Sscottl	       // just making noise?
235171568Sscottl	       // see 10.9.1: target does not want and answer.
236171568Sscottl	  //}
237171568Sscottl
238171568Sscottl     } else
239171568Sscottl     if(nop_in->ttt == -1) {
240171568Sscottl	  /*
241171568Sscottl	   | it is an answer to a nop_in from us
242171568Sscottl	   */
243171568Sscottl	  if(nop_in->itt != -1) {
244171568Sscottl#ifdef ISC_WAIT4PING
245171568Sscottl	       // XXX: MUTEX please
246171568Sscottl	       if(sp->flags & ISC_WAIT4PING) {
247171568Sscottl		    i_nqueue_rsp(sp, pq);
248171568Sscottl		    wakeup(&sp->rsp);
249171568Sscottl		    return;
250171568Sscottl	       }
251171568Sscottl#endif
252171568Sscottl	  }
253171568Sscottl     }
254171568Sscottl     /*
255171568Sscottl      | drop it
256171568Sscottl      */
257171568Sscottl     pdu_free(sp->isc, pq);
258171568Sscottl     return;
259171568Sscottl}
260171568Sscottl
261171568Sscottlint
262171568Sscottli_prepPDU(isc_session_t *sp, pduq_t *pq)
263171568Sscottl{
264171568Sscottl     size_t	len, n;
265171568Sscottl     pdu_t	*pp = &pq->pdu;
266171568Sscottl     bhs_t	*bhp = &pp->ipdu.bhs;
267171568Sscottl
268171568Sscottl     len = sizeof(bhs_t);
269171568Sscottl     if(pp->ahs_len) {
270171568Sscottl	  len += pp->ahs_len;
271171568Sscottl	  bhp->AHSLength =  pp->ahs_len / 4;
272171568Sscottl     }
273211095Sdes     if(ISOK2DIG(sp->hdrDigest, pp))
274171568Sscottl	  len += 4;
275171568Sscottl     if(pp->ds_len) {
276171568Sscottl	  n = pp->ds_len;
277171568Sscottl	  len += n;
278171568Sscottl#if BYTE_ORDER == LITTLE_ENDIAN
279171568Sscottl	  bhp->DSLength = ((n & 0x00ff0000) >> 16)
280171568Sscottl	       | (n & 0x0000ff00)
281171568Sscottl	       | ((n & 0x000000ff) << 16);
282171568Sscottl#else
283171568Sscottl	  bhp->DSLength = n;
284171568Sscottl#endif
285171568Sscottl	  if(len & 03) {
286171568Sscottl	       n = 4 - (len & 03);
287171568Sscottl	       len += n;
288171568Sscottl	  }
289211095Sdes	  if(ISOK2DIG(sp->dataDigest, pp))
290171568Sscottl	       len += 4;
291171568Sscottl     }
292171568Sscottl
293171568Sscottl     pq->len = len;
294171568Sscottl     len -= sizeof(bhs_t);
295171568Sscottl     if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
296171568Sscottl	  xdebug("%d] pdu len=%zd > %d",
297171568Sscottl		 sp->sid, len, sp->opt.maxBurstLength);
298171568Sscottl	  // XXX: when this happens it used to hang ...
299171568Sscottl	  return E2BIG;
300171568Sscottl     }
301171568Sscottl     return 0;
302171568Sscottl}
303171568Sscottl
304171568Sscottlint
305171568Sscottlisc_qout(isc_session_t *sp, pduq_t *pq)
306171568Sscottl{
307171568Sscottl     int error = 0;
308171568Sscottl
309171568Sscottl     debug_called(8);
310171568Sscottl
311171568Sscottl     if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
312171568Sscottl	  return error;
313171568Sscottl
314171568Sscottl     if(pq->pdu.ipdu.bhs.I)
315171568Sscottl	  i_nqueue_isnd(sp, pq);
316171568Sscottl     else
317171568Sscottl     if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
318171568Sscottl	  i_nqueue_wsnd(sp, pq);
319171568Sscottl     else
320171568Sscottl	  i_nqueue_csnd(sp, pq);
321171568Sscottl
322171568Sscottl     sdebug(5, "enqued: pq=%p", pq);
323185289Sscottl
324185289Sscottl     mtx_lock(&sp->io_mtx);
325185289Sscottl     sp->flags |= ISC_OQNOTEMPTY;
326185289Sscottl     if(sp->flags & ISC_OWAITING)
327211095Sdes	  wakeup(&sp->flags);
328185289Sscottl     mtx_unlock(&sp->io_mtx);
329185289Sscottl
330171568Sscottl     return error;
331171568Sscottl}
332171568Sscottl/*
333171568Sscottl | called when a fullPhase is restarted
334171568Sscottl */
335211095Sdesvoid
336171568Sscottlism_restart(isc_session_t *sp)
337171568Sscottl{
338171568Sscottl     int lastcmd;
339171568Sscottl
340171568Sscottl     sdebug(2, "restart ...");
341171568Sscottl     lastcmd = iscsi_requeue(sp);
342171568Sscottl#if 0
343171568Sscottl     if(lastcmd != sp->sn.cmd) {
344171568Sscottl	  sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
345171568Sscottl	  sp->sn.cmd = lastcmd;
346171568Sscottl     }
347171568Sscottl#endif
348185289Sscottl     mtx_lock(&sp->io_mtx);
349185289Sscottl     if(sp->flags & ISC_OWAITING) {
350185289Sscottl	  wakeup(&sp->flags);
351185289Sscottl     }
352185289Sscottl     mtx_unlock(&sp->io_mtx);
353185289Sscottl
354211095Sdes     sdebug(2, "restarted sn.cmd=0x%x lastcmd=0x%x", sp->sn.cmd, lastcmd);
355171568Sscottl}
356171568Sscottl
357171568Sscottlvoid
358171568Sscottlism_recv(isc_session_t *sp, pduq_t *pq)
359171568Sscottl{
360171568Sscottl     bhs_t	*bhs;
361171568Sscottl     int	statSN;
362171568Sscottl
363171568Sscottl     debug_called(8);
364171568Sscottl
365171568Sscottl     bhs = &pq->pdu.ipdu.bhs;
366171568Sscottl     statSN = ntohl(bhs->OpcodeSpecificFields[1]);
367171568Sscottl
368171568Sscottl#ifdef notyet
369171568Sscottl     if(sp->sn.expCmd != sn->cmd) {
370171568Sscottl	  sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
371171568Sscottl		 sn->expCmd, sn->cmd);
372171568Sscottl     }
373171568Sscottl#endif
374171568Sscottl     sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
375171568Sscottl	    bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
376171568Sscottl
377171568Sscottl     switch(bhs->opcode) {
378171568Sscottl     case ISCSI_READ_DATA: {
379171568Sscottl	  data_in_t 	*cmd = &pq->pdu.ipdu.data_in;
380171568Sscottl
381171568Sscottl	  if(cmd->S == 0)
382171568Sscottl	       break;
383171568Sscottl     }
384171568Sscottl
385171568Sscottl     default:
386171568Sscottl	  if(statSN > (sp->sn.stat + 1)) {
387171568Sscottl	       sdebug(1, "we lost some rec=0x%x exp=0x%x",
388171568Sscottl		      statSN, sp->sn.stat);
389171568Sscottl	       // XXX: must do some error recovery here.
390171568Sscottl	  }
391171568Sscottl	  sp->sn.stat = statSN;
392171568Sscottl     }
393171568Sscottl
394171568Sscottl     switch(bhs->opcode) {
395171568Sscottl     case ISCSI_LOGIN_RSP:
396171568Sscottl     case ISCSI_TEXT_RSP:
397171568Sscottl     case ISCSI_LOGOUT_RSP:
398171568Sscottl	  i_nqueue_rsp(sp, pq);
399171568Sscottl	  wakeup(&sp->rsp);
400171568Sscottl	  sdebug(3, "wakeup rsp");
401171568Sscottl	  break;
402171568Sscottl
403171568Sscottl     case ISCSI_NOP_IN:		_nop_in(sp, pq);	break;
404171568Sscottl     case ISCSI_SCSI_RSP:	_scsi_rsp(sp, pq);	break;
405171568Sscottl     case ISCSI_READ_DATA:	_read_data(sp, pq);	break;
406171568Sscottl     case ISCSI_R2T:		_r2t(sp, pq);		break;
407171568Sscottl     case ISCSI_REJECT:		_reject(sp, pq);	break;
408171568Sscottl     case ISCSI_ASYNC:		_async(sp, pq);		break;
409171568Sscottl
410171568Sscottl     case ISCSI_TASK_RSP:
411171568Sscottl     default:
412171568Sscottl	  sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
413171568Sscottl		 bhs->opcode, ntohl(bhs->itt));
414171568Sscottl	  break;
415171568Sscottl     }
416171568Sscottl}
417211095Sdes
418185289Sscottl/*
419185289Sscottl | go through the out queues looking for work
420185289Sscottl | if either nothing to do, or window is closed
421185289Sscottl | return.
422185289Sscottl */
423171568Sscottlstatic int
424171568Sscottlproc_out(isc_session_t *sp)
425171568Sscottl{
426171568Sscottl     sn_t	*sn = &sp->sn;
427171568Sscottl     pduq_t	*pq;
428211095Sdes     int	error, which;
429171568Sscottl
430171568Sscottl     debug_called(8);
431211095Sdes     error = 0;
432171568Sscottl
433185289Sscottl     while(sp->flags & ISC_LINK_UP) {
434171568Sscottl	  pdu_t *pp;
435171568Sscottl	  bhs_t	*bhs;
436171568Sscottl	  /*
437171568Sscottl	   | check if there is outstanding work in:
438185289Sscottl	   | 1- the Immediate queue
439171568Sscottl	   | 2- the R2T queue
440171568Sscottl	   | 3- the cmd queue, only if the command window allows it.
441171568Sscottl	   */
442171568Sscottl	  which = BIT(0) | BIT(1);
443185289Sscottl	  if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
444171568Sscottl	       which |= BIT(2);
445171568Sscottl
446185289Sscottl	  sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd);
447185289Sscottl
448171568Sscottl	  if((pq = i_dqueue_snd(sp, which)) == NULL)
449171568Sscottl	       break;
450185289Sscottl	  sdebug(4, "pq=%p", pq);
451171568Sscottl
452171568Sscottl	  pp = &pq->pdu;
453171568Sscottl	  bhs = &pp->ipdu.bhs;
454171568Sscottl	  switch(bhs->opcode) {
455171568Sscottl	  case ISCSI_SCSI_CMD:
456171568Sscottl	       sn->itt++;
457171568Sscottl	       bhs->itt = htonl(sn->itt);
458171568Sscottl
459171568Sscottl	  case ISCSI_LOGIN_CMD:
460171568Sscottl	  case ISCSI_TEXT_CMD:
461171568Sscottl	  case ISCSI_LOGOUT_CMD:
462171568Sscottl	  case ISCSI_SNACK:
463171568Sscottl	  case ISCSI_NOP_OUT:
464171568Sscottl	  case ISCSI_TASK_CMD:
465171568Sscottl	       bhs->CmdSN = htonl(sn->cmd);
466171568Sscottl	       if(bhs->I == 0)
467171568Sscottl		    sn->cmd++;
468171568Sscottl
469171568Sscottl	  case ISCSI_WRITE_DATA:
470211095Sdes	       bhs->ExpStSN = htonl(sn->stat + 1);
471171568Sscottl	       break;
472171568Sscottl
473171568Sscottl	  default:
474171568Sscottl	       // XXX: can this happen?
475171568Sscottl	       xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
476171568Sscottl		      bhs->opcode,
477171568Sscottl		      sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
478171568Sscottl	       // XXX: and now?
479171568Sscottl	  }
480171568Sscottl
481185289Sscottl	  sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
482171568Sscottl		bhs->opcode,
483171568Sscottl		sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
484171568Sscottl
485211095Sdes	  if(bhs->opcode != ISCSI_NOP_OUT)
486211095Sdes	       /*
487211095Sdes		| enqued till ack is received
488211095Sdes		| note: sosend(...) does not mean the packet left
489211095Sdes		| the host so that freeing resources has to wait
490211095Sdes	        */
491171568Sscottl	       i_nqueue_hld(sp, pq);
492171568Sscottl
493211095Sdes	  error = isc_sendPDU(sp, pq);
494211095Sdes	  if(bhs->opcode == ISCSI_NOP_OUT)
495211095Sdes	       pdu_free(sp->isc, pq);
496211095Sdes	  if(error) {
497211095Sdes	       xdebug("error=%d opcode=0x%x ccb=%p itt=%x",
498211095Sdes		      error, bhs->opcode, pq->ccb, ntohl(bhs->itt));
499211095Sdes	       i_remove_hld(sp, pq);
500185289Sscottl	       switch(error) {
501185289Sscottl	       case EPIPE:
502185289Sscottl		    sp->flags &= ~ISC_LINK_UP;
503185289Sscottl
504185289Sscottl	       case EAGAIN:
505185289Sscottl		    xdebug("requed");
506185289Sscottl		    i_rqueue_pdu(sp, pq);
507171568Sscottl		    break;
508185289Sscottl
509185289Sscottl	       default:
510211095Sdes		    if(pq->ccb) {
511185289Sscottl			 xdebug("back to cam");
512185289Sscottl			 pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error?
513211095Sdes			 XPT_DONE(sp, pq->ccb);
514185289Sscottl			 pdu_free(sp->isc, pq);
515211095Sdes		    }
516185289Sscottl		    else
517185289Sscottl			 xdebug("we lost it!");
518171568Sscottl	       }
519171568Sscottl	  }
520171568Sscottl     }
521185289Sscottl     return error;
522171568Sscottl}
523211095Sdes
524171568Sscottl/*
525171568Sscottl | survives link breakdowns.
526171568Sscottl */
527171568Sscottlstatic void
528211095Sdesism_out(void *vp)
529171568Sscottl{
530171568Sscottl     isc_session_t 	*sp = (isc_session_t *)vp;
531185289Sscottl     int		error;
532171568Sscottl
533171568Sscottl     debug_called(8);
534171568Sscottl
535171568Sscottl     sp->flags |= ISC_SM_RUNNING;
536185289Sscottl     sdebug(3, "started sp->flags=%x", sp->flags);
537171568Sscottl     do {
538185289Sscottl	  if((sp->flags & ISC_HOLD) == 0) {
539185289Sscottl	       error = proc_out(sp);
540185289Sscottl	       if(error) {
541185289Sscottl		    sdebug(3, "error=%d", error);
542185289Sscottl	       }
543185289Sscottl	  }
544211095Sdes	  mtx_lock(&sp->io_mtx);
545185289Sscottl	  if((sp->flags & ISC_LINK_UP) == 0) {
546211095Sdes	       sdebug(3, "ISC_LINK_UP==0, sp->flags=%x ", sp->flags);
547211095Sdes	       if(sp->soc != NULL)
548211095Sdes		    sdebug(3, "so_state=%x", sp->soc->so_state);
549185289Sscottl	       wakeup(&sp->soc);
550185289Sscottl	  }
551185289Sscottl
552185289Sscottl	  if(!(sp->flags & ISC_OQNOTEMPTY)) {
553171568Sscottl	       sp->flags |= ISC_OWAITING;
554185289Sscottl	       if(msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) {
555185289Sscottl		    if(sp->flags & ISC_CON_RUNNING)
556211095Sdes			 _nop_out(sp);
557185289Sscottl	       }
558171568Sscottl	       sp->flags &= ~ISC_OWAITING;
559171568Sscottl	  }
560185289Sscottl	  sp->flags &= ~ISC_OQNOTEMPTY;
561185289Sscottl	  mtx_unlock(&sp->io_mtx);
562171568Sscottl     } while(sp->flags & ISC_SM_RUN);
563171568Sscottl
564171568Sscottl     sp->flags &= ~ISC_SM_RUNNING;
565185289Sscottl     sdebug(3, "dropped ISC_SM_RUNNING");
566171568Sscottl
567211095Sdes     wakeup(&sp->soc);
568211095Sdes     wakeup(sp); // XXX: do we need this one?
569211095Sdes
570171568Sscottl#if __FreeBSD_version >= 700000
571171568Sscottl     destroy_dev(sp->dev);
572171568Sscottl#endif
573171568Sscottl
574185289Sscottl     debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid);
575171568Sscottl
576211095Sdes#if __FreeBSD_version >= 800000
577172836Sjulian     kproc_exit(0);
578211095Sdes#else
579211095Sdes     kthread_exit(0);
580211095Sdes#endif
581171568Sscottl}
582171568Sscottl
583171568Sscottl#if 0
584171568Sscottlstatic int
585171568Sscottlisc_dump_options(SYSCTL_HANDLER_ARGS)
586171568Sscottl{
587171568Sscottl     int error;
588171568Sscottl     isc_session_t *sp;
589171568Sscottl     char	buf[1024], *bp;
590171568Sscottl
591171568Sscottl     sp = (isc_session_t *)arg1;
592171568Sscottl     bp = buf;
593171568Sscottl     sprintf(bp, "targetname='%s'", sp->opt.targetName);
594171568Sscottl     bp += strlen(bp);
595171568Sscottl     sprintf(bp, " targetname='%s'", sp->opt.targetAddress);
596171568Sscottl     error = SYSCTL_OUT(req, buf, strlen(buf));
597171568Sscottl     return error;
598171568Sscottl}
599171568Sscottl#endif
600171568Sscottl
601171568Sscottlstatic int
602171568Sscottlisc_dump_stats(SYSCTL_HANDLER_ARGS)
603171568Sscottl{
604171568Sscottl     isc_session_t	*sp;
605171568Sscottl     struct isc_softc	*sc;
606171568Sscottl     char	buf[1024], *bp;
607171568Sscottl     int 	error, n;
608171568Sscottl
609171568Sscottl     sp = (isc_session_t *)arg1;
610171568Sscottl     sc = sp->isc;
611171568Sscottl
612171568Sscottl     bp = buf;
613171568Sscottl     n = sizeof(buf);
614171568Sscottl     snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
615171568Sscottl     bp += strlen(bp);
616171568Sscottl     n -= strlen(bp);
617171568Sscottl     snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
618171568Sscottl		  sp->flags, sc->npdu_alloc, sc->npdu_max);
619171568Sscottl     bp += strlen(bp);
620171568Sscottl     n -= strlen(bp);
621171568Sscottl     snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
622171568Sscottl		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
623171568Sscottl     error = SYSCTL_OUT(req, buf, strlen(buf));
624171568Sscottl     return error;
625171568Sscottl}
626171568Sscottl
627171568Sscottlstatic int
628171568Sscottlisc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
629171568Sscottl{
630171568Sscottl     char	buf[128], **cp;
631171568Sscottl     int 	error;
632171568Sscottl
633171568Sscottl     cp = (char **)arg1;
634171568Sscottl     snprintf(buf, sizeof(buf), "%s", *cp);
635171568Sscottl     error = SYSCTL_OUT(req, buf, strlen(buf));
636171568Sscottl     return error;
637171568Sscottl}
638171568Sscottl
639171568Sscottlstatic int
640171568Sscottlisc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
641171568Sscottl{
642171568Sscottl     char	buf[128], **cp;
643171568Sscottl     int 	error;
644171568Sscottl
645171568Sscottl     cp = (char **)arg1;
646171568Sscottl     snprintf(buf, sizeof(buf), "%s", *cp);
647171568Sscottl     error = SYSCTL_OUT(req, buf, strlen(buf));
648171568Sscottl     return error;
649171568Sscottl}
650171568Sscottl
651171568Sscottlstatic void
652171568Sscottlisc_add_sysctls(isc_session_t *sp)
653171568Sscottl{
654171568Sscottl     debug_called(8);
655231378Sed     sdebug(6, "sid=%d %s", sp->sid, devtoname(sp->dev));
656171568Sscottl
657171568Sscottl     sysctl_ctx_init(&sp->clist);
658171568Sscottl     sp->oid = SYSCTL_ADD_NODE(&sp->clist,
659171568Sscottl			       SYSCTL_CHILDREN(sp->isc->oid),
660171568Sscottl			       OID_AUTO,
661231378Sed			       devtoname(sp->dev) + 5, // iscsi0
662171568Sscottl			       CTLFLAG_RD,
663171568Sscottl			       0,
664171568Sscottl			       "initiator");
665171568Sscottl     SYSCTL_ADD_PROC(&sp->clist,
666171568Sscottl		     SYSCTL_CHILDREN(sp->oid),
667171568Sscottl		     OID_AUTO,
668171568Sscottl		     "targetname",
669217556Smdf		     CTLTYPE_STRING | CTLFLAG_RD,
670171568Sscottl		     (void *)&sp->opt.targetName, 0,
671171568Sscottl		     isc_sysctl_targetName, "A", "target name");
672171568Sscottl
673171568Sscottl     SYSCTL_ADD_PROC(&sp->clist,
674171568Sscottl		     SYSCTL_CHILDREN(sp->oid),
675171568Sscottl		     OID_AUTO,
676171568Sscottl		     "targeaddress",
677217556Smdf		     CTLTYPE_STRING | CTLFLAG_RD,
678171568Sscottl		     (void *)&sp->opt.targetAddress, 0,
679171568Sscottl		     isc_sysctl_targetAddress, "A", "target address");
680171568Sscottl
681171568Sscottl     SYSCTL_ADD_PROC(&sp->clist,
682171568Sscottl		     SYSCTL_CHILDREN(sp->oid),
683171568Sscottl		     OID_AUTO,
684171568Sscottl		     "stats",
685217556Smdf		     CTLTYPE_STRING | CTLFLAG_RD,
686171568Sscottl		     (void *)sp, 0,
687171568Sscottl		     isc_dump_stats, "A", "statistics");
688185289Sscottl
689185289Sscottl     SYSCTL_ADD_INT(&sp->clist,
690185289Sscottl		     SYSCTL_CHILDREN(sp->oid),
691185289Sscottl		     OID_AUTO,
692185289Sscottl		     "douio",
693185289Sscottl		     CTLFLAG_RW,
694185289Sscottl		     &sp->douio, 0, "enable uio on read");
695171568Sscottl}
696171568Sscottl
697171568Sscottlvoid
698171568Sscottlism_stop(isc_session_t *sp)
699171568Sscottl{
700171568Sscottl     struct isc_softc *sc = sp->isc;
701171568Sscottl     int	n;
702171568Sscottl
703171568Sscottl     debug_called(8);
704171568Sscottl     sdebug(2, "terminating");
705171568Sscottl     /*
706171568Sscottl      | first stop the receiver
707171568Sscottl      */
708171568Sscottl     isc_stop_receiver(sp);
709171568Sscottl     /*
710171568Sscottl      | now stop the xmitter
711171568Sscottl      */
712171568Sscottl     n = 5;
713171568Sscottl     sp->flags &= ~ISC_SM_RUN;
714171568Sscottl     while(n-- && (sp->flags & ISC_SM_RUNNING)) {
715171568Sscottl	  sdebug(2, "n=%d", n);
716171568Sscottl	  wakeup(&sp->flags);
717171568Sscottl	  tsleep(sp, PRIBIO, "-", 5*hz);
718171568Sscottl     }
719171568Sscottl     sdebug(2, "final n=%d", n);
720171568Sscottl     sp->flags &= ~ISC_FFPHASE;
721171568Sscottl
722171568Sscottl     iscsi_cleanup(sp);
723171568Sscottl
724171568Sscottl     (void)i_pdu_flush(sp);
725171568Sscottl
726211095Sdes     ic_destroy(sp);
727171568Sscottl
728211095Sdes     sx_xlock(&sc->unit_sx);
729211095Sdes     free_unr(sc->unit, sp->sid);
730211095Sdes     sx_xunlock(&sc->unit_sx);
731211095Sdes
732211095Sdes     mtx_lock(&sc->isc_mtx);
733171568Sscottl     TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
734171568Sscottl     sc->nsess--;
735211095Sdes     mtx_unlock(&sc->isc_mtx);
736171568Sscottl
737171568Sscottl#if __FreeBSD_version < 700000
738171568Sscottl     destroy_dev(sp->dev);
739171568Sscottl#endif
740171568Sscottl
741171568Sscottl     mtx_destroy(&sp->rsp_mtx);
742171568Sscottl     mtx_destroy(&sp->rsv_mtx);
743171568Sscottl     mtx_destroy(&sp->hld_mtx);
744171568Sscottl     mtx_destroy(&sp->snd_mtx);
745171568Sscottl     mtx_destroy(&sp->io_mtx);
746171568Sscottl
747171568Sscottl     i_freeopt(&sp->opt);
748171568Sscottl
749171568Sscottl     if(sysctl_ctx_free(&sp->clist))
750171568Sscottl	  xdebug("sysctl_ctx_free failed");
751171568Sscottl
752171568Sscottl     free(sp, M_ISCSI);
753171568Sscottl}
754171568Sscottl
755171568Sscottlint
756171568Sscottlism_start(isc_session_t *sp)
757171568Sscottl{
758171568Sscottl     debug_called(8);
759171568Sscottl    /*
760171568Sscottl     | now is a good time to do some initialization
761171568Sscottl     */
762171568Sscottl     TAILQ_INIT(&sp->rsp);
763171568Sscottl     TAILQ_INIT(&sp->rsv);
764171568Sscottl     TAILQ_INIT(&sp->csnd);
765171568Sscottl     TAILQ_INIT(&sp->isnd);
766171568Sscottl     TAILQ_INIT(&sp->wsnd);
767171568Sscottl     TAILQ_INIT(&sp->hld);
768211095Sdes
769171568Sscottl     mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF);
770171568Sscottl     mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF);
771171568Sscottl     mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF);
772171568Sscottl     mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF);
773171568Sscottl     mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF);
774171568Sscottl
775171568Sscottl     isc_add_sysctls(sp);
776171568Sscottl
777171568Sscottl     sp->flags |= ISC_SM_RUN;
778171568Sscottl
779185289Sscottl     debug(4, "starting ism_proc: sp->sid=%d", sp->sid);
780211095Sdes
781211095Sdes#if __FreeBSD_version >= 800000
782211095Sdes     return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid);
783211095Sdes#else
784211095Sdes     return kthread_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid);
785211095Sdes#endif
786171568Sscottl}
787