[Midnightbsd-cvs] src: iscsi/initiator: Add iscsi device

laffer1 at midnightbsd.org laffer1 at midnightbsd.org
Tue Nov 11 16:31:57 EST 2008


Log Message:
-----------
Add iscsi device

Added Files:
-----------
    src/sys/dev/iscsi/initiator:
        isc_cam.c (r1.1)
        isc_sm.c (r1.1)
        isc_soc.c (r1.1)
        isc_subr.c (r1.1)
        iscsi.c (r1.1)
        iscsi.h (r1.1)
        iscsi_subr.c (r1.1)
        iscsivar.h (r1.1)

-------------- next part --------------
--- /dev/null
+++ sys/dev/iscsi/initiator/iscsivar.h
@@ -0,0 +1,556 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsivar.h,v 1.1 2007/07/24 15:35:02 scottl Exp $
+ */
+/*
+ | $Id: iscsivar.h,v 1.30 2007/04/22 10:12:11 danny Exp danny $
+ */
+#ifndef ISCSI_INITIATOR_DEBUG
+#define ISCSI_INITIATOR_DEBUG 1
+#endif
+
+#ifdef ISCSI_INITIATOR_DEBUG
+extern int iscsi_debug;
+#define debug(level, fmt, args...)	do {if(level <= iscsi_debug)\
+	printf("%s: " fmt "\n", __func__ , ##args);} while(0)
+#define sdebug(level, fmt, args...)	do {if(level <= iscsi_debug)\
+     	printf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0)
+#define debug_called(level)		do {if(level <= iscsi_debug)\
+	printf("%s: called\n",  __func__);} while(0)
+#else
+#define debug(level, fmt, args...)
+#define debug_called(level)
+#define sdebug(level, fmt, args...)
+#endif /* ISCSI_INITIATOR_DEBUG */
+
+#define xdebug(fmt, args...)	printf("%s: " fmt "\n", __func__ , ##args)
+
+#define MAX_SESSIONS		ISCSI_MAX_TARGETS
+
+typedef uint32_t digest_t(const void *, int len, uint32_t ocrc);
+
+MALLOC_DECLARE(M_ISCSI);
+MALLOC_DECLARE(M_PDU);
+
+#ifndef BIT
+#define BIT(n)	(1 <<(n))
+#endif
+
+#define ISC_SM_RUN	BIT(0)
+#define ISC_SM_RUNNING	BIT(1)
+#define ISC_SM_HOLD	BIT(2)
+
+#define ISC_CON_RUN	BIT(3)
+#define ISC_CON_RUNNING	BIT(4)
+#define ISC_KILL	BIT(5)
+#define ISC_IWAITING	BIT(6)
+//#define ISC_OWAITING	BIT(7)
+#define ISC_FFPHASE	BIT(8)
+#define ISC_FFPWAIT	BIT(9)
+
+#define ISC_MEMWAIT	BIT(10)
+#define ISC_SIGNALED	BIT(11)
+#define ISC_FROZEN	BIT(12)
+#define ISC_STALLED	BIT(13)
+
+#define ISC_SHUTDOWN	BIT(31)
+
+/*
+ | some stats
+ */
+struct i_stats {
+     int	npdu;	// number of pdus malloc'ed.
+     int	nrecv;	// unprocessed received pdus
+     int	nsent;	// sent pdus
+
+     int	nrsp, max_rsp;
+     int	nrsv, max_rsv;
+     int	ncsnd, max_csnd;
+     int	nisnd, max_isnd;
+     int	nwsnd, max_wsnd;
+     int	nhld, max_hld;
+
+     struct bintime t_sent;
+     struct bintime t_recv;
+};
+
+/*
+ | one per 'session'
+ */
+typedef struct isc_session {
+     TAILQ_ENTRY(isc_session)	sp_link;
+     int		flags;
+     struct cdev	*dev;
+     struct socket	*soc;
+     struct file	*fp;
+     struct thread	*td;
+
+     struct proc 	*proc; // the userland process
+     int		signal;
+
+     struct proc 	*soc_proc;
+
+     struct proc	*stp;	// the sm thread
+
+     struct isc_softc	*isc;
+
+     digest_t   	*hdrDigest;     // the digest alg. if any
+     digest_t   	*dataDigest;    // the digest alg. if any
+
+     int		sid;		// Session ID
+     int		targetid;
+//     int		cid;		// Connection ID
+//     int		tsih;		// target session identifier handle
+     sn_t       	sn;             // sequence number stuff;
+     int		cws;		// current window size
+
+     int		target_nluns; // this and target_lun are
+				      // hopefully temporal till I
+				      // figure out a better way.
+     lun_id_t		target_lun[ISCSI_MAX_LUNS];
+
+     struct mtx		rsp_mtx;
+     struct mtx		rsv_mtx;
+     struct mtx		snd_mtx;
+     struct mtx		hld_mtx;
+     struct mtx		io_mtx;
+
+     TAILQ_HEAD(,pduq)	rsp;
+     TAILQ_HEAD(,pduq)	rsv;
+     TAILQ_HEAD(,pduq)	csnd;
+     TAILQ_HEAD(,pduq)	isnd;
+     TAILQ_HEAD(,pduq)	wsnd;
+     TAILQ_HEAD(,pduq)	hld;
+     /*
+      | negotiable values
+      */
+     isc_opt_t		opt;
+
+     struct i_stats	stats;
+     struct cam_path	*cam_path;
+     bhs_t		bhs;
+     struct uio		uio;
+     struct iovec	iov;
+     /*
+      | sysctl stuff
+      */
+     struct sysctl_ctx_list	clist;
+     struct sysctl_oid	*oid;
+} isc_session_t;
+
+typedef struct pduq {
+     TAILQ_ENTRY(pduq)	pq_link;
+
+     caddr_t		buf;
+     u_int		len;	// the total length of the pdu
+     pdu_t		pdu;
+     union ccb		*ccb;
+
+     struct uio		uio;
+     struct iovec	iov[5];	// XXX: careful ...
+     struct mbuf	*mp;
+     struct bintime	ts;
+} pduq_t;
+
+struct isc_softc {
+     //int		state;
+     struct cdev	*dev;
+     eventhandler_tag	eh;
+     char		isid[6];	// Initiator Session ID (48 bits)
+     struct mtx		mtx;
+
+     int			nsess;
+     TAILQ_HEAD(,isc_session)	isc_sess;
+     isc_session_t		*sessions[MAX_SESSIONS];
+
+     struct mtx			pdu_mtx;
+#ifdef  ISCSI_INITIATOR_DEBUG
+     int			 npdu_alloc, npdu_max; // for instrumentation
+#endif
+#define MAX_PDUS	256 // XXX: at the moment this is arbitrary
+     uma_zone_t			pdu_zone; // pool of free pdu's
+     /*
+      | cam stuff
+      */
+     struct cam_sim		*cam_sim;
+     struct cam_path		*cam_path;
+     struct mtx			cam_mtx;
+     /*
+      | sysctl stuff
+      */
+     struct sysctl_ctx_list	clist;
+     struct sysctl_oid		*oid;
+};
+
+#ifdef  ISCSI_INITIATOR_DEBUG
+extern struct mtx iscsi_dbg_mtx;
+#endif
+
+void	isc_start_receiver(isc_session_t *sp);
+void	isc_stop_receiver(isc_session_t *sp);
+
+int	isc_sendPDU(isc_session_t *sp, pduq_t *pq);
+int	isc_qout(isc_session_t *sp, pduq_t *pq);
+int	i_prepPDU(isc_session_t *sp, pduq_t *pq);
+
+int	ism_fullfeature(struct cdev *dev, int flag);
+
+int	i_pdu_flush(isc_session_t *sc);
+int	i_setopt(isc_session_t *sp, isc_opt_t *opt);
+void	i_freeopt(isc_opt_t *opt);
+
+int	ic_init(struct isc_softc *sc);
+void	ic_destroy(struct isc_softc *sc);
+int	ic_fullfeature(struct cdev *dev);
+void	ic_lost_target(isc_session_t *sp, int target);
+int	ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp);
+
+void	ism_recv(isc_session_t *sp, pduq_t *pq);
+int	ism_start(isc_session_t *sp);
+void	ism_stop(isc_session_t *sp);
+
+int	scsi_encap(struct cam_sim *sim, union ccb *ccb);
+int	scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void	iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void	iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void	iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
+void	iscsi_async(isc_session_t *sp,  pduq_t *pq);
+void	iscsi_cleanup(isc_session_t *sp);
+int	iscsi_requeue(isc_session_t *sp);
+
+void	ic_freeze(isc_session_t *sp);
+void	ic_release(isc_session_t *sp);
+
+// Serial Number Arithmetic
+#define _MAXINCR	0x7FFFFFFF	// 2 ^ 31 - 1
+#define SNA_GT(i1, i2)	((i1 != i2) && (\
+	(i1 < i2 && i2 - i1 > _MAXINCR) ||\
+	(i1 > i2 && i1 - i2 < _MAXINCR))?1: 0)
+
+/*
+ | inlines
+ */
+#ifdef _CAM_CAM_XPT_SIM_H
+
+#if __FreeBSD_version <  600000
+#define CAM_LOCK(arg)
+#define CAM_ULOCK(arg)
+
+static __inline void
+XPT_DONE(struct isc_softc *isp, union ccb *ccb)
+{
+     mtx_lock(&Giant);
+     xpt_done(ccb);
+     mtx_unlock(&Giant);
+}
+#elif __FreeBSD_version >= 700000
+#define CAM_LOCK(arg)	mtx_lock(&arg->cam_mtx)
+#define CAM_UNLOCK(arg)	mtx_unlock(&arg->cam_mtx)
+
+static __inline void
+XPT_DONE(struct isc_softc *isp, union ccb *ccb)
+{
+     CAM_LOCK(isp);
+     xpt_done(ccb);
+     CAM_UNLOCK(isp);
+}
+#else
+//__FreeBSD_version >= 600000
+#define CAM_LOCK(arg)
+#define CAM_UNLOCK(arg)
+#define XPT_DONE(ignore, arg)	xpt_done(arg)
+#endif
+
+#endif /* _CAM_CAM_XPT_SIM_H */
+
+static __inline pduq_t *
+pdu_alloc(struct isc_softc *isc, int wait)
+{
+     pduq_t	*pq;
+
+     pq = (pduq_t *)uma_zalloc(isc->pdu_zone, wait? M_WAITOK: M_NOWAIT);
+     if(pq == NULL) {
+	  // will not happend if M_WAITOK ...
+	  mtx_unlock(&isc->pdu_mtx);
+	  debug(1, "out of mem");
+	  return NULL;
+     }
+#ifdef ISCSI_INITIATOR_DEBUG
+     mtx_lock(&isc->pdu_mtx);
+     isc->npdu_alloc++;
+     if(isc->npdu_alloc > isc->npdu_max)
+	  isc->npdu_max = isc->npdu_alloc;
+     mtx_unlock(&isc->pdu_mtx);
+#endif
+     memset(pq, 0, sizeof(pduq_t));
+
+     return pq;
+}
+
+static __inline void
+pdu_free(struct isc_softc *isc, pduq_t *pq)
+{
+     if(pq->mp)
+	  m_freem(pq->mp);
+     if(pq->buf != NULL)
+	  free(pq->buf, M_ISCSI);
+     mtx_lock(&isc->pdu_mtx);
+     uma_zfree(isc->pdu_zone, pq);
+#ifdef ISCSI_INITIATOR_DEBUG
+     isc->npdu_alloc--;
+#endif
+     mtx_unlock(&isc->pdu_mtx);
+}
+
+static __inline void
+i_nqueue_rsp(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->rsp_mtx);
+     if(++sp->stats.nrsp > sp->stats.max_rsp)
+	  sp->stats.max_rsp = sp->stats.nrsp;
+     TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link);
+     mtx_unlock(&sp->rsp_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_rsp(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->rsp_mtx);
+     if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) {
+	  sp->stats.nrsp--;
+	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
+     }
+     mtx_unlock(&sp->rsp_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_rsv(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->rsv_mtx);
+     if(++sp->stats.nrsv > sp->stats.max_rsv)
+	  sp->stats.max_rsv = sp->stats.nrsv;
+     TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link);
+     mtx_unlock(&sp->rsv_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_rsv(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->rsv_mtx);
+     if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) {
+	  sp->stats.nrsv--;
+	  TAILQ_REMOVE(&sp->rsv, pq, pq_link);
+     }
+     mtx_unlock(&sp->rsv_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_csnd(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->snd_mtx);
+     if(++sp->stats.ncsnd > sp->stats.max_csnd)
+	  sp->stats.max_csnd = sp->stats.ncsnd;
+     TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link);
+     mtx_unlock(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_csnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
+	  sp->stats.ncsnd--;
+	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
+     }
+     mtx_unlock(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_isnd(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->snd_mtx);
+     if(++sp->stats.nisnd > sp->stats.max_isnd)
+	  sp->stats.max_isnd = sp->stats.nisnd;
+     TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link);
+     mtx_unlock(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_isnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
+	  sp->stats.nisnd--;
+	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
+     }
+     mtx_unlock(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->snd_mtx);
+     if(++sp->stats.nwsnd > sp->stats.max_wsnd)
+	  sp->stats.max_wsnd = sp->stats.nwsnd;
+     TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link);
+     mtx_unlock(&sp->snd_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_wsnd(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->snd_mtx);
+     if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
+	  sp->stats.nwsnd--;
+	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
+     }
+     mtx_unlock(&sp->snd_mtx);
+
+     return pq;
+}
+
+static __inline pduq_t *
+i_dqueue_snd(isc_session_t *sp, int which)
+{
+     pduq_t *pq;
+
+     pq = NULL;
+     mtx_lock(&sp->snd_mtx);
+     if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
+	  sp->stats.nisnd--;
+	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
+     } else
+     if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
+	  sp->stats.nwsnd--;
+	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
+     } else
+     if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
+	  sp->stats.ncsnd--;
+	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
+     }
+     mtx_unlock(&sp->snd_mtx);
+
+     return pq;
+}
+
+/*
+ | Waiting for ACK (or something :-)
+ */
+static __inline void
+i_nqueue_hld(isc_session_t *sp, pduq_t *pq)
+{
+     getbintime(&pq->ts);
+     mtx_lock(&sp->hld_mtx);
+     if(++sp->stats.nhld > sp->stats.max_hld)
+	  sp->stats.max_hld = sp->stats.nhld;
+     TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link);
+     mtx_unlock(&sp->hld_mtx);
+     return;
+}
+
+static __inline void
+i_remove_hld(isc_session_t *sp, pduq_t *pq)
+{
+     mtx_lock(&sp->hld_mtx);
+     sp->stats.nhld--;
+     TAILQ_REMOVE(&sp->hld, pq, pq_link);
+     mtx_unlock(&sp->hld_mtx);
+}
+
+static __inline pduq_t *
+i_dqueue_hld(isc_session_t *sp)
+{
+     pduq_t *pq;
+
+     mtx_lock(&sp->hld_mtx);
+     if((pq = TAILQ_FIRST(&sp->hld)) != NULL) {
+	  sp->stats.nhld--;
+	  TAILQ_REMOVE(&sp->hld, pq, pq_link);
+     }
+     mtx_unlock(&sp->hld_mtx);
+
+     return pq;
+}
+
+static __inline pduq_t *
+i_search_hld(isc_session_t *sp, int itt, int keep)
+{
+     pduq_t	*pq, *tmp;
+
+     pq = NULL;
+
+     mtx_lock(&sp->hld_mtx);
+     TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, tmp) {
+	  if(pq->pdu.ipdu.bhs.itt == itt) {
+	       if(!keep) {
+		    sp->stats.nhld--;
+		    TAILQ_REMOVE(&sp->hld, pq, pq_link);
+	       }
+	       break;
+	  }
+     }
+     mtx_unlock(&sp->hld_mtx);
+
+     return pq;
+}
+
+static __inline void
+i_mbufcopy(struct mbuf *mp, caddr_t dp, int len)
+{
+     struct mbuf *m;
+     caddr_t bp;
+
+     for(m = mp; m != NULL; m = m->m_next) {
+	  bp = mtod(m, caddr_t);
+	  /*
+	   | the pdu is word (4 octed) aligned
+	   | so len <= packet
+	   */
+	  memcpy(dp, bp, MIN(len, m->m_len));
+	  dp += m->m_len;
+	  len -= m->m_len;
+	  if(len <= 0)
+	       break;
+     }
+}
--- /dev/null
+++ sys/dev/iscsi/initiator/isc_soc.c
@@ -0,0 +1,576 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | iSCSI
+ | $Id: isc_soc.c,v 1.26 2007/05/19 06:09:01 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/isc_soc.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/user.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+#ifndef USE_MBUF
+#define USE_MBUF
+#endif
+
+#ifdef USE_MBUF
+/*
+ |  a dummy function for freeing external storage for mbuf
+ */
+static void
+nil_fn(void *a, void *b)
+{
+}
+static int nil_refcnt = 0;
+#endif /* USE_MBUF */
+
+int
+isc_sendPDU(isc_session_t *sp, pduq_t *pq)
+{
+     pdu_t		*pp = &pq->pdu;
+     int		len, error;
+#ifdef USE_MBUF
+     struct mbuf        *mh, **mp;
+#else
+     struct uio		*uio = &pq->uio;
+     struct iovec	*iv;
+#endif /* USE_MBUF */
+
+     debug_called(8);
+
+#ifndef USE_MBUF
+     bzero(uio, sizeof(struct uio));
+     uio->uio_rw	= UIO_WRITE;
+     uio->uio_segflg	= UIO_SYSSPACE;
+     uio->uio_td	= sp->td;
+     uio->uio_iov 	= iv = pq->iov;
+
+     iv->iov_base	= &pp->ipdu;
+     iv->iov_len	= sizeof(union ipdu_u);
+     uio->uio_resid	= pq->len;
+     iv++;
+#else /* USE_MBUF */
+     /*  mbuf for the iSCSI header */
+     MGETHDR(mh, M_TRYWAIT, MT_DATA);
+     mh->m_len = mh->m_pkthdr.len = sizeof(union ipdu_u);
+     mh->m_pkthdr.rcvif = NULL;
+     MH_ALIGN(mh, sizeof(union ipdu_u));
+     bcopy(&pp->ipdu, mh->m_data, sizeof(union ipdu_u));
+     mh->m_next = NULL;
+#endif /* USE_MBUF */
+
+     if(sp->hdrDigest)
+	  pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
+     if(pp->ahs_len) {
+#ifndef USE_MBUF
+	  iv->iov_base	= pp->ahs;
+	  iv->iov_len	= pp->ahs_len;
+	  iv++;
+#else /* USE_MBUF */
+          /* Add any AHS to the iSCSI hdr mbuf */
+          /* XXX Assert: (mh->m_pkthdr.len + pp->ahs_len) < MHLEN */
+          bcopy(pp->ahs, (mh->m_data + mh->m_len), pp->ahs_len);
+          mh->m_len += pp->ahs_len;
+          mh->m_pkthdr.len += pp->ahs_len;
+#endif /* USE_MBUF */
+	  if(sp->hdrDigest)
+	       pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
+     }
+     if(sp->hdrDigest) {
+	  debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
+#ifndef USE_MBUF
+	  iv->iov_base	= &pp->hdr_dig;
+	  iv->iov_len	= sizeof(int);
+	  iv++;
+#else /* USE_MBUF */
+          /* Add header digest to the iSCSI hdr mbuf */ 
+          /* XXX Assert: (mh->m_pkthdr.len + 4) < MHLEN */
+          bcopy(&pp->hdr_dig, (mh->m_data + mh->m_len), sizeof(int));
+          mh->m_len += sizeof(int);
+          mh->m_pkthdr.len += sizeof(int);
+#endif /* USE_MBUF */
+     }
+#ifdef USE_MBUF
+     mp = &mh->m_next;
+#endif /* USE_MBUF */
+     if(pq->pdu.ds) {
+#ifndef USE_MBUF
+	  iv->iov_base	= pp->ds;
+	  iv->iov_len	= pp->ds_len;
+	  while(iv->iov_len & 03) // the specs say it must be int alligned
+	       iv->iov_len++;
+	  iv++;
+#else /* USE_MBUF */
+          struct mbuf   *md;
+          int           off = 0;
+
+          len = pp->ds_len;
+	  while(len & 03) // the specs say it must be int alligned
+	       len++;
+
+          while (len > 0) {
+                int       l;
+          
+                MGET(md, M_TRYWAIT, MT_DATA);
+		md->m_ext.ref_cnt = &nil_refcnt;
+                l = min(MCLBYTES, len);
+                MEXTADD(md, pp->ds + off, l, nil_fn,
+                        NULL, 0, EXT_EXTREF);
+                md->m_len = l;
+                md->m_next = NULL;
+                mh->m_pkthdr.len += l;
+                *mp = md;
+                mp = &md->m_next;
+
+                len -= l;
+                off += l;
+          } 
+#endif /* USE_MBUF */
+     }
+     if(sp->dataDigest) {
+#ifdef USE_MBUF
+          struct mbuf   *me;
+
+#endif /* USE_MBUF */
+	  pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
+#ifndef USE_MBUF
+	  iv->iov_base	= &pp->ds_dig;
+	  iv->iov_len	= sizeof(int);
+	  iv++;
+#else /* USE_MBUF */
+          MGET(me, M_TRYWAIT, MT_DATA);
+          me->m_len = sizeof(int);
+          MH_ALIGN(mh, sizeof(int));
+          bcopy(&pp->ds_dig, me->m_data, sizeof(int));
+          me->m_next = NULL;
+     
+          mh->m_pkthdr.len += sizeof(int);
+          *mp = me;
+#endif /* USE_MBUF */
+     }
+
+#ifndef USE_MBUF
+     uio->uio_iovcnt	= iv - pq->iov;
+     sdebug(5, "opcode=%x iovcnt=%d uio_resid=%d itt=%x",
+	    pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid,
+	    ntohl(pp->ipdu.bhs.itt));
+     sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p",
+	    sp, sp->soc, uio, sp->td);
+
+     do {
+	  len = uio->uio_resid;
+	  error = sosend(sp->soc, NULL, uio, 0, 0, 0, sp->td);
+	  if(uio->uio_resid == 0 || error || len == uio->uio_resid) {
+	       if(uio->uio_resid) {
+		    sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d",
+			   uio->uio_resid, uio->uio_iovcnt, error, len);
+		    if(error == 0)
+			 error = EAGAIN; // 35
+	       }
+	       break;
+	  }
+	  /*
+	   | XXX: untested code
+	   */
+	  sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d",
+		uio->uio_resid, uio->uio_iovcnt);
+	  iv = uio->uio_iov;
+	  len -= uio->uio_resid;
+	  while(uio->uio_iovcnt > 0) {
+	       if(iv->iov_len > len) {
+		    caddr_t	bp = (caddr_t)iv->iov_base;
+
+		    iv->iov_len -= len;
+		    iv->iov_base = (void *)&bp[len];
+		    break;
+	       }
+	       len -= iv->iov_len;
+	       uio->uio_iovcnt--;
+	       uio->uio_iov++;
+	       iv++;
+	  }
+     } while(uio->uio_resid);
+
+     if(error == 0) {
+	  sp->stats.nsent++;
+	  getbintime(&sp->stats.t_sent);
+#else /* USE_MBUF */
+     if ((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, sp->td)) != 0) {
+           m_freem(mh);
+           return (error);
+#endif /* USE_MBUF */
+     }
+#ifndef USE_MBUF
+     return error;
+#else /* USE_MBUF */
+     sp->stats.nsent++;
+     getbintime(&sp->stats.t_sent);
+     return 0;
+#endif /* USE_MBUF */
+}
+
+/*
+ | wait till a PDU header is received
+ | from the socket.
+ */
+/*
+   The format of the BHS is:
+
+   Byte/     0       |       1       |       2       |       3       |
+      /              |               |               |               |
+     |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
+     +---------------+---------------+---------------+---------------+
+    0|.|I| Opcode    |F|  Opcode-specific fields                     |
+     +---------------+---------------+---------------+---------------+
+    4|TotalAHSLength | DataSegmentLength                             |
+     +---------------+---------------+---------------+---------------+
+    8| LUN or Opcode-specific fields                                 |
+     +                                                               +
+   12|                                                               |
+     +---------------+---------------+---------------+---------------+
+   16| Initiator Task Tag                                            |
+     +---------------+---------------+---------------+---------------+
+   20/ Opcode-specific fields                                        /
+    +/                                                               /
+     +---------------+---------------+---------------+---------------+
+   48
+ */
+static __inline int
+so_getbhs(isc_session_t *sp)
+{
+     bhs_t *bhs		= &sp->bhs;
+     struct uio		*uio = &sp->uio;
+     struct iovec	*iov = &sp->iov;
+     int		error, flags;
+
+     debug_called(8);
+
+     iov->iov_base	= bhs;
+     iov->iov_len	= sizeof(bhs_t);
+
+     uio->uio_iov	= iov;
+     uio->uio_iovcnt	= 1;
+     uio->uio_rw	= UIO_READ;
+     uio->uio_segflg	= UIO_SYSSPACE;
+     uio->uio_td	= curthread; // why ...
+     uio->uio_resid	= sizeof(bhs_t);
+
+     flags = MSG_WAITALL;
+     error = soreceive(sp->soc, NULL, uio, 0, 0, &flags);
+
+     if(error)
+	  debug(2, "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd",
+		error,
+		sp->soc->so_error, uio->uio_resid, iov->iov_len);
+     if(!error && (uio->uio_resid > 0)) {
+	  debug(2, "error=%d so_error=%d uio->uio_resid=%d iov.iov_len=%zd so_state=%x",
+		error,
+		sp->soc->so_error, uio->uio_resid, iov->iov_len, sp->soc->so_state);
+	  error = EAGAIN; // EPIPE;
+     }
+	  
+     return error;
+}
+
+/*
+ | so_recv gets called when there is at least
+ | an iSCSI header in the queue
+ */
+static int
+so_recv(isc_session_t *sp, pduq_t *pq)
+{
+     struct socket	*so = sp->soc;
+     sn_t		*sn = &sp->sn;
+     struct uio		*uio = &pq->uio;
+     pdu_t		*pp;
+     int		error;
+     size_t		n, len;
+     bhs_t		*bhs;
+     u_int		max, exp;
+
+     debug_called(8);
+     /*
+      | now calculate how much data should be in the buffer
+      | NOTE: digest is not verified/calculated - yet
+      */
+     pp = &pq->pdu;
+     bhs = &pp->ipdu.bhs;
+
+     len = 0;
+     if(bhs->AHSLength) {
+	  pp->ahs_len = bhs->AHSLength * 4;
+	  len += pp->ahs_len;
+     }
+     if(sp->hdrDigest)
+	  len += 4;
+     if(bhs->DSLength) {
+	  n = bhs->DSLength;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	  pp->ds_len = ((n & 0x00ff0000) >> 16)
+	       | (n & 0x0000ff00)
+	       | ((n & 0x000000ff) << 16);
+#else
+	  pp->ds_len = n;
+#endif
+	  len += pp->ds_len;
+	  while(len & 03)
+	       len++;
+	  if(sp->dataDigest)
+	       len += 4;
+     }
+
+     if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) {
+#if 0
+	  xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d",
+		 len, sp->opt.maxRecvDataSegmentLength);
+	  // deep trouble here, probably all we can do is
+	  // force a disconnect, XXX: check RFC ...
+	  log(LOG_ERR,
+	      "so_recv: impossible PDU length(%ld) from iSCSI %s/%s\n",
+	      len, sp->opt.targetAddress, sp->opt.targetName);
+#endif
+	  /*
+	   | XXX: this will realy screwup the stream.
+	   | should clear up the buffer till a valid header
+	   | is found, or just close connection ...
+	   | should read the RFC.
+	   */
+	  error = E2BIG;
+	  goto out;
+     }
+     if(len) {
+	  int	flags;
+
+	  uio->uio_resid = len;
+	  uio->uio_td = curthread; // why ...
+	  flags = MSG_WAITALL;
+
+	  error = soreceive(so, NULL, uio, &pq->mp, NULL, &flags);
+	  //if(error == EAGAIN)
+	  // XXX: this needs work! it hangs iscontrol
+	  if(error || uio->uio_resid)
+	       goto out;
+     }
+     pq->len += len;
+     sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x",
+	    pq->len, bhs->opcode, pp->ahs_len, pp->ds_len);
+
+     max = ntohl(bhs->MaxCmdSN);
+     exp = ntohl(bhs->ExpStSN);
+
+     if(max < exp - 1 &&
+	max > exp - _MAXINCR) {
+	  sdebug(2,  "bad cmd window size");
+	  error = EIO; // XXX: for now;
+	  goto out; // error
+     }
+
+     if(SNA_GT(max, sn->maxCmd))
+	  sn->maxCmd = max;
+
+     if(SNA_GT(exp, sn->expCmd))
+	  sn->expCmd = exp;
+
+     sp->cws = sn->maxCmd - sn->expCmd + 1;
+
+     return 0;
+
+ out:
+     // XXX: need some work here
+     xdebug("have a problem, error=%d", error);
+     pdu_free(sp->isc, pq);
+     if(!error && uio->uio_resid > 0)
+	  error = EPIPE;
+     return error;
+}
+/*
+ | wait for something to arrive.
+ | and if the pdu is without errors, process it.
+ */
+static int
+so_input(isc_session_t *sp)
+{
+     pduq_t		*pq;
+     int		error;
+
+     debug_called(8);
+     /*
+      | first read in the iSCSI header
+      */
+     error = so_getbhs(sp);
+     if(error == 0) {
+	  /*
+	   | now read the rest.
+	   */
+	  pq = pdu_alloc(sp->isc, 1);  // OK to WAIT
+	  pq->pdu.ipdu.bhs = sp->bhs;
+	  pq->len = sizeof(bhs_t);	// so far only the header was read
+	  error = so_recv(sp, pq);
+	  if(error != 0) {
+	       error += 0x800; // XXX: just to see the error.
+	       // terminal error
+	       // XXX: close connection and exit
+	  }
+	  else {
+	       sp->stats.nrecv++;
+	       getbintime(&sp->stats.t_recv);
+	       ism_recv(sp, pq);
+	  }
+     }
+     return error;
+}
+
+/*
+ | one per active (connected) session.
+ | this thread is responsible for reading
+ | in packets from the target.
+ */
+static void
+isc_soc(void *vp)
+{
+     isc_session_t	*sp = (isc_session_t *)vp;
+     struct socket	*so = sp->soc;
+     int		error;
+
+     debug_called(8);
+
+     sp->flags |= ISC_CON_RUNNING;
+
+     if(sp->cam_path)
+	  ic_release(sp);
+
+     error = 0;
+     while(sp->flags & ISC_CON_RUN) {
+	  // XXX: hunting ...
+	  if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) {
+	       debug(2, "sp->soc=%p", sp->soc);
+	       break;
+	  }
+	  error = so_input(sp);
+	  if(error == 0) {
+#ifdef ISC_OWAITING
+	       mtx_lock(&sp->io_mtx);
+	       if(sp->flags & ISC_OWAITING) {
+		    sp->flags &= ~ISC_OWAITING;
+	       }
+	       wakeup(&sp->flags);
+	       mtx_unlock(&sp->io_mtx);
+#else
+	       wakeup(&sp->flags);
+#endif
+
+	  } else if(error == EPIPE)
+	       break;
+	  else if(error == EAGAIN) {
+	       if(so->so_state & SS_ISCONNECTED) 
+		    // there seems to be a problem in 6.0 ...
+		    tsleep(sp, PRIBIO, "isc_soc", 2*hz);
+	  }
+     }
+     sdebug(2, "terminated, flags=%x so_count=%d so_state=%x error=%d",
+	    sp->flags, so->so_count, so->so_state, error);
+     if((sp->proc != NULL) && sp->signal) {
+	  PROC_LOCK(sp->proc);
+	  psignal(sp->proc, sp->signal);
+	  PROC_UNLOCK(sp->proc);
+	  sp->flags |= ISC_SIGNALED;
+	  sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal);
+     }
+     else {
+	  // we have to do something ourselves
+	  // like closing this session ...
+     }
+     /*
+      | we've been terminated
+      */
+     // do we need this mutex ...?
+     mtx_lock(&sp->io_mtx);
+     sp->flags &= ~ISC_CON_RUNNING;
+     wakeup(&sp->soc);
+
+     mtx_unlock(&sp->io_mtx);
+
+     kthread_exit(0);
+}
+
+void
+isc_stop_receiver(isc_session_t *sp)
+{
+     int n = 5;
+     debug_called(8);
+
+     sdebug(4, "sp=%p sp->soc=%p", sp, sp? sp->soc: 0);
+     soshutdown(sp->soc, SHUT_RD);
+
+     mtx_lock(&sp->io_mtx);
+     sp->flags &= ~ISC_CON_RUN;
+     while(n-- && (sp->flags & ISC_CON_RUNNING)) {
+	  sdebug(3, "waiting n=%d... flags=%x", n, sp->flags);
+	  msleep(&sp->soc, &sp->io_mtx, PRIBIO, "isc_stpc", 5*hz);
+     }
+     mtx_unlock(&sp->io_mtx);
+
+     if(sp->fp != NULL)
+	  fdrop(sp->fp, sp->td);
+     fputsock(sp->soc);
+     sp->soc = NULL;
+     sp->fp = NULL;
+}
+
+void
+isc_start_receiver(isc_session_t *sp)
+{
+     debug_called(8);
+
+     sp->flags |= ISC_CON_RUN;
+     kthread_create(isc_soc, sp, &sp->soc_proc, 0, 0, "iscsi%d", sp->sid);
+}
--- /dev/null
+++ sys/dev/iscsi/initiator/iscsi_subr.c
@@ -0,0 +1,567 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | $Id: iscsi_subr.c,v 1.17 2006/11/26 14:50:43 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/iscsi_subr.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/callout.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/kthread.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_periph.h>
+#include <cam/scsi/scsi_message.h>
+#include <sys/eventhandler.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+/*
+ | Interface to the SCSI layer
+ */
+void
+iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb 		*ccb = opq->ccb;
+     struct ccb_scsiio	*csio = &ccb->csio;
+     pdu_t		*opp = &opq->pdu;
+     bhs_t		*bhp = &opp->ipdu.bhs;
+     r2t_t		*r2t = &pq->pdu.ipdu.r2t;
+     pduq_t	*wpq;
+     int	error;
+
+     debug_called(8);
+     sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt),
+	   ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W);
+
+     switch(bhp->opcode) {
+     case ISCSI_SCSI_CMD:
+	  if(opp->ipdu.scsi_req.W) {
+	       data_out_t	*cmd;
+	       u_int		ddtl = ntohl(r2t->ddtl);
+	       u_int		edtl = ntohl(opp->ipdu.scsi_req.edtlen);
+	       u_int		bleft, bs, dsn, bo;
+	       caddr_t		bp = csio->data_ptr;
+
+	       bo = ntohl(r2t->bo);
+	       bleft = ddtl;
+
+	       if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC
+		    bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl);
+	       else
+		    bs = ddtl;
+	       dsn = 0;
+	       sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x",
+		      edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength);
+	       while(bleft > 0) {
+		    wpq = pdu_alloc(sp->isc, 1);
+		    if(wpq == NULL) {
+			 // should not happen if above is 1
+			 sdebug(1, "now what?");
+			 return;
+		    }
+		    cmd = &wpq->pdu.ipdu.data_out;
+		    cmd->opcode = ISCSI_WRITE_DATA;
+		    cmd->lun[0]	= r2t->lun[0];
+		    cmd->lun[1]	= r2t->lun[1];
+		    cmd->ttt	= r2t->ttt;
+		    cmd->itt	= r2t->itt;
+
+		    cmd->dsn	= htonl(dsn);
+		    cmd->bo	= htonl(bo);
+
+		    cmd->F 	= (bs < bleft)? 0: 1; // is this the last one?
+		    bs = MIN(bs, bleft);
+		    
+		    wpq->pdu.ds_len	= bs;
+		    wpq->pdu.ds		= bp;
+		    
+		    error = isc_qout(sp, wpq);
+		    sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error);
+		    if(error)
+			 break;
+		    bo += bs;
+		    bp += bs;
+		    bleft -= bs;
+		    dsn++;
+	       }
+	  }
+	  break;
+
+     default:
+	  // XXX: should not happen ...
+	  xdebug("huh? opcode=0x%x", bhp->opcode);
+     }
+}
+
+static int
+getSenseData(u_int status, union ccb *ccb, pduq_t *pq)
+{
+     pdu_t		*pp = &pq->pdu;
+     struct		ccb_scsiio *scsi = (struct ccb_scsiio *)ccb;
+     struct		scsi_sense_data *sense = &scsi->sense_data;
+     struct mbuf	*m = pq->mp;
+     scsi_rsp_t		*cmd = &pp->ipdu.scsi_rsp;
+     caddr_t		bp;
+     int		sense_len, mustfree = 0;
+
+     bp = mtod(pq->mp, caddr_t);
+     if((sense_len = scsi_2btoul(bp)) == 0)
+	  return 0;
+     debug(4, "sense_len=%d", sense_len);
+     /*
+      | according to the specs, the sense data cannot
+      | be larger than 252 ...
+      */
+     if(sense_len > m->m_len) {
+	  bp = malloc(sense_len, M_ISCSI, M_WAITOK);
+	  debug(3, "calling i_mbufcopy(len=%d)", sense_len);
+	  i_mbufcopy(pq->mp, bp, sense_len);
+	  mustfree++;
+     }
+     scsi->scsi_status = status;
+
+     bcopy(bp+2, sense, min(sense_len, scsi->sense_len));
+     scsi->sense_resid = 0;
+     if(cmd->flag & (BIT(1)|BIT(2)))
+	  scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt);
+     debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x",
+	   sense_len,
+	   ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid,
+	   pp->ds_len, sense->error_code, sense->flags);
+
+     if(mustfree)
+	  free(bp, M_ISCSI);
+
+     return 1;
+}
+
+/*
+ | Some information is from SAM draft.
+ */
+static void
+_scsi_done(struct isc_softc *isp, u_int response, u_int status, union ccb *ccb, pduq_t *pq)
+{
+     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
+
+     debug_called(8);
+
+     if(status || response) {
+	  debug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq);
+	  if(pq != NULL)
+	       debug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len);
+     }
+     ccb_h->status = 0;
+     switch(response) {
+     case 0: // Command Completed at Target
+	  switch(status) {
+	  case 0:	// Good, all is ok
+	       ccb_h->status = CAM_REQ_CMP;
+	       break;
+	       
+	  case 0x02: 	// Check Condition
+	       if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq))
+		    ccb_h->status |= CAM_AUTOSNS_VALID;
+
+	  case 0x14:	// Intermediate-Condition Met
+	  case 0x10:	// Intermediate
+	  case 0x04:	// Condition Met
+	       ccb_h->status |= CAM_SCSI_STATUS_ERROR;
+	       break;
+
+	  case 0x08:
+	       ccb_h->status = CAM_BUSY;
+	       break;
+
+	  case 0x18: // Reservation Conflict
+	  case 0x28: // Task Set Full
+	       ccb_h->status = CAM_REQUEUE_REQ;
+	       break;
+	  default:
+	       //case 0x22: // Command Terminated
+	       //case 0x30: // ACA Active
+	       //case 0x40: // Task Aborted
+	       ccb_h->status = CAM_REQ_ABORTED;
+	  }
+	  break;
+
+     default:
+	  if((response >= 0x80) && (response <= 0xFF)) {
+	       // Vendor specific ...
+	  }
+     case 1: // target failure
+	  ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED;
+	  break;
+     }
+     debug(5, "ccb_h->status=%x", ccb_h->status);
+
+     XPT_DONE(isp, ccb);
+}
+
+/*
+ | returns the lowest cmdseq that was not acked
+ */
+int
+iscsi_requeue(isc_session_t *sp)
+{
+     pduq_t	*pq;
+     u_int	i, n, last;
+
+     debug_called(8);
+     last = -1;
+     i = 0;
+     while((pq = i_dqueue_hld(sp)) != NULL) {
+	  i++;
+	  _scsi_done(sp->isc, 0, 0x28, pq->ccb, NULL);
+	  n = ntohl(pq->pdu.ipdu.bhs.CmdSN);
+	  if(last > n)
+	       last = n;
+	  sdebug(2, "last=%x n=%x", last, n);
+	  pdu_free(sp->isc, pq);
+     }
+     return i? last: sp->sn.cmd;
+}
+
+int
+i_pdu_flush(isc_session_t *sp)
+{
+     int	n = 0;
+     pduq_t	*pq;
+
+     debug_called(8);
+     while((pq = i_dqueue_rsp(sp)) != NULL) {
+	  pdu_free(sp->isc, pq);
+	  n++;
+     }
+     while((pq = i_dqueue_rsv(sp)) != NULL) {
+	  pdu_free(sp->isc, pq);
+	  n++;
+     }
+     while((pq = i_dqueue_snd(sp, -1)) != NULL) {
+	  pdu_free(sp->isc, pq);
+	  n++;
+     }
+     while((pq = i_dqueue_hld(sp)) != NULL) {
+	  pdu_free(sp->isc, pq);
+	  n++;
+     }
+     if(n != 0)
+	  xdebug("%d pdus recovered, should have been ZERO!", n);
+     return n;
+}
+/*
+ | called from ism_destroy.
+ */
+void
+iscsi_cleanup(isc_session_t *sp)
+{
+     pduq_t *pq, *pqtmp;
+
+     debug_called(8);
+
+     TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, pqtmp) {
+	  sdebug(3, "hld pq=%p", pq);
+	  if(pq->ccb)
+	       _scsi_done(sp->isc, 1, 0x40, pq->ccb, NULL);
+	  TAILQ_REMOVE(&sp->hld, pq, pq_link);
+	  pdu_free(sp->isc, pq);
+     }
+     while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) {
+	  sdebug(3, "pq=%p", pq);
+	  if(pq->ccb)
+	       _scsi_done(sp->isc, 1, 0x40, pq->ccb, NULL);
+	  pdu_free(sp->isc, pq);
+     }
+
+     wakeup(&sp->rsp);
+}
+
+void
+iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     pdu_t		*pp = &pq->pdu;
+     scsi_rsp_t		*cmd = &pp->ipdu.scsi_rsp;
+
+     debug_called(8);
+
+     _scsi_done(sp->isc, cmd->response, cmd->status, opq->ccb, pq);
+
+     pdu_free(sp->isc, opq);
+}
+
+// see RFC 3720, 10.9.1 page 146
+void
+iscsi_async(isc_session_t *sp, pduq_t *pq)
+{
+     pdu_t		*pp = &pq->pdu;
+     async_t		*cmd = &pp->ipdu.async;
+
+     debug_called(8);
+
+     sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode);
+     switch(cmd->asyncEvent) {
+     case 0: // check status ...
+	  break;
+     case 1: // target request logout
+	  break;
+     case 2: // target indicates it wants to drop connection
+	  break;
+
+     case 3: // target indicates it will drop all connections.
+	  isc_stop_receiver(sp);
+	  break;
+
+     case 4: // target request parameter negotiation
+	  break;
+     default:
+	  break;
+     }
+}
+
+void
+iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb 		*ccb = opq->ccb;
+     //reject_t		*reject = &pq->pdu.ipdu.reject;
+
+     debug_called(8);
+     //XXX: check RFC 10.17.1 (page 176)
+     ccb->ccb_h.status = CAM_REQ_ABORTED;
+     XPT_DONE(sp->isc, ccb);
+ 
+     pdu_free(sp->isc, opq);
+}
+
+/*
+ | deal with lun
+ */
+static int
+dwl(isc_session_t *sp, int lun, u_char *lp)
+{
+     int	i;
+
+     debug_called(8);
+
+     /*
+      | mapping LUN to iSCSI LUN
+      | check the SAM-2 specs
+      | hint: maxLUNS is a small number, cam's LUN is 32bits
+      | iSCSI is 64bits, scsi is ?
+      */
+     // XXX: check if this will pass the endian test
+     if(lun < 256) {
+	  lp[0] = 0;
+	  lp[1] = lun;
+     } else
+     if(lun < 16384) {
+	  lp[0] = (1 << 5) | ((lun >> 8) & 0x3f);
+	  lp[1] = lun & 0xff;
+     } 
+     else {
+	  xdebug("lun %d: is unsupported!", lun);
+	  return -1;
+     }
+
+     for(i = 0; i < sp->target_nluns; i++)
+	  if(sp->target_lun[i] == lun)
+	       return 0;
+     if(sp->target_nluns < ISCSI_MAX_LUNS)
+	  sp->target_lun[sp->target_nluns++] = lun;
+
+     sdebug(3, "nluns=%d lun=%d", sp->target_nluns, lun);
+
+     return 0;
+}
+
+/*
+ | encapsulate the scsi command and 
+ */
+int
+scsi_encap(struct cam_sim *sim, union ccb *ccb)
+{
+     struct isc_softc	*isp = (struct isc_softc *)cam_sim_softc(sim);
+     isc_session_t	*sp;
+     struct ccb_scsiio	*csio = &ccb->csio;
+     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
+     pduq_t		*pq;
+     scsi_req_t		*cmd;
+
+     debug_called(8);
+
+     debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0);
+     sp = ccb_h->spriv_ptr0;
+
+     if((pq = pdu_alloc(isp, 1)) == NULL) { // cannot happen
+	  sdebug(3, "freezing");
+	  ccb->ccb_h.status = CAM_REQUEUE_REQ;
+	  ic_freeze(sp);
+	  return 0;
+     }
+#if 0
+     if((sp->flags & ISC_FFPHASE) == 0) {
+	  ccb->ccb_h.status = CAM_DEV_NOT_THERE; // CAM_NO_NEXUS;
+	  sdebug(3, "no active session with target %d", ccb_h->target_id);
+	  goto bad;
+     }
+#endif
+     cmd = &pq->pdu.ipdu.scsi_req;
+     cmd->opcode = ISCSI_SCSI_CMD;
+     cmd->F = 1;
+     /*
+      | map tag option, default is UNTAGGED
+      */
+     switch(csio->tag_action) {
+     case MSG_SIMPLE_Q_TAG:	cmd->attr = iSCSI_TASK_SIMPLE;	break;
+     case MSG_HEAD_OF_Q_TAG:	cmd->attr = iSCSI_TASK_ORDER;	break;
+     case MSG_ORDERED_Q_TAG:	cmd->attr = iSCSI_TASK_HOFQ;	break;
+     case MSG_ACA_TASK:		cmd->attr = iSCSI_TASK_ACA;	break;
+     }
+
+     dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun);
+
+     if((ccb_h->flags & CAM_CDB_POINTER) != 0) {
+	  if((ccb_h->flags & CAM_CDB_PHYS) == 0) {
+	       if(csio->cdb_len > 16) {
+		    sdebug(3, "oversize cdb %d > 16", csio->cdb_len);
+		    goto invalid;
+	       }
+	  }
+	  else {
+	       sdebug(3, "not phys");
+	       goto invalid;
+	  }
+     }
+
+     if(csio->cdb_len > sizeof(cmd->cdb))
+	  xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb));
+
+     memcpy(cmd->cdb,
+	    ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes,
+	    csio->cdb_len);
+
+     cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT;
+     cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN;
+     cmd->edtlen = htonl(csio->dxfer_len);
+
+     pq->ccb = ccb;
+     /*
+      | place it in the out queue
+      */
+     if(isc_qout(sp, pq) == 0)
+	  return 1; 
+ invalid:
+     ccb->ccb_h.status = CAM_REQ_INVALID;
+     pdu_free(isp, pq);
+     return 0;
+}
+
+int
+scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
+{
+     union ccb 		*ccb = opq->ccb;
+     struct ccb_scsiio	*csio = &ccb->csio;
+     pdu_t		*opp = &opq->pdu;
+     bhs_t		*bhp = &opp->ipdu.bhs;
+     
+     debug_called(8);
+     sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d",
+	    pq, opq, bhp->opcode, pq->pdu.ds_len);
+     if(ccb == NULL) {
+	  sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d",
+		 ntohl(pq->pdu.ipdu.bhs.itt),
+		 pq, opq, bhp->opcode, pq->pdu.ds_len);
+	  xdebug("%d] ccb == NULL!", sp->sid);
+	  return 0;
+     }
+     if(pq->pdu.ds_len != 0) {
+	  switch(bhp->opcode) {
+	  case ISCSI_SCSI_CMD: {
+	       scsi_req_t *cmd = &opp->ipdu.scsi_req;
+	       sdebug(5, "itt=0x%x opcode=%x R=%d",
+		      ntohl(pq->pdu.ipdu.bhs.itt),
+		      pq->pdu.ipdu.bhs.opcode, cmd->R);
+
+	       switch(pq->pdu.ipdu.bhs.opcode) {
+	       case ISCSI_READ_DATA: // SCSI Data in
+	       {
+		    caddr_t	bp = mtod(pq->mp, caddr_t);
+		    data_in_t 	*rcmd = &pq->pdu.ipdu.data_in;
+
+		    if(cmd->R) {
+			 sdebug(5, "copy to=%p from=%p l1=%d l2=%d",
+				csio->data_ptr, bp,
+				ntohl(cmd->edtlen), pq->pdu.ds_len);
+			 if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) {
+			      int		offset, len = pq->pdu.ds_len;
+			      caddr_t		dp;
+
+			      offset = ntohl(rcmd->bo);
+			      dp = csio->data_ptr + offset;
+			      i_mbufcopy(pq->mp, dp, len);
+			 }
+			 else {
+			      xdebug("edtlen=%d < ds_len=%d",
+				     ntohl(cmd->edtlen), pq->pdu.ds_len);
+			 }
+		    }
+		    if(rcmd->S) {
+			 /*
+			  | contains also the SCSI Status
+			  */
+			 _scsi_done(sp->isc, 0, rcmd->status, opq->ccb, NULL);
+			 return 0;
+		    } else
+			 return 1;
+	       }
+	       break;
+	       }
+	  }
+	  default:
+	       sdebug(3, "opcode=%02x", bhp->opcode);
+	       break;
+	  }
+     }
+     /*
+      | XXX: error ...
+      */
+     return 1;
+}
--- /dev/null
+++ sys/dev/iscsi/initiator/isc_cam.c
@@ -0,0 +1,424 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/isc_cam.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/callout.h>
+#if __FreeBSD_version >= 700000
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#endif
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/uio.h>
+#include <sys/sysctl.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_periph.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+// XXX: untested/incomplete
+void
+ic_freeze(isc_session_t *sp)
+{
+     debug_called(8);
+#if 0
+     sdebug(2, "freezing path=%p", sp->cam_path == NULL? 0: sp->cam_path);
+     if((sp->cam_path != NULL) && !(sp->flags & ISC_FROZEN)) {
+	  xpt_freeze_devq(sp->cam_path, 1);
+     }
+#endif
+     sp->flags |= ISC_FROZEN;
+}
+
+// XXX: untested/incomplete
+void
+ic_release(isc_session_t *sp)
+{
+     debug_called(8);
+#if 0
+     sdebug(2, "release path=%p", sp->cam_path == NULL? 0: sp->cam_path);
+     if((sp->cam_path != NULL) && (sp->flags & ISC_FROZEN)) {
+	  xpt_release_devq(sp->cam_path, 1, TRUE);
+     }
+#endif
+     sp->flags &= ~ISC_FROZEN;
+}
+
+void
+ic_lost_target(isc_session_t *sp, int target)
+{
+     struct isc_softc   *isp = sp->isc;
+
+     debug_called(8);
+     sdebug(2, "target=%d", target);
+     if(sp->cam_path != NULL) {
+	  mtx_lock(&isp->cam_mtx);
+	  xpt_async(AC_LOST_DEVICE, sp->cam_path, NULL);
+	  xpt_free_path(sp->cam_path);
+	  mtx_unlock(&isp->cam_mtx);
+	  sp->cam_path = 0; // XXX
+     }
+}
+
+static void
+_scan_callback(struct cam_periph *periph, union ccb *ccb)
+{
+     isc_session_t *sp = (isc_session_t *)ccb->ccb_h.spriv_ptr0;
+
+     debug_called(8);
+
+     free(ccb, M_TEMP);
+
+     if(sp->flags & ISC_FFPWAIT) {
+	  sp->flags &= ~ISC_FFPWAIT;
+	  wakeup(sp);
+     }
+}
+
+static void
+_scan_target(isc_session_t *sp, int target)
+{
+     union ccb		*ccb;
+
+     debug_called(8);
+     sdebug(2, "target=%d", target);
+
+     if((ccb = malloc(sizeof(union ccb), M_TEMP, M_WAITOK | M_ZERO)) == NULL) {
+	  xdebug("scan failed (can't allocate CCB)");
+	  return;
+     }
+     CAM_LOCK(sp->isc);
+     xpt_setup_ccb(&ccb->ccb_h, sp->cam_path, 5/*priority (low)*/);
+     ccb->ccb_h.func_code	= XPT_SCAN_BUS;
+     ccb->ccb_h.cbfcnp		= _scan_callback;
+     ccb->crcn.flags		= CAM_FLAG_NONE;
+     ccb->ccb_h.spriv_ptr0	= sp;
+
+     xpt_action(ccb);
+     CAM_UNLOCK(sp->isc);
+}
+
+int
+ic_fullfeature(struct cdev *dev)
+{
+     struct isc_softc 	*isp = dev->si_drv1;
+     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
+
+     debug_called(8);
+     sdebug(3, "dev=%d sc=%p", minor(dev), isp);
+
+     sp->flags &= ~ISC_FFPHASE;
+     sp->flags |= ISC_FFPWAIT;
+
+     CAM_LOCK(isp);
+     if(xpt_create_path(&sp->cam_path, xpt_periph, cam_sim_path(sp->isc->cam_sim),
+			sp->sid, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+	  xdebug("can't create cam path");
+	  CAM_UNLOCK(isp);
+	  return ENODEV; // XXX
+     }
+     CAM_UNLOCK(isp);
+
+     _scan_target(sp, sp->sid);
+
+     while(sp->flags & ISC_FFPWAIT)
+	  tsleep(sp, PRIBIO, "ffp", 5*hz); // the timeout time should
+					    // be configurable
+     if(sp->target_nluns > 0) {
+	  sp->flags |= ISC_FFPHASE;
+	  return 0;
+     }
+
+     return ENODEV;
+}
+
+static void
+_inq(struct cam_sim *sim, union ccb *ccb, int maxluns)
+{
+     struct ccb_pathinq *cpi = &ccb->cpi;
+
+     debug_called(4);
+
+     cpi->version_num = 1; /* XXX??? */
+     cpi->hba_inquiry = PI_SDTR_ABLE | PI_TAG_ABLE | PI_WIDE_32;
+     cpi->target_sprt = 0;
+     cpi->hba_misc = 0;
+     cpi->hba_eng_cnt = 0;
+     cpi->max_target = ISCSI_MAX_TARGETS - 1;
+     cpi->initiator_id = ISCSI_MAX_TARGETS;
+     cpi->max_lun = maxluns;
+     cpi->bus_id = cam_sim_bus(sim);
+     cpi->base_transfer_speed = 3300;
+     strncpy(cpi->sim_vid, "FreeBSD", SIM_IDLEN);
+     strncpy(cpi->hba_vid, "iSCSI", HBA_IDLEN);
+     strncpy(cpi->dev_name, cam_sim_name(sim), DEV_IDLEN);
+     cpi->unit_number = cam_sim_unit(sim);
+     cpi->ccb_h.status = CAM_REQ_CMP;
+}
+
+static void
+ic_action(struct cam_sim *sim, union ccb *ccb)
+{
+     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
+     struct isc_softc	*isp = (struct isc_softc *)cam_sim_softc(sim);
+     isc_session_t	*sp;
+
+     debug_called(8);
+
+     if((ccb_h->target_id != CAM_TARGET_WILDCARD) && (ccb_h->target_id < MAX_SESSIONS))
+	  sp = isp->sessions[ccb_h->target_id];
+     else
+	  sp = NULL;
+
+     ccb_h->spriv_ptr0 = sp;
+
+     debug(4, "func_code=0x%x flags=0x%x status=0x%x target=%d lun=%d retry_count=%d timeout=%d",
+	   ccb_h->func_code, ccb->ccb_h.flags, ccb->ccb_h.status,
+	   ccb->ccb_h.target_id, ccb->ccb_h.target_lun, 
+	   ccb->ccb_h.retry_count, ccb_h->timeout);
+     /*
+      | first quick check
+      */
+     switch(ccb_h->func_code) {
+     default:
+	  // XXX: maybe check something else?
+	  break;
+
+     case XPT_SCSI_IO:
+     case XPT_RESET_DEV:
+     case XPT_GET_TRAN_SETTINGS:
+     case XPT_SET_TRAN_SETTINGS:
+     case XPT_CALC_GEOMETRY:
+	  if(sp == NULL) {
+	       ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+#if __FreeBSD_version < 700000
+	       XPT_DONE(isp, ccb);
+#else
+	       xpt_done(ccb);
+#endif
+	       return;
+	  }
+	  break;
+
+     case XPT_PATH_INQ:
+     case XPT_NOOP:
+	  if(sp == NULL && ccb->ccb_h.target_id != CAM_TARGET_WILDCARD) {
+	       ccb->ccb_h.status = CAM_DEV_NOT_THERE;
+#if __FreeBSD_version < 700000
+	       XPT_DONE(isp, ccb);
+#else
+	       xpt_done(ccb);
+#endif
+	       debug(4, "status = CAM_DEV_NOT_THERE");
+	       return;
+	  }
+     }
+
+     switch(ccb_h->func_code) {
+
+     case XPT_PATH_INQ:
+	  _inq(sim, ccb, (sp? sp->opt.maxluns: ISCSI_MAX_LUNS) - 1);
+	  break;
+
+     case XPT_RESET_BUS: // (can just be a stub that does nothing and completes)
+     {
+	  struct ccb_pathinq *cpi = &ccb->cpi;
+
+	  debug(3, "XPT_RESET_BUS");
+	  cpi->ccb_h.status = CAM_REQ_CMP;
+	  break;
+     }
+
+     case XPT_SCSI_IO: 
+     {
+	  struct ccb_scsiio* csio = &ccb->csio;
+
+	  debug(4, "XPT_SCSI_IO cmd=0x%x", csio->cdb_io.cdb_bytes[0]);
+	  if(sp == NULL) {
+	       ccb_h->status = CAM_REQ_INVALID; //CAM_NO_NEXUS;
+	       debug(4, "xpt_done.status=%d", ccb_h->status);
+	       break;
+	  }
+	  if(ccb_h->target_lun == CAM_LUN_WILDCARD) {
+	       debug(3, "target=%d: bad lun (-1)", ccb_h->target_id);
+	       ccb_h->status = CAM_LUN_INVALID;
+	       break;
+	  }
+#if __FreeBSD_version < 700000
+	  if(scsi_encap(sim, ccb) != 0)
+	       return;
+#else
+	  mtx_unlock(&isp->cam_mtx);
+	  if(scsi_encap(sim, ccb) != 0) {
+	       mtx_lock(&isp->cam_mtx);
+	       return;
+	  }
+	  mtx_lock(&isp->cam_mtx);
+#endif
+	  break;
+     }
+ 
+     case XPT_CALC_GEOMETRY:
+     {
+	  struct	ccb_calc_geometry *ccg;
+
+	  ccg = &ccb->ccg;
+	  debug(6, "XPT_CALC_GEOMETRY vsize=%jd bsize=%d", ccg->volume_size, ccg->block_size);
+	  if(ccg->block_size == 0 ||
+	     (ccg->volume_size < ccg->block_size)) {
+	       // print error message  ...
+	       /* XXX: what error is appropiate? */
+	       break;
+	  } else
+	       cam_calc_geometry(ccg, /*extended*/1);
+	  break;
+     }
+
+     case XPT_GET_TRAN_SETTINGS:
+     default:
+	  ccb_h->status = CAM_REQ_INVALID;
+	  break;
+     }
+#if __FreeBSD_version < 700000
+     XPT_DONE(isp, ccb);
+#else
+     xpt_done(ccb);
+#endif
+     return;
+}
+
+static void
+ic_poll(struct cam_sim *sim)
+{
+     debug_called(8);
+
+}
+
+int
+ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp)
+{
+     int	i;
+
+     debug_called(8);
+
+     if(sp && sp->isc->cam_sim) {
+	  cp->path_id = cam_sim_path(sp->isc->cam_sim);
+	  cp->target_id = sp->sid;
+	  cp->target_nluns = sp->target_nluns; // XXX: -1?
+	  for(i = 0; i < cp->target_nluns; i++)
+	       cp->target_lun[i] = sp->target_lun[i];
+	  return 0;
+     }
+     return ENXIO;
+}
+
+void
+ic_destroy(struct isc_softc *isp)
+{
+     debug_called(8);
+
+     CAM_LOCK(isp); // can't harm :-)
+
+     xpt_async(AC_LOST_DEVICE, isp->cam_path, NULL);
+     xpt_free_path(isp->cam_path);
+     
+     xpt_bus_deregister(cam_sim_path(isp->cam_sim));
+     cam_sim_free(isp->cam_sim, TRUE /*free_devq*/);
+
+     CAM_UNLOCK(isp);
+}
+
+int
+ic_init(struct isc_softc *isp)
+{
+     struct cam_sim	*sim;
+     struct cam_devq	*devq;
+     struct cam_path	*path;
+
+     if((devq = cam_simq_alloc(256)) == NULL)
+	  return ENOMEM;
+
+#if __FreeBSD_version >= 700000
+     mtx_init(&isp->cam_mtx, "isc-cam", NULL, MTX_DEF);
+#else
+     isp->cam_mtx = Giant;
+#endif
+     sim = cam_sim_alloc(ic_action, ic_poll,
+			 "iscsi", isp, 0/*unit*/,
+#if __FreeBSD_version >= 700000
+			 &isp->cam_mtx,
+#endif
+			 1/*max_dev_transactions*/,
+			 100/*max_tagged_dev_transactions*/,
+			 devq);
+     if(sim == NULL) {
+	  cam_simq_free(devq);
+#if __FreeBSD_version >= 700000
+	  mtx_destroy(&isp->cam_mtx);
+#endif
+	  return ENXIO;
+     }
+     CAM_LOCK(isp);
+     if(xpt_bus_register(sim, NULL, 0/*bus_number*/) != CAM_SUCCESS)
+	  goto bad;
+
+     if(xpt_create_path(&path, xpt_periph, cam_sim_path(sim),
+			CAM_TARGET_WILDCARD, CAM_LUN_WILDCARD) != CAM_REQ_CMP) {
+	  xpt_bus_deregister(cam_sim_path(sim));
+	  goto bad;
+     }
+
+     CAM_UNLOCK(isp);
+
+     isp->cam_sim = sim;
+     isp->cam_path = path;
+
+     debug(2, "cam subsystem initialized"); // XXX: add dev ...
+     debug(4, "sim=%p path=%p", sim, path);
+     return 0;
+
+ bad:
+     cam_sim_free(sim, /*free_devq*/TRUE);
+     CAM_UNLOCK(isp);
+#if __FreeBSD_version >= 700000
+     mtx_destroy(&isp->cam_mtx);
+#endif
+     return ENXIO;
+}
--- /dev/null
+++ sys/dev/iscsi/initiator/iscsi.h
@@ -0,0 +1,407 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/dev/iscsi/initiator/iscsi.h,v 1.1 2007/07/24 15:35:02 scottl Exp $
+ */
+/*
+ | $Id: iscsi.h,v 1.17 2006/12/01 09:10:17 danny Exp danny $
+ */
+#define	TRUE	1
+#define FALSE	0
+#ifndef _KERNEL
+typedef int boolean_t;
+#endif
+
+#include <cam/cam.h>
+
+#define ISCSIDEV	"iscsi"
+
+#define ISCSI_MAX_TARGETS	4 //64
+
+#define ISCSI_MAX_LUNS		4
+
+/*
+ | iSCSI commands
+ */
+
+/*
+ | Initiator Opcodes:
+ */
+#define ISCSI_NOP_OUT		0x00
+#define ISCSI_SCSI_CMD		0x01
+#define ISCSI_TASK_CMD		0x02
+#define ISCSI_LOGIN_CMD		0x03
+#define ISCSI_TEXT_CMD		0x04
+#define ISCSI_WRITE_DATA	0x05
+#define ISCSI_LOGOUT_CMD	0x06
+#define ISCSI_SNACK		0x10
+/*
+ | Target Opcodes:
+ */
+#define ISCSI_NOP_IN		0x20
+#define ISCSI_SCSI_RSP		0x21
+#define ISCSI_TASK_RSP		0x22
+#define ISCSI_LOGIN_RSP		0x23
+#define ISCSI_TEXT_RSP		0x24
+#define ISCSI_READ_DATA		0x25
+#define ISCSI_LOGOUT_RSP	0x26
+#define ISCSI_R2T		0x31
+#define ISCSI_ASYNC		0x32
+#define ISCSI_REJECT		0x3f
+/*
+ | PDU stuff
+ */
+/*
+ | BHS Basic Header Segment
+ */
+typedef struct bhs {
+     // the order is network byte order!
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+     u_char	__:7;
+     u_char	F:1;			// Final bit
+     u_char	___[2];
+
+     u_int	AHSLength:8;		// in 4byte words
+     u_int	DSLength:24;		// in bytes
+
+     u_int	LUN[2];			// or Opcode-specific fields
+     u_int	itt;
+     u_int	OpcodeSpecificFields[7];
+#define	CmdSN		OpcodeSpecificFields[1]
+#define	ExpStSN		OpcodeSpecificFields[2]
+#define MaxCmdSN	OpcodeSpecificFields[3]
+} bhs_t;
+
+typedef struct ahs {
+     u_int	len:16;
+     u_int	type:8;
+     u_int	spec:8;
+     char	data[0];
+} ahs_t;
+
+typedef struct {
+     // Sequence Numbers
+     // (computers were invented to count, right?)
+     int	cmd;
+     int	expcmd;
+     int	maxcmd;
+} req_sn_t;
+
+typedef struct {
+     // Sequence Numbers
+     // (computers were invented to count, right?)
+     int	stat;
+     int	expcmd;
+     int	maxcmd;
+} rsp_sn_t;
+
+typedef struct scsi_req {
+     u_char	opcode:6; // 0x01
+     u_char	I:1;
+     u_char	_:1;
+
+     u_char	attr:3;
+     u_char	_0:2;
+     u_char	W:1;
+     u_char	R:1;
+     u_char	F:1;
+#define		iSCSI_TASK_UNTAGGED	0
+#define		iSCSI_TASK_SIMPLE	1
+#define		iSCSI_TASK_ORDER	2
+#define		iSCSI_TASK_HOFQ		3
+#define		iSCSI_TASK_ACA		4
+     char	_1[2];
+     int	len;
+     int	lun[2];
+     int	itt;
+     int	edtlen;		// expectect data transfere length
+     int	cmdSN;
+     int	extStatSN;
+     int	cdb[4];
+} scsi_req_t;
+
+typedef struct scsi_rsp {
+     char	opcode;	// 0x21
+     u_char	flag;
+     u_char	response;
+     u_char	status;
+
+     int	len;
+     int	_[2];
+     int	itt;
+     int	stag;
+     rsp_sn_t	sn;
+     int	expdatasn;
+     int	bdrcnt;	// bidirectional residual count
+     int	rcnt;	// residual count
+} scsi_rsp_t;
+
+typedef struct nop_out {
+     // the order is network byte order!
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+     u_char	__:7;
+     u_char	F:1;			// Final bit
+     u_char	___[2];
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;
+     u_int	ttt;
+     req_sn_t	sn;
+     u_int	mbz[3];
+} nop_out_t; 
+
+typedef struct nop_in {
+     // the order is network byte order!
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+     u_char	__:7;
+     u_char	F:1;			// Final bit
+     u_char	___[2];
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;
+     u_int	ttt;
+     rsp_sn_t	sn;
+     u_int	____[2];
+     
+} nop_in_t;
+
+typedef struct r2t {
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+     u_char	__:7;
+     u_char	F:1;			// Final bit
+     u_char	___[2];  
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;
+     u_int	ttt;
+     rsp_sn_t	sn;
+     u_int	r2tSN;
+     u_int	bo;
+     u_int	ddtl;
+} r2t_t;
+
+typedef struct data_out {
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+     u_char	__:7;
+     u_char	F:1;			// Final bit
+     u_char	___[2];  
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;
+     u_int	ttt;
+     rsp_sn_t	sn;
+     u_int	dsn;	// data seq. number
+     u_int	bo;
+     u_int	____;
+} data_out_t;
+
+typedef struct data_in {
+     u_char	opcode:6;
+     u_char	I:1;
+     u_char	_:1;
+
+     u_char	S:1;
+     u_char	U:1;
+     u_char	O:1;
+     u_char	__:3;
+     u_char	A:1;
+     u_char	F:1;			// Final bit
+     u_char	___[1]; 
+     u_char	status;
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;
+     u_int	ttt;
+     rsp_sn_t	sn;
+     u_int	dataSN;
+     u_int	bo;
+     u_int	____;
+} data_in_t;
+
+typedef struct reject {
+     u_char	opcode:6;
+     u_char	_:2;
+     u_char	F:1;
+     u_char	__:7;
+     u_char	reason;
+     u_char	___;
+
+     u_int	len;
+     u_int	____[2];
+     u_int	tt[2];	// must be -1
+     rsp_sn_t	sn;
+     u_int	dataSN;	// or R2TSN or reserved
+     u_int	_____[2];
+} reject_t;
+
+typedef struct async {
+     u_char	opcode:6;
+     u_char	_:2;
+     u_char	F:1;
+     u_char	__:7;
+     u_char	___[2];
+
+     u_int	len;
+     u_int	lun[2];
+     u_int	itt;	// must be -1
+     u_int	____;
+     rsp_sn_t	sn;
+
+     u_char	asyncEvent;
+     u_char	asyncVCode;
+     u_char	param1[2];
+     u_char	param2[2];
+     u_char	param3[2];
+
+     u_int	_____;
+     
+} async_t;  
+
+union ipdu_u {
+     bhs_t	bhs;
+     scsi_req_t	scsi_req;
+     scsi_rsp_t	scsi_rsp;
+     nop_out_t	nop_out;
+     nop_in_t	nop_in;
+     r2t_t	r2t;
+     data_out_t	data_out;
+     data_in_t	data_in;
+     reject_t	reject;
+     async_t	async;
+};
+
+/*
+ | Sequence Numbers
+ */
+typedef struct {
+     u_int	itt;
+     u_int      cmd;
+     u_int      expCmd;
+     u_int      maxCmd;
+     u_int      stat;
+     u_int      expStat;
+     u_int      data;
+} sn_t;
+
+/*
+ | in-core version of a Protocol Data Unit
+ */
+typedef struct {
+     union ipdu_u	ipdu;
+
+     ahs_t		*ahs;
+     u_int		ahs_len;
+     u_int		ahs_size;	// the allocated size
+     u_int		hdr_dig;	// header digest
+
+     u_char		*ds;
+     u_int		ds_len;
+     u_int		ds_size;	// the allocated size
+     u_int		ds_dig;		// data digest
+} pdu_t;
+
+typedef struct opvals {
+     int	port;
+     int	tags;
+     int	maxluns;
+     int	sockbufsize;
+
+     int	maxConnections;
+     int	maxRecvDataSegmentLength;
+     int	maxXmitDataSegmentLength; // pseudo ...
+     int	maxBurstLength;
+     int	firstBurstLength;
+     int	defaultTime2Wait;
+     int	defaultTime2Retain;
+     int	maxOutstandingR2T;
+     int	errorRecoveryLevel;
+     int	targetPortalGroupTag;
+
+     boolean_t	initialR2T;
+     boolean_t	immediateData;
+     boolean_t	dataPDUInOrder;
+     boolean_t	dataSequenceInOrder;
+     char	*headerDigest;
+     char	*dataDigest;
+     char	*sessionType;
+     char	*sendTargets;
+     char	*targetAddress;
+     char	*targetAlias;
+     char	*targetName;
+     char	*initiatorName;
+     char	*initiatorAlias;
+     char	*authMethod;
+     char	*chapSecret;
+     char	*chapIName;
+     char	*chapDigest;
+     char	*tgtChapName;
+     char	*tgtChapSecret;
+     int	tgtChallengeLen;
+     u_char	tgtChapID;
+     char	*tgtChapDigest;
+     char	*iqn;
+} isc_opt_t;
+
+/*
+ | ioctl
+ */
+#define ISCSISETSES	_IOR('i', 1, int)
+#define ISCSISETSOC	_IOW('i', 2, int)
+#define ISCSISETOPT	_IOW('i', 5, isc_opt_t)
+#define ISCSIGETOPT	_IOR('i', 6, isc_opt_t)
+
+#define ISCSISEND	_IOW('i', 10, pdu_t)
+#define ISCSIRECV	_IOWR('i', 11, pdu_t)
+
+#define ISCSIPING	_IO('i', 20)
+#define ISCSISIGNAL	_IOW('i', 21, int *)
+
+#define ISCSISTART	_IO('i', 30)
+#define ISCSIRESTART	_IO('i', 31)
+#define ISCSISTOP	_IO('i', 32)
+
+typedef struct iscsi_cam {
+     path_id_t		path_id;
+     target_id_t	target_id;
+     int		target_nluns;
+     lun_id_t		target_lun[ISCSI_MAX_LUNS];
+} iscsi_cam_t;
+
+#define ISCSIGETCAM	_IOR('i', 33, iscsi_cam_t)
--- /dev/null
+++ sys/dev/iscsi/initiator/iscsi.c
@@ -0,0 +1,810 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | iSCSI
+ | $Id: iscsi.c,v 1.35 2007/04/22 08:58:29 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/iscsi.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/mbuf.h>
+#include <sys/syslog.h>
+#include <vm/uma.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+static char *iscsi_driver_version = "2.0.99";
+
+static struct isc_softc isc;
+
+MALLOC_DEFINE(M_ISCSI, "iSCSI", "iSCSI driver");
+
+#ifdef ISCSI_INITIATOR_DEBUG
+int iscsi_debug = ISCSI_INITIATOR_DEBUG;
+SYSCTL_INT(_debug, OID_AUTO, iscsi_initiator, CTLFLAG_RW, &iscsi_debug, 0,
+	"iSCSI driver debug flag");
+
+struct mtx iscsi_dbg_mtx;
+#endif
+
+
+static char isid[6+1] = {
+     0x80,
+     'D',
+     'I',
+     'B',
+     '0',
+     '0',
+     0
+};
+
+static int	i_create_session(struct cdev *dev, int *ndev);
+
+static int	i_ping(struct cdev *dev);
+static int	i_send(struct cdev *dev, caddr_t arg, struct thread *td);
+static int	i_recv(struct cdev *dev, caddr_t arg, struct thread *td);
+static int	i_setsoc(isc_session_t *sp, int fd, struct thread *td);
+
+static d_open_t iscsi_open;
+static d_close_t iscsi_close;
+static d_ioctl_t iscsi_ioctl;
+#ifdef ISCSI_INITIATOR_DEBUG
+static d_read_t iscsi_read;
+#endif
+
+static struct cdevsw iscsi_cdevsw = {
+     .d_version = D_VERSION,
+     .d_open	= iscsi_open,
+     .d_close	= iscsi_close,
+     .d_ioctl	= iscsi_ioctl,
+#ifdef ISCSI_INITIATOR_DEBUG
+     .d_read	= iscsi_read,
+#endif
+     .d_name	= "iSCSI",
+};
+
+static int
+iscsi_open(struct cdev *dev, int flags, int otype, struct thread *td)
+{
+     debug_called(8);
+
+     debug(7, "dev=%d", minor(dev));
+
+     if(minor(dev) > MAX_SESSIONS) {
+	  // should not happen
+          return ENODEV;
+     }
+     if(minor(dev) == MAX_SESSIONS) {
+#if 1
+	  struct isc_softc *sc = (struct isc_softc *)dev->si_drv1;
+
+	  // this should be in iscsi_start
+	  if(sc->cam_sim == NULL)
+	       ic_init(sc);
+#endif
+     }
+     return 0;
+}
+
+static int
+iscsi_close(struct cdev *dev, int flag, int otyp, struct thread *td)
+{
+     struct isc		*sc;
+     isc_session_t	*sp;
+
+     debug_called(8);
+
+     debug(3, "flag=%x", flag);
+
+     sc = (struct isc *)dev->si_drv1;
+     if(minor(dev) == MAX_SESSIONS) {
+	  return 0;
+     }
+     sp = (isc_session_t *)dev->si_drv2;
+     if(sp != NULL) {
+	  sdebug(2, "session=%d flags=%x", minor(dev), sp->flags );
+	  /*
+	   | if still in full phase, this probably means
+	   | that something went realy bad.
+	   | it could be a result from 'shutdown', in which case
+	   | we will ignore it (so buffers can be flushed).
+	   | the problem is that there is no way of differentiating
+	   | between a shutdown procedure and 'iscontrol' dying.
+	   */
+	  if(sp->flags & ISC_FFPHASE)
+	       // delay in case this is a shutdown.
+	       tsleep(sp, PRIBIO, "isc-cls", 60*hz);
+	  ism_stop(sp);
+     }
+     debug(2, "done");
+     return 0;
+}
+
+static int
+iscsi_ioctl(struct cdev *dev, u_long cmd, caddr_t arg, int mode, struct thread *td)
+{
+     struct isc		*sc;
+     isc_session_t	*sp;
+     isc_opt_t		*opt;
+     int		error;
+
+     sc = (struct isc *)dev->si_drv1;
+     debug_called(8);
+
+     error = 0;
+     if(minor(dev) == MAX_SESSIONS) {
+	  /*
+	   | non Session commands
+	   */
+	  if(sc == NULL)
+	       return ENXIO;
+
+	  switch(cmd) {
+	  case ISCSISETSES:
+	       error = i_create_session(dev, (int *)arg);
+	       if(error == 0)
+		    
+	       break;
+
+	  default:
+	       error = ENXIO; // XXX:
+	  }
+	  return error;
+     }
+     sp = (isc_session_t *)dev->si_drv2;
+     /*
+      | session commands
+      */
+     if(sp == NULL)
+	  return ENXIO;
+
+     sdebug(6, "dev=%d cmd=%d", minor(dev), (int)(cmd & 0xff));
+
+     switch(cmd) {
+     case ISCSISETSOC:
+	  error = i_setsoc(sp, *(u_int *)arg, td);
+	  break;
+
+     case ISCSISETOPT:
+	  opt = (isc_opt_t *)arg;
+	  error = i_setopt(sp, opt);
+	  break;
+
+     case ISCSISEND:
+	  error = i_send(dev, arg, td);
+	  break;
+
+     case ISCSIRECV:
+	  error = i_recv(dev, arg, td);
+	  break;
+
+     case ISCSIPING:
+	  error = i_ping(dev);
+	  break;
+
+     case ISCSISTART:
+	  error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 1);
+	  if(error == 0) {
+	       sp->proc = td->td_proc;
+	       SYSCTL_ADD_UINT(&sp->clist,
+			       SYSCTL_CHILDREN(sp->oid),
+			       OID_AUTO,
+			       "pid",
+			       CTLFLAG_RD,
+			       &sp->proc->p_pid, sizeof(pid_t), "control process id");
+	  }
+	  break;
+
+     case ISCSIRESTART:
+	  error = sp->soc == NULL? ENOTCONN: ism_fullfeature(dev, 2);
+	  break;
+
+     case ISCSISTOP:
+	  error = ism_fullfeature(dev, 0);
+	  break;
+	  
+     case ISCSISIGNAL: {
+	  int sig = *(int *)arg;
+
+	  if(sig < 0 || sig > _SIG_MAXSIG)
+	       error = EINVAL;
+	  else
+		sp->signal = sig;
+	  break;
+     }
+
+     case ISCSIGETCAM: {
+	  iscsi_cam_t *cp = (iscsi_cam_t *)arg;
+
+	  error = ic_getCamVals(sp, cp);
+	  break;
+     }
+
+     default:
+	  error = ENOIOCTL;
+     }
+
+     return error;
+}
+
+static int
+iscsi_read(struct cdev *dev, struct uio *uio, int ioflag)
+{
+#ifdef  ISCSI_INITIATOR_DEBUG
+     struct isc_softc	*sc;
+     isc_session_t	*sp;
+     pduq_t 		*pq;
+     char		buf[1024];
+
+     sc = (struct isc_softc *)dev->si_drv1;
+     sp = (isc_session_t *)dev->si_drv2;
+     if(minor(dev) == MAX_SESSIONS) {
+	  sprintf(buf, "/----- Session ------/\n");
+	  uiomove(buf, strlen(buf), uio);
+	  int	i = 0;
+
+	  TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       sprintf(buf, "%03d] '%s' '%s'\n", i++, sp->opt.targetAddress, sp->opt.targetName);
+	       uiomove(buf, strlen(buf), uio);
+	  }
+     }
+     else {
+	  int	i = 0;
+	  struct socket	*so = sp->soc;
+#define pukeit(i, pq) do {\
+	       sprintf(buf, "%03d] %06x %02x %x %ld %jd\n",\
+		       i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
+		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
+		       (long)pq->ts.sec, pq->ts.frac);\
+	       } while(0)
+
+	  sprintf(buf, "%d/%d /---- hld -----/\n", sp->stats.nhld, sp->stats.max_hld);
+	  uiomove(buf, strlen(buf), uio);
+	  TAILQ_FOREACH(pq, &sp->hld, pq_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       pukeit(i, pq); i++;
+	       uiomove(buf, strlen(buf), uio);
+	  }
+	  sprintf(buf, "%d/%d /---- rsp -----/\n", sp->stats.nrsp, sp->stats.max_rsp);
+	  uiomove(buf, strlen(buf), uio);
+	  i = 0;
+	  TAILQ_FOREACH(pq, &sp->rsp, pq_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       pukeit(i, pq); i++;
+	       uiomove(buf, strlen(buf), uio);
+	  }
+	  sprintf(buf, "%d/%d /---- csnd -----/\n", sp->stats.ncsnd, sp->stats.max_csnd);
+	  i = 0;
+	  uiomove(buf, strlen(buf), uio);
+	  TAILQ_FOREACH(pq, &sp->csnd, pq_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       pukeit(i, pq); i++;
+	       uiomove(buf, strlen(buf), uio);
+	  }
+	  sprintf(buf, "%d/%d /---- wsnd -----/\n", sp->stats.nwsnd, sp->stats.max_wsnd);
+	  i = 0;
+	  uiomove(buf, strlen(buf), uio);
+	  TAILQ_FOREACH(pq, &sp->wsnd, pq_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       pukeit(i, pq); i++;
+	       uiomove(buf, strlen(buf), uio);
+	  }
+	  sprintf(buf, "%d/%d /---- isnd -----/\n", sp->stats.nisnd, sp->stats.max_isnd);
+	  i = 0;
+	  uiomove(buf, strlen(buf), uio);
+	  TAILQ_FOREACH(pq, &sp->isnd, pq_link) {
+	       if(uio->uio_resid == 0)
+		    return 0;
+	       pukeit(i, pq); i++;
+	       uiomove(buf, strlen(buf), uio);
+	  }
+
+	  sprintf(buf, "/---- Stats ---/\n");
+	  uiomove(buf, strlen(buf), uio);
+
+	  sprintf(buf, "recv=%d sent=%d\n", sp->stats.nrecv, sp->stats.nsent);
+	  uiomove(buf, strlen(buf), uio);
+
+	  sprintf(buf, "flags=%x pdus: alloc=%d max=%d\n", 
+		  sp->flags, sc->npdu_alloc, sc->npdu_max);
+	  uiomove(buf, strlen(buf), uio);
+
+	  sprintf(buf, "cws=%d last cmd=%x exp=%x max=%x stat=%x itt=%x\n",
+		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
+	  uiomove(buf, strlen(buf), uio);
+
+	  sprintf(buf, "/---- socket -----/\nso_count=%d so_state=%x\n", so->so_count, so->so_state);
+	  uiomove(buf, strlen(buf), uio);
+
+     }
+#endif
+     return 0;
+}
+
+static int
+i_ping(struct cdev *dev)
+{
+     return 0;
+}
+/*
+ | low level I/O
+ */
+static int
+i_setsoc(isc_session_t *sp, int fd, struct thread *td)
+{
+     int error = 0;
+
+     if(sp->soc != NULL)
+	  isc_stop_receiver(sp);
+
+     error = fget(td, fd, &sp->fp);
+     if(error)
+	  return error;
+
+     if((error = fgetsock(td, fd, &sp->soc, 0)) == 0) {
+	  sp->td = td;
+	  isc_start_receiver(sp);
+     }
+     else {
+	  fdrop(sp->fp, td);
+	  sp->fp = NULL;
+     }
+
+     return error;
+}
+
+static int
+i_send(struct cdev *dev, caddr_t arg, struct thread *td)
+{
+     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
+     struct isc_softc	*sc = (struct isc_softc *)dev->si_drv1;
+     caddr_t		bp;
+     pduq_t		*pq;
+     pdu_t		*pp;
+     int		n, error;
+
+     debug_called(8);
+
+     if(sp->soc == NULL)
+	  return ENOTCONN;
+
+     if((pq = pdu_alloc(sc, 0)) == NULL)
+	  return EAGAIN;
+     pp = &pq->pdu;
+
+     pq->pdu = *(pdu_t *)arg;
+     if((error = i_prepPDU(sp, pq)) != 0)
+	  return error;
+
+     sdebug(4, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
+
+     pq->buf = bp = malloc(pq->len - sizeof(union ipdu_u), M_ISCSI, M_WAITOK);
+
+     if(pp->ahs_len) {
+	  n = pp->ahs_len;
+	  copyin(pp->ahs, bp, n);
+	  pp->ahs = (ahs_t *)bp;
+	  bp += n;
+     }
+     if(pp->ds_len) {
+	  n = pp->ds_len;
+	  copyin(pp->ds, bp, n); // can fail ...
+	  pp->ds = bp;
+	  bp += n;
+	  while(n & 03) {
+	       n++;
+	       *bp++ = 0;
+	  }
+     }
+
+     error = isc_qout(sp, pq);
+
+     return error;
+}
+
+/*
+ | NOTE: must calculate digest if requiered.
+ */
+static int
+i_recv(struct cdev *dev, caddr_t arg, struct thread *td)
+{
+     isc_session_t	*sp = (isc_session_t *)dev->si_drv2;
+     pduq_t		*pq;
+     pdu_t		*pp, *up;
+     caddr_t		bp;
+     int		error, mustfree, cnt;
+     size_t		need, have, n;
+
+     debug_called(8);
+
+     if(sp == NULL)
+	  return EIO;
+
+     if(sp->soc == NULL)
+	  return ENOTCONN;
+     cnt = 6;     // XXX: maybe the user can request a time out?
+     mtx_lock(&sp->rsp_mtx);
+     while((pq = TAILQ_FIRST(&sp->rsp)) == NULL) {
+	  msleep(&sp->rsp, &sp->rsp_mtx, PRIBIO, "isc_rsp", hz*10);
+	  if(cnt-- == 0) break; // XXX: for now, needs work
+     }
+     if(pq != NULL) {
+	  sp->stats.nrsp--;
+	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
+     }
+     mtx_unlock(&sp->rsp_mtx);
+
+     sdebug(3, "cnt=%d", cnt);
+
+     if(pq == NULL) {
+	  error = ENOTCONN;
+	  sdebug(3, "error=%d sp->flags=%x ", error, sp->flags);
+	  return error;
+     }
+     up = (pdu_t *)arg;
+     pp = &pq->pdu;
+     up->ipdu = pp->ipdu;
+     n = 0;
+     up->ds_len = 0;
+     up->ahs_len = 0;
+     error = 0;
+
+     if(pq->mp) {
+	  u_int	len;
+
+	  // Grr...
+	  len = 0;
+	  if(pp->ahs_len) {
+	       len += pp->ahs_len;
+	       if(sp->hdrDigest)
+		    len += 4;
+	  }
+	  if(pp->ds_len) {
+	       len += pp->ds_len;
+	       if(sp->hdrDigest)
+		    len += 4;
+	  }
+
+	  mustfree = 0;
+	  if(len > pq->mp->m_len) {
+	       mustfree++;
+	       bp = malloc(len, M_ISCSI, M_WAITOK);
+	       sdebug(4, "need mbufcopy: %d", len);
+	       i_mbufcopy(pq->mp, bp, len);
+	  } 
+	  else
+	       bp = mtod(pq->mp, caddr_t);
+
+	  if(pp->ahs_len) {
+	       need = pp->ahs_len;
+	       if(sp->hdrDigest)
+		    need += 4;
+	       n = MIN(up->ahs_size, need);
+	       error = copyout(bp, (caddr_t)up->ahs, n);
+	       up->ahs_len = n;
+	       bp += need;
+	  }
+	  if(!error && pp->ds_len) {
+	       need = pp->ds_len;
+	       if(sp->hdrDigest)
+		    need += 4;
+	       if((have = up->ds_size) == 0) {
+		    have = up->ahs_size - n;
+		    up->ds = (caddr_t)up->ahs + n;
+	       }
+	       n = MIN(have, need);
+	       error = copyout(bp, (caddr_t)up->ds, n);
+	       up->ds_len = n;
+	  }
+
+	  if(mustfree)
+	       free(bp, M_ISCSI);
+     }
+
+     sdebug(6, "len=%d ahs_len=%d ds_len=%d", pq->len, pp->ahs_len, pp->ds_len);
+
+     pdu_free(sp->isc, pq);
+
+     return error;
+}
+
+static int
+i_create_session(struct cdev *dev, int *ndev)
+{ 
+     struct isc_softc		*sc = (struct isc_softc *)dev->si_drv1;
+     isc_session_t	*sp;
+     int		error, n;
+
+     debug_called(8);
+     sp = (isc_session_t *)malloc(sizeof *sp, M_ISCSI, M_WAITOK | M_ZERO);
+     if(sp == NULL)
+	  return ENOMEM;
+     mtx_lock(&sc->mtx);
+     /*
+      | search for the lowest unused sid
+      */
+     for(n = 0; n < MAX_SESSIONS; n++)
+	  if(sc->sessions[n] == NULL)
+	       break;
+     if(n == MAX_SESSIONS) {
+	  mtx_unlock(&sc->mtx);
+	  free(sp, M_ISCSI);
+	  return EPERM;
+     }
+     TAILQ_INSERT_TAIL(&sc->isc_sess, sp, sp_link);
+     sc->nsess++;
+     mtx_unlock(&sc->mtx);
+
+     sc->sessions[n] = sp;
+     sp->dev = make_dev(&iscsi_cdevsw, n, UID_ROOT, GID_WHEEL, 0600, "iscsi%d", n);
+     *ndev = sp->sid = n;
+     sp->isc = sc;
+     sp->dev->si_drv1 = sc;
+     sp->dev->si_drv2 = sp;
+
+     sp->opt.maxRecvDataSegmentLength = 8192;
+     sp->opt.maxXmitDataSegmentLength = 8192;
+
+     sp->opt.maxBurstLength = 65536;	// 64k
+
+     sdebug(2, "sessionID=%d", n);
+     error = ism_start(sp);
+
+     sdebug(2, "error=%d", error);
+
+     return error;
+}
+
+#ifdef notused
+static void
+iscsi_counters(isc_session_t *sp)
+{
+     int	h, r, s;
+     pduq_t	*pq;
+
+#define _puke(i, pq) do {\
+	       debug(2, "%03d] %06x %02x %x %ld %jd %x\n",\
+		       i, ntohl( pq->pdu.ipdu.bhs.CmdSN), \
+		       pq->pdu.ipdu.bhs.opcode, ntohl(pq->pdu.ipdu.bhs.itt),\
+		       (long)pq->ts.sec, pq->ts.frac, pq->flags);\
+	       } while(0)
+
+     h = r = s = 0; 
+     TAILQ_FOREACH(pq, &sp->hld, pq_link) {
+	  _puke(h, pq);
+	  h++;
+     }
+     TAILQ_FOREACH(pq, &sp->rsp, pq_link) r++;
+     TAILQ_FOREACH(pq, &sp->csnd, pq_link) s++;
+     TAILQ_FOREACH(pq, &sp->wsnd, pq_link) s++;
+     TAILQ_FOREACH(pq, &sp->isnd, pq_link) s++;
+     debug(2, "hld=%d rsp=%d snd=%d", h, r, s);
+}
+#endif
+
+static void
+iscsi_shutdown(void *v)
+{
+     struct isc_softc	*sc = (struct isc_softc *)v;
+     isc_session_t	*sp;
+     int	n;
+
+     debug_called(8);
+     if(sc == NULL) {
+	  xdebug("sc is NULL!");
+	  return;
+     }
+     if(sc->eh == NULL)
+	  debug(2, "sc->eh is NULL!");
+     else {
+	  EVENTHANDLER_DEREGISTER(shutdown_pre_sync, sc->eh);
+	  debug(2, "done n=%d", sc->nsess);
+     }
+     n = 0;
+     TAILQ_FOREACH(sp, &sc->isc_sess, sp_link) {
+	  debug(2, "%2d] sp->flags=0x%08x", n, sp->flags);
+	  n++;
+     }
+}
+
+static void
+iscsi_start(void)
+{
+     struct isc_softc *sc = &isc;
+ 
+     debug_called(8);
+
+     memset(sc, 0, sizeof(struct isc_softc));
+
+     sc->dev = make_dev(&iscsi_cdevsw, MAX_SESSIONS, UID_ROOT, GID_WHEEL, 0600, "iscsi");
+     sc->dev->si_drv1 = sc;
+
+     TAILQ_INIT(&sc->isc_sess);
+     
+     sc->pdu_zone = uma_zcreate("pdu", sizeof(pduq_t),
+				NULL, NULL, NULL, NULL,
+				0, 0);
+     if(sc->pdu_zone == NULL) {
+	  printf("iscsi_initiator: uma_zcreate failed");
+	  // XXX: and now what?
+     }
+     uma_zone_set_max(sc->pdu_zone, MAX_PDUS*2+1);
+     mtx_init(&sc->mtx, "iscsi", NULL, MTX_DEF);
+     mtx_init(&sc->pdu_mtx, "iscsi pdu pool", NULL, MTX_DEF);
+
+#if 0
+     // XXX: this will cause a panic if the
+     //      module is loaded too early
+     if(ic_init(sc) != 0)
+	  return;
+#else
+     sc->cam_sim = NULL;
+#endif
+     if((sc->eh = EVENTHANDLER_REGISTER(shutdown_pre_sync, iscsi_shutdown,
+					sc, SHUTDOWN_PRI_DEFAULT-1)) == NULL)
+	  xdebug("shutdown event registration failed\n");
+     /*
+      | sysctl stuff
+      */
+     sysctl_ctx_init(&sc->clist);
+     sc->oid = SYSCTL_ADD_NODE(&sc->clist,
+			       SYSCTL_STATIC_CHILDREN(_net),
+			       OID_AUTO,
+			       "iscsi",
+			       CTLFLAG_RD,
+			       0,
+			       "iSCSI Subsystem");
+
+     SYSCTL_ADD_STRING(&sc->clist,
+		       SYSCTL_CHILDREN(sc->oid),
+		       OID_AUTO,
+		       "driver_version",
+		       CTLFLAG_RD,
+		       iscsi_driver_version,
+		       0,
+		       "iscsi driver version");
+ 
+     SYSCTL_ADD_STRING(&sc->clist,
+		       SYSCTL_CHILDREN(sc->oid),
+		       OID_AUTO,
+		       "isid",
+		       CTLFLAG_RW,
+		       isid,
+		       6+1,
+		       "initiator part of the Session Identifier");
+
+     SYSCTL_ADD_INT(&sc->clist,
+		    SYSCTL_CHILDREN(sc->oid),
+		    OID_AUTO,
+		    "sessions",
+		    CTLFLAG_RD,
+		    &sc->nsess,
+		    sizeof(sc->nsess),
+		    "number of active session");
+}
+
+/*
+ | Notes:
+ |	unload SHOULD fail if there is activity
+ |	activity: there is/are active session/s
+ */
+static void
+iscsi_stop(void)
+{
+     struct isc_softc	*sc = &isc;
+     isc_session_t	*sp, *sp_tmp;
+
+     debug_called(8);
+
+     /*
+      | go through all the sessions
+      | Note: close should have done this ...
+      */
+     TAILQ_FOREACH_SAFE(sp, &sc->isc_sess, sp_link, sp_tmp) {
+	  //XXX: check for activity ...
+	  ism_stop(sp);
+     }
+     if(sc->cam_sim != NULL)
+	  ic_destroy(sc);
+
+     mtx_destroy(&sc->mtx);
+     mtx_destroy(&sc->pdu_mtx);
+     uma_zdestroy(sc->pdu_zone);
+
+     if(sc->dev)
+	  destroy_dev(sc->dev);
+
+     if(sysctl_ctx_free(&sc->clist))
+	  xdebug("sysctl_ctx_free failed");
+}
+
+static int
+iscsi_modevent(module_t mod, int what, void *arg)
+{
+     debug_called(8);
+
+     switch(what) {
+     case MOD_LOAD:
+	  iscsi_start();
+	  break;
+
+     case MOD_QUIESCE:
+#if 1
+	  if(isc.nsess) {
+	       xdebug("iscsi module busy(nsess=%d), cannot unload", isc.nsess);
+	       log(LOG_ERR, "iscsi module busy, cannot unload");
+	  }
+	  return isc.nsess;
+#endif
+     case MOD_SHUTDOWN:
+	  break;
+
+     case MOD_UNLOAD:
+	  iscsi_stop();
+	  break;
+
+     default:
+	  break;
+     }
+     return 0;
+}
+moduledata_t iscsi_mod = {
+         "iscsi",
+         (modeventhand_t) iscsi_modevent,
+         0
+};
+
+DECLARE_MODULE(iscsi, iscsi_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
--- /dev/null
+++ sys/dev/iscsi/initiator/isc_sm.c
@@ -0,0 +1,786 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | iSCSI - Session Manager
+ | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/isc_sm.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/bus.h>
+
+#include <cam/cam.h>
+#include <cam/cam_ccb.h>
+#include <cam/cam_sim.h>
+#include <cam/cam_xpt_sim.h>
+#include <cam/cam_periph.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+static void
+_async(isc_session_t *sp, pduq_t *pq)
+{
+     debug_called(8);
+
+     iscsi_async(sp, pq);
+
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_reject(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t	*opq;
+     pdu_t	*pdu;
+     reject_t	*reject;
+     int	itt;
+
+     debug_called(8);
+     pdu = mtod(pq->mp, pdu_t *);
+     itt = pdu->ipdu.bhs.itt;
+     reject = &pq->pdu.ipdu.reject;
+     sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
+     opq = i_search_hld(sp, itt, 0);
+     if(opq != NULL)
+	  iscsi_reject(sp, opq, pq);
+     else {
+	  switch(pq->pdu.ipdu.bhs.opcode) {
+	  case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
+	       sdebug(2, "ISCSI_LOGOUT_CMD ...");
+	       break;
+	  default:
+	       xdebug("%d] we lost something itt=%x",
+		      sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+	  }
+     }
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_r2t(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t	*opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
+     if(opq != NULL) {
+	  iscsi_r2t(sp, opq, pq);
+     } 
+     else {
+	  r2t_t		*r2t = &pq->pdu.ipdu.r2t;
+
+	  xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
+		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
+		 ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
+     }
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_scsi_rsp(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t	*opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
+     debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
+     if(opq != NULL)
+	  iscsi_done(sp, opq, pq);
+     else
+	  xdebug("%d] we lost something itt=%x",
+		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+     pdu_free(sp->isc, pq);
+}
+
+static void
+_read_data(isc_session_t *sp, pduq_t *pq)
+{
+     pduq_t		*opq;
+
+     debug_called(8);
+     opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
+     if(opq != NULL) {
+	  if(scsi_decap(sp, opq, pq) != 1) {
+	       i_remove_hld(sp, opq); // done
+	       pdu_free(sp->isc, opq);
+	  }
+     }
+     else
+	  xdebug("%d] we lost something itt=%x",
+		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
+     pdu_free(sp->isc, pq);
+}
+/*
+ | this is a kludge,
+ | the jury is not back with a veredict, user or kernel
+ */
+static void
+_nop_out(isc_session_t *sp)
+{
+     pduq_t	*pq;
+     nop_out_t	*nop_out;
+
+     debug_called(8);
+
+     sdebug(4, "cws=%d", sp->cws);
+     if(sp->cws == 0) {
+	  /*
+	   | only send a nop if window is closed.
+	   */
+	  if((pq = pdu_alloc(sp->isc, 0)) == NULL)
+	       // I guess we ran out of resources
+	       return;
+	  nop_out = &pq->pdu.ipdu.nop_out;
+	  nop_out->opcode = ISCSI_NOP_OUT;
+	  nop_out->itt = htonl(sp->sn.itt);
+	  nop_out->ttt = -1;
+	  nop_out->I = 1;
+	  nop_out->F = 1;
+	  if(isc_qout(sp, pq) != 0) {
+	       sdebug(1, "failed");
+	       pdu_free(sp->isc, pq);
+	  }
+     }
+}
+
+static void
+_nop_in(isc_session_t *sp, pduq_t *pq)
+{
+     pdu_t	*pp = &pq->pdu;
+     nop_in_t	*nop_in = &pp->ipdu.nop_in;
+     bhs_t	*bhs = &pp->ipdu.bhs;
+
+     debug_called(8);
+
+     sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
+     if(nop_in->itt == -1) {
+	  if(pp->ds_len != 0) {
+	       /*
+		| according to RFC 3720 this should be zero
+		| what to do if not?
+		*/
+	       xdebug("%d] dslen not zero", sp->sid);
+	  }
+	  if(nop_in->ttt != -1) {
+	       nop_out_t	*nop_out;
+	       /*
+		| target wants a nop_out
+	        */
+	       bhs->opcode = ISCSI_NOP_OUT;
+	       bhs->I = 1;
+	       bhs->F = 1;
+	       /*
+		| we are reusing the pdu, so bhs->ttt == nop_in->ttt;
+		| and need to zero out 'Reserved'
+		| small cludge here.
+	        */
+	       nop_out = &pp->ipdu.nop_out;
+	       nop_out->sn.maxcmd = 0;
+	       memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
+
+	       (void)isc_qout(sp, pq); //XXX: should check return?
+	       return;
+	  }
+	  //else {
+	       // just making noise?
+	       // see 10.9.1: target does not want and answer.
+	  //}
+
+     } else
+     if(nop_in->ttt == -1) {
+	  /*
+	   | it is an answer to a nop_in from us
+	   */
+	  if(nop_in->itt != -1) {
+#ifdef ISC_WAIT4PING
+	       // XXX: MUTEX please
+	       if(sp->flags & ISC_WAIT4PING) {
+		    i_nqueue_rsp(sp, pq);
+		    wakeup(&sp->rsp);
+		    return;
+	       }
+#endif
+	  }
+     }
+     /*
+      | drop it
+      */
+     pdu_free(sp->isc, pq);
+     return;
+}
+
+int
+i_prepPDU(isc_session_t *sp, pduq_t *pq)
+{
+     size_t	len, n;
+     pdu_t	*pp = &pq->pdu;
+     bhs_t	*bhp = &pp->ipdu.bhs;
+
+     len = sizeof(bhs_t);
+     if(pp->ahs_len) {
+	  len += pp->ahs_len;
+	  bhp->AHSLength =  pp->ahs_len / 4;
+     }
+     if(sp->hdrDigest)
+	  len += 4;
+     if(pp->ds_len) {
+	  n = pp->ds_len;
+	  len += n;
+#if BYTE_ORDER == LITTLE_ENDIAN
+	  bhp->DSLength = ((n & 0x00ff0000) >> 16)
+	       | (n & 0x0000ff00)
+	       | ((n & 0x000000ff) << 16);
+#else
+	  bhp->DSLength = n;
+#endif
+	  if(len & 03) {
+	       n = 4 - (len & 03);
+	       len += n;
+	  }
+	  if(sp->dataDigest)
+	       len += 4;
+     }
+
+     pq->len = len;
+     len -= sizeof(bhs_t);
+     if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
+	  xdebug("%d] pdu len=%zd > %d",
+		 sp->sid, len, sp->opt.maxBurstLength);
+	  // XXX: when this happens it used to hang ...
+	  return E2BIG;
+     }
+     return 0;
+}
+
+int
+isc_qout(isc_session_t *sp, pduq_t *pq)
+{
+     int error = 0;
+
+     debug_called(8);
+
+     if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
+	  return error;
+
+     if(pq->pdu.ipdu.bhs.I)
+	  i_nqueue_isnd(sp, pq);
+     else
+     if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
+	  i_nqueue_wsnd(sp, pq);
+     else
+	  i_nqueue_csnd(sp, pq);
+
+     sdebug(5, "enqued: pq=%p", pq);
+#ifdef ISC_OWAITING
+     if(sp->flags & ISC_OWAITING) {
+	  mtx_lock(&sp->io_mtx);	// XXX
+	  wakeup(&sp->flags);
+	  mtx_unlock(&sp->io_mtx);	// XXX
+     }
+#else
+     wakeup(&sp->flags);
+#endif
+     return error;
+}
+/*
+ | called when a fullPhase is restarted
+ */
+static int
+ism_restart(isc_session_t *sp)
+{
+     int lastcmd;
+
+     sdebug(2, "restart ...");
+     sp->flags |= ISC_SM_HOLD;
+     lastcmd = iscsi_requeue(sp);
+#if 0
+     if(lastcmd != sp->sn.cmd) {
+	  sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
+	  sp->sn.cmd = lastcmd;
+     }
+#endif
+     sp->flags &= ~ISC_SM_HOLD;
+     return 0;
+}
+
+int
+ism_fullfeature(struct cdev *dev, int flag)
+{
+     isc_session_t *sp = (isc_session_t *)dev->si_drv2;
+     int	error;
+
+     sdebug(2, "flag=%d", flag);
+
+     error = 0;
+     switch(flag) {
+     case 0: // stop
+	  sp->flags &= ~ISC_FFPHASE;
+	  break;
+     case 1: // start
+	  error = ic_fullfeature(dev);
+	  break;
+     case 2: // restart
+	  error = ism_restart(sp);
+	  break;
+     }
+     return error;
+}
+
+void
+ism_recv(isc_session_t *sp, pduq_t *pq)
+{
+     bhs_t	*bhs;
+     int	statSN;
+
+     debug_called(8);
+
+     bhs = &pq->pdu.ipdu.bhs;
+     statSN = ntohl(bhs->OpcodeSpecificFields[1]);
+#if 0
+     {
+	  /*
+	   | this code is only for debugging.
+	   */
+	  sn_t	*sn = &sp->sn;
+	  if(sp->cws == 0) {
+	       if((sp->flags & ISC_STALLED) == 0) {
+		    sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
+			   sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
+		    sp->flags |= ISC_STALLED;
+	       } else 
+	       if(sp->flags & ISC_STALLED) {
+		    sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
+			   sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
+		    sp->flags &= ~ISC_STALLED;;
+	       }
+	  }
+     }
+#endif
+
+#ifdef notyet
+     if(sp->sn.expCmd != sn->cmd) {
+	  sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
+		 sn->expCmd, sn->cmd);
+     }
+#endif
+     sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
+	    bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
+
+     switch(bhs->opcode) {
+     case ISCSI_READ_DATA: {
+	  data_in_t 	*cmd = &pq->pdu.ipdu.data_in;
+
+	  if(cmd->S == 0)
+	       break;
+     }
+
+     default:
+	  if(statSN > (sp->sn.stat + 1)) {
+	       sdebug(1, "we lost some rec=0x%x exp=0x%x",
+		      statSN, sp->sn.stat);
+	       // XXX: must do some error recovery here.
+	  }
+	  sp->sn.stat = statSN;
+     }
+
+     switch(bhs->opcode) {
+     case ISCSI_LOGIN_RSP:
+     case ISCSI_TEXT_RSP:
+     case ISCSI_LOGOUT_RSP:
+	  i_nqueue_rsp(sp, pq);
+	  wakeup(&sp->rsp);
+	  sdebug(3, "wakeup rsp");
+	  break;
+
+     case ISCSI_NOP_IN:		_nop_in(sp, pq);	break;
+     case ISCSI_SCSI_RSP:	_scsi_rsp(sp, pq);	break;
+     case ISCSI_READ_DATA:	_read_data(sp, pq);	break;
+     case ISCSI_R2T:		_r2t(sp, pq);		break;
+     case ISCSI_REJECT:		_reject(sp, pq);	break;
+     case ISCSI_ASYNC:		_async(sp, pq);		break;
+
+     case ISCSI_TASK_RSP:
+     default:
+	  sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
+		 bhs->opcode, ntohl(bhs->itt));
+	  break;
+     }
+}
+

+static int
+proc_out(isc_session_t *sp)
+{
+     sn_t	*sn = &sp->sn;
+     pduq_t	*pq;
+     int	error, ndone = 0;
+     int	which;
+
+     debug_called(8);
+
+     while(1) {
+	  pdu_t *pp;
+	  bhs_t	*bhs;
+
+	  /*
+	   | check if there is outstanding work in:
+	   | 1- the Inmediate queue
+	   | 2- the R2T queue
+	   | 3- the cmd queue, only if the command window allows it.
+	   */
+	  which = BIT(0) | BIT(1);
+	  if(SNA_GT(sn->cmd, sn->maxCmd) == 0)
+	       which |= BIT(2);
+
+	  if((pq = i_dqueue_snd(sp, which)) == NULL)
+	       break;
+
+	  pp = &pq->pdu;
+	  bhs = &pp->ipdu.bhs;
+	  switch(bhs->opcode) {
+	  case ISCSI_SCSI_CMD:
+	       sn->itt++;
+	       bhs->itt = htonl(sn->itt);
+
+	  case ISCSI_LOGIN_CMD:
+	  case ISCSI_TEXT_CMD:
+	  case ISCSI_LOGOUT_CMD:
+	  case ISCSI_SNACK:
+	  case ISCSI_NOP_OUT:
+	  case ISCSI_TASK_CMD:
+	       bhs->CmdSN = htonl(sn->cmd);
+	       if(bhs->I == 0)
+		    sn->cmd++;
+
+	  case ISCSI_WRITE_DATA:
+	       bhs->ExpStSN = htonl(sn->stat);
+	       break;
+
+	  default:
+	       // XXX: can this happen?
+	       xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
+		      bhs->opcode,
+		      sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
+	       // XXX: and now?
+	  }
+
+	  sdebug(5, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
+		bhs->opcode,
+		sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
+
+	  if(pq->ccb)
+	       i_nqueue_hld(sp, pq);
+
+	  if((error = isc_sendPDU(sp, pq)) == 0)
+	       ndone++;
+	  else {
+	       xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
+		      error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt));
+	       if(error == EPIPE) {
+		    // XXX: better do some error recovery ...
+		    break;
+	       }
+#if 0
+	       if(pq->ccb) {
+		    i_remove_hld(sp, pq);
+		    pq->ccb->ccb_h.status |= CAM_UNREC_HBA_ERROR; // some better error?
+		    XPT_DONE(pq->ccb);
+	       }
+	       else {
+		    // XXX: now what?
+		    // how do we pass back an error?
+	       }
+#endif
+	  }
+	  if(pq->ccb == NULL || error)
+	       pdu_free(sp->isc, pq);
+     }
+     return ndone;
+}
+

+/*
+ | survives link breakdowns.
+ */
+static void
+ism_proc(void *vp)
+{
+     isc_session_t 	*sp = (isc_session_t *)vp;
+     int		odone;
+
+     debug_called(8);
+     sdebug(3, "started");
+
+     sp->flags |= ISC_SM_RUNNING;
+     do {
+	  if(sp->flags & ISC_SM_HOLD)
+	       odone = 0;
+	  else
+	       odone = proc_out(sp);
+	  sdebug(7, "odone=%d", odone);
+	  if(odone == 0) {
+	       mtx_lock(&sp->io_mtx);
+#ifdef ISC_OWAITING
+	       sp->flags |= ISC_OWAITING;
+#endif
+	       if((msleep(&sp->flags, &sp->io_mtx, PRIBIO, "isc_proc", hz*30) == EWOULDBLOCK)
+		  && (sp->flags & ISC_CON_RUNNING))
+		    _nop_out(sp);
+#ifdef ISC_OWAITING
+	       sp->flags &= ~ISC_OWAITING;
+#endif
+	       mtx_unlock(&sp->io_mtx);
+	  }
+     } while(sp->flags & ISC_SM_RUN);
+
+     sp->flags &= ~ISC_SM_RUNNING;
+
+#if __FreeBSD_version >= 700000
+     destroy_dev(sp->dev);
+#endif
+
+     sdebug(3, "terminated");
+
+     wakeup(sp);
+     kthread_exit(0);
+}
+
+#if 0
+static int
+isc_dump_options(SYSCTL_HANDLER_ARGS)
+{
+     int error;
+     isc_session_t *sp;
+     char	buf[1024], *bp;
+
+     sp = (isc_session_t *)arg1;
+     bp = buf;
+     sprintf(bp, "targetname='%s'", sp->opt.targetName);
+     bp += strlen(bp);
+     sprintf(bp, " targetname='%s'", sp->opt.targetAddress);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+#endif
+
+static int
+isc_dump_stats(SYSCTL_HANDLER_ARGS)
+{
+     isc_session_t	*sp;
+     struct isc_softc	*sc;
+     char	buf[1024], *bp;
+     int 	error, n;
+
+     sp = (isc_session_t *)arg1;
+     sc = sp->isc;
+
+     bp = buf;
+     n = sizeof(buf);
+     snprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
+     bp += strlen(bp);
+     n -= strlen(bp);
+     snprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d", 
+		  sp->flags, sc->npdu_alloc, sc->npdu_max);
+     bp += strlen(bp);
+     n -= strlen(bp);
+     snprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
+		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+
+static int
+isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
+{
+     char	buf[128], **cp;
+     int 	error;
+
+     cp = (char **)arg1;
+     snprintf(buf, sizeof(buf), "%s", *cp);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+     
+static int
+isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
+{
+     char	buf[128], **cp;
+     int 	error;
+
+     cp = (char **)arg1;
+     snprintf(buf, sizeof(buf), "%s", *cp);
+     error = SYSCTL_OUT(req, buf, strlen(buf));
+     return error;
+}
+     
+static void
+isc_add_sysctls(isc_session_t *sp)
+{
+     debug_called(8);
+     sdebug(6, "sid=%d %s", sp->sid, sp->dev->si_name);
+
+     sysctl_ctx_init(&sp->clist);
+     sp->oid = SYSCTL_ADD_NODE(&sp->clist,
+			       SYSCTL_CHILDREN(sp->isc->oid),
+			       OID_AUTO,
+			       sp->dev->si_name+5, // iscsi0
+			       CTLFLAG_RD,
+			       0,
+			       "initiator");
+     SYSCTL_ADD_PROC(&sp->clist,
+		     SYSCTL_CHILDREN(sp->oid),
+		     OID_AUTO,
+		     "targetname",
+		     CTLFLAG_RD,
+		     (void *)&sp->opt.targetName, 0,
+		     isc_sysctl_targetName, "A", "target name");
+
+     SYSCTL_ADD_PROC(&sp->clist,
+		     SYSCTL_CHILDREN(sp->oid),
+		     OID_AUTO,
+		     "targeaddress",
+		     CTLFLAG_RD,
+		     (void *)&sp->opt.targetAddress, 0,
+		     isc_sysctl_targetAddress, "A", "target address");
+
+     SYSCTL_ADD_PROC(&sp->clist,
+		     SYSCTL_CHILDREN(sp->oid),
+		     OID_AUTO,
+		     "stats",
+		     CTLFLAG_RD,
+		     (void *)sp, 0,
+		     isc_dump_stats, "A", "statistics");
+}
+
+void
+ism_stop(isc_session_t *sp)
+{
+     struct isc_softc *sc = sp->isc;
+     int	n;
+
+     debug_called(8);
+     sdebug(2, "terminating");
+     /*
+      | first stop the receiver
+      */
+     isc_stop_receiver(sp);
+     /*
+      | now stop the xmitter
+      */
+     n = 5;
+     sp->flags &= ~ISC_SM_RUN;
+     while(n-- && (sp->flags & ISC_SM_RUNNING)) {
+	  sdebug(2, "n=%d", n);
+	  wakeup(&sp->flags);
+	  tsleep(sp, PRIBIO, "-", 5*hz);
+     }
+     sdebug(2, "final n=%d", n);
+     sp->flags &= ~ISC_FFPHASE;
+     
+     iscsi_cleanup(sp);
+
+     (void)i_pdu_flush(sp);
+
+     ic_lost_target(sp, sp->sid);
+
+     mtx_lock(&sc->mtx);
+     TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
+     sc->nsess--;
+     mtx_unlock(&sc->mtx);
+
+#if __FreeBSD_version < 700000
+     destroy_dev(sp->dev);
+#endif
+
+     mtx_destroy(&sp->rsp_mtx);
+     mtx_destroy(&sp->rsv_mtx);
+     mtx_destroy(&sp->hld_mtx);
+     mtx_destroy(&sp->snd_mtx);
+     mtx_destroy(&sp->io_mtx);
+
+     i_freeopt(&sp->opt);
+     sc->sessions[sp->sid] = NULL;
+
+     if(sysctl_ctx_free(&sp->clist))
+	  xdebug("sysctl_ctx_free failed");
+
+     free(sp, M_ISCSI);
+}
+
+int
+ism_start(isc_session_t *sp)
+{
+     debug_called(8);
+    /*
+     | now is a good time to do some initialization
+     */
+     TAILQ_INIT(&sp->rsp);
+     TAILQ_INIT(&sp->rsv);
+     TAILQ_INIT(&sp->csnd);
+     TAILQ_INIT(&sp->isnd);
+     TAILQ_INIT(&sp->wsnd);
+     TAILQ_INIT(&sp->hld);
+#if 1
+     mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_DEF);
+     mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_DEF);
+     mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_DEF);
+     mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_DEF);
+#else
+     mtx_init(&sp->rsv_mtx, "iscsi-rsv", NULL, MTX_SPIN);
+     mtx_init(&sp->rsp_mtx, "iscsi-rsp", NULL, MTX_SPIN);
+     mtx_init(&sp->snd_mtx, "iscsi-snd", NULL, MTX_SPIN);
+     mtx_init(&sp->hld_mtx, "iscsi-hld", NULL, MTX_SPIN);
+#endif
+     mtx_init(&sp->io_mtx, "iscsi-io", NULL, MTX_DEF);
+
+     isc_add_sysctls(sp);
+
+     sp->flags |= ISC_SM_RUN;
+
+     return kthread_create(ism_proc, sp, &sp->stp, 0, 0, "ism_%d", sp->sid);
+}
--- /dev/null
+++ sys/dev/iscsi/initiator/isc_subr.c
@@ -0,0 +1,258 @@
+/*-
+ * Copyright (c) 2005-2007 Daniel Braniss <danny at cs.huji.ac.il>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ */
+/*
+ | iSCSI
+ | $Id: isc_subr.c,v 1.20 2006/12/01 09:10:17 danny Exp danny $
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/dev/iscsi/initiator/isc_subr.c,v 1.1 2007/07/24 15:35:02 scottl Exp $");
+
+#include "opt_iscsi_initiator.h"
+
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/conf.h>
+#include <sys/systm.h>
+#include <sys/malloc.h>
+#include <sys/ctype.h>
+#include <sys/errno.h>
+#include <sys/sysctl.h>
+#include <sys/file.h>
+#include <sys/uio.h>
+#include <sys/socketvar.h>
+#include <sys/socket.h>
+#include <sys/protosw.h>
+#include <sys/proc.h>
+#include <sys/ioccom.h>
+#include <sys/queue.h>
+#include <sys/kthread.h>
+#include <sys/syslog.h>
+#include <sys/mbuf.h>
+#include <sys/libkern.h>
+
+#include <dev/iscsi/initiator/iscsi.h>
+#include <dev/iscsi/initiator/iscsivar.h>
+
+MALLOC_DEFINE(M_PDU, "iSCSI pdu", "iSCSI driver");
+
+static char *
+i_strdupin(char *s, size_t maxlen)
+{
+     size_t	len;
+     char	*p, *q;
+
+     p = malloc(maxlen, M_ISCSI, M_WAITOK);
+     if(copyinstr(s, p, maxlen, &len)) {
+	  free(p, M_ISCSI);
+	  return NULL;
+     }
+     q = malloc(len, M_ISCSI, M_WAITOK);
+     bcopy(p, q, len);
+     free(p, M_ISCSI);
+
+     return q;
+}
+
+/*****************************************************************/
+/*                                                               */
+/* CRC LOOKUP TABLE                                              */
+/* ================                                              */
+/* The following CRC lookup table was generated automagically    */
+/* by the Rocksoft^tm Model CRC Algorithm Table Generation       */
+/* Program V1.0 using the following model parameters:            */
+/*                                                               */
+/*    Width   : 4 bytes.                                         */
+/*    Poly    : 0x1EDC6F41L                                      */
+/*    Reverse : TRUE.                                            */
+/*                                                               */
+/* For more information on the Rocksoft^tm Model CRC Algorithm,  */
+/* see the document titled "A Painless Guide to CRC Error        */
+/* Detection Algorithms" by Ross Williams                        */
+/* (ross at guest.adelaide.edu.au.). This document is likely to be  */
+/* in the FTP archive "ftp.adelaide.edu.au/pub/rocksoft".        */
+/*                                                               */
+/*****************************************************************/
+
+static uint32_t crc32Table[256] = {
+    0x00000000L, 0xF26B8303L, 0xE13B70F7L, 0x1350F3F4L,
+    0xC79A971FL, 0x35F1141CL, 0x26A1E7E8L, 0xD4CA64EBL,
+    0x8AD958CFL, 0x78B2DBCCL, 0x6BE22838L, 0x9989AB3BL,
+    0x4D43CFD0L, 0xBF284CD3L, 0xAC78BF27L, 0x5E133C24L,
+    0x105EC76FL, 0xE235446CL, 0xF165B798L, 0x030E349BL,
+    0xD7C45070L, 0x25AFD373L, 0x36FF2087L, 0xC494A384L,
+    0x9A879FA0L, 0x68EC1CA3L, 0x7BBCEF57L, 0x89D76C54L,
+    0x5D1D08BFL, 0xAF768BBCL, 0xBC267848L, 0x4E4DFB4BL,
+    0x20BD8EDEL, 0xD2D60DDDL, 0xC186FE29L, 0x33ED7D2AL,
+    0xE72719C1L, 0x154C9AC2L, 0x061C6936L, 0xF477EA35L,
+    0xAA64D611L, 0x580F5512L, 0x4B5FA6E6L, 0xB93425E5L,
+    0x6DFE410EL, 0x9F95C20DL, 0x8CC531F9L, 0x7EAEB2FAL,
+    0x30E349B1L, 0xC288CAB2L, 0xD1D83946L, 0x23B3BA45L,
+    0xF779DEAEL, 0x05125DADL, 0x1642AE59L, 0xE4292D5AL,
+    0xBA3A117EL, 0x4851927DL, 0x5B016189L, 0xA96AE28AL,
+    0x7DA08661L, 0x8FCB0562L, 0x9C9BF696L, 0x6EF07595L,
+    0x417B1DBCL, 0xB3109EBFL, 0xA0406D4BL, 0x522BEE48L,
+    0x86E18AA3L, 0x748A09A0L, 0x67DAFA54L, 0x95B17957L,
+    0xCBA24573L, 0x39C9C670L, 0x2A993584L, 0xD8F2B687L,
+    0x0C38D26CL, 0xFE53516FL, 0xED03A29BL, 0x1F682198L,
+    0x5125DAD3L, 0xA34E59D0L, 0xB01EAA24L, 0x42752927L,
+    0x96BF4DCCL, 0x64D4CECFL, 0x77843D3BL, 0x85EFBE38L,
+    0xDBFC821CL, 0x2997011FL, 0x3AC7F2EBL, 0xC8AC71E8L,
+    0x1C661503L, 0xEE0D9600L, 0xFD5D65F4L, 0x0F36E6F7L,
+    0x61C69362L, 0x93AD1061L, 0x80FDE395L, 0x72966096L,
+    0xA65C047DL, 0x5437877EL, 0x4767748AL, 0xB50CF789L,
+    0xEB1FCBADL, 0x197448AEL, 0x0A24BB5AL, 0xF84F3859L,
+    0x2C855CB2L, 0xDEEEDFB1L, 0xCDBE2C45L, 0x3FD5AF46L,
+    0x7198540DL, 0x83F3D70EL, 0x90A324FAL, 0x62C8A7F9L,
+    0xB602C312L, 0x44694011L, 0x5739B3E5L, 0xA55230E6L,
+    0xFB410CC2L, 0x092A8FC1L, 0x1A7A7C35L, 0xE811FF36L,
+    0x3CDB9BDDL, 0xCEB018DEL, 0xDDE0EB2AL, 0x2F8B6829L,
+    0x82F63B78L, 0x709DB87BL, 0x63CD4B8FL, 0x91A6C88CL,
+    0x456CAC67L, 0xB7072F64L, 0xA457DC90L, 0x563C5F93L,
+    0x082F63B7L, 0xFA44E0B4L, 0xE9141340L, 0x1B7F9043L,
+    0xCFB5F4A8L, 0x3DDE77ABL, 0x2E8E845FL, 0xDCE5075CL,
+    0x92A8FC17L, 0x60C37F14L, 0x73938CE0L, 0x81F80FE3L,
+    0x55326B08L, 0xA759E80BL, 0xB4091BFFL, 0x466298FCL,
+    0x1871A4D8L, 0xEA1A27DBL, 0xF94AD42FL, 0x0B21572CL,
+    0xDFEB33C7L, 0x2D80B0C4L, 0x3ED04330L, 0xCCBBC033L,
+    0xA24BB5A6L, 0x502036A5L, 0x4370C551L, 0xB11B4652L,
+    0x65D122B9L, 0x97BAA1BAL, 0x84EA524EL, 0x7681D14DL,
+    0x2892ED69L, 0xDAF96E6AL, 0xC9A99D9EL, 0x3BC21E9DL,
+    0xEF087A76L, 0x1D63F975L, 0x0E330A81L, 0xFC588982L,
+    0xB21572C9L, 0x407EF1CAL, 0x532E023EL, 0xA145813DL,
+    0x758FE5D6L, 0x87E466D5L, 0x94B49521L, 0x66DF1622L,
+    0x38CC2A06L, 0xCAA7A905L, 0xD9F75AF1L, 0x2B9CD9F2L,
+    0xFF56BD19L, 0x0D3D3E1AL, 0x1E6DCDEEL, 0xEC064EEDL,
+    0xC38D26C4L, 0x31E6A5C7L, 0x22B65633L, 0xD0DDD530L,
+    0x0417B1DBL, 0xF67C32D8L, 0xE52CC12CL, 0x1747422FL,
+    0x49547E0BL, 0xBB3FFD08L, 0xA86F0EFCL, 0x5A048DFFL,
+    0x8ECEE914L, 0x7CA56A17L, 0x6FF599E3L, 0x9D9E1AE0L,
+    0xD3D3E1ABL, 0x21B862A8L, 0x32E8915CL, 0xC083125FL,
+    0x144976B4L, 0xE622F5B7L, 0xF5720643L, 0x07198540L,
+    0x590AB964L, 0xAB613A67L, 0xB831C993L, 0x4A5A4A90L,
+    0x9E902E7BL, 0x6CFBAD78L, 0x7FAB5E8CL, 0x8DC0DD8FL,
+    0xE330A81AL, 0x115B2B19L, 0x020BD8EDL, 0xF0605BEEL,
+    0x24AA3F05L, 0xD6C1BC06L, 0xC5914FF2L, 0x37FACCF1L,
+    0x69E9F0D5L, 0x9B8273D6L, 0x88D28022L, 0x7AB90321L,
+    0xAE7367CAL, 0x5C18E4C9L, 0x4F48173DL, 0xBD23943EL,
+    0xF36E6F75L, 0x0105EC76L, 0x12551F82L, 0xE03E9C81L,
+    0x34F4F86AL, 0xC69F7B69L, 0xD5CF889DL, 0x27A40B9EL,
+    0x79B737BAL, 0x8BDCB4B9L, 0x988C474DL, 0x6AE7C44EL,
+    0xBE2DA0A5L, 0x4C4623A6L, 0x5F16D052L, 0xAD7D5351L
+};
+
+static uint32_t
+i_crc32c(const void *buf, size_t size, uint32_t crc)
+{
+     const uint8_t *p = buf;
+
+     crc = crc ^ 0xffffffff;
+     while (size--)
+	  crc = crc32Table[(crc ^ *p++) & 0xff] ^ (crc >> 8);
+     crc = crc ^ 0xffffffff;
+     return crc;
+}
+
+/*
+ | XXX: not finished coding
+ */
+int
+i_setopt(isc_session_t *sp, isc_opt_t *opt)
+{
+     if(opt->maxRecvDataSegmentLength > 0) {
+	  sp->opt.maxRecvDataSegmentLength = opt->maxRecvDataSegmentLength;
+	  sdebug(2, "maxRecvDataSegmentLength=%d", sp->opt.maxRecvDataSegmentLength);
+     }
+     if(opt->maxXmitDataSegmentLength > 0) {
+	  // danny's RFC
+	  sp->opt.maxXmitDataSegmentLength = opt->maxXmitDataSegmentLength;
+	  sdebug(2, "maXmitDataSegmentLength=%d", sp->opt.maxXmitDataSegmentLength);
+     }
+     if(opt->maxBurstLength != 0) {
+	  sp->opt.maxBurstLength = opt->maxBurstLength;
+	  sdebug(2, "maxBurstLength=%d", sp->opt.maxBurstLength);
+     }
+
+     if(opt->targetAddress != NULL) {
+	  if(sp->opt.targetAddress != NULL)
+	       free(sp->opt.targetAddress, M_ISCSI);
+	  sp->opt.targetAddress = i_strdupin(opt->targetAddress, 128);
+	  sdebug(4, "opt.targetAddress='%s'", sp->opt.targetAddress);
+     }
+     if(opt->targetName != NULL) {
+	  if(sp->opt.targetName != NULL)
+	       free(sp->opt.targetName, M_ISCSI);
+	  sp->opt.targetName = i_strdupin(opt->targetName, 128);
+	  sdebug(4, "opt.targetName='%s'", sp->opt.targetName);
+     }
+     if(opt->initiatorName != NULL) {
+	  if(sp->opt.initiatorName != NULL)
+	       free(sp->opt.initiatorName, M_ISCSI);
+	  sp->opt.initiatorName = i_strdupin(opt->initiatorName, 128);
+	  sdebug(4, "opt.initiatorName='%s'", sp->opt.initiatorName);
+     }
+
+     if(opt->maxluns > 0) {
+	  if(opt->maxluns > ISCSI_MAX_LUNS)
+	       sp->opt.maxluns = ISCSI_MAX_LUNS; // silently chop it down ...
+	  sp->opt.maxluns = opt->maxluns;
+	  sdebug(4, "opt.maxluns=%d", sp->opt.maxluns);
+     }
+
+     if(opt->headerDigest != NULL) {
+	  sdebug(2, "opt.headerDigest='%s'", opt->headerDigest);
+	  if(strcmp(opt->headerDigest, "CRC32C") == 0) {
+	       sp->hdrDigest = (digest_t *)i_crc32c;
+	       sdebug(2, "headerDigest set");
+	  }
+     }
+     if(opt->dataDigest != NULL) {
+	  if(strcmp(opt->dataDigest, "CRC32C") == 0) {
+	       sp->dataDigest = (digest_t *)i_crc32c;
+	       sdebug(2, "dataDigest set");
+	  }
+     }
+
+     return 0;
+}
+
+void
+i_freeopt(isc_opt_t *opt)
+{
+     if(opt->targetAddress != NULL) {
+	  free(opt->targetAddress, M_ISCSI);
+	  opt->targetAddress = NULL;
+     }
+     if(opt->targetName != NULL) {
+	  free(opt->targetName, M_ISCSI);
+	  opt->targetName = NULL;
+     }
+     if(opt->initiatorName != NULL) {
+	  free(opt->initiatorName, M_ISCSI);
+	  opt->initiatorName = NULL;
+     }
+}


More information about the Midnightbsd-cvs mailing list