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