/*- * Copyright (c) 2005-2007 Daniel Braniss * 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$ */ /* | $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; } }