]> CyberLeo.Net >> Repos - FreeBSD/releng/10.0.git/blob - sys/dev/iscsi_initiator/iscsi.c
- Copy stable/10 (r259064) to releng/10.0 as part of the
[FreeBSD/releng/10.0.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      cap_rights_t rights;
386      int error = 0;
387
388      if(sp->soc != NULL)
389           isc_stop_receiver(sp);
390
391      error = fget(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), &sp->fp);
392      if(error)
393           return error;
394
395      error = fgetsock(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT),
396         &sp->soc, 0);
397      if(error == 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 = dev->si_drv2;
413      caddr_t            bp;
414      pduq_t             *pq;
415      pdu_t              *pp;
416      int                n, error;
417
418      debug_called(8);
419
420      if(sp->soc == NULL)
421           return ENOTCONN;
422
423      if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
424           return EAGAIN;
425      pp = &pq->pdu;
426      pq->pdu = *(pdu_t *)arg;
427      if((error = i_prepPDU(sp, pq)) != 0)
428           goto out;
429
430      bp = NULL;
431      if((pq->len - sizeof(union ipdu_u)) > 0) {
432           pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSIBUF, M_NOWAIT);
433           if(pq->buf == NULL) {
434                error = EAGAIN;
435                goto out;
436           }
437      }
438      else
439           pq->buf = NULL; // just in case?
440
441      sdebug(2, "len=%d ahs_len=%d ds_len=%d buf=%zu@%p",
442             pq->len, pp->ahs_len, pp->ds_len, pq->len - sizeof(union ipdu_u), bp);
443
444      if(pp->ahs_len) {
445           // XXX: never tested, looks suspicious
446           n = pp->ahs_len;
447           error = copyin(pp->ahs_addr, bp, n);
448           if(error != 0) {
449                sdebug(3, "copyin ahs: error=%d", error);
450                goto out;
451           }
452           pp->ahs_addr = (ahs_t *)bp;
453           bp += n;
454      }
455      if(pp->ds_len) {
456           n = pp->ds_len;
457           error = copyin(pp->ds_addr, bp, n);
458           if(error != 0) {
459                sdebug(3, "copyin ds: error=%d", error);
460                goto out;
461           }
462           pp->ds_addr = bp;
463           bp += n;
464           while(n & 03) {
465                n++;
466                *bp++ = 0;
467           }
468      }
469
470      error = isc_qout(sp, pq);
471      if(error == 0)
472           wakeup(&sp->flags); // XXX: to 'push' proc_out ...
473 out:
474      if(error)
475           pdu_free(sp->isc, pq);
476
477      return error;
478 }
479
480 static int
481 i_recv(struct cdev *dev, caddr_t arg, struct thread *td)
482 {
483      isc_session_t      *sp = dev->si_drv2;
484      pduq_t             *pq;
485      pdu_t              *pp, *up;
486      caddr_t            bp;
487      int                error, mustfree, cnt;
488      size_t             need, have, n;
489
490      debug_called(8);
491
492      if(sp == NULL)
493           return EIO;
494
495      if(sp->soc == NULL)
496           return ENOTCONN;
497      cnt = 6;     // XXX: maybe the user can request a time out?
498      mtx_lock(&sp->rsp_mtx);
499      while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
500           msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
501           if(cnt-- == 0) break; // XXX: for now, needs work
502      }
503      if(pq != NULL) {
504           sp->stats.nrsp--;
505           TAILQ_REMOVE(&sp->rsp, pq, pq_link);
506      }
507      mtx_unlock(&sp->rsp_mtx);
508
509      sdebug(6, "cnt=%d", cnt);
510
511      if(pq == NULL) {
512           error = ENOTCONN;
513           sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
514           return error;
515      }
516      up = (pdu_t *)arg;
517      pp = &pq->pdu;
518      up->ipdu = pp->ipdu;
519      n = 0;
520      up->ds_len = 0;
521      up->ahs_len = 0;
522      error = 0;
523
524      if(pq->mp) {
525           u_int len;
526
527           // Grr...
528           len = 0;
529           if(pp->ahs_len) {
530                len += pp->ahs_len;
531           }
532           if(pp->ds_len) {
533                len += pp->ds_len;
534           }
535
536           mustfree = 0;
537           if(len > pq->mp->m_len) {
538                mustfree++;
539                bp = malloc(len, M_TMP, M_WAITOK);
540                sdebug(4, "need mbufcopy: %d", len);
541                i_mbufcopy(pq->mp, bp, len);
542           } 
543           else
544                bp = mtod(pq->mp, caddr_t);
545
546           if(pp->ahs_len) {
547                need = pp->ahs_len;
548                n = MIN(up->ahs_size, need);
549                error = copyout(bp, (caddr_t)up->ahs_addr, n);
550                up->ahs_len = n;
551                bp += need;
552           }
553           if(!error && pp->ds_len) {
554                need = pp->ds_len;
555                if((have = up->ds_size) == 0) {
556                     have = up->ahs_size - n;
557                     up->ds_addr = (caddr_t)up->ahs_addr + n;
558                }
559                n = MIN(have, need);
560                error = copyout(bp, (caddr_t)up->ds_addr, n);
561                up->ds_len = n;
562           }
563
564           if(mustfree)
565                free(bp, M_TMP);
566      }
567
568      sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
569
570      pdu_free(sp->isc, pq);
571
572      return error;
573 }
574
575 static int
576 i_fullfeature(struct cdev *dev, int flag)
577 {
578      isc_session_t      *sp = dev->si_drv2;
579      int                error;
580
581      sdebug(2, "flag=%d", flag);
582
583      error = 0;
584      switch(flag) {
585      case 0: // stop
586          sp->flags &= ~ISC_FFPHASE;
587          break;
588      case 1: // start
589          sp->flags |= ISC_FFPHASE;
590          error = ic_init(sp);
591          break;
592      case 2: // restart
593          sp->flags |= ISC_FFPHASE;
594          ism_restart(sp);
595          break;
596      }
597      return error;
598 }
599
600 static int
601 i_create_session(struct cdev *dev, int *ndev)
602
603      struct isc_softc   *sc = dev->si_drv1;
604      isc_session_t      *sp;
605      int                error, n;
606
607      debug_called(8);
608
609      sp = malloc(sizeof(isc_session_t), M_ISCSI, M_WAITOK | M_ZERO);
610      if(sp == NULL)
611           return ENOMEM;
612
613      sx_xlock(&sc->unit_sx);
614      if((n = alloc_unr(sc->unit)) < 0) {
615           sx_unlock(&sc->unit_sx);
616           free(sp, M_ISCSI);
617           xdebug("too many sessions!");
618           return EPERM;
619      }
620      sx_unlock(&sc->unit_sx);
621
622      mtx_lock(&sc->isc_mtx);
623      TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
624      isc->nsess++;
625      mtx_unlock(&sc->isc_mtx);
626
627      sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
628      *ndev = sp->sid = n;
629      sp->isc = sc;
630      sp->dev->si_drv1 = sc;
631      sp->dev->si_drv2 = sp;
632
633      sp->opt.maxRecvDataSegmentLength = 8192;
634      sp->opt.maxXmitDataSegmentLength = 8192;
635      sp->opt.maxBurstLength = 65536;    // 64k
636      sp->opt.maxluns = ISCSI_MAX_LUNS;
637
638      error = ism_start(sp);
639
640      return error;
641 }
642
643 #ifdef notused
644 static void
645 iscsi_counters(isc_session_t *sp)
646 {
647      int        h, r, s;
648      pduq_t     *pq;
649
650 #define _puke(i, pq) do {\
651                debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
652                        i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
653                        pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
654                        (long)pq->ts.sec, pq->ts.frac, pq->flags);\
655                } while(0)
656
657      h = r = s = 0; 
658      TAILQ_FOREACH(pq, &sp->hld, pq_link) {
659           _puke(h, pq);
660           h++;
661      }
662      TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
663      TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
664      TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
665      TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
666      debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
667 }
668 #endif
669
670 static void
671 iscsi_shutdown(void *v)
672 {
673      struct isc_softc   *sc = v;
674      isc_session_t      *sp;
675      int        n;
676
677      debug_called(8);
678      if(sc == NULL) {
679           xdebug("sc is NULL!");
680           return;
681      }
682 #ifdef DO_EVENTHANDLER
683      if(sc->eh == NULL)
684           debug(2, "sc->eh is NULL");
685      else {
686           EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
687           debug(2, "done n=%d", sc->nsess);
688      }
689 #endif
690      n = 0;
691      TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
692           debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
693           n++;
694      }
695      debug(2, "done");
696 }
697
698 static void
699 free_pdus(struct isc_softc *sc)
700 {
701      debug_called(8);
702
703      if(sc->pdu_zone != NULL) {
704           uma_zdestroy(sc->pdu_zone);
705           sc->pdu_zone = NULL;
706      }
707 }
708
709 static int
710 iscsi_start(void)
711 {
712      debug_called(8);
713
714      TUNABLE_INT_FETCH("net.iscsi_initiator.max_sessions", &max_sessions);
715      TUNABLE_INT_FETCH("net.iscsi_initiator.max_pdus", &max_pdus);
716
717      isc =  malloc(sizeof(struct isc_softc), M_ISCSI, M_ZERO|M_WAITOK);
718      mtx_init(&isc->isc_mtx, "iscsi-isc", NULL, MTX_DEF);
719
720      TAILQ_INIT(&isc->isc_sess);
721      /*
722       | now init the free pdu list
723       */
724      isc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
725                                  NULL, NULL, NULL, NULL,
726                                  0, 0);
727      uma_zone_set_max(isc->pdu_zone, max_pdus);
728      isc->unit = new_unrhdr(0, max_sessions-1, NULL);
729      sx_init(&isc->unit_sx, "iscsi sx");
730
731 #ifdef DO_EVENTHANDLER
732      if((isc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
733                                         sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
734           xdebug("shutdown event registration failed\n");
735 #endif
736      /*
737       | sysctl stuff
738       */
739      sysctl_ctx_init(&isc->clist);
740      isc->oid = SYSCTL_ADD_NODE(&isc->clist,
741                                SYSCTL_STATIC_CHILDREN(_net),
742                                OID_AUTO,
743                                "iscsi_initiator",
744                                CTLFLAG_RD,
745                                0,
746                                "iSCSI Subsystem");
747
748      SYSCTL_ADD_STRING(&isc->clist,
749                        SYSCTL_CHILDREN(isc->oid),
750                        OID_AUTO,
751                        "driver_version",
752                        CTLFLAG_RD,
753                        iscsi_driver_version,
754                        0,
755                        "iscsi driver version");
756  
757      SYSCTL_ADD_STRING(&isc->clist,
758                        SYSCTL_CHILDREN(isc->oid),
759                        OID_AUTO,
760                        "isid",
761                        CTLFLAG_RW,
762                        isid,
763                        6+1,
764                        "initiator part of the Session Identifier");
765
766      SYSCTL_ADD_INT(&isc->clist,
767                     SYSCTL_CHILDREN(isc->oid),
768                     OID_AUTO,
769                     "sessions",
770                     CTLFLAG_RD,
771                     &isc->nsess,
772                     sizeof(isc->nsess),
773                     "number of active session");
774
775 #ifdef ISCSI_INITIATOR_DEBUG
776      mtx_init(&iscsi_dbg_mtx, "iscsi_dbg", NULL, MTX_DEF);
777 #endif
778
779      isc->dev = make_dev_credf(MAKEDEV_CHECKNAME, &iscsi_cdevsw, max_sessions,
780                                NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi");
781      if (isc->dev == NULL) {
782           xdebug("iscsi_initiator: make_dev_credf failed");
783           return (EEXIST);
784      }
785      isc->dev->si_drv1 = isc;
786
787      printf("iscsi: version %s\n", iscsi_driver_version);
788      return (0);
789 }
790
791 /*
792  | Notes:
793  |      unload SHOULD fail if there is activity
794  |      activity: there is/are active session/s
795  */
796 static void
797 iscsi_stop(void)
798 {
799      isc_session_t      *sp, *sp_tmp;
800
801      debug_called(8);
802
803      /*
804       | go through all the sessions
805       | Note: close should have done this ...
806       */
807      TAILQ_FOREACH_SAFE(sp, &isc->isc_sess, sp_link, sp_tmp) {
808           //XXX: check for activity ...
809           ism_stop(sp);
810           if(sp->cam_sim != NULL)
811                ic_destroy(sp);
812      }
813      mtx_destroy(&isc->isc_mtx);
814      sx_destroy(&isc->unit_sx);
815
816      free_pdus(isc);
817
818      if(isc->dev)
819           destroy_dev(isc->dev);
820
821      if(sysctl_ctx_free(&isc->clist))
822           xdebug("sysctl_ctx_free failed");
823
824      iscsi_shutdown(isc); // XXX: check EVENTHANDLER_ ...
825
826 #ifdef ISCSI_INITIATOR_DEBUG
827      mtx_destroy(&iscsi_dbg_mtx);
828 #endif
829
830      free(isc, M_ISCSI);
831 }
832
833 static int
834 iscsi_modevent(module_t mod, int what, void *arg)
835 {
836      int error = 0;
837
838      debug_called(8);
839
840      switch(what) {
841      case MOD_LOAD:
842           error = iscsi_start();
843           break;
844
845      case MOD_QUIESCE:
846           if(isc->nsess) {
847                xdebug("iscsi module busy(nsess=%d), cannot unload", isc->nsess);
848                log(LOG_ERR, "iscsi module busy, cannot unload");
849           }
850           return isc->nsess;
851
852      case MOD_SHUTDOWN:
853           break;
854
855      case MOD_UNLOAD:
856           iscsi_stop();
857           break;
858
859      default:
860           break;
861      }
862      return (error);
863 }
864
865 moduledata_t iscsi_mod = {
866          "iscsi_initiator",
867          (modeventhand_t) iscsi_modevent,
868          0
869 };
870
871 #ifdef ISCSI_ROOT
872 static void
873 iscsi_rootconf(void)
874 {
875 #if 0
876         nfs_setup_diskless();
877         if (nfs_diskless_valid)
878                 rootdevnames[0] = "nfs:";
879 #endif
880         printf("** iscsi_rootconf **\n");
881 }
882
883 SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
884 #endif
885
886 DECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
887 MODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1);