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