iscsi.c revision 171568
1171568Sscottl/*-
2171568Sscottl * Copyright (c) 2005-2007 Daniel Braniss <danny@cs.huji.ac.il>
3171568Sscottl * All rights reserved.
4171568Sscottl *
5171568Sscottl * Redistribution and use in source and binary forms, with or without
6171568Sscottl * modification, are permitted provided that the following conditions
7171568Sscottl * are met:
8171568Sscottl * 1. Redistributions of source code must retain the above copyright
9171568Sscottl *    notice, this list of conditions and the following disclaimer.
10171568Sscottl * 2. Redistributions in binary form must reproduce the above copyright
11171568Sscottl *    notice, this list of conditions and the following disclaimer in the
12171568Sscottl *    documentation and/or other materials provided with the distribution.
13171568Sscottl *
14171568Sscottl * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15171568Sscottl * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16171568Sscottl * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17171568Sscottl * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18171568Sscottl * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19171568Sscottl * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20171568Sscottl * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21171568Sscottl * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22171568Sscottl * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23171568Sscottl * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24171568Sscottl * SUCH DAMAGE.
25171568Sscottl *
26171568Sscottl */
27171568Sscottl/*
28171568Sscottl | iSCSI
29171568Sscottl | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 danny Exp danny $
30171568Sscottl */
31171568Sscottl
32171568Sscottl#include <sys/cdefs.h>
33171568Sscottl__FBSDID("$FreeBSD: head/sys/dev/iscsi/initiator/iscsi.c 171568 2007-07-24 15:35:02Z scottl $");
34171568Sscottl
35171568Sscottl#include "opt_iscsi_initiator.h"
36171568Sscottl
37171568Sscottl#include <sys/param.h>
38171568Sscottl#include <sys/kernel.h>
39171568Sscottl#include <sys/module.h>
40171568Sscottl#include <sys/conf.h>
41171568Sscottl#include <sys/bus.h>
42171568Sscottl#include <sys/systm.h>
43171568Sscottl#include <sys/malloc.h>
44171568Sscottl#include <sys/ctype.h>
45171568Sscottl#include <sys/errno.h>
46171568Sscottl#include <sys/sysctl.h>
47171568Sscottl#include <sys/file.h>
48171568Sscottl#include <sys/uio.h>
49171568Sscottl#include <sys/socketvar.h>
50171568Sscottl#include <sys/socket.h>
51171568Sscottl#include <sys/protosw.h>
52171568Sscottl#include <sys/proc.h>
53171568Sscottl#include <sys/ioccom.h>
54171568Sscottl#include <sys/queue.h>
55171568Sscottl#include <sys/kthread.h>
56171568Sscottl#include <sys/mbuf.h>
57171568Sscottl#include <sys/syslog.h>
58171568Sscottl#include <vm/uma.h>
59171568Sscottl
60171568Sscottl#include <dev/iscsi/initiator/iscsi.h>
61171568Sscottl#include <dev/iscsi/initiator/iscsivar.h>
62171568Sscottl
63171568Sscottlstatic char *iscsi_driver_version = "2.0.99";
64171568Sscottl
65171568Sscottlstatic struct isc_softc isc;
66171568Sscottl
67171568SscottlMALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
68171568Sscottl
69171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
70171568Sscottlint iscsi_debug = ISCSI_INITIATOR_DEBUG;
71171568SscottlSYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
72171568Sscottl	"iSCSI driver debug flag");
73171568Sscottl
74171568Sscottlstruct mtx iscsi_dbg_mtx;
75171568Sscottl#endif
76171568Sscottl
77171568Sscottl
78171568Sscottlstatic char isid[6+1] = {
79171568Sscottl     0x80,
80171568Sscottl     'D',
81171568Sscottl     'I',
82171568Sscottl     'B',
83171568Sscottl     '0',
84171568Sscottl     '0',
85171568Sscottl     0
86171568Sscottl};
87171568Sscottl
88171568Sscottlstatic int	i_create_session(struct cdev *dev, int *ndev);
89171568Sscottl
90171568Sscottlstatic int	i_ping(struct cdev *dev);
91171568Sscottlstatic int	i_send(struct cdev *dev, caddr_t arg, struct thread *td);
92171568Sscottlstatic int	i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
93171568Sscottlstatic int	i_setsoc(isc_session_t *sp, int fd, struct thread *td);
94171568Sscottl
95171568Sscottlstatic d_open_t iscsi_open;
96171568Sscottlstatic d_close_t iscsi_close;
97171568Sscottlstatic d_ioctl_t iscsi_ioctl;
98171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
99171568Sscottlstatic d_read_t iscsi_read;
100171568Sscottl#endif
101171568Sscottl
102171568Sscottlstatic struct cdevsw iscsi_cdevsw = {
103171568Sscottl     .d_version = D_VERSION,
104171568Sscottl     .d_open	= iscsi_open,
105171568Sscottl     .d_close	= iscsi_close,
106171568Sscottl     .d_ioctl	= iscsi_ioctl,
107171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
108171568Sscottl     .d_read	= iscsi_read,
109171568Sscottl#endif
110171568Sscottl     .d_name	= "iSCSI",
111171568Sscottl};
112171568Sscottl
113171568Sscottlstatic int
114171568Sscottliscsi_open(struct cdev *dev, int flags, int otype, struct thread *td)
115171568Sscottl{
116171568Sscottl     debug_called(8);
117171568Sscottl
118171568Sscottl     debug(7, "dev=%d", minor(dev));
119171568Sscottl
120171568Sscottl     if(minor(dev) > MAX_SESSIONS) {
121171568Sscottl	  // should not happen
122171568Sscottl          return ENODEV;
123171568Sscottl     }
124171568Sscottl     if(minor(dev) == MAX_SESSIONS) {
125171568Sscottl#if 1
126171568Sscottl	  struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
127171568Sscottl
128171568Sscottl	  // this should be in iscsi_start
129171568Sscottl	  if(sc->cam_sim == NULL)
130171568Sscottl	       ic_init(sc);
131171568Sscottl#endif
132171568Sscottl     }
133171568Sscottl     return 0;
134171568Sscottl}
135171568Sscottl
136171568Sscottlstatic int
137171568Sscottliscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td)
138171568Sscottl{
139171568Sscottl     struct isc		*sc;
140171568Sscottl     isc_session_t	*sp;
141171568Sscottl
142171568Sscottl     debug_called(8);
143171568Sscottl
144171568Sscottl     debug(3, "flag=%x", flag);
145171568Sscottl
146171568Sscottl     sc = (struct isc *)dev->si_drv1;
147171568Sscottl     if(minor(dev) == MAX_SESSIONS) {
148171568Sscottl	  return 0;
149171568Sscottl     }
150171568Sscottl     sp = (isc_session_t *)dev->si_drv2;
151171568Sscottl     if(sp != NULL) {
152171568Sscottl	  sdebug(2, "session=%d flags=%x", minor(dev), sp->flags );
153171568Sscottl	  /*
154171568Sscottl	   | if still in full phase, this probably means
155171568Sscottl	   | that something went realy bad.
156171568Sscottl	   | it could be a result from 'shutdown', in which case
157171568Sscottl	   | we will ignore it (so buffers can be flushed).
158171568Sscottl	   | the problem is that there is no way of differentiating
159171568Sscottl	   | between a shutdown procedure and 'iscontrol' dying.
160171568Sscottl	   */
161171568Sscottl	  if(sp->flags & ISC_FFPHASE)
162171568Sscottl	       // delay in case this is a shutdown.
163171568Sscottl	       tsleep(sp, PRIBIO, "isc-cls", 60*hz);
164171568Sscottl	  ism_stop(sp);
165171568Sscottl     }
166171568Sscottl     debug(2, "done");
167171568Sscottl     return 0;
168171568Sscottl}
169171568Sscottl
170171568Sscottlstatic int
171171568Sscottliscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
172171568Sscottl{
173171568Sscottl     struct isc		*sc;
174171568Sscottl     isc_session_t	*sp;
175171568Sscottl     isc_opt_t		*opt;
176171568Sscottl     int		error;
177171568Sscottl
178171568Sscottl     sc = (struct isc *)dev->si_drv1;
179171568Sscottl     debug_called(8);
180171568Sscottl
181171568Sscottl     error = 0;
182171568Sscottl     if(minor(dev) == MAX_SESSIONS) {
183171568Sscottl	  /*
184171568Sscottl	   | non Session commands
185171568Sscottl	   */
186171568Sscottl	  if(sc == NULL)
187171568Sscottl	       return ENXIO;
188171568Sscottl
189171568Sscottl	  switch(cmd) {
190171568Sscottl	  case ISCSISETSES:
191171568Sscottl	       error = i_create_session(dev, (int *)arg);
192171568Sscottl	       if(error == 0)
193171568Sscottl
194171568Sscottl	       break;
195171568Sscottl
196171568Sscottl	  default:
197171568Sscottl	       error = ENXIO; // XXX:
198171568Sscottl	  }
199171568Sscottl	  return error;
200171568Sscottl     }
201171568Sscottl     sp = (isc_session_t *)dev->si_drv2;
202171568Sscottl     /*
203171568Sscottl      | session commands
204171568Sscottl      */
205171568Sscottl     if(sp == NULL)
206171568Sscottl	  return ENXIO;
207171568Sscottl
208171568Sscottl     sdebug(6, "dev=%d cmd=%d", minor(dev), (int)(cmd & 0xff));
209171568Sscottl
210171568Sscottl     switch(cmd) {
211171568Sscottl     case ISCSISETSOC:
212171568Sscottl	  error = i_setsoc(sp, *(u_int *)arg, td);
213171568Sscottl	  break;
214171568Sscottl
215171568Sscottl     case ISCSISETOPT:
216171568Sscottl	  opt = (isc_opt_t *)arg;
217171568Sscottl	  error = i_setopt(sp, opt);
218171568Sscottl	  break;
219171568Sscottl
220171568Sscottl     case ISCSISEND:
221171568Sscottl	  error = i_send(dev, arg, td);
222171568Sscottl	  break;
223171568Sscottl
224171568Sscottl     case ISCSIRECV:
225171568Sscottl	  error = i_recv(dev, arg, td);
226171568Sscottl	  break;
227171568Sscottl
228171568Sscottl     case ISCSIPING:
229171568Sscottl	  error = i_ping(dev);
230171568Sscottl	  break;
231171568Sscottl
232171568Sscottl     case ISCSISTART:
233171568Sscottl	  error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1);
234171568Sscottl	  if(error == 0) {
235171568Sscottl	       sp->proc = td->td_proc;
236171568Sscottl	       SYSCTL_ADD_UINT(&sp->clist,
237171568Sscottl			       SYSCTL_CHILDREN(sp->oid),
238171568Sscottl			       OID_AUTO,
239171568Sscottl			       "pid",
240171568Sscottl			       CTLFLAG_RD,
241171568Sscottl			       &sp->proc->p_pid, sizeof(pid_t), "control process id");
242171568Sscottl	  }
243171568Sscottl	  break;
244171568Sscottl
245171568Sscottl     case ISCSIRESTART:
246171568Sscottl	  error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 2);
247171568Sscottl	  break;
248171568Sscottl
249171568Sscottl     case ISCSISTOP:
250171568Sscottl	  error = ism_fullfeature(dev, 0);
251171568Sscottl	  break;
252171568Sscottl
253171568Sscottl     case ISCSISIGNAL: {
254171568Sscottl	  int sig = *(int *)arg;
255171568Sscottl
256171568Sscottl	  if(sig < 0 || sig > _SIG_MAXSIG)
257171568Sscottl	       error = EINVAL;
258171568Sscottl	  else
259171568Sscottl		sp->signal = sig;
260171568Sscottl	  break;
261171568Sscottl     }
262171568Sscottl
263171568Sscottl     case ISCSIGETCAM: {
264171568Sscottl	  iscsi_cam_t *cp = (iscsi_cam_t *)arg;
265171568Sscottl
266171568Sscottl	  error = ic_getCamVals(sp, cp);
267171568Sscottl	  break;
268171568Sscottl     }
269171568Sscottl
270171568Sscottl     default:
271171568Sscottl	  error = ENOIOCTL;
272171568Sscottl     }
273171568Sscottl
274171568Sscottl     return error;
275171568Sscottl}
276171568Sscottl
277171568Sscottlstatic int
278171568Sscottliscsi_read(struct cdev *dev, struct uio *uio, int ioflag)
279171568Sscottl{
280171568Sscottl#ifdef  ISCSI_INITIATOR_DEBUG
281171568Sscottl     struct isc_softc	*sc;
282171568Sscottl     isc_session_t	*sp;
283171568Sscottl     pduq_t 		*pq;
284171568Sscottl     char		buf[1024];
285171568Sscottl
286171568Sscottl     sc = (struct isc_softc *)dev->si_drv1;
287171568Sscottl     sp = (isc_session_t *)dev->si_drv2;
288171568Sscottl     if(minor(dev) == MAX_SESSIONS) {
289171568Sscottl	  sprintf(buf, "/----- Session ------/\n");
290171568Sscottl	  uiomove(buf, strlen(buf), uio);
291171568Sscottl	  int	i = 0;
292171568Sscottl
293171568Sscottl	  TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
294171568Sscottl	       if(uio->uio_resid == 0)
295171568Sscottl		    return 0;
296171568Sscottl	       sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
297171568Sscottl	       uiomove(buf, strlen(buf), uio);
298171568Sscottl	  }
299171568Sscottl     }
300171568Sscottl     else {
301171568Sscottl	  int	i = 0;
302171568Sscottl	  struct socket	*so = sp->soc;
303171568Sscottl#define pukeit(i, pq) do {\
304171568Sscottl	       sprintf(buf, "%03d] %06x %02x %x %ld %jd\n",\
305171568Sscottl		       i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
306171568Sscottl		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
307171568Sscottl		       (long)pq->ts.sec, pq->ts.frac);\
308171568Sscottl	       } while(0)
309171568Sscottl
310171568Sscottl	  sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
311171568Sscottl	  uiomove(buf, strlen(buf), uio);
312171568Sscottl	  TAILQ_FOREACH(pq, &sp->hld, pq_link) {
313171568Sscottl	       if(uio->uio_resid == 0)
314171568Sscottl		    return 0;
315171568Sscottl	       pukeit(i, pq); i++;
316171568Sscottl	       uiomove(buf, strlen(buf), uio);
317171568Sscottl	  }
318171568Sscottl	  sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
319171568Sscottl	  uiomove(buf, strlen(buf), uio);
320171568Sscottl	  i = 0;
321171568Sscottl	  TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
322171568Sscottl	       if(uio->uio_resid == 0)
323171568Sscottl		    return 0;
324171568Sscottl	       pukeit(i, pq); i++;
325171568Sscottl	       uiomove(buf, strlen(buf), uio);
326171568Sscottl	  }
327171568Sscottl	  sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
328171568Sscottl	  i = 0;
329171568Sscottl	  uiomove(buf, strlen(buf), uio);
330171568Sscottl	  TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
331171568Sscottl	       if(uio->uio_resid == 0)
332171568Sscottl		    return 0;
333171568Sscottl	       pukeit(i, pq); i++;
334171568Sscottl	       uiomove(buf, strlen(buf), uio);
335171568Sscottl	  }
336171568Sscottl	  sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
337171568Sscottl	  i = 0;
338171568Sscottl	  uiomove(buf, strlen(buf), uio);
339171568Sscottl	  TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
340171568Sscottl	       if(uio->uio_resid == 0)
341171568Sscottl		    return 0;
342171568Sscottl	       pukeit(i, pq); i++;
343171568Sscottl	       uiomove(buf, strlen(buf), uio);
344171568Sscottl	  }
345171568Sscottl	  sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
346171568Sscottl	  i = 0;
347171568Sscottl	  uiomove(buf, strlen(buf), uio);
348171568Sscottl	  TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
349171568Sscottl	       if(uio->uio_resid == 0)
350171568Sscottl		    return 0;
351171568Sscottl	       pukeit(i, pq); i++;
352171568Sscottl	       uiomove(buf, strlen(buf), uio);
353171568Sscottl	  }
354171568Sscottl
355171568Sscottl	  sprintf(buf, "/---- Stats ---/\n");
356171568Sscottl	  uiomove(buf, strlen(buf), uio);
357171568Sscottl
358171568Sscottl	  sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
359171568Sscottl	  uiomove(buf, strlen(buf), uio);
360171568Sscottl
361171568Sscottl	  sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n",
362171568Sscottl		  sp->flags, sc->npdu_alloc, sc->npdu_max);
363171568Sscottl	  uiomove(buf, strlen(buf), uio);
364171568Sscottl
365171568Sscottl	  sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
366171568Sscottl		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
367171568Sscottl	  uiomove(buf, strlen(buf), uio);
368171568Sscottl
369171568Sscottl	  sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state);
370171568Sscottl	  uiomove(buf, strlen(buf), uio);
371171568Sscottl
372171568Sscottl     }
373171568Sscottl#endif
374171568Sscottl     return 0;
375171568Sscottl}
376171568Sscottl
377171568Sscottlstatic int
378171568Sscottli_ping(struct cdev *dev)
379171568Sscottl{
380171568Sscottl     return 0;
381171568Sscottl}
382171568Sscottl/*
383171568Sscottl | low level I/O
384171568Sscottl */
385171568Sscottlstatic int
386171568Sscottli_setsoc(isc_session_t *sp, int fd, struct thread *td)
387171568Sscottl{
388171568Sscottl     int error = 0;
389171568Sscottl
390171568Sscottl     if(sp->soc != NULL)
391171568Sscottl	  isc_stop_receiver(sp);
392171568Sscottl
393171568Sscottl     error = fget(td, fd, &sp->fp);
394171568Sscottl     if(error)
395171568Sscottl	  return error;
396171568Sscottl
397171568Sscottl     if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) {
398171568Sscottl	  sp->td = td;
399171568Sscottl	  isc_start_receiver(sp);
400171568Sscottl     }
401171568Sscottl     else {
402171568Sscottl	  fdrop(sp->fp, td);
403171568Sscottl	  sp->fp = NULL;
404171568Sscottl     }
405171568Sscottl
406171568Sscottl     return error;
407171568Sscottl}
408171568Sscottl
409171568Sscottlstatic int
410171568Sscottli_send(struct cdev *dev, caddr_t arg, struct thread *td)
411171568Sscottl{
412171568Sscottl     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
413171568Sscottl     struct isc_softc	*sc = (struct isc_softc *)dev->si_drv1;
414171568Sscottl     caddr_t		bp;
415171568Sscottl     pduq_t		*pq;
416171568Sscottl     pdu_t		*pp;
417171568Sscottl     int		n, error;
418171568Sscottl
419171568Sscottl     debug_called(8);
420171568Sscottl
421171568Sscottl     if(sp->soc == NULL)
422171568Sscottl	  return ENOTCONN;
423171568Sscottl
424171568Sscottl     if((pq = pdu_alloc(sc, 0)) == NULL)
425171568Sscottl	  return EAGAIN;
426171568Sscottl     pp = &pq->pdu;
427171568Sscottl
428171568Sscottl     pq->pdu = *(pdu_t *)arg;
429171568Sscottl     if((error = i_prepPDU(sp, pq)) != 0)
430171568Sscottl	  return error;
431171568Sscottl
432171568Sscottl     sdebug(4, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
433171568Sscottl
434171568Sscottl     pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_WAITOK);
435171568Sscottl
436171568Sscottl     if(pp->ahs_len) {
437171568Sscottl	  n = pp->ahs_len;
438171568Sscottl	  copyin(pp->ahs, bp, n);
439171568Sscottl	  pp->ahs = (ahs_t *)bp;
440171568Sscottl	  bp += n;
441171568Sscottl     }
442171568Sscottl     if(pp->ds_len) {
443171568Sscottl	  n = pp->ds_len;
444171568Sscottl	  copyin(pp->ds, bp, n); // can fail ...
445171568Sscottl	  pp->ds = bp;
446171568Sscottl	  bp += n;
447171568Sscottl	  while(n & 03) {
448171568Sscottl	       n++;
449171568Sscottl	       *bp++ = 0;
450171568Sscottl	  }
451171568Sscottl     }
452171568Sscottl
453171568Sscottl     error = isc_qout(sp, pq);
454171568Sscottl
455171568Sscottl     return error;
456171568Sscottl}
457171568Sscottl
458171568Sscottl/*
459171568Sscottl | NOTE: must calculate digest if requiered.
460171568Sscottl */
461171568Sscottlstatic int
462171568Sscottli_recv(struct cdev *dev, caddr_t arg, struct thread *td)
463171568Sscottl{
464171568Sscottl     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
465171568Sscottl     pduq_t		*pq;
466171568Sscottl     pdu_t		*pp, *up;
467171568Sscottl     caddr_t		bp;
468171568Sscottl     int		error, mustfree, cnt;
469171568Sscottl     size_t		need, have, n;
470171568Sscottl
471171568Sscottl     debug_called(8);
472171568Sscottl
473171568Sscottl     if(sp == NULL)
474171568Sscottl	  return EIO;
475171568Sscottl
476171568Sscottl     if(sp->soc == NULL)
477171568Sscottl	  return ENOTCONN;
478171568Sscottl     cnt = 6;     // XXX: maybe the user can request a time out?
479171568Sscottl     mtx_lock(&sp->rsp_mtx);
480171568Sscottl     while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
481171568Sscottl	  msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
482171568Sscottl	  if(cnt-- == 0) break; // XXX: for now, needs work
483171568Sscottl     }
484171568Sscottl     if(pq != NULL) {
485171568Sscottl	  sp->stats.nrsp--;
486171568Sscottl	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
487171568Sscottl     }
488171568Sscottl     mtx_unlock(&sp->rsp_mtx);
489171568Sscottl
490171568Sscottl     sdebug(3, "cnt=%d", cnt);
491171568Sscottl
492171568Sscottl     if(pq == NULL) {
493171568Sscottl	  error = ENOTCONN;
494171568Sscottl	  sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
495171568Sscottl	  return error;
496171568Sscottl     }
497171568Sscottl     up = (pdu_t *)arg;
498171568Sscottl     pp = &pq->pdu;
499171568Sscottl     up->ipdu = pp->ipdu;
500171568Sscottl     n = 0;
501171568Sscottl     up->ds_len = 0;
502171568Sscottl     up->ahs_len = 0;
503171568Sscottl     error = 0;
504171568Sscottl
505171568Sscottl     if(pq->mp) {
506171568Sscottl	  u_int	len;
507171568Sscottl
508171568Sscottl	  // Grr...
509171568Sscottl	  len = 0;
510171568Sscottl	  if(pp->ahs_len) {
511171568Sscottl	       len += pp->ahs_len;
512171568Sscottl	       if(sp->hdrDigest)
513171568Sscottl		    len += 4;
514171568Sscottl	  }
515171568Sscottl	  if(pp->ds_len) {
516171568Sscottl	       len += pp->ds_len;
517171568Sscottl	       if(sp->hdrDigest)
518171568Sscottl		    len += 4;
519171568Sscottl	  }
520171568Sscottl
521171568Sscottl	  mustfree = 0;
522171568Sscottl	  if(len > pq->mp->m_len) {
523171568Sscottl	       mustfree++;
524171568Sscottl	       bp = malloc(len, M_ISCSI, M_WAITOK);
525171568Sscottl	       sdebug(4, "need mbufcopy: %d", len);
526171568Sscottl	       i_mbufcopy(pq->mp, bp, len);
527171568Sscottl	  }
528171568Sscottl	  else
529171568Sscottl	       bp = mtod(pq->mp, caddr_t);
530171568Sscottl
531171568Sscottl	  if(pp->ahs_len) {
532171568Sscottl	       need = pp->ahs_len;
533171568Sscottl	       if(sp->hdrDigest)
534171568Sscottl		    need += 4;
535171568Sscottl	       n = MIN(up->ahs_size, need);
536171568Sscottl	       error = copyout(bp, (caddr_t)up->ahs, n);
537171568Sscottl	       up->ahs_len = n;
538171568Sscottl	       bp += need;
539171568Sscottl	  }
540171568Sscottl	  if(!error && pp->ds_len) {
541171568Sscottl	       need = pp->ds_len;
542171568Sscottl	       if(sp->hdrDigest)
543171568Sscottl		    need += 4;
544171568Sscottl	       if((have = up->ds_size) == 0) {
545171568Sscottl		    have = up->ahs_size - n;
546171568Sscottl		    up->ds = (caddr_t)up->ahs + n;
547171568Sscottl	       }
548171568Sscottl	       n = MIN(have, need);
549171568Sscottl	       error = copyout(bp, (caddr_t)up->ds, n);
550171568Sscottl	       up->ds_len = n;
551171568Sscottl	  }
552171568Sscottl
553171568Sscottl	  if(mustfree)
554171568Sscottl	       free(bp, M_ISCSI);
555171568Sscottl     }
556171568Sscottl
557171568Sscottl     sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
558171568Sscottl
559171568Sscottl     pdu_free(sp->isc, pq);
560171568Sscottl
561171568Sscottl     return error;
562171568Sscottl}
563171568Sscottl
564171568Sscottlstatic int
565171568Sscottli_create_session(struct cdev *dev, int *ndev)
566171568Sscottl{
567171568Sscottl     struct isc_softc		*sc = (struct isc_softc *)dev->si_drv1;
568171568Sscottl     isc_session_t	*sp;
569171568Sscottl     int		error, n;
570171568Sscottl
571171568Sscottl     debug_called(8);
572171568Sscottl     sp = (isc_session_t *)malloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO);
573171568Sscottl     if(sp == NULL)
574171568Sscottl	  return ENOMEM;
575171568Sscottl     mtx_lock(&sc->mtx);
576171568Sscottl     /*
577171568Sscottl      | search for the lowest unused sid
578171568Sscottl      */
579171568Sscottl     for(n = 0; n < MAX_SESSIONS; n++)
580171568Sscottl	  if(sc->sessions[n] == NULL)
581171568Sscottl	       break;
582171568Sscottl     if(n == MAX_SESSIONS) {
583171568Sscottl	  mtx_unlock(&sc->mtx);
584171568Sscottl	  free(sp, M_ISCSI);
585171568Sscottl	  return EPERM;
586171568Sscottl     }
587171568Sscottl     TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
588171568Sscottl     sc->nsess++;
589171568Sscottl     mtx_unlock(&sc->mtx);
590171568Sscottl
591171568Sscottl     sc->sessions[n] = sp;
592171568Sscottl     sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
593171568Sscottl     *ndev = sp->sid = n;
594171568Sscottl     sp->isc = sc;
595171568Sscottl     sp->dev->si_drv1 = sc;
596171568Sscottl     sp->dev->si_drv2 = sp;
597171568Sscottl
598171568Sscottl     sp->opt.maxRecvDataSegmentLength = 8192;
599171568Sscottl     sp->opt.maxXmitDataSegmentLength = 8192;
600171568Sscottl
601171568Sscottl     sp->opt.maxBurstLength = 65536;	// 64k
602171568Sscottl
603171568Sscottl     sdebug(2, "sessionID=%d", n);
604171568Sscottl     error = ism_start(sp);
605171568Sscottl
606171568Sscottl     sdebug(2, "error=%d", error);
607171568Sscottl
608171568Sscottl     return error;
609171568Sscottl}
610171568Sscottl
611171568Sscottl#ifdef notused
612171568Sscottlstatic void
613171568Sscottliscsi_counters(isc_session_t *sp)
614171568Sscottl{
615171568Sscottl     int	h, r, s;
616171568Sscottl     pduq_t	*pq;
617171568Sscottl
618171568Sscottl#define _puke(i, pq) do {\
619171568Sscottl	       debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
620171568Sscottl		       i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
621171568Sscottl		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
622171568Sscottl		       (long)pq->ts.sec, pq->ts.frac, pq->flags);\
623171568Sscottl	       } while(0)
624171568Sscottl
625171568Sscottl     h = r = s = 0;
626171568Sscottl     TAILQ_FOREACH(pq, &sp->hld, pq_link) {
627171568Sscottl	  _puke(h, pq);
628171568Sscottl	  h++;
629171568Sscottl     }
630171568Sscottl     TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
631171568Sscottl     TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
632171568Sscottl     TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
633171568Sscottl     TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
634171568Sscottl     debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
635171568Sscottl}
636171568Sscottl#endif
637171568Sscottl
638171568Sscottlstatic void
639171568Sscottliscsi_shutdown(void *v)
640171568Sscottl{
641171568Sscottl     struct isc_softc	*sc = (struct isc_softc *)v;
642171568Sscottl     isc_session_t	*sp;
643171568Sscottl     int	n;
644171568Sscottl
645171568Sscottl     debug_called(8);
646171568Sscottl     if(sc == NULL) {
647171568Sscottl	  xdebug("sc is NULL!");
648171568Sscottl	  return;
649171568Sscottl     }
650171568Sscottl     if(sc->eh == NULL)
651171568Sscottl	  debug(2, "sc->eh is NULL!");
652171568Sscottl     else {
653171568Sscottl	  EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
654171568Sscottl	  debug(2, "done n=%d", sc->nsess);
655171568Sscottl     }
656171568Sscottl     n = 0;
657171568Sscottl     TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
658171568Sscottl	  debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
659171568Sscottl	  n++;
660171568Sscottl     }
661171568Sscottl}
662171568Sscottl
663171568Sscottlstatic void
664171568Sscottliscsi_start(void)
665171568Sscottl{
666171568Sscottl     struct isc_softc *sc = &isc;
667171568Sscottl
668171568Sscottl     debug_called(8);
669171568Sscottl
670171568Sscottl     memset(sc, 0, sizeof(struct isc_softc));
671171568Sscottl
672171568Sscottl     sc->dev = make_dev(&iscsi_cdevsw, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi");
673171568Sscottl     sc->dev->si_drv1 = sc;
674171568Sscottl
675171568Sscottl     TAILQ_INIT(&sc->isc_sess);
676171568Sscottl
677171568Sscottl     sc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
678171568Sscottl				NULL, NULL, NULL, NULL,
679171568Sscottl				0, 0);
680171568Sscottl     if(sc->pdu_zone == NULL) {
681171568Sscottl	  printf("iscsi_initiator: uma_zcreate failed");
682171568Sscottl	  // XXX: and now what?
683171568Sscottl     }
684171568Sscottl     uma_zone_set_max(sc->pdu_zone, MAX_PDUS*2+1);
685171568Sscottl     mtx_init(&sc->mtx, "iscsi", NULL, MTX_DEF);
686171568Sscottl     mtx_init(&sc->pdu_mtx, "iscsi pdu pool", NULL, MTX_DEF);
687171568Sscottl
688171568Sscottl#if 0
689171568Sscottl     // XXX: this will cause a panic if the
690171568Sscottl     //      module is loaded too early
691171568Sscottl     if(ic_init(sc) != 0)
692171568Sscottl	  return;
693171568Sscottl#else
694171568Sscottl     sc->cam_sim = NULL;
695171568Sscottl#endif
696171568Sscottl     if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
697171568Sscottl					sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
698171568Sscottl	  xdebug("shutdown event registration failed\n");
699171568Sscottl     /*
700171568Sscottl      | sysctl stuff
701171568Sscottl      */
702171568Sscottl     sysctl_ctx_init(&sc->clist);
703171568Sscottl     sc->oid = SYSCTL_ADD_NODE(&sc->clist,
704171568Sscottl			       SYSCTL_STATIC_CHILDREN(_net),
705171568Sscottl			       OID_AUTO,
706171568Sscottl			       "iscsi",
707171568Sscottl			       CTLFLAG_RD,
708171568Sscottl			       0,
709171568Sscottl			       "iSCSI Subsystem");
710171568Sscottl
711171568Sscottl     SYSCTL_ADD_STRING(&sc->clist,
712171568Sscottl		       SYSCTL_CHILDREN(sc->oid),
713171568Sscottl		       OID_AUTO,
714171568Sscottl		       "driver_version",
715171568Sscottl		       CTLFLAG_RD,
716171568Sscottl		       iscsi_driver_version,
717171568Sscottl		       0,
718171568Sscottl		       "iscsi driver version");
719171568Sscottl
720171568Sscottl     SYSCTL_ADD_STRING(&sc->clist,
721171568Sscottl		       SYSCTL_CHILDREN(sc->oid),
722171568Sscottl		       OID_AUTO,
723171568Sscottl		       "isid",
724171568Sscottl		       CTLFLAG_RW,
725171568Sscottl		       isid,
726171568Sscottl		       6+1,
727171568Sscottl		       "initiator part of the Session Identifier");
728171568Sscottl
729171568Sscottl     SYSCTL_ADD_INT(&sc->clist,
730171568Sscottl		    SYSCTL_CHILDREN(sc->oid),
731171568Sscottl		    OID_AUTO,
732171568Sscottl		    "sessions",
733171568Sscottl		    CTLFLAG_RD,
734171568Sscottl		    &sc->nsess,
735171568Sscottl		    sizeof(sc->nsess),
736171568Sscottl		    "number of active session");
737171568Sscottl}
738171568Sscottl
739171568Sscottl/*
740171568Sscottl | Notes:
741171568Sscottl |	unload SHOULD fail if there is activity
742171568Sscottl |	activity: there is/are active session/s
743171568Sscottl */
744171568Sscottlstatic void
745171568Sscottliscsi_stop(void)
746171568Sscottl{
747171568Sscottl     struct isc_softc	*sc = &isc;
748171568Sscottl     isc_session_t	*sp, *sp_tmp;
749171568Sscottl
750171568Sscottl     debug_called(8);
751171568Sscottl
752171568Sscottl     /*
753171568Sscottl      | go through all the sessions
754171568Sscottl      | Note: close should have done this ...
755171568Sscottl      */
756171568Sscottl     TAILQ_FOREACH_SAFE(sp, &sc->isc_sess, sp_link, sp_tmp) {
757171568Sscottl	  //XXX: check for activity ...
758171568Sscottl	  ism_stop(sp);
759171568Sscottl     }
760171568Sscottl     if(sc->cam_sim != NULL)
761171568Sscottl	  ic_destroy(sc);
762171568Sscottl
763171568Sscottl     mtx_destroy(&sc->mtx);
764171568Sscottl     mtx_destroy(&sc->pdu_mtx);
765171568Sscottl     uma_zdestroy(sc->pdu_zone);
766171568Sscottl
767171568Sscottl     if(sc->dev)
768171568Sscottl	  destroy_dev(sc->dev);
769171568Sscottl
770171568Sscottl     if(sysctl_ctx_free(&sc->clist))
771171568Sscottl	  xdebug("sysctl_ctx_free failed");
772171568Sscottl}
773171568Sscottl
774171568Sscottlstatic int
775171568Sscottliscsi_modevent(module_t mod, int what, void *arg)
776171568Sscottl{
777171568Sscottl     debug_called(8);
778171568Sscottl
779171568Sscottl     switch(what) {
780171568Sscottl     case MOD_LOAD:
781171568Sscottl	  iscsi_start();
782171568Sscottl	  break;
783171568Sscottl
784171568Sscottl     case MOD_QUIESCE:
785171568Sscottl#if 1
786171568Sscottl	  if(isc.nsess) {
787171568Sscottl	       xdebug("iscsi module busy(nsess=%d), cannot unload", isc.nsess);
788171568Sscottl	       log(LOG_ERR, "iscsi module busy, cannot unload");
789171568Sscottl	  }
790171568Sscottl	  return isc.nsess;
791171568Sscottl#endif
792171568Sscottl     case MOD_SHUTDOWN:
793171568Sscottl	  break;
794171568Sscottl
795171568Sscottl     case MOD_UNLOAD:
796171568Sscottl	  iscsi_stop();
797171568Sscottl	  break;
798171568Sscottl
799171568Sscottl     default:
800171568Sscottl	  break;
801171568Sscottl     }
802171568Sscottl     return 0;
803171568Sscottl}
804171568Sscottlmoduledata_t iscsi_mod = {
805171568Sscottl         "iscsi",
806171568Sscottl         (modeventhand_t) iscsi_modevent,
807171568Sscottl         0
808171568Sscottl};
809171568Sscottl
810171568SscottlDECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
811