1171568Sscottl/*-
2234233Sjpaetzel * Copyright (c) 2005-2011 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/*
28211095Sdes | $Id: iscsi.c 752 2009-08-20 11:23:28Z danny $
29171568Sscottl */
30171568Sscottl
31171568Sscottl#include <sys/cdefs.h>
32171568Sscottl__FBSDID("$FreeBSD$");
33171568Sscottl
34171568Sscottl#include "opt_iscsi_initiator.h"
35171568Sscottl
36171568Sscottl#include <sys/param.h>
37224778Srwatson#include <sys/capability.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>
59211095Sdes#include <sys/sx.h>
60171568Sscottl
61254657Strasz#include <dev/iscsi_initiator/iscsi.h>
62254657Strasz#include <dev/iscsi_initiator/iscsivar.h>
63234233Sjpaetzelstatic char *iscsi_driver_version = "2.3.1";
64171568Sscottl
65211095Sdesstatic struct isc_softc *isc;
66171568Sscottl
67171568SscottlMALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
68211095SdesMALLOC_DEFINE(M_ISCSIBUF, "iSCbuf", "iSCSI buffers");
69227293Sedstatic MALLOC_DEFINE(M_TMP, "iSCtmp", "iSCSI tmp");
70171568Sscottl
71171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
72171568Sscottlint iscsi_debug = ISCSI_INITIATOR_DEBUG;
73171568SscottlSYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
74171568Sscottl	"iSCSI driver debug flag");
75171568Sscottl
76171568Sscottlstruct mtx iscsi_dbg_mtx;
77171568Sscottl#endif
78171568Sscottl
79211095Sdesstatic int max_sessions = MAX_SESSIONS;
80211095SdesSYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_sessions, CTLFLAG_RDTUN, &max_sessions, MAX_SESSIONS,
81211095Sdes	   "Max sessions allowed");
82211095Sdesstatic int max_pdus = MAX_PDUS;
83211095SdesSYSCTL_INT(_net, OID_AUTO, iscsi_initiator_max_pdus, CTLFLAG_RDTUN, &max_pdus, MAX_PDUS,
84211095Sdes	   "Max pdu pool");
85171568Sscottl
86171568Sscottlstatic char isid[6+1] = {
87171568Sscottl     0x80,
88171568Sscottl     'D',
89171568Sscottl     'I',
90171568Sscottl     'B',
91171568Sscottl     '0',
92171568Sscottl     '0',
93171568Sscottl     0
94171568Sscottl};
95171568Sscottl
96171568Sscottlstatic int	i_create_session(struct cdev *dev, int *ndev);
97171568Sscottl
98171568Sscottlstatic int	i_ping(struct cdev *dev);
99171568Sscottlstatic int	i_send(struct cdev *dev, caddr_t arg, struct thread *td);
100171568Sscottlstatic int	i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
101171568Sscottlstatic int	i_setsoc(isc_session_t *sp, int fd, struct thread *td);
102211095Sdesstatic int	i_fullfeature(struct cdev *dev, int flag);
103171568Sscottl
104171568Sscottlstatic d_open_t iscsi_open;
105171568Sscottlstatic d_close_t iscsi_close;
106171568Sscottlstatic d_ioctl_t iscsi_ioctl;
107171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
108171568Sscottlstatic d_read_t iscsi_read;
109171568Sscottl#endif
110171568Sscottl
111171568Sscottlstatic struct cdevsw iscsi_cdevsw = {
112171568Sscottl     .d_version = D_VERSION,
113171568Sscottl     .d_open	= iscsi_open,
114171568Sscottl     .d_close	= iscsi_close,
115171568Sscottl     .d_ioctl	= iscsi_ioctl,
116171568Sscottl#ifdef ISCSI_INITIATOR_DEBUG
117171568Sscottl     .d_read	= iscsi_read,
118171568Sscottl#endif
119171568Sscottl     .d_name	= "iSCSI",
120171568Sscottl};
121171568Sscottl
122171568Sscottlstatic int
123171568Sscottliscsi_open(struct cdev *dev, int flags, int otype, struct thread *td)
124171568Sscottl{
125171568Sscottl     debug_called(8);
126171568Sscottl
127183397Sed     debug(7, "dev=%d", dev2unit(dev));
128171568Sscottl
129211095Sdes     if(dev2unit(dev) > max_sessions) {
130171568Sscottl	  // should not happen
131171568Sscottl          return ENODEV;
132171568Sscottl     }
133171568Sscottl     return 0;
134171568Sscottl}
135171568Sscottl
136171568Sscottlstatic int
137171568Sscottliscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td)
138171568Sscottl{
139171568Sscottl     isc_session_t	*sp;
140171568Sscottl
141171568Sscottl     debug_called(8);
142171568Sscottl
143211095Sdes     debug(3, "session=%d flag=%x", dev2unit(dev), flag);
144171568Sscottl
145211095Sdes     if(dev2unit(dev) == max_sessions) {
146171568Sscottl	  return 0;
147171568Sscottl     }
148211095Sdes     sp = dev->si_drv2;
149171568Sscottl     if(sp != NULL) {
150211095Sdes	  sdebug(3, "sp->flags=%x", sp->flags );
151171568Sscottl	  /*
152171568Sscottl	   | if still in full phase, this probably means
153171568Sscottl	   | that something went realy bad.
154171568Sscottl	   | it could be a result from 'shutdown', in which case
155171568Sscottl	   | we will ignore it (so buffers can be flushed).
156171568Sscottl	   | the problem is that there is no way of differentiating
157171568Sscottl	   | between a shutdown procedure and 'iscontrol' dying.
158171568Sscottl	   */
159171568Sscottl	  if(sp->flags & ISC_FFPHASE)
160171568Sscottl	       // delay in case this is a shutdown.
161171568Sscottl	       tsleep(sp, PRIBIO, "isc-cls", 60*hz);
162171568Sscottl	  ism_stop(sp);
163171568Sscottl     }
164171568Sscottl     debug(2, "done");
165171568Sscottl     return 0;
166171568Sscottl}
167171568Sscottl
168171568Sscottlstatic int
169171568Sscottliscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
170171568Sscottl{
171211095Sdes     struct isc_softc	*sc;
172171568Sscottl     isc_session_t	*sp;
173171568Sscottl     isc_opt_t		*opt;
174171568Sscottl     int		error;
175171568Sscottl
176171568Sscottl     debug_called(8);
177171568Sscottl
178171568Sscottl     error = 0;
179211095Sdes     if(dev2unit(dev) == max_sessions) {
180171568Sscottl	  /*
181171568Sscottl	   | non Session commands
182171568Sscottl	   */
183211095Sdes	  sc = dev->si_drv1;
184171568Sscottl	  if(sc == NULL)
185171568Sscottl	       return ENXIO;
186171568Sscottl
187171568Sscottl	  switch(cmd) {
188171568Sscottl	  case ISCSISETSES:
189171568Sscottl	       error = i_create_session(dev, (int *)arg);
190171568Sscottl	       if(error == 0)
191211095Sdes		    break;
192171568Sscottl
193171568Sscottl	  default:
194211095Sdes	       error = ENXIO;
195171568Sscottl	  }
196171568Sscottl	  return error;
197171568Sscottl     }
198171568Sscottl     /*
199171568Sscottl      | session commands
200171568Sscottl      */
201211095Sdes     sp = dev->si_drv2;
202171568Sscottl     if(sp == NULL)
203171568Sscottl	  return ENXIO;
204171568Sscottl
205183397Sed     sdebug(6, "dev=%d cmd=%d", dev2unit(dev), (int)(cmd & 0xff));
206171568Sscottl
207171568Sscottl     switch(cmd) {
208171568Sscottl     case ISCSISETSOC:
209171568Sscottl	  error = i_setsoc(sp, *(u_int *)arg, td);
210171568Sscottl	  break;
211171568Sscottl
212171568Sscottl     case ISCSISETOPT:
213171568Sscottl	  opt = (isc_opt_t *)arg;
214171568Sscottl	  error = i_setopt(sp, opt);
215171568Sscottl	  break;
216171568Sscottl
217171568Sscottl     case ISCSISEND:
218171568Sscottl	  error = i_send(dev, arg, td);
219171568Sscottl	  break;
220171568Sscottl
221171568Sscottl     case ISCSIRECV:
222171568Sscottl	  error = i_recv(dev, arg, td);
223171568Sscottl	  break;
224171568Sscottl
225171568Sscottl     case ISCSIPING:
226171568Sscottl	  error = i_ping(dev);
227171568Sscottl	  break;
228171568Sscottl
229171568Sscottl     case ISCSISTART:
230211095Sdes	  error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 1);
231171568Sscottl	  if(error == 0) {
232171568Sscottl	       sp->proc = td->td_proc;
233217323Smdf	       SYSCTL_ADD_INT(&sp->clist, SYSCTL_CHILDREN(sp->oid),
234234233Sjpaetzel			       OID_AUTO, "pid", CTLFLAG_RD,
235234233Sjpaetzel			       &sp->proc->p_pid, sizeof(pid_t), "control process id");
236171568Sscottl	  }
237171568Sscottl	  break;
238171568Sscottl
239171568Sscottl     case ISCSIRESTART:
240211095Sdes	  error = sp->soc == NULL? ENOTCONN: i_fullfeature(dev, 2);
241171568Sscottl	  break;
242171568Sscottl
243171568Sscottl     case ISCSISTOP:
244211095Sdes	  error = i_fullfeature(dev, 0);
245171568Sscottl	  break;
246171568Sscottl
247171568Sscottl     case ISCSISIGNAL: {
248171568Sscottl	  int sig = *(int *)arg;
249171568Sscottl
250171568Sscottl	  if(sig < 0 || sig > _SIG_MAXSIG)
251171568Sscottl	       error = EINVAL;
252171568Sscottl	  else
253171568Sscottl		sp->signal = sig;
254171568Sscottl	  break;
255171568Sscottl     }
256171568Sscottl
257171568Sscottl     case ISCSIGETCAM: {
258171568Sscottl	  iscsi_cam_t *cp = (iscsi_cam_t *)arg;
259171568Sscottl
260171568Sscottl	  error = ic_getCamVals(sp, cp);
261171568Sscottl	  break;
262171568Sscottl     }
263171568Sscottl
264171568Sscottl     default:
265171568Sscottl	  error = ENOIOCTL;
266171568Sscottl     }
267171568Sscottl
268171568Sscottl     return error;
269171568Sscottl}
270171568Sscottl
271171568Sscottlstatic int
272171568Sscottliscsi_read(struct cdev *dev, struct uio *uio, int ioflag)
273171568Sscottl{
274171568Sscottl#ifdef  ISCSI_INITIATOR_DEBUG
275171568Sscottl     struct isc_softc	*sc;
276171568Sscottl     isc_session_t	*sp;
277171568Sscottl     pduq_t 		*pq;
278171568Sscottl     char		buf[1024];
279171568Sscottl
280211095Sdes     sc = dev->si_drv1;
281211095Sdes     sp = dev->si_drv2;
282211095Sdes     if(dev2unit(dev) == max_sessions) {
283171568Sscottl	  sprintf(buf, "/----- Session ------/\n");
284171568Sscottl	  uiomove(buf, strlen(buf), uio);
285171568Sscottl	  int	i = 0;
286171568Sscottl
287171568Sscottl	  TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
288171568Sscottl	       if(uio->uio_resid == 0)
289171568Sscottl		    return 0;
290171568Sscottl	       sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
291171568Sscottl	       uiomove(buf, strlen(buf), uio);
292171568Sscottl	  }
293234233Sjpaetzel	  sprintf(buf, "free npdu_alloc=%d, npdu_max=%d\n", sc->npdu_alloc, sc->npdu_max);
294185289Sscottl	  uiomove(buf, strlen(buf), uio);
295171568Sscottl     }
296171568Sscottl     else {
297171568Sscottl	  int	i = 0;
298171568Sscottl	  struct socket	*so = sp->soc;
299171568Sscottl#define pukeit(i, pq) do {\
300211182Sjhb	       sprintf(buf, "%03d] %06x %02x %06x %06x %jd\n",\
301211095Sdes		       i, ntohl(pq->pdu.ipdu.bhs.CmdSN),\
302171568Sscottl		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
303211095Sdes		       ntohl(pq->pdu.ipdu.bhs.ExpStSN),\
304211182Sjhb		       (intmax_t)pq->ts.sec);\
305171568Sscottl	       } while(0)
306171568Sscottl
307171568Sscottl	  sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
308171568Sscottl	  uiomove(buf, strlen(buf), uio);
309171568Sscottl	  TAILQ_FOREACH(pq, &sp->hld, pq_link) {
310171568Sscottl	       if(uio->uio_resid == 0)
311171568Sscottl		    return 0;
312171568Sscottl	       pukeit(i, pq); i++;
313171568Sscottl	       uiomove(buf, strlen(buf), uio);
314171568Sscottl	  }
315171568Sscottl	  sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
316171568Sscottl	  uiomove(buf, strlen(buf), uio);
317171568Sscottl	  i = 0;
318171568Sscottl	  TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
319171568Sscottl	       if(uio->uio_resid == 0)
320171568Sscottl		    return 0;
321171568Sscottl	       pukeit(i, pq); i++;
322171568Sscottl	       uiomove(buf, strlen(buf), uio);
323171568Sscottl	  }
324171568Sscottl	  sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
325171568Sscottl	  i = 0;
326171568Sscottl	  uiomove(buf, strlen(buf), uio);
327171568Sscottl	  TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
328171568Sscottl	       if(uio->uio_resid == 0)
329171568Sscottl		    return 0;
330171568Sscottl	       pukeit(i, pq); i++;
331171568Sscottl	       uiomove(buf, strlen(buf), uio);
332171568Sscottl	  }
333171568Sscottl	  sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
334171568Sscottl	  i = 0;
335171568Sscottl	  uiomove(buf, strlen(buf), uio);
336171568Sscottl	  TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
337171568Sscottl	       if(uio->uio_resid == 0)
338171568Sscottl		    return 0;
339171568Sscottl	       pukeit(i, pq); i++;
340171568Sscottl	       uiomove(buf, strlen(buf), uio);
341171568Sscottl	  }
342171568Sscottl	  sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
343171568Sscottl	  i = 0;
344171568Sscottl	  uiomove(buf, strlen(buf), uio);
345171568Sscottl	  TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
346171568Sscottl	       if(uio->uio_resid == 0)
347171568Sscottl		    return 0;
348171568Sscottl	       pukeit(i, pq); i++;
349171568Sscottl	       uiomove(buf, strlen(buf), uio);
350171568Sscottl	  }
351171568Sscottl
352171568Sscottl	  sprintf(buf, "/---- Stats ---/\n");
353171568Sscottl	  uiomove(buf, strlen(buf), uio);
354171568Sscottl
355171568Sscottl	  sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
356171568Sscottl	  uiomove(buf, strlen(buf), uio);
357171568Sscottl
358171568Sscottl	  sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n",
359171568Sscottl		  sp->flags, sc->npdu_alloc, sc->npdu_max);
360171568Sscottl	  uiomove(buf, strlen(buf), uio);
361171568Sscottl
362171568Sscottl	  sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
363171568Sscottl		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
364171568Sscottl	  uiomove(buf, strlen(buf), uio);
365171568Sscottl
366171568Sscottl	  sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state);
367171568Sscottl	  uiomove(buf, strlen(buf), uio);
368171568Sscottl
369171568Sscottl     }
370171568Sscottl#endif
371171568Sscottl     return 0;
372171568Sscottl}
373171568Sscottl
374171568Sscottlstatic int
375171568Sscottli_ping(struct cdev *dev)
376171568Sscottl{
377171568Sscottl     return 0;
378171568Sscottl}
379171568Sscottl/*
380171568Sscottl | low level I/O
381171568Sscottl */
382171568Sscottlstatic int
383171568Sscottli_setsoc(isc_session_t *sp, int fd, struct thread *td)
384171568Sscottl{
385255219Spjd     cap_rights_t rights;
386171568Sscottl     int error = 0;
387171568Sscottl
388171568Sscottl     if(sp->soc != NULL)
389171568Sscottl	  isc_stop_receiver(sp);
390171568Sscottl
391255219Spjd     error = fget(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT), &sp->fp);
392171568Sscottl     if(error)
393171568Sscottl	  return error;
394171568Sscottl
395255219Spjd     error = fgetsock(td, fd, cap_rights_init(&rights, CAP_SOCK_CLIENT),
396255219Spjd        &sp->soc, 0);
397255219Spjd     if(error == 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{
412211095Sdes     isc_session_t	*sp = dev->si_drv2;
413171568Sscottl     caddr_t		bp;
414171568Sscottl     pduq_t		*pq;
415171568Sscottl     pdu_t		*pp;
416171568Sscottl     int		n, error;
417171568Sscottl
418171568Sscottl     debug_called(8);
419171568Sscottl
420171568Sscottl     if(sp->soc == NULL)
421171568Sscottl	  return ENOTCONN;
422171568Sscottl
423211095Sdes     if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
424171568Sscottl	  return EAGAIN;
425171568Sscottl     pp = &pq->pdu;
426171568Sscottl     pq->pdu = *(pdu_t *)arg;
427171568Sscottl     if((error = i_prepPDU(sp, pq)) != 0)
428185289Sscottl	  goto out;
429171568Sscottl
430211095Sdes     bp = NULL;
431211095Sdes     if((pq->len - sizeof(union ipdu_u)) > 0) {
432211095Sdes	  pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSIBUF, M_NOWAIT);
433211095Sdes	  if(pq->buf == NULL) {
434211095Sdes	       error = EAGAIN;
435211095Sdes	       goto out;
436211095Sdes	  }
437211095Sdes     }
438211095Sdes     else
439211095Sdes	  pq->buf = NULL; // just in case?
440171568Sscottl
441211095Sdes     sdebug(2, "len=%d ahs_len=%d ds_len=%d buf=%zu@%p",
442211095Sdes	    pq->len, pp->ahs_len, pp->ds_len, pq->len - sizeof(union ipdu_u), bp);
443211095Sdes
444171568Sscottl     if(pp->ahs_len) {
445211095Sdes	  // XXX: never tested, looks suspicious
446171568Sscottl	  n = pp->ahs_len;
447211095Sdes	  error = copyin(pp->ahs_addr, bp, n);
448185289Sscottl	  if(error != 0) {
449185289Sscottl	       sdebug(3, "copyin ahs: error=%d", error);
450185289Sscottl	       goto out;
451185289Sscottl	  }
452211095Sdes	  pp->ahs_addr = (ahs_t *)bp;
453171568Sscottl	  bp += n;
454171568Sscottl     }
455171568Sscottl     if(pp->ds_len) {
456171568Sscottl	  n = pp->ds_len;
457211095Sdes	  error = copyin(pp->ds_addr, bp, n);
458185289Sscottl	  if(error != 0) {
459185289Sscottl	       sdebug(3, "copyin ds: error=%d", error);
460185289Sscottl	       goto out;
461185289Sscottl	  }
462211095Sdes	  pp->ds_addr = bp;
463171568Sscottl	  bp += n;
464171568Sscottl	  while(n & 03) {
465171568Sscottl	       n++;
466171568Sscottl	       *bp++ = 0;
467171568Sscottl	  }
468171568Sscottl     }
469171568Sscottl
470171568Sscottl     error = isc_qout(sp, pq);
471185289Sscottl     if(error == 0)
472185289Sscottl	  wakeup(&sp->flags); // XXX: to 'push' proc_out ...
473185289Sscottlout:
474185289Sscottl     if(error)
475211095Sdes	  pdu_free(sp->isc, pq);
476171568Sscottl
477171568Sscottl     return error;
478171568Sscottl}
479171568Sscottl
480171568Sscottlstatic int
481171568Sscottli_recv(struct cdev *dev, caddr_t arg, struct thread *td)
482171568Sscottl{
483211095Sdes     isc_session_t	*sp = dev->si_drv2;
484171568Sscottl     pduq_t		*pq;
485171568Sscottl     pdu_t		*pp, *up;
486171568Sscottl     caddr_t		bp;
487171568Sscottl     int		error, mustfree, cnt;
488171568Sscottl     size_t		need, have, n;
489171568Sscottl
490171568Sscottl     debug_called(8);
491171568Sscottl
492171568Sscottl     if(sp == NULL)
493171568Sscottl	  return EIO;
494171568Sscottl
495171568Sscottl     if(sp->soc == NULL)
496171568Sscottl	  return ENOTCONN;
497171568Sscottl     cnt = 6;     // XXX: maybe the user can request a time out?
498171568Sscottl     mtx_lock(&sp->rsp_mtx);
499171568Sscottl     while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
500171568Sscottl	  msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
501171568Sscottl	  if(cnt-- == 0) break; // XXX: for now, needs work
502171568Sscottl     }
503171568Sscottl     if(pq != NULL) {
504171568Sscottl	  sp->stats.nrsp--;
505171568Sscottl	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
506171568Sscottl     }
507171568Sscottl     mtx_unlock(&sp->rsp_mtx);
508171568Sscottl
509211095Sdes     sdebug(6, "cnt=%d", cnt);
510171568Sscottl
511171568Sscottl     if(pq == NULL) {
512171568Sscottl	  error = ENOTCONN;
513171568Sscottl	  sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
514171568Sscottl	  return error;
515171568Sscottl     }
516171568Sscottl     up = (pdu_t *)arg;
517171568Sscottl     pp = &pq->pdu;
518171568Sscottl     up->ipdu = pp->ipdu;
519171568Sscottl     n = 0;
520171568Sscottl     up->ds_len = 0;
521171568Sscottl     up->ahs_len = 0;
522171568Sscottl     error = 0;
523171568Sscottl
524171568Sscottl     if(pq->mp) {
525171568Sscottl	  u_int	len;
526171568Sscottl
527171568Sscottl	  // Grr...
528171568Sscottl	  len = 0;
529171568Sscottl	  if(pp->ahs_len) {
530171568Sscottl	       len += pp->ahs_len;
531171568Sscottl	  }
532171568Sscottl	  if(pp->ds_len) {
533171568Sscottl	       len += pp->ds_len;
534171568Sscottl	  }
535171568Sscottl
536171568Sscottl	  mustfree = 0;
537171568Sscottl	  if(len > pq->mp->m_len) {
538171568Sscottl	       mustfree++;
539211095Sdes	       bp = malloc(len, M_TMP, M_WAITOK);
540171568Sscottl	       sdebug(4, "need mbufcopy: %d", len);
541171568Sscottl	       i_mbufcopy(pq->mp, bp, len);
542171568Sscottl	  }
543171568Sscottl	  else
544171568Sscottl	       bp = mtod(pq->mp, caddr_t);
545171568Sscottl
546171568Sscottl	  if(pp->ahs_len) {
547171568Sscottl	       need = pp->ahs_len;
548171568Sscottl	       n = MIN(up->ahs_size, need);
549211095Sdes	       error = copyout(bp, (caddr_t)up->ahs_addr, n);
550171568Sscottl	       up->ahs_len = n;
551171568Sscottl	       bp += need;
552171568Sscottl	  }
553171568Sscottl	  if(!error && pp->ds_len) {
554171568Sscottl	       need = pp->ds_len;
555171568Sscottl	       if((have = up->ds_size) == 0) {
556171568Sscottl		    have = up->ahs_size - n;
557211095Sdes		    up->ds_addr = (caddr_t)up->ahs_addr + n;
558171568Sscottl	       }
559171568Sscottl	       n = MIN(have, need);
560211095Sdes	       error = copyout(bp, (caddr_t)up->ds_addr, n);
561171568Sscottl	       up->ds_len = n;
562171568Sscottl	  }
563171568Sscottl
564171568Sscottl	  if(mustfree)
565211095Sdes	       free(bp, M_TMP);
566171568Sscottl     }
567171568Sscottl
568171568Sscottl     sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
569171568Sscottl
570171568Sscottl     pdu_free(sp->isc, pq);
571171568Sscottl
572171568Sscottl     return error;
573171568Sscottl}
574171568Sscottl
575171568Sscottlstatic int
576211095Sdesi_fullfeature(struct cdev *dev, int flag)
577211095Sdes{
578211095Sdes     isc_session_t	*sp = dev->si_drv2;
579211095Sdes     int		error;
580211095Sdes
581211095Sdes     sdebug(2, "flag=%d", flag);
582211095Sdes
583211095Sdes     error = 0;
584211095Sdes     switch(flag) {
585211095Sdes     case 0: // stop
586211095Sdes         sp->flags &= ~ISC_FFPHASE;
587211095Sdes         break;
588211095Sdes     case 1: // start
589211095Sdes         sp->flags |= ISC_FFPHASE;
590211095Sdes         error = ic_init(sp);
591211095Sdes         break;
592211095Sdes     case 2: // restart
593211095Sdes         sp->flags |= ISC_FFPHASE;
594211095Sdes         ism_restart(sp);
595211095Sdes         break;
596211095Sdes     }
597211095Sdes     return error;
598211095Sdes}
599211095Sdes
600211095Sdesstatic int
601171568Sscottli_create_session(struct cdev *dev, int *ndev)
602171568Sscottl{
603211095Sdes     struct isc_softc	*sc = dev->si_drv1;
604171568Sscottl     isc_session_t	*sp;
605171568Sscottl     int		error, n;
606171568Sscottl
607171568Sscottl     debug_called(8);
608211095Sdes
609211095Sdes     sp = malloc(sizeof(isc_session_t), M_ISCSI, M_WAITOK | M_ZERO);
610171568Sscottl     if(sp == NULL)
611171568Sscottl	  return ENOMEM;
612211095Sdes
613211095Sdes     sx_xlock(&sc->unit_sx);
614211095Sdes     if((n = alloc_unr(sc->unit)) < 0) {
615211095Sdes	  sx_unlock(&sc->unit_sx);
616171568Sscottl	  free(sp, M_ISCSI);
617211095Sdes	  xdebug("too many sessions!");
618171568Sscottl	  return EPERM;
619171568Sscottl     }
620211095Sdes     sx_unlock(&sc->unit_sx);
621211095Sdes
622211095Sdes     mtx_lock(&sc->isc_mtx);
623171568Sscottl     TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
624211095Sdes     isc->nsess++;
625211095Sdes     mtx_unlock(&sc->isc_mtx);
626171568Sscottl
627171568Sscottl     sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
628171568Sscottl     *ndev = sp->sid = n;
629171568Sscottl     sp->isc = sc;
630171568Sscottl     sp->dev->si_drv1 = sc;
631171568Sscottl     sp->dev->si_drv2 = sp;
632171568Sscottl
633171568Sscottl     sp->opt.maxRecvDataSegmentLength = 8192;
634171568Sscottl     sp->opt.maxXmitDataSegmentLength = 8192;
635171568Sscottl     sp->opt.maxBurstLength = 65536;	// 64k
636211095Sdes     sp->opt.maxluns = ISCSI_MAX_LUNS;
637171568Sscottl
638171568Sscottl     error = ism_start(sp);
639171568Sscottl
640171568Sscottl     return error;
641171568Sscottl}
642171568Sscottl
643171568Sscottl#ifdef notused
644171568Sscottlstatic void
645171568Sscottliscsi_counters(isc_session_t *sp)
646171568Sscottl{
647171568Sscottl     int	h, r, s;
648171568Sscottl     pduq_t	*pq;
649171568Sscottl
650171568Sscottl#define _puke(i, pq) do {\
651171568Sscottl	       debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
652171568Sscottl		       i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
653171568Sscottl		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
654171568Sscottl		       (long)pq->ts.sec, pq->ts.frac, pq->flags);\
655171568Sscottl	       } while(0)
656171568Sscottl
657171568Sscottl     h = r = s = 0;
658171568Sscottl     TAILQ_FOREACH(pq, &sp->hld, pq_link) {
659171568Sscottl	  _puke(h, pq);
660171568Sscottl	  h++;
661171568Sscottl     }
662171568Sscottl     TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
663171568Sscottl     TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
664171568Sscottl     TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
665171568Sscottl     TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
666171568Sscottl     debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
667171568Sscottl}
668171568Sscottl#endif
669171568Sscottl
670171568Sscottlstatic void
671171568Sscottliscsi_shutdown(void *v)
672171568Sscottl{
673211095Sdes     struct isc_softc	*sc = v;
674171568Sscottl     isc_session_t	*sp;
675171568Sscottl     int	n;
676171568Sscottl
677171568Sscottl     debug_called(8);
678171568Sscottl     if(sc == NULL) {
679171568Sscottl	  xdebug("sc is NULL!");
680171568Sscottl	  return;
681171568Sscottl     }
682211095Sdes#ifdef DO_EVENTHANDLER
683171568Sscottl     if(sc->eh == NULL)
684185289Sscottl	  debug(2, "sc->eh is NULL");
685171568Sscottl     else {
686171568Sscottl	  EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
687171568Sscottl	  debug(2, "done n=%d", sc->nsess);
688171568Sscottl     }
689211095Sdes#endif
690171568Sscottl     n = 0;
691171568Sscottl     TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
692171568Sscottl	  debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
693171568Sscottl	  n++;
694171568Sscottl     }
695185289Sscottl     debug(2, "done");
696171568Sscottl}
697171568Sscottl
698171568Sscottlstatic void
699185289Sscottlfree_pdus(struct isc_softc *sc)
700185289Sscottl{
701185289Sscottl     debug_called(8);
702185289Sscottl
703185289Sscottl     if(sc->pdu_zone != NULL) {
704185289Sscottl	  uma_zdestroy(sc->pdu_zone);
705185289Sscottl	  sc->pdu_zone = NULL;
706185289Sscottl     }
707185289Sscottl}
708185289Sscottl
709255855Straszstatic int
710171568Sscottliscsi_start(void)
711171568Sscottl{
712171568Sscottl     debug_called(8);
713171568Sscottl
714211095Sdes     TUNABLE_INT_FETCH("net.iscsi_initiator.max_sessions", &max_sessions);
715211095Sdes     TUNABLE_INT_FETCH("net.iscsi_initiator.max_pdus", &max_pdus);
716171568Sscottl
717211095Sdes     isc =  malloc(sizeof(struct isc_softc), M_ISCSI, M_ZERO|M_WAITOK);
718255812Strasz     mtx_init(&isc->isc_mtx, "iscsi-isc", NULL, MTX_DEF);
719171568Sscottl
720211095Sdes     TAILQ_INIT(&isc->isc_sess);
721211095Sdes     /*
722211095Sdes      | now init the free pdu list
723211095Sdes      */
724211095Sdes     isc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
725211095Sdes				 NULL, NULL, NULL, NULL,
726211095Sdes				 0, 0);
727211095Sdes     uma_zone_set_max(isc->pdu_zone, max_pdus);
728211095Sdes     isc->unit = new_unrhdr(0, max_sessions-1, NULL);
729211095Sdes     sx_init(&isc->unit_sx, "iscsi sx");
730171568Sscottl
731185289Sscottl#ifdef DO_EVENTHANDLER
732211095Sdes     if((isc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
733171568Sscottl					sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
734171568Sscottl	  xdebug("shutdown event registration failed\n");
735185289Sscottl#endif
736171568Sscottl     /*
737171568Sscottl      | sysctl stuff
738171568Sscottl      */
739211095Sdes     sysctl_ctx_init(&isc->clist);
740211095Sdes     isc->oid = SYSCTL_ADD_NODE(&isc->clist,
741171568Sscottl			       SYSCTL_STATIC_CHILDREN(_net),
742171568Sscottl			       OID_AUTO,
743211095Sdes			       "iscsi_initiator",
744171568Sscottl			       CTLFLAG_RD,
745171568Sscottl			       0,
746171568Sscottl			       "iSCSI Subsystem");
747171568Sscottl
748211095Sdes     SYSCTL_ADD_STRING(&isc->clist,
749211095Sdes		       SYSCTL_CHILDREN(isc->oid),
750171568Sscottl		       OID_AUTO,
751171568Sscottl		       "driver_version",
752171568Sscottl		       CTLFLAG_RD,
753171568Sscottl		       iscsi_driver_version,
754171568Sscottl		       0,
755171568Sscottl		       "iscsi driver version");
756171568Sscottl
757211095Sdes     SYSCTL_ADD_STRING(&isc->clist,
758211095Sdes		       SYSCTL_CHILDREN(isc->oid),
759171568Sscottl		       OID_AUTO,
760171568Sscottl		       "isid",
761171568Sscottl		       CTLFLAG_RW,
762171568Sscottl		       isid,
763171568Sscottl		       6+1,
764171568Sscottl		       "initiator part of the Session Identifier");
765171568Sscottl
766211095Sdes     SYSCTL_ADD_INT(&isc->clist,
767211095Sdes		    SYSCTL_CHILDREN(isc->oid),
768171568Sscottl		    OID_AUTO,
769171568Sscottl		    "sessions",
770171568Sscottl		    CTLFLAG_RD,
771211095Sdes		    &isc->nsess,
772211095Sdes		    sizeof(isc->nsess),
773171568Sscottl		    "number of active session");
774185289Sscottl
775234233Sjpaetzel#ifdef ISCSI_INITIATOR_DEBUG
776234233Sjpaetzel     mtx_init(&iscsi_dbg_mtx, "iscsi_dbg", NULL, MTX_DEF);
777234233Sjpaetzel#endif
778234233Sjpaetzel
779255855Strasz     isc->dev = make_dev_credf(MAKEDEV_CHECKNAME, &iscsi_cdevsw, max_sessions,
780255855Strasz			       NULL, UID_ROOT, GID_WHEEL, 0600, "iscsi");
781255855Strasz     if (isc->dev == NULL) {
782255855Strasz	  xdebug("iscsi_initiator: make_dev_credf failed");
783255855Strasz	  return (EEXIST);
784255855Strasz     }
785255855Strasz     isc->dev->si_drv1 = isc;
786255855Strasz
787185289Sscottl     printf("iscsi: version %s\n", iscsi_driver_version);
788255855Strasz     return (0);
789171568Sscottl}
790171568Sscottl
791171568Sscottl/*
792171568Sscottl | Notes:
793171568Sscottl |	unload SHOULD fail if there is activity
794171568Sscottl |	activity: there is/are active session/s
795171568Sscottl */
796171568Sscottlstatic void
797171568Sscottliscsi_stop(void)
798171568Sscottl{
799171568Sscottl     isc_session_t	*sp, *sp_tmp;
800171568Sscottl
801171568Sscottl     debug_called(8);
802171568Sscottl
803171568Sscottl     /*
804171568Sscottl      | go through all the sessions
805171568Sscottl      | Note: close should have done this ...
806171568Sscottl      */
807211095Sdes     TAILQ_FOREACH_SAFE(sp, &isc->isc_sess, sp_link, sp_tmp) {
808171568Sscottl	  //XXX: check for activity ...
809171568Sscottl	  ism_stop(sp);
810211095Sdes	  if(sp->cam_sim != NULL)
811211095Sdes	       ic_destroy(sp);
812171568Sscottl     }
813211095Sdes     mtx_destroy(&isc->isc_mtx);
814211095Sdes     sx_destroy(&isc->unit_sx);
815171568Sscottl
816211095Sdes     free_pdus(isc);
817171568Sscottl
818211095Sdes     if(isc->dev)
819211095Sdes	  destroy_dev(isc->dev);
820171568Sscottl
821211095Sdes     if(sysctl_ctx_free(&isc->clist))
822171568Sscottl	  xdebug("sysctl_ctx_free failed");
823185289Sscottl
824211095Sdes     iscsi_shutdown(isc); // XXX: check EVENTHANDLER_ ...
825234233Sjpaetzel
826234233Sjpaetzel#ifdef ISCSI_INITIATOR_DEBUG
827234233Sjpaetzel     mtx_destroy(&iscsi_dbg_mtx);
828234233Sjpaetzel#endif
829234233Sjpaetzel
830211095Sdes     free(isc, M_ISCSI);
831171568Sscottl}
832171568Sscottl
833171568Sscottlstatic int
834171568Sscottliscsi_modevent(module_t mod, int what, void *arg)
835171568Sscottl{
836255855Strasz     int error = 0;
837255855Strasz
838171568Sscottl     debug_called(8);
839171568Sscottl
840171568Sscottl     switch(what) {
841171568Sscottl     case MOD_LOAD:
842255855Strasz	  error = iscsi_start();
843171568Sscottl	  break;
844171568Sscottl
845171568Sscottl     case MOD_QUIESCE:
846211095Sdes	  if(isc->nsess) {
847211095Sdes	       xdebug("iscsi module busy(nsess=%d), cannot unload", isc->nsess);
848171568Sscottl	       log(LOG_ERR, "iscsi module busy, cannot unload");
849171568Sscottl	  }
850211095Sdes	  return isc->nsess;
851211095Sdes
852171568Sscottl     case MOD_SHUTDOWN:
853171568Sscottl	  break;
854171568Sscottl
855171568Sscottl     case MOD_UNLOAD:
856171568Sscottl	  iscsi_stop();
857171568Sscottl	  break;
858171568Sscottl
859171568Sscottl     default:
860171568Sscottl	  break;
861171568Sscottl     }
862255855Strasz     return (error);
863171568Sscottl}
864185289Sscottl
865171568Sscottlmoduledata_t iscsi_mod = {
866255570Strasz         "iscsi_initiator",
867241394Skevlo         (modeventhand_t) iscsi_modevent,
868241394Skevlo         0
869171568Sscottl};
870171568Sscottl
871185289Sscottl#ifdef ISCSI_ROOT
872185289Sscottlstatic void
873185289Sscottliscsi_rootconf(void)
874185289Sscottl{
875185289Sscottl#if 0
876185289Sscottl	nfs_setup_diskless();
877185289Sscottl	if (nfs_diskless_valid)
878185289Sscottl		rootdevnames[0] = "nfs:";
879185289Sscottl#endif
880185289Sscottl	printf("** iscsi_rootconf **\n");
881185289Sscottl}
882185289Sscottl
883185289SscottlSYSINIT(cpu_rootconf1, SI_SUB_ROOT_CONF, SI_ORDER_FIRST, iscsi_rootconf, NULL)
884185289Sscottl#endif
885185289Sscottl
886255570StraszDECLARE_MODULE(iscsi_initiator, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
887255570StraszMODULE_DEPEND(iscsi_initiator, cam, 1, 1, 1);
888