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>
63static char *iscsi_driver_version = "2.3.1";
64
65static struct isc_softc *isc;
66
67MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
68MALLOC_DEFINE(M_ISCSIBUF, "iSCbuf", "iSCSI buffers");
69static MALLOC_DEFINE(M_TMP, "iSCtmp", "iSCSI tmp");
70
71#ifdef ISCSI_INITIATOR_DEBUG
72int iscsi_debug = ISCSI_INITIATOR_DEBUG;
73SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
74	"iSCSI driver debug flag");
75
76struct mtx iscsi_dbg_mtx;
77#endif
78
79static int max_sessions = MAX_SESSIONS;
80SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_sessions, CTLFLAG_RDTUN, &max_sessions, MAX_SESSIONS,
81	   "Max sessions allowed");
82static int max_pdus = MAX_PDUS;
83SYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_pdus, CTLFLAG_RDTUN, &max_pdus, MAX_PDUS,
84	   "Max pdu pool");
85
86static char isid[6+1] = {
87     0x80,
88     'D',
89     'I',
90     'B',
91     '0',
92     '0',
93     0
94};
95
96static int	i_create_session(struct cdev *dev, int *ndev);
97
98static int	i_ping(struct cdev *dev);
99static int	i_send(struct cdev *dev, caddr_t arg, struct thread *td);
100static int	i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
101static int	i_setsoc(isc_session_t *sp, int fd, struct thread *td);
102static int	i_fullfeature(struct cdev *dev, int flag);
103
104static d_open_t iscsi_open;
105static d_close_t iscsi_close;
106static d_ioctl_t iscsi_ioctl;
107#ifdef ISCSI_INITIATOR_DEBUG
108static d_read_t iscsi_read;
109#endif
110
111static 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
122static int
123iscsi_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
136static int
137iscsi_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
168static int
169iscsi_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
271static int
272iscsi_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
374static int
375i_ping(struct cdev *dev)
376{
377     return 0;
378}
379/*
380 | low level I/O
381 */
382static int
383i_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
409static int
410i_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 ...
473out:
474     if(error)
475	  pdu_free(sp->isc, pq);
476
477     return error;
478}
479
480static int
481i_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
575static int
576i_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
600static int
601i_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
644static void
645iscsi_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
670static void
671iscsi_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
698static void
699free_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
709static int
710iscsi_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 */
796static void
797iscsi_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
833static int
834iscsi_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
865moduledata_t iscsi_mod = {
866         "iscsi_initiator",
867         (modeventhand_t) iscsi_modevent,
868         0
869};
870
871#ifdef ISCSI_ROOT
872static void
873iscsi_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
883SYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
884#endif
885
886DECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
887MODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1);
888