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