1 /*-
2  * Copyright (c) 2005-2008 Daniel Braniss <danny@cs.huji.ac.il>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD: src/sys/dev/iscsi/initiator/iscsivar.h,v 1.2 2008/11/25 07:17:11 scottl Exp $
27  */
28 /*
29  | $Id: iscsivar.h,v 1.30 2007/04/22 10:12:11 danny Exp danny $
30  */
31 #ifndef ISCSI_INITIATOR_DEBUG
32 #define ISCSI_INITIATOR_DEBUG 1
33 #endif
34 
35 #ifdef ISCSI_INITIATOR_DEBUG
36 extern int iscsi_debug;
37 #define debug(level, fmt, args...)	do {if(level <= iscsi_debug)\
38 	kprintf("%s: " fmt "\n", __func__ , ##args);} while(0)
39 #define sdebug(level, fmt, args...)	do {if(level <= iscsi_debug)\
40 	kprintf("%d] %s: " fmt "\n", sp->sid, __func__ , ##args);} while(0)
41 #define debug_called(level)		do {if(level <= iscsi_debug)\
42 	kprintf("%s: called\n",  __func__);} while(0)
43 #else
44 #define debug(level, fmt, args...)
45 #define debug_called(level)
46 #define sdebug(level, fmt, args...)
47 #endif /* ISCSI_INITIATOR_DEBUG */
48 
49 #define xdebug(fmt, args...)	kprintf(">>> %s: " fmt "\n", __func__ , ##args)
50 
51 #define PROC_LOCK(p)
52 #define PROC_UNLOCK(p)
53 
54 #define MAX_SESSIONS		256
55 
56 typedef uint32_t digest_t(const void *, int len, uint32_t ocrc);
57 
58 typedef struct objcache	*objcache_t;
59 
60 MALLOC_DECLARE(M_ISCSI);
61 MALLOC_DECLARE(M_PDU);
62 
63 #ifndef BIT
64 #define BIT(n)	(1 <<(n))
65 #endif
66 
67 #define ISC_SM_RUN	BIT(0)
68 #define ISC_SM_RUNNING	BIT(1)
69 
70 #define ISC_LINK_UP	BIT(2)
71 #define ISC_CON_RUN	BIT(3)
72 #define ISC_CON_RUNNING	BIT(4)
73 #define ISC_KILL	BIT(5)
74 #define ISC_OQNOTEMPTY	BIT(6)
75 #define ISC_OWAITING	BIT(7)
76 #define ISC_FFPHASE	BIT(8)
77 #define ISC_FFPWAIT	BIT(9)
78 
79 #define ISC_MEMWAIT	BIT(10)
80 #define ISC_SIGNALED	BIT(11)
81 #define ISC_FROZEN	BIT(12)
82 #define ISC_STALLED	BIT(13)
83 
84 #define ISC_HOLD	BIT(14)
85 #define ISC_HOLDED	BIT(15)
86 
87 #define ISC_SHUTDOWN	BIT(31)
88 
89 /*
90  | some stats
91  */
92 struct i_stats {
93      int	npdu;	// number of pdus malloc'ed.
94      int	nrecv;	// unprocessed received pdus
95      int	nsent;	// sent pdus
96 
97      int	nrsp, max_rsp;
98      int	nrsv, max_rsv;
99      int	ncsnd, max_csnd;
100      int	nisnd, max_isnd;
101      int	nwsnd, max_wsnd;
102      int	nhld, max_hld;
103 
104      struct timeval t_sent;
105      struct timeval t_recv;
106 };
107 
108 /*
109  | one per 'session'
110  */
111 
112 typedef TAILQ_HEAD(, pduq) queue_t;
113 
114 typedef struct isc_session {
115      TAILQ_ENTRY(isc_session)	sp_link;
116      int		flags;
117      struct cdev	*dev;
118      struct socket	*soc;
119      struct file	*fp;
120      struct thread	*td;
121 
122      struct proc 	*proc; // the userland process
123      int		signal;
124 
125      struct thread 	*soc_thr;
126 
127      struct thread	*stp;	// the sm thread
128 
129      struct isc_softc	*isc;
130 
131      digest_t   	*hdrDigest;     // the digest alg. if any
132      digest_t   	*dataDigest;    // the digest alg. if any
133 
134      int		sid;		// Session ID
135      int		targetid;
136 //     int		cid;		// Connection ID
137 //     int		tsih;		// target session identifier handle
138      sn_t       	sn;             // sequence number stuff;
139      int		cws;		// current window size
140 
141      int		target_nluns; // this and target_lun are
142 				      // hopefully temporal till I
143 				      // figure out a better way.
144      lun_id_t		target_lun[ISCSI_MAX_LUNS];
145 
146      struct mtx		rsp_mtx;
147      struct mtx		rsv_mtx;
148      struct mtx		snd_mtx;
149      struct mtx		hld_mtx;
150      struct mtx		io_mtx;
151      queue_t		rsp;
152      queue_t		rsv;
153      queue_t		csnd;
154      queue_t		isnd;
155      queue_t		wsnd;
156      queue_t		hld;
157 
158      /*
159       | negotiable values
160       */
161      isc_opt_t		opt;
162 
163      struct i_stats	stats;
164      struct cam_path	*cam_path;
165      bhs_t		bhs;
166      struct uio		uio;
167      struct iovec	iov;
168      /*
169       | sysctl stuff
170       */
171      struct sysctl_ctx_list	clist;
172      struct sysctl_oid	*oid;
173      int	douio;	//XXX: turn on/off uio on read
174 } isc_session_t;
175 
176 typedef struct pduq {
177      TAILQ_ENTRY(pduq)	pq_link;
178 
179      caddr_t		buf;
180      u_int		len;	// the total length of the pdu
181      pdu_t		pdu;
182      union ccb		*ccb;
183 
184      struct uio		uio;
185      struct iovec	iov[5];	// XXX: careful ...
186      struct mbuf	*mp;
187      struct timeval	ts;
188      int 		refcnt;
189      queue_t		*pduq;
190 } pduq_t;
191 
192 struct isc_softc {
193      //int		state;
194      struct cdev	*dev;
195      eventhandler_tag	eh;
196      char		isid[6];	// Initiator Session ID (48 bits)
197      struct lock	lock;
198 
199      int			nsess;
200      TAILQ_HEAD(,isc_session)	isc_sess;
201      isc_session_t		*sessions[MAX_SESSIONS];
202 
203      struct lock		pdu_lock;
204 #ifdef  ISCSI_INITIATOR_DEBUG
205      int			 npdu_alloc, npdu_max; // for instrumentation
206 #endif
207 #define MAX_PDUS	(MAX_SESSIONS*256) // XXX: at the moment this is arbitrary
208      objcache_t			pdu_zone; // pool of free pdu's
209      TAILQ_HEAD(,pduq)		freepdu;
210      /*
211       | cam stuff
212       */
213      struct cam_sim		*cam_sim;
214      struct cam_path		*cam_path;
215      struct lock		cam_lock;
216      /*
217       | sysctl stuff
218       */
219      struct sysctl_ctx_list	clist;
220      struct sysctl_oid		*oid;
221 };
222 
223 #ifdef  ISCSI_INITIATOR_DEBUG
224 extern struct lock iscsi_dbg_lock;
225 #endif
226 
227 void	isc_start_receiver(isc_session_t *sp);
228 void	isc_stop_receiver(isc_session_t *sp);
229 
230 int	isc_sendPDU(isc_session_t *sp, pduq_t *pq);
231 int	isc_qout(isc_session_t *sp, pduq_t *pq);
232 int	i_prepPDU(isc_session_t *sp, pduq_t *pq);
233 
234 int	ism_fullfeature(struct cdev *dev, int flag);
235 
236 int	i_pdu_flush(isc_session_t *sc);
237 int	i_setopt(isc_session_t *sp, isc_opt_t *opt);
238 void	i_freeopt(isc_opt_t *opt);
239 
240 int	ic_init(struct isc_softc *sc);
241 void	ic_destroy(struct isc_softc *sc);
242 int	ic_fullfeature(struct cdev *dev);
243 void	ic_lost_target(isc_session_t *sp, int target);
244 int	ic_getCamVals(isc_session_t *sp, iscsi_cam_t *cp);
245 
246 void	ism_recv(isc_session_t *sp, pduq_t *pq);
247 int	ism_start(isc_session_t *sp);
248 void	ism_stop(isc_session_t *sp);
249 
250 int	scsi_encap(struct cam_sim *sim, union ccb *ccb);
251 int	scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
252 void	iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
253 void	iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
254 void	iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq);
255 void	iscsi_async(isc_session_t *sp,  pduq_t *pq);
256 void	iscsi_cleanup(isc_session_t *sp);
257 int	iscsi_requeue(isc_session_t *sp);
258 
259 void	ic_freeze(isc_session_t *sp);
260 void	ic_release(isc_session_t *sp);
261 
262 // Serial Number Arithmetic
263 #define _MAXINCR	0x7FFFFFFF	// 2 ^ 31 - 1
264 #define SNA_GT(i1, i2)	((i1 != i2) && (\
265 	(i1 < i2 && i2 - i1 > _MAXINCR) ||\
266 	(i1 > i2 && i1 - i2 < _MAXINCR))?1: 0)
267 
268 /*
269  * inlines
270  *
271  * DragonFly note: CAM locks itself, peripherals do not lock CAM.
272  */
273 #ifdef _CAM_CAM_XPT_SIM_H
274 
275 #define CAM_LOCK(arg)	/*lockmgr(&arg->cam_lock, LK_EXCLUSIVE)*/
276 #define CAM_UNLOCK(arg)	/*lockmgr(&arg->cam_lock, LK_RELEASE)*/
277 
278 static __inline void
279 XPT_DONE(struct isc_softc *isp, union ccb *ccb)
280 {
281      CAM_LOCK(isp);
282      xpt_done(ccb);
283      CAM_UNLOCK(isp);
284 }
285 
286 #endif /* _CAM_CAM_XPT_SIM_H */
287 
288 #define iscsi_lock_ex(mtx)	mtx_lock_ex_quick(mtx, __func__)
289 #define iscsi_unlock_ex(mtx)	mtx_unlock(mtx)
290 #define issleep(id, mtx, flags, wmesg, to)	\
291 				mtxsleep(id, mtx, flags, wmesg, to)
292 
293 static __inline pduq_t *
294 pdu_alloc(struct isc_softc *isc, int wait)
295 {
296      pduq_t	*pq;
297 
298      lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
299      if((pq = TAILQ_FIRST(&isc->freepdu)) == NULL) {
300 	  lockmgr(&isc->pdu_lock, LK_RELEASE);
301 	  pq = objcache_get(isc->pdu_zone, wait /* M_WAITOK or M_NOWAIT*/);
302      }
303      else {
304 	  TAILQ_REMOVE(&isc->freepdu, pq, pq_link);
305 	  lockmgr(&isc->pdu_lock, LK_RELEASE);
306      }
307 
308      if(pq == NULL) {
309 	  debug(7, "out of mem");
310 	  return NULL;
311      }
312 #ifdef ISCSI_INITIATOR_DEBUG
313      lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
314      isc->npdu_alloc++;
315      if(isc->npdu_alloc > isc->npdu_max)
316 	  isc->npdu_max = isc->npdu_alloc;
317      lockmgr(&isc->pdu_lock, LK_RELEASE);
318 #endif
319      memset(pq, 0, sizeof(pduq_t));
320 
321      return pq;
322 }
323 
324 static __inline void
325 pdu_free(struct isc_softc *isc, pduq_t *pq)
326 {
327      if(pq->mp)
328 	  m_freem(pq->mp);
329 #ifdef NO_USE_MBUF
330      if(pq->buf != NULL)
331 	  kfree(pq->buf, M_ISCSI);
332 #endif
333      lockmgr(&isc->pdu_lock, LK_EXCLUSIVE);
334      TAILQ_INSERT_TAIL(&isc->freepdu, pq, pq_link);
335 #ifdef ISCSI_INITIATOR_DEBUG
336      isc->npdu_alloc--;
337 #endif
338      lockmgr(&isc->pdu_lock, LK_RELEASE);
339 }
340 
341 static __inline void
342 i_nqueue_rsp(isc_session_t *sp, pduq_t *pq)
343 {
344      iscsi_lock_ex(&sp->rsp_mtx);
345      if(++sp->stats.nrsp > sp->stats.max_rsp)
346 	  sp->stats.max_rsp = sp->stats.nrsp;
347      TAILQ_INSERT_TAIL(&sp->rsp, pq, pq_link);
348      iscsi_unlock_ex(&sp->rsp_mtx);
349 }
350 
351 static __inline pduq_t *
352 i_dqueue_rsp(isc_session_t *sp)
353 {
354      pduq_t *pq;
355 
356      iscsi_lock_ex(&sp->rsp_mtx);
357      if((pq = TAILQ_FIRST(&sp->rsp)) != NULL) {
358 	  sp->stats.nrsp--;
359 	  TAILQ_REMOVE(&sp->rsp, pq, pq_link);
360      }
361      iscsi_unlock_ex(&sp->rsp_mtx);
362 
363      return pq;
364 }
365 
366 static __inline void
367 i_nqueue_rsv(isc_session_t *sp, pduq_t *pq)
368 {
369      iscsi_lock_ex(&sp->rsv_mtx);
370      if(++sp->stats.nrsv > sp->stats.max_rsv)
371 	  sp->stats.max_rsv = sp->stats.nrsv;
372      TAILQ_INSERT_TAIL(&sp->rsv, pq, pq_link);
373      iscsi_unlock_ex(&sp->rsv_mtx);
374 }
375 
376 static __inline pduq_t *
377 i_dqueue_rsv(isc_session_t *sp)
378 {
379      pduq_t *pq;
380 
381      iscsi_lock_ex(&sp->rsv_mtx);
382      if((pq = TAILQ_FIRST(&sp->rsv)) != NULL) {
383 	  sp->stats.nrsv--;
384 	  TAILQ_REMOVE(&sp->rsv, pq, pq_link);
385      }
386      iscsi_unlock_ex(&sp->rsv_mtx);
387 
388      return pq;
389 }
390 
391 static __inline void
392 i_nqueue_csnd(isc_session_t *sp, pduq_t *pq)
393 {
394      iscsi_lock_ex(&sp->snd_mtx);
395      if(++sp->stats.ncsnd > sp->stats.max_csnd)
396 	  sp->stats.max_csnd = sp->stats.ncsnd;
397      TAILQ_INSERT_TAIL(&sp->csnd, pq, pq_link);
398      iscsi_unlock_ex(&sp->snd_mtx);
399 }
400 
401 static __inline pduq_t *
402 i_dqueue_csnd(isc_session_t *sp)
403 {
404      pduq_t *pq;
405 
406      iscsi_lock_ex(&sp->snd_mtx);
407      if((pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
408 	  sp->stats.ncsnd--;
409 	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
410      }
411      iscsi_unlock_ex(&sp->snd_mtx);
412 
413      return pq;
414 }
415 
416 static __inline void
417 i_nqueue_isnd(isc_session_t *sp, pduq_t *pq)
418 {
419      iscsi_lock_ex(&sp->snd_mtx);
420      if(++sp->stats.nisnd > sp->stats.max_isnd)
421 	  sp->stats.max_isnd = sp->stats.nisnd;
422      TAILQ_INSERT_TAIL(&sp->isnd, pq, pq_link);
423      iscsi_unlock_ex(&sp->snd_mtx);
424 }
425 
426 static __inline pduq_t *
427 i_dqueue_isnd(isc_session_t *sp)
428 {
429      pduq_t *pq;
430 
431      iscsi_lock_ex(&sp->snd_mtx);
432      if((pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
433 	  sp->stats.nisnd--;
434 	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
435      }
436      iscsi_unlock_ex(&sp->snd_mtx);
437 
438      return pq;
439 }
440 
441 static __inline void
442 i_nqueue_wsnd(isc_session_t *sp, pduq_t *pq)
443 {
444      iscsi_lock_ex(&sp->snd_mtx);
445      if(++sp->stats.nwsnd > sp->stats.max_wsnd)
446 	  sp->stats.max_wsnd = sp->stats.nwsnd;
447      TAILQ_INSERT_TAIL(&sp->wsnd, pq, pq_link);
448      iscsi_unlock_ex(&sp->snd_mtx);
449 }
450 
451 static __inline pduq_t *
452 i_dqueue_wsnd(isc_session_t *sp)
453 {
454      pduq_t *pq;
455 
456      iscsi_lock_ex(&sp->snd_mtx);
457      if((pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
458 	  sp->stats.nwsnd--;
459 	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
460      }
461      iscsi_unlock_ex(&sp->snd_mtx);
462 
463      return pq;
464 }
465 
466 static __inline pduq_t *
467 i_dqueue_snd(isc_session_t *sp, int which)
468 {
469      pduq_t *pq;
470 
471      pq = NULL;
472      iscsi_lock_ex(&sp->snd_mtx);
473      if((which & BIT(0)) && (pq = TAILQ_FIRST(&sp->isnd)) != NULL) {
474 	  sp->stats.nisnd--;
475 	  TAILQ_REMOVE(&sp->isnd, pq, pq_link);
476 	  pq->pduq = &sp->isnd;	// remember where you came from
477      } else
478      if((which & BIT(1)) && (pq = TAILQ_FIRST(&sp->wsnd)) != NULL) {
479 	  sp->stats.nwsnd--;
480 	  TAILQ_REMOVE(&sp->wsnd, pq, pq_link);
481 	  pq->pduq = &sp->wsnd;	// remember where you came from
482      } else
483      if((which & BIT(2)) && (pq = TAILQ_FIRST(&sp->csnd)) != NULL) {
484 	  sp->stats.ncsnd--;
485 	  TAILQ_REMOVE(&sp->csnd, pq, pq_link);
486 	  pq->pduq = &sp->csnd;	// remember where you came from
487      }
488      iscsi_unlock_ex(&sp->snd_mtx);
489 
490      return pq;
491 }
492 
493 static __inline void
494 i_rqueue_pdu(isc_session_t *sp, pduq_t *pq)
495 {
496      iscsi_lock_ex(&sp->snd_mtx);
497      KASSERT(pq->pduq != NULL, ("pq->pduq is NULL"));
498      TAILQ_INSERT_TAIL(pq->pduq, pq, pq_link);
499      iscsi_unlock_ex(&sp->snd_mtx);
500 }
501 
502 /*
503  | Waiting for ACK (or something :-)
504  */
505 static __inline void
506 i_nqueue_hld(isc_session_t *sp, pduq_t *pq)
507 {
508      getmicrouptime(&pq->ts);
509      iscsi_lock_ex(&sp->hld_mtx);
510      if(++sp->stats.nhld > sp->stats.max_hld)
511 	  sp->stats.max_hld = sp->stats.nhld;
512      TAILQ_INSERT_TAIL(&sp->hld, pq, pq_link);
513      iscsi_unlock_ex(&sp->hld_mtx);
514      return;
515 }
516 
517 static __inline void
518 i_remove_hld(isc_session_t *sp, pduq_t *pq)
519 {
520      iscsi_lock_ex(&sp->hld_mtx);
521      sp->stats.nhld--;
522      TAILQ_REMOVE(&sp->hld, pq, pq_link);
523      iscsi_unlock_ex(&sp->hld_mtx);
524 }
525 
526 static __inline pduq_t *
527 i_dqueue_hld(isc_session_t *sp)
528 {
529      pduq_t *pq;
530 
531      iscsi_lock_ex(&sp->hld_mtx);
532      if((pq = TAILQ_FIRST(&sp->hld)) != NULL) {
533 	  sp->stats.nhld--;
534 	  TAILQ_REMOVE(&sp->hld, pq, pq_link);
535      }
536      iscsi_unlock_ex(&sp->hld_mtx);
537 
538      return pq;
539 }
540 
541 static __inline pduq_t *
542 i_search_hld(isc_session_t *sp, int itt, int keep)
543 {
544      pduq_t	*pq, *tmp;
545 
546      pq = NULL;
547 
548      iscsi_lock_ex(&sp->hld_mtx);
549      TAILQ_FOREACH_MUTABLE(pq, &sp->hld, pq_link, tmp) {
550 	  if(pq->pdu.ipdu.bhs.itt == itt) {
551 	       if(!keep) {
552 		    sp->stats.nhld--;
553 		    TAILQ_REMOVE(&sp->hld, pq, pq_link);
554 	       }
555 	       break;
556 	  }
557      }
558      iscsi_unlock_ex(&sp->hld_mtx);
559 
560      return pq;
561 }
562 
563 static __inline void
564 i_mbufcopy(struct mbuf *mp, caddr_t dp, int len)
565 {
566      struct mbuf *m;
567      caddr_t bp;
568 
569      for(m = mp; m != NULL; m = m->m_next) {
570 	  bp = mtod(m, caddr_t);
571 	  /*
572 	   | the pdu is word (4 octed) aligned
573 	   | so len <= packet
574 	   */
575 	  memcpy(dp, bp, MIN(len, m->m_len));
576 	  dp += m->m_len;
577 	  len -= m->m_len;
578 	  if(len <= 0)
579 	       break;
580      }
581 }
582