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