]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/iscsi_initiator/isc_sm.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.git] / sys / dev / iscsi_initiator / isc_sm.c
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$");
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
68 static 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
78 static 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
107 static 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
127 static 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
145 static 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  */
167 static 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
196 static 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
261 int
262 i_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
304 int
305 isc_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  */
335 void
336 ism_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
357 void
358 ism_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  */
423 static int
424 proc_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  */
527 static void
528 ism_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
584 static int
585 isc_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
601 static int
602 isc_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
627 static int
628 isc_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      
639 static int
640 isc_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      
651 static void
652 isc_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
697 void
698 ism_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
755 int
756 ism_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 }