]> CyberLeo.Net >> Repos - FreeBSD/releng/7.2.git/blob - sys/dev/iscsi/initiator/iscsi.c
Create releng/7.2 from stable/7 in preparation for 7.2-RELEASE.
[FreeBSD/releng/7.2.git] / sys / dev / iscsi / initiator / iscsi.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
29  | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 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/module.h>
40 #include <sys/conf.h>
41 #include <sys/bus.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/mbuf.h>
57 #include <sys/syslog.h>
58 #include <vm/uma.h>
59
60 #include <dev/iscsi/initiator/iscsi.h>
61 #include <dev/iscsi/initiator/iscsivar.h>
62
63 static char *iscsi_driver_version = "2.0.99";
64
65 static struct isc_softc isc;
66
67 MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
68
69 #ifdef ISCSI_INITIATOR_DEBUG
70 int iscsi_debug = ISCSI_INITIATOR_DEBUG;
71 SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
72         "iSCSI driver debug flag");
73
74 struct mtx iscsi_dbg_mtx;
75 #endif
76
77
78 static char isid[6+1] = {
79      0x80,
80      'D',
81      'I',
82      'B',
83      '0',
84      '0',
85      0
86 };
87
88 static int      i_create_session(struct cdev *dev, int *ndev);
89
90 static int      i_ping(struct cdev *dev);
91 static int      i_send(struct cdev *dev, caddr_t arg, struct thread *td);
92 static int      i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
93 static int      i_setsoc(isc_session_t *sp, int fd, struct thread *td);
94
95 static d_open_t iscsi_open;
96 static d_close_t iscsi_close;
97 static d_ioctl_t iscsi_ioctl;
98 #ifdef ISCSI_INITIATOR_DEBUG
99 static d_read_t iscsi_read;
100 #endif
101
102 static struct cdevsw iscsi_cdevsw = {
103      .d_version = D_VERSION,
104      .d_open    = iscsi_open,
105      .d_close   = iscsi_close,
106      .d_ioctl   = iscsi_ioctl,
107 #ifdef ISCSI_INITIATOR_DEBUG
108      .d_read    = iscsi_read,
109 #endif
110      .d_name    = "iSCSI",
111 };
112
113 static int
114 iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td)
115 {
116      debug_called(8);
117
118      debug(7, "dev=%d", minor(dev));
119
120      if(minor(dev) > MAX_SESSIONS) {
121           // should not happen
122           return ENODEV;
123      }
124      if(minor(dev) == MAX_SESSIONS) {
125 #if 1
126           struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
127
128           // this should be in iscsi_start
129           if(sc->cam_sim == NULL)
130                ic_init(sc);
131 #endif
132      }
133      return 0;
134 }
135
136 static int
137 iscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td)
138 {
139      struct isc         *sc;
140      isc_session_t      *sp;
141
142      debug_called(8);
143
144      debug(3, "flag=%x", flag);
145
146      sc = (struct isc *)dev->si_drv1;
147      if(minor(dev) == MAX_SESSIONS) {
148           return 0;
149      }
150      sp = (isc_session_t *)dev->si_drv2;
151      if(sp != NULL) {
152           sdebug(2, "session=%d flags=%x", minor(dev), sp->flags );
153           /*
154            | if still in full phase, this probably means
155            | that something went realy bad.
156            | it could be a result from 'shutdown', in which case
157            | we will ignore it (so buffers can be flushed).
158            | the problem is that there is no way of differentiating
159            | between a shutdown procedure and 'iscontrol' dying.
160            */
161           if(sp->flags & ISC_FFPHASE)
162                // delay in case this is a shutdown.
163                tsleep(sp, PRIBIO, "isc-cls", 60*hz);
164           ism_stop(sp);
165      }
166      debug(2, "done");
167      return 0;
168 }
169
170 static int
171 iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
172 {
173      struct isc         *sc;
174      isc_session_t      *sp;
175      isc_opt_t          *opt;
176      int                error;
177
178      sc = (struct isc *)dev->si_drv1;
179      debug_called(8);
180
181      error = 0;
182      if(minor(dev) == MAX_SESSIONS) {
183           /*
184            | non Session commands
185            */
186           if(sc == NULL)
187                return ENXIO;
188
189           switch(cmd) {
190           case ISCSISETSES:
191                error = i_create_session(dev, (int *)arg);
192                if(error == 0)
193                     
194                break;
195
196           default:
197                error = ENXIO; // XXX:
198           }
199           return error;
200      }
201      sp = (isc_session_t *)dev->si_drv2;
202      /*
203       | session commands
204       */
205      if(sp == NULL)
206           return ENXIO;
207
208      sdebug(6, "dev=%d cmd=%d", minor(dev), (int)(cmd & 0xff));
209
210      switch(cmd) {
211      case ISCSISETSOC:
212           error = i_setsoc(sp, *(u_int *)arg, td);
213           break;
214
215      case ISCSISETOPT:
216           opt = (isc_opt_t *)arg;
217           error = i_setopt(sp, opt);
218           break;
219
220      case ISCSISEND:
221           error = i_send(dev, arg, td);
222           break;
223
224      case ISCSIRECV:
225           error = i_recv(dev, arg, td);
226           break;
227
228      case ISCSIPING:
229           error = i_ping(dev);
230           break;
231
232      case ISCSISTART:
233           error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1);
234           if(error == 0) {
235                sp->proc = td->td_proc;
236                SYSCTL_ADD_UINT(&sp->clist,
237                                SYSCTL_CHILDREN(sp->oid),
238                                OID_AUTO,
239                                "pid",
240                                CTLFLAG_RD,
241                                &sp->proc->p_pid, sizeof(pid_t), "control process id");
242           }
243           break;
244
245      case ISCSIRESTART:
246           error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 2);
247           break;
248
249      case ISCSISTOP:
250           error = ism_fullfeature(dev, 0);
251           break;
252           
253      case ISCSISIGNAL: {
254           int sig = *(int *)arg;
255
256           if(sig < 0 || sig > _SIG_MAXSIG)
257                error = EINVAL;
258           else
259                 sp->signal = sig;
260           break;
261      }
262
263      case ISCSIGETCAM: {
264           iscsi_cam_t *cp = (iscsi_cam_t *)arg;
265
266           error = ic_getCamVals(sp, cp);
267           break;
268      }
269
270      default:
271           error = ENOIOCTL;
272      }
273
274      return error;
275 }
276
277 static int
278 iscsi_read(struct cdev *dev, struct uio *uio, int ioflag)
279 {
280 #ifdef  ISCSI_INITIATOR_DEBUG
281      struct isc_softc   *sc;
282      isc_session_t      *sp;
283      pduq_t             *pq;
284      char               buf[1024];
285
286      sc = (struct isc_softc *)dev->si_drv1;
287      sp = (isc_session_t *)dev->si_drv2;
288      if(minor(dev) == MAX_SESSIONS) {
289           sprintf(buf, "/----- Session ------/\n");
290           uiomove(buf, strlen(buf), uio);
291           int   i = 0;
292
293           TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
294                if(uio->uio_resid == 0)
295                     return 0;
296                sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
297                uiomove(buf, strlen(buf), uio);
298           }
299      }
300      else {
301           int   i = 0;
302           struct socket *so = sp->soc;
303 #define pukeit(i, pq) do {\
304                sprintf(buf, "%03d] %06x %02x %x %ld %jd\n",\
305                        i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
306                        pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
307                        (long)pq->ts.sec, pq->ts.frac);\
308                } while(0)
309
310           sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
311           uiomove(buf, strlen(buf), uio);
312           TAILQ_FOREACH(pq, &sp->hld, pq_link) {
313                if(uio->uio_resid == 0)
314                     return 0;
315                pukeit(i, pq); i++;
316                uiomove(buf, strlen(buf), uio);
317           }
318           sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
319           uiomove(buf, strlen(buf), uio);
320           i = 0;
321           TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
322                if(uio->uio_resid == 0)
323                     return 0;
324                pukeit(i, pq); i++;
325                uiomove(buf, strlen(buf), uio);
326           }
327           sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
328           i = 0;
329           uiomove(buf, strlen(buf), uio);
330           TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
331                if(uio->uio_resid == 0)
332                     return 0;
333                pukeit(i, pq); i++;
334                uiomove(buf, strlen(buf), uio);
335           }
336           sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
337           i = 0;
338           uiomove(buf, strlen(buf), uio);
339           TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
340                if(uio->uio_resid == 0)
341                     return 0;
342                pukeit(i, pq); i++;
343                uiomove(buf, strlen(buf), uio);
344           }
345           sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
346           i = 0;
347           uiomove(buf, strlen(buf), uio);
348           TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
349                if(uio->uio_resid == 0)
350                     return 0;
351                pukeit(i, pq); i++;
352                uiomove(buf, strlen(buf), uio);
353           }
354
355           sprintf(buf, "/---- Stats ---/\n");
356           uiomove(buf, strlen(buf), uio);
357
358           sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
359           uiomove(buf, strlen(buf), uio);
360
361           sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", 
362                   sp->flags, sc->npdu_alloc, sc->npdu_max);
363           uiomove(buf, strlen(buf), uio);
364
365           sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
366                   sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
367           uiomove(buf, strlen(buf), uio);
368
369           sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state);
370           uiomove(buf, strlen(buf), uio);
371
372      }
373 #endif
374      return 0;
375 }
376
377 static int
378 i_ping(struct cdev *dev)
379 {
380      return 0;
381 }
382 /*
383  | low level I/O
384  */
385 static int
386 i_setsoc(isc_session_t *sp, int fd, struct thread *td)
387 {
388      int error = 0;
389
390      if(sp->soc != NULL)
391           isc_stop_receiver(sp);
392
393      error = fget(td, fd, &sp->fp);
394      if(error)
395           return error;
396
397      if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) {
398           sp->td = td;
399           isc_start_receiver(sp);
400      }
401      else {
402           fdrop(sp->fp, td);
403           sp->fp = NULL;
404      }
405
406      return error;
407 }
408
409 static int
410 i_send(struct cdev *dev, caddr_t arg, struct thread *td)
411 {
412      isc_session_t      *sp = (isc_session_t *)dev->si_drv2;
413      struct isc_softc   *sc = (struct isc_softc *)dev->si_drv1;
414      caddr_t            bp;
415      pduq_t             *pq;
416      pdu_t              *pp;
417      int                n, error;
418
419      debug_called(8);
420
421      if(sp->soc == NULL)
422           return ENOTCONN;
423
424      if((pq = pdu_alloc(sc, 0)) == NULL)
425           return EAGAIN;
426      pp = &pq->pdu;
427
428      pq->pdu = *(pdu_t *)arg;
429      if((error = i_prepPDU(sp, pq)) != 0)
430           return error;
431
432      sdebug(4, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
433
434      pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_WAITOK);
435
436      if(pp->ahs_len) {
437           n = pp->ahs_len;
438           copyin(pp->ahs, bp, n);
439           pp->ahs = (ahs_t *)bp;
440           bp += n;
441      }
442      if(pp->ds_len) {
443           n = pp->ds_len;
444           copyin(pp->ds, bp, n); // can fail ...
445           pp->ds = bp;
446           bp += n;
447           while(n & 03) {
448                n++;
449                *bp++ = 0;
450           }
451      }
452
453      error = isc_qout(sp, pq);
454
455      return error;
456 }
457
458 /*
459  | NOTE: must calculate digest if requiered.
460  */
461 static int
462 i_recv(struct cdev *dev, caddr_t arg, struct thread *td)
463 {
464      isc_session_t      *sp = (isc_session_t *)dev->si_drv2;
465      pduq_t             *pq;
466      pdu_t              *pp, *up;
467      caddr_t            bp;
468      int                error, mustfree, cnt;
469      size_t             need, have, n;
470
471      debug_called(8);
472
473      if(sp == NULL)
474           return EIO;
475
476      if(sp->soc == NULL)
477           return ENOTCONN;
478      cnt = 6;     // XXX: maybe the user can request a time out?
479      mtx_lock(&sp->rsp_mtx);
480      while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
481           msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
482           if(cnt-- == 0) break; // XXX: for now, needs work
483      }
484      if(pq != NULL) {
485           sp->stats.nrsp--;
486           TAILQ_REMOVE(&sp->rsp, pq, pq_link);
487      }
488      mtx_unlock(&sp->rsp_mtx);
489
490      sdebug(3, "cnt=%d", cnt);
491
492      if(pq == NULL) {
493           error = ENOTCONN;
494           sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
495           return error;
496      }
497      up = (pdu_t *)arg;
498      pp = &pq->pdu;
499      up->ipdu = pp->ipdu;
500      n = 0;
501      up->ds_len = 0;
502      up->ahs_len = 0;
503      error = 0;
504
505      if(pq->mp) {
506           u_int len;
507
508           // Grr...
509           len = 0;
510           if(pp->ahs_len) {
511                len += pp->ahs_len;
512                if(sp->hdrDigest)
513                     len += 4;
514           }
515           if(pp->ds_len) {
516                len += pp->ds_len;
517                if(sp->hdrDigest)
518                     len += 4;
519           }
520
521           mustfree = 0;
522           if(len > pq->mp->m_len) {
523                mustfree++;
524                bp = malloc(len, M_ISCSI, M_WAITOK);
525                sdebug(4, "need mbufcopy: %d", len);
526                i_mbufcopy(pq->mp, bp, len);
527           } 
528           else
529                bp = mtod(pq->mp, caddr_t);
530
531           if(pp->ahs_len) {
532                need = pp->ahs_len;
533                if(sp->hdrDigest)
534                     need += 4;
535                n = MIN(up->ahs_size, need);
536                error = copyout(bp, (caddr_t)up->ahs, n);
537                up->ahs_len = n;
538                bp += need;
539           }
540           if(!error && pp->ds_len) {
541                need = pp->ds_len;
542                if(sp->hdrDigest)
543                     need += 4;
544                if((have = up->ds_size) == 0) {
545                     have = up->ahs_size - n;
546                     up->ds = (caddr_t)up->ahs + n;
547                }
548                n = MIN(have, need);
549                error = copyout(bp, (caddr_t)up->ds, n);
550                up->ds_len = n;
551           }
552
553           if(mustfree)
554                free(bp, M_ISCSI);
555      }
556
557      sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
558
559      pdu_free(sp->isc, pq);
560
561      return error;
562 }
563
564 static int
565 i_create_session(struct cdev *dev, int *ndev)
566
567      struct isc_softc           *sc = (struct isc_softc *)dev->si_drv1;
568      isc_session_t      *sp;
569      int                error, n;
570
571      debug_called(8);
572      sp = (isc_session_t *)malloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO);
573      if(sp == NULL)
574           return ENOMEM;
575      mtx_lock(&sc->mtx);
576      /*
577       | search for the lowest unused sid
578       */
579      for(n = 0; n < MAX_SESSIONS; n++)
580           if(sc->sessions[n] == NULL)
581                break;
582      if(n == MAX_SESSIONS) {
583           mtx_unlock(&sc->mtx);
584           free(sp, M_ISCSI);
585           return EPERM;
586      }
587      TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
588      sc->nsess++;
589      mtx_unlock(&sc->mtx);
590
591      sc->sessions[n] = sp;
592      sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
593      *ndev = sp->sid = n;
594      sp->isc = sc;
595      sp->dev->si_drv1 = sc;
596      sp->dev->si_drv2 = sp;
597
598      sp->opt.maxRecvDataSegmentLength = 8192;
599      sp->opt.maxXmitDataSegmentLength = 8192;
600
601      sp->opt.maxBurstLength = 65536;    // 64k
602
603      sdebug(2, "sessionID=%d", n);
604      error = ism_start(sp);
605
606      sdebug(2, "error=%d", error);
607
608      return error;
609 }
610
611 #ifdef notused
612 static void
613 iscsi_counters(isc_session_t *sp)
614 {
615      int        h, r, s;
616      pduq_t     *pq;
617
618 #define _puke(i, pq) do {\
619                debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
620                        i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
621                        pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
622                        (long)pq->ts.sec, pq->ts.frac, pq->flags);\
623                } while(0)
624
625      h = r = s = 0; 
626      TAILQ_FOREACH(pq, &sp->hld, pq_link) {
627           _puke(h, pq);
628           h++;
629      }
630      TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
631      TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
632      TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
633      TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
634      debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
635 }
636 #endif
637
638 static void
639 iscsi_shutdown(void *v)
640 {
641      struct isc_softc   *sc = (struct isc_softc *)v;
642      isc_session_t      *sp;
643      int        n;
644
645      debug_called(8);
646      if(sc == NULL) {
647           xdebug("sc is NULL!");
648           return;
649      }
650      if(sc->eh == NULL)
651           debug(2, "sc->eh is NULL!");
652      else {
653           EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
654           debug(2, "done n=%d", sc->nsess);
655      }
656      n = 0;
657      TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
658           debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
659           n++;
660      }
661 }
662
663 static void
664 iscsi_start(void)
665 {
666      struct isc_softc *sc = &isc;
667  
668      debug_called(8);
669
670      memset(sc, 0, sizeof(struct isc_softc));
671
672      sc->dev = make_dev(&iscsi_cdevsw, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi");
673      sc->dev->si_drv1 = sc;
674
675      TAILQ_INIT(&sc->isc_sess);
676      
677      sc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
678                                 NULL, NULL, NULL, NULL,
679                                 0, 0);
680      if(sc->pdu_zone == NULL) {
681           printf("iscsi_initiator: uma_zcreate failed");
682           // XXX: and now what?
683      }
684      uma_zone_set_max(sc->pdu_zone, MAX_PDUS*2+1);
685      mtx_init(&sc->mtx, "iscsi", NULL, MTX_DEF);
686      mtx_init(&sc->pdu_mtx, "iscsi pdu pool", NULL, MTX_DEF);
687
688 #if 0
689      // XXX: this will cause a panic if the
690      //      module is loaded too early
691      if(ic_init(sc) != 0)
692           return;
693 #else
694      sc->cam_sim = NULL;
695 #endif
696      if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
697                                         sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
698           xdebug("shutdown event registration failed\n");
699      /*
700       | sysctl stuff
701       */
702      sysctl_ctx_init(&sc->clist);
703      sc->oid = SYSCTL_ADD_NODE(&sc->clist,
704                                SYSCTL_STATIC_CHILDREN(_net),
705                                OID_AUTO,
706                                "iscsi",
707                                CTLFLAG_RD,
708                                0,
709                                "iSCSI Subsystem");
710
711      SYSCTL_ADD_STRING(&sc->clist,
712                        SYSCTL_CHILDREN(sc->oid),
713                        OID_AUTO,
714                        "driver_version",
715                        CTLFLAG_RD,
716                        iscsi_driver_version,
717                        0,
718                        "iscsi driver version");
719  
720      SYSCTL_ADD_STRING(&sc->clist,
721                        SYSCTL_CHILDREN(sc->oid),
722                        OID_AUTO,
723                        "isid",
724                        CTLFLAG_RW,
725                        isid,
726                        6+1,
727                        "initiator part of the Session Identifier");
728
729      SYSCTL_ADD_INT(&sc->clist,
730                     SYSCTL_CHILDREN(sc->oid),
731                     OID_AUTO,
732                     "sessions",
733                     CTLFLAG_RD,
734                     &sc->nsess,
735                     sizeof(sc->nsess),
736                     "number of active session");
737 }
738
739 /*
740  | Notes:
741  |      unload SHOULD fail if there is activity
742  |      activity: there is/are active session/s
743  */
744 static void
745 iscsi_stop(void)
746 {
747      struct isc_softc   *sc = &isc;
748      isc_session_t      *sp, *sp_tmp;
749
750      debug_called(8);
751
752      /*
753       | go through all the sessions
754       | Note: close should have done this ...
755       */
756      TAILQ_FOREACH_SAFE(sp, &sc->isc_sess, sp_link, sp_tmp) {
757           //XXX: check for activity ...
758           ism_stop(sp);
759      }
760      if(sc->cam_sim != NULL)
761           ic_destroy(sc);
762
763      mtx_destroy(&sc->mtx);
764      mtx_destroy(&sc->pdu_mtx);
765      uma_zdestroy(sc->pdu_zone);
766
767      if(sc->dev)
768           destroy_dev(sc->dev);
769
770      if(sysctl_ctx_free(&sc->clist))
771           xdebug("sysctl_ctx_free failed");
772 }
773
774 static int
775 iscsi_modevent(module_t mod, int what, void *arg)
776 {
777      debug_called(8);
778
779      switch(what) {
780      case MOD_LOAD:
781           iscsi_start();
782           break;
783
784      case MOD_QUIESCE:
785 #if 1
786           if(isc.nsess) {
787                xdebug("iscsi module busy(nsess=%d), cannot unload", isc.nsess);
788                log(LOG_ERR, "iscsi module busy, cannot unload");
789           }
790           return isc.nsess;
791 #endif
792      case MOD_SHUTDOWN:
793           break;
794
795      case MOD_UNLOAD:
796           iscsi_stop();
797           break;
798
799      default:
800           break;
801      }
802      return 0;
803 }
804 moduledata_t iscsi_mod = {
805          "iscsi",
806          (modeventhand_t) iscsi_modevent,
807          0
808 };
809
810 DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
811 MODULE_DEPEND(iscsi, cam, 1, 1, 1);