isc_sm.c revision 231378
1/*-
2 * Copyright (c) 2005-2010 Daniel Braniss <danny@cs.huji.ac.il>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
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.
13 *
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
24 * SUCH DAMAGE.
25 *
26 */
27/*
28 | iSCSI - Session Manager
29 | $Id: isc_sm.c 743 2009-08-08 10:54:53Z danny $
30 */
31
32#include <sys/cdefs.h>
33__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/isc_sm.c 231378 2012-02-10 12:35:57Z ed $");
34
35#include "opt_iscsi_initiator.h"
36
37#include <sys/param.h>
38#include <sys/kernel.h>
39#include <sys/conf.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>
45#include <sys/file.h>
46#include <sys/uio.h>
47#include <sys/socketvar.h>
48#include <sys/socket.h>
49#include <sys/protosw.h>
50#include <sys/proc.h>
51#include <sys/ioccom.h>
52#include <sys/queue.h>
53#include <sys/kthread.h>
54#include <sys/syslog.h>
55#include <sys/mbuf.h>
56#include <sys/bus.h>
57#include <sys/sx.h>
58
59#include <cam/cam.h>
60#include <cam/cam_ccb.h>
61#include <cam/cam_sim.h>
62#include <cam/cam_xpt_sim.h>
63#include <cam/cam_periph.h>
64
65#include <dev/iscsi/initiator/iscsi.h>
66#include <dev/iscsi/initiator/iscsivar.h>
67
68static void
69_async(isc_session_t *sp, pduq_t *pq)
70{
71     debug_called(8);
72
73     iscsi_async(sp, pq);
74
75     pdu_free(sp->isc, pq);
76}
77
78static void
79_reject(isc_session_t *sp, pduq_t *pq)
80{
81     pduq_t	*opq;
82     pdu_t	*pdu;
83     reject_t	*reject;
84     int	itt;
85
86     debug_called(8);
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);
92     if(opq != NULL)
93	  iscsi_reject(sp, opq, pq);
94     else {
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 ...");
98	       break;
99	  default:
100	       xdebug("%d] we lost something itt=%x",
101		      sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
102	  }
103     }
104     pdu_free(sp->isc, pq);
105}
106
107static void
108_r2t(isc_session_t *sp, pduq_t *pq)
109{
110     pduq_t	*opq;
111
112     debug_called(8);
113     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
114     if(opq != NULL) {
115	  iscsi_r2t(sp, opq, pq);
116     }
117     else {
118	  r2t_t		*r2t = &pq->pdu.ipdu.r2t;
119
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));
123     }
124     pdu_free(sp->isc, pq);
125}
126
127static void
128_scsi_rsp(isc_session_t *sp, pduq_t *pq)
129{
130     pduq_t	*opq;
131
132     debug_called(8);
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);
135     if(opq != NULL) {
136	  iscsi_done(sp, opq, pq);
137	  i_acked_hld(sp, &pq->pdu);
138     }
139     else
140	  xdebug("%d] we lost something itt=%x",
141		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
142     pdu_free(sp->isc, pq);
143}
144
145static void
146_read_data(isc_session_t *sp, pduq_t *pq)
147{
148     pduq_t		*opq;
149
150     debug_called(8);
151     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
152     if(opq != NULL) {
153	  if(scsi_decap(sp, opq, pq) != 1) {
154	       i_remove_hld(sp, opq); // done
155	       pdu_free(sp->isc, opq);
156	  }
157     }
158     else
159	  xdebug("%d] we lost something itt=%x",
160		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
161     pdu_free(sp->isc, pq);
162}
163/*
164 | this is a kludge,
165 | the jury is not back with a veredict, user or kernel
166 */
167static void
168_nop_out(isc_session_t *sp)
169{
170     pduq_t	*pq;
171     nop_out_t	*nop_out;
172
173     debug_called(8);
174
175     sdebug(4, "cws=%d", sp->cws);
176     if(sp->cws == 0) {
177	  /*
178	   | only send a nop if window is closed.
179	   */
180	  if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
181	       // I guess we ran out of resources
182	       return;
183	  nop_out = &pq->pdu.ipdu.nop_out;
184	  nop_out->opcode = ISCSI_NOP_OUT;
185	  nop_out->itt = htonl(sp->sn.itt);
186	  nop_out->ttt = -1;
187	  nop_out->I = 1;
188	  nop_out->F = 1;
189	  if(isc_qout(sp, pq) != 0) {
190	       sdebug(1, "failed");
191	       pdu_free(sp->isc, pq);
192	  }
193     }
194}
195
196static void
197_nop_in(isc_session_t *sp, pduq_t *pq)
198{
199     pdu_t	*pp = &pq->pdu;
200     nop_in_t	*nop_in = &pp->ipdu.nop_in;
201     bhs_t	*bhs = &pp->ipdu.bhs;
202
203     debug_called(8);
204
205     sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
206     if(nop_in->itt == -1) {
207	  if(pp->ds_len != 0) {
208	       /*
209		| according to RFC 3720 this should be zero
210		| what to do if not?
211		*/
212	       xdebug("%d] dslen not zero", sp->sid);
213	  }
214	  if(nop_in->ttt != -1) {
215	       nop_out_t	*nop_out;
216	       /*
217		| target wants a nop_out
218	        */
219	       bhs->opcode = ISCSI_NOP_OUT;
220	       bhs->I = 1;
221	       bhs->F = 1;
222	       /*
223		| we are reusing the pdu, so bhs->ttt == nop_in->ttt;
224		| and need to zero out 'Reserved'
225		| small cludge here.
226	        */
227	       nop_out = &pp->ipdu.nop_out;
228	       nop_out->sn.maxcmd = 0;
229	       memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
230	       (void)isc_qout(sp, pq); //XXX: should check return?
231	       return;
232	  }
233	  //else {
234	       // just making noise?
235	       // see 10.9.1: target does not want and answer.
236	  //}
237
238     } else
239     if(nop_in->ttt == -1) {
240	  /*
241	   | it is an answer to a nop_in from us
242	   */
243	  if(nop_in->itt != -1) {
244#ifdef ISC_WAIT4PING
245	       // XXX: MUTEX please
246	       if(sp->flags & ISC_WAIT4PING) {
247		    i_nqueue_rsp(sp, pq);
248		    wakeup(&sp->rsp);
249		    return;
250	       }
251#endif
252	  }
253     }
254     /*
255      | drop it
256      */
257     pdu_free(sp->isc, pq);
258     return;
259}
260
261int
262i_prepPDU(isc_session_t *sp, pduq_t *pq)
263{
264     size_t	len, n;
265     pdu_t	*pp = &pq->pdu;
266     bhs_t	*bhp = &pp->ipdu.bhs;
267
268     len = sizeof(bhs_t);
269     if(pp->ahs_len) {
270	  len += pp->ahs_len;
271	  bhp->AHSLength =  pp->ahs_len / 4;
272     }
273     if(ISOK2DIG(sp->hdrDigest, pp))
274	  len += 4;
275     if(pp->ds_len) {
276	  n = pp->ds_len;
277	  len += n;
278#if BYTE_ORDER == LITTLE_ENDIAN
279	  bhp->DSLength = ((n & 0x00ff0000) >> 16)
280	       | (n & 0x0000ff00)
281	       | ((n & 0x000000ff) << 16);
282#else
283	  bhp->DSLength = n;
284#endif
285	  if(len & 03) {
286	       n = 4 - (len & 03);
287	       len += n;
288	  }
289	  if(ISOK2DIG(sp->dataDigest, pp))
290	       len += 4;
291     }
292
293     pq->len = len;
294     len -= sizeof(bhs_t);
295     if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
296	  xdebug("%d] pdu len=%zd > %d",
297		 sp->sid, len, sp->opt.maxBurstLength);
298	  // XXX: when this happens it used to hang ...
299	  return E2BIG;
300     }
301     return 0;
302}
303
304int
305isc_qout(isc_session_t *sp, pduq_t *pq)
306{
307     int error = 0;
308
309     debug_called(8);
310
311     if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
312	  return error;
313
314     if(pq->pdu.ipdu.bhs.I)
315	  i_nqueue_isnd(sp, pq);
316     else
317     if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
318	  i_nqueue_wsnd(sp, pq);
319     else
320	  i_nqueue_csnd(sp, pq);
321
322     sdebug(5, "enqued: pq=%p", pq);
323
324     mtx_lock(&sp->io_mtx);
325     sp->flags |= ISC_OQNOTEMPTY;
326     if(sp->flags & ISC_OWAITING)
327	  wakeup(&sp->flags);
328     mtx_unlock(&sp->io_mtx);
329
330     return error;
331}
332/*
333 | called when a fullPhase is restarted
334 */
335void
336ism_restart(isc_session_t *sp)
337{
338     int lastcmd;
339
340     sdebug(2, "restart ...");
341     lastcmd = iscsi_requeue(sp);
342#if 0
343     if(lastcmd != sp->sn.cmd) {
344	  sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
345	  sp->sn.cmd = lastcmd;
346     }
347#endif
348     mtx_lock(&sp->io_mtx);
349     if(sp->flags & ISC_OWAITING) {
350	  wakeup(&sp->flags);
351     }
352     mtx_unlock(&sp->io_mtx);
353
354     sdebug(2, "restarted sn.cmd=0x%x lastcmd=0x%x", sp->sn.cmd, lastcmd);
355}
356
357void
358ism_recv(isc_session_t *sp, pduq_t *pq)
359{
360     bhs_t	*bhs;
361     int	statSN;
362
363     debug_called(8);
364
365     bhs = &pq->pdu.ipdu.bhs;
366     statSN = ntohl(bhs->OpcodeSpecificFields[1]);
367
368#ifdef notyet
369     if(sp->sn.expCmd != sn->cmd) {
370	  sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
371		 sn->expCmd, sn->cmd);
372     }
373#endif
374     sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
375	    bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
376
377     switch(bhs->opcode) {
378     case ISCSI_READ_DATA: {
379	  data_in_t 	*cmd = &pq->pdu.ipdu.data_in;
380
381	  if(cmd->S == 0)
382	       break;
383     }
384
385     default:
386	  if(statSN > (sp->sn.stat + 1)) {
387	       sdebug(1, "we lost some rec=0x%x exp=0x%x",
388		      statSN, sp->sn.stat);
389	       // XXX: must do some error recovery here.
390	  }
391	  sp->sn.stat = statSN;
392     }
393
394     switch(bhs->opcode) {
395     case ISCSI_LOGIN_RSP:
396     case ISCSI_TEXT_RSP:
397     case ISCSI_LOGOUT_RSP:
398	  i_nqueue_rsp(sp, pq);
399	  wakeup(&sp->rsp);
400	  sdebug(3, "wakeup rsp");
401	  break;
402
403     case ISCSI_NOP_IN:		_nop_in(sp, pq);	break;
404     case ISCSI_SCSI_RSP:	_scsi_rsp(sp, pq);	break;
405     case ISCSI_READ_DATA:	_read_data(sp, pq);	break;
406     case ISCSI_R2T:		_r2t(sp, pq);		break;
407     case ISCSI_REJECT:		_reject(sp, pq);	break;
408     case ISCSI_ASYNC:		_async(sp, pq);		break;
409
410     case ISCSI_TASK_RSP:
411     default:
412	  sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
413		 bhs->opcode, ntohl(bhs->itt));
414	  break;
415     }
416}
417
418/*
419 | go through the out queues looking for work
420 | if either nothing to do, or window is closed
421 | return.
422 */
423static int
424proc_out(isc_session_t *sp)
425{
426     sn_t	*sn = &sp->sn;
427     pduq_t	*pq;
428     int	error, which;
429
430     debug_called(8);
431     error = 0;
432
433     while(sp->flags & ISC_LINK_UP) {
434	  pdu_t *pp;
435	  bhs_t	*bhs;
436	  /*
437	   | check if there is outstanding work in:
438	   | 1- the Immediate queue
439	   | 2- the R2T queue
440	   | 3- the cmd queue, only if the command window allows it.
441	   */
442	  which = BIT(0) | BIT(1);
443	  if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
444	       which |= BIT(2);
445
446	  sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd);
447
448	  if((pq = i_dqueue_snd(sp, which)) == NULL)
449	       break;
450	  sdebug(4, "pq=%p", pq);
451
452	  pp = &pq->pdu;
453	  bhs = &pp->ipdu.bhs;
454	  switch(bhs->opcode) {
455	  case ISCSI_SCSI_CMD:
456	       sn->itt++;
457	       bhs->itt = htonl(sn->itt);
458
459	  case ISCSI_LOGIN_CMD:
460	  case ISCSI_TEXT_CMD:
461	  case ISCSI_LOGOUT_CMD:
462	  case ISCSI_SNACK:
463	  case ISCSI_NOP_OUT:
464	  case ISCSI_TASK_CMD:
465	       bhs->CmdSN = htonl(sn->cmd);
466	       if(bhs->I == 0)
467		    sn->cmd++;
468
469	  case ISCSI_WRITE_DATA:
470	       bhs->ExpStSN = htonl(sn->stat + 1);
471	       break;
472
473	  default:
474	       // XXX: can this happen?
475	       xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
476		      bhs->opcode,
477		      sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
478	       // XXX: and now?
479	  }
480
481	  sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
482		bhs->opcode,
483		sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
484
485	  if(bhs->opcode != ISCSI_NOP_OUT)
486	       /*
487		| enqued till ack is received
488		| note: sosend(...) does not mean the packet left
489		| the host so that freeing resources has to wait
490	        */
491	       i_nqueue_hld(sp, pq);
492
493	  error = isc_sendPDU(sp, pq);
494	  if(bhs->opcode == ISCSI_NOP_OUT)
495	       pdu_free(sp->isc, pq);
496	  if(error) {
497	       xdebug("error=%d opcode=0x%x ccb=%p itt=%x",
498		      error, bhs->opcode, pq->ccb, ntohl(bhs->itt));
499	       i_remove_hld(sp, pq);
500	       switch(error) {
501	       case EPIPE:
502		    sp->flags &= ~ISC_LINK_UP;
503
504	       case EAGAIN:
505		    xdebug("requed");
506		    i_rqueue_pdu(sp, pq);
507		    break;
508
509	       default:
510		    if(pq->ccb) {
511			 xdebug("back to cam");
512			 pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error?
513			 XPT_DONE(sp, pq->ccb);
514			 pdu_free(sp->isc, pq);
515		    }
516		    else
517			 xdebug("we lost it!");
518	       }
519	  }
520     }
521     return error;
522}
523
524/*
525 | survives link breakdowns.
526 */
527static void
528ism_out(void *vp)
529{
530     isc_session_t 	*sp = (isc_session_t *)vp;
531     int		error;
532
533     debug_called(8);
534
535     sp->flags |= ISC_SM_RUNNING;
536     sdebug(3, "started sp->flags=%x", sp->flags);
537     do {
538	  if((sp->flags & ISC_HOLD) == 0) {
539	       error = proc_out(sp);
540	       if(error) {
541		    sdebug(3, "error=%d", error);
542	       }
543	  }
544	  mtx_lock(&sp->io_mtx);
545	  if((sp->flags & ISC_LINK_UP) == 0) {
546	       sdebug(3, "ISC_LINK_UP==0, sp->flags=%x ", sp->flags);
547	       if(sp->soc != NULL)
548		    sdebug(3, "so_state=%x", sp->soc->so_state);
549	       wakeup(&sp->soc);
550	  }
551
552	  if(!(sp->flags & ISC_OQNOTEMPTY)) {
553	       sp->flags |= ISC_OWAITING;
554	       if(msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK) {
555		    if(sp->flags & ISC_CON_RUNNING)
556			 _nop_out(sp);
557	       }
558	       sp->flags &= ~ISC_OWAITING;
559	  }
560	  sp->flags &= ~ISC_OQNOTEMPTY;
561	  mtx_unlock(&sp->io_mtx);
562     } while(sp->flags & ISC_SM_RUN);
563
564     sp->flags &= ~ISC_SM_RUNNING;
565     sdebug(3, "dropped ISC_SM_RUNNING");
566
567     wakeup(&sp->soc);
568     wakeup(sp); // XXX: do we need this one?
569
570#if __FreeBSD_version >= 700000
571     destroy_dev(sp->dev);
572#endif
573
574     debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid);
575
576#if __FreeBSD_version >= 800000
577     kproc_exit(0);
578#else
579     kthread_exit(0);
580#endif
581}
582
583#if 0
584static int
585isc_dump_options(SYSCTL_HANDLER_ARGS)
586{
587     int error;
588     isc_session_t *sp;
589     char	buf[1024], *bp;
590
591     sp = (isc_session_t *)arg1;
592     bp = buf;
593     sprintf(bp, "targetname='%s'", sp->opt.targetName);
594     bp += strlen(bp);
595     sprintf(bp, " targetname='%s'", sp->opt.targetAddress);
596     error = SYSCTL_OUT(req, buf, strlen(buf));
597     return error;
598}
599#endif
600
601static int
602isc_dump_stats(SYSCTL_HANDLER_ARGS)
603{
604     isc_session_t	*sp;
605     struct isc_softc	*sc;
606     char	buf[1024], *bp;
607     int 	error, n;
608
609     sp = (isc_session_t *)arg1;
610     sc = sp->isc;
611
612     bp = buf;
613     n = sizeof(buf);
614     snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
615     bp += strlen(bp);
616     n -= strlen(bp);
617     snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
618		  sp->flags, sc->npdu_alloc, sc->npdu_max);
619     bp += strlen(bp);
620     n -= strlen(bp);
621     snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
622		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
623     error = SYSCTL_OUT(req, buf, strlen(buf));
624     return error;
625}
626
627static int
628isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
629{
630     char	buf[128], **cp;
631     int 	error;
632
633     cp = (char **)arg1;
634     snprintf(buf, sizeof(buf), "%s", *cp);
635     error = SYSCTL_OUT(req, buf, strlen(buf));
636     return error;
637}
638
639static int
640isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
641{
642     char	buf[128], **cp;
643     int 	error;
644
645     cp = (char **)arg1;
646     snprintf(buf, sizeof(buf), "%s", *cp);
647     error = SYSCTL_OUT(req, buf, strlen(buf));
648     return error;
649}
650
651static void
652isc_add_sysctls(isc_session_t *sp)
653{
654     debug_called(8);
655     sdebug(6, "sid=%d %s", sp->sid, devtoname(sp->dev));
656
657     sysctl_ctx_init(&sp->clist);
658     sp->oid = SYSCTL_ADD_NODE(&sp->clist,
659			       SYSCTL_CHILDREN(sp->isc->oid),
660			       OID_AUTO,
661			       devtoname(sp->dev) + 5, // iscsi0
662			       CTLFLAG_RD,
663			       0,
664			       "initiator");
665     SYSCTL_ADD_PROC(&sp->clist,
666		     SYSCTL_CHILDREN(sp->oid),
667		     OID_AUTO,
668		     "targetname",
669		     CTLTYPE_STRING | CTLFLAG_RD,
670		     (void *)&sp->opt.targetName, 0,
671		     isc_sysctl_targetName, "A", "target name");
672
673     SYSCTL_ADD_PROC(&sp->clist,
674		     SYSCTL_CHILDREN(sp->oid),
675		     OID_AUTO,
676		     "targeaddress",
677		     CTLTYPE_STRING | CTLFLAG_RD,
678		     (void *)&sp->opt.targetAddress, 0,
679		     isc_sysctl_targetAddress, "A", "target address");
680
681     SYSCTL_ADD_PROC(&sp->clist,
682		     SYSCTL_CHILDREN(sp->oid),
683		     OID_AUTO,
684		     "stats",
685		     CTLTYPE_STRING | CTLFLAG_RD,
686		     (void *)sp, 0,
687		     isc_dump_stats, "A", "statistics");
688
689     SYSCTL_ADD_INT(&sp->clist,
690		     SYSCTL_CHILDREN(sp->oid),
691		     OID_AUTO,
692		     "douio",
693		     CTLFLAG_RW,
694		     &sp->douio, 0, "enable uio on read");
695}
696
697void
698ism_stop(isc_session_t *sp)
699{
700     struct isc_softc *sc = sp->isc;
701     int	n;
702
703     debug_called(8);
704     sdebug(2, "terminating");
705     /*
706      | first stop the receiver
707      */
708     isc_stop_receiver(sp);
709     /*
710      | now stop the xmitter
711      */
712     n = 5;
713     sp->flags &= ~ISC_SM_RUN;
714     while(n-- && (sp->flags & ISC_SM_RUNNING)) {
715	  sdebug(2, "n=%d", n);
716	  wakeup(&sp->flags);
717	  tsleep(sp, PRIBIO, "-", 5*hz);
718     }
719     sdebug(2, "final n=%d", n);
720     sp->flags &= ~ISC_FFPHASE;
721
722     iscsi_cleanup(sp);
723
724     (void)i_pdu_flush(sp);
725
726     ic_destroy(sp);
727
728     sx_xlock(&sc->unit_sx);
729     free_unr(sc->unit, sp->sid);
730     sx_xunlock(&sc->unit_sx);
731
732     mtx_lock(&sc->isc_mtx);
733     TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
734     sc->nsess--;
735     mtx_unlock(&sc->isc_mtx);
736
737#if __FreeBSD_version < 700000
738     destroy_dev(sp->dev);
739#endif
740
741     mtx_destroy(&sp->rsp_mtx);
742     mtx_destroy(&sp->rsv_mtx);
743     mtx_destroy(&sp->hld_mtx);
744     mtx_destroy(&sp->snd_mtx);
745     mtx_destroy(&sp->io_mtx);
746
747     i_freeopt(&sp->opt);
748
749     if(sysctl_ctx_free(&sp->clist))
750	  xdebug("sysctl_ctx_free failed");
751
752     free(sp, M_ISCSI);
753}
754
755int
756ism_start(isc_session_t *sp)
757{
758     debug_called(8);
759    /*
760     | now is a good time to do some initialization
761     */
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);
768
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);
773     mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF);
774
775     isc_add_sysctls(sp);
776
777     sp->flags |= ISC_SM_RUN;
778
779     debug(4, "starting ism_proc: sp->sid=%d", sp->sid);
780
781#if __FreeBSD_version >= 800000
782     return kproc_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid);
783#else
784     return kthread_create(ism_out, sp, &sp->stp, 0, 0, "isc_out %d", sp->sid);
785#endif
786}
787