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