1/*-
2 * Copyright (c) 2005-2010 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 */
27/*
28 | $Id: iscsi_subr.c 743 2009-08-08 10:54:53Z danny $
29 */
30
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
33
34#include "opt_iscsi_initiator.h"
35
36#include <sys/param.h>
37#include <sys/kernel.h>
38#include <sys/callout.h>
39#include <sys/malloc.h>
40#include <sys/mbuf.h>
41#include <sys/kthread.h>
42#include <sys/lock.h>
43#include <sys/mutex.h>
44#include <sys/uio.h>
45#include <sys/sysctl.h>
46#include <sys/sx.h>
47
48#include <cam/cam.h>
49#include <cam/cam_ccb.h>
50#include <cam/cam_sim.h>
51#include <cam/cam_xpt_sim.h>
52#include <cam/cam_periph.h>
53#include <cam/scsi/scsi_message.h>
54#include <sys/eventhandler.h>
55
56#include <dev/iscsi/initiator/iscsi.h>
57#include <dev/iscsi/initiator/iscsivar.h>
58
59/*
60 | Interface to the SCSI layer
61 */
62void
63iscsi_r2t(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
64{
65     union ccb 		*ccb = opq->ccb;
66     struct ccb_scsiio	*csio = &ccb->csio;
67     pdu_t		*opp = &opq->pdu;
68     bhs_t		*bhp = &opp->ipdu.bhs;
69     r2t_t		*r2t = &pq->pdu.ipdu.r2t;
70     pduq_t	*wpq;
71     int	error;
72
73     debug_called(8);
74     sdebug(4, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt),
75	   ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W);
76
77     switch(bhp->opcode) {
78     case ISCSI_SCSI_CMD:
79	  if(opp->ipdu.scsi_req.W) {
80	       data_out_t	*cmd;
81	       u_int		ddtl = ntohl(r2t->ddtl);
82	       u_int		edtl = ntohl(opp->ipdu.scsi_req.edtlen);
83	       u_int		bleft, bs, dsn, bo;
84	       caddr_t		bp = csio->data_ptr;
85
86	       bo = ntohl(r2t->bo);
87	       bp += MIN(bo, edtl - ddtl);
88	       bleft = ddtl;
89
90	       if(sp->opt.maxXmitDataSegmentLength > 0) // danny's RFC
91		    bs = MIN(sp->opt.maxXmitDataSegmentLength, ddtl);
92	       else
93		    bs = ddtl;
94	       dsn = 0;
95	       sdebug(4, "edtl=%x ddtl=%x bo=%x dsn=%x bs=%x maxX=%x",
96		      edtl, ddtl, bo, dsn, bs, sp->opt.maxXmitDataSegmentLength);
97	       while(bleft > 0) {
98		    wpq = pdu_alloc(sp->isc, M_NOWAIT); // testing ...
99		    if(wpq == NULL) {
100			 sdebug(3, "itt=%x r2tSN=%d bo=%x ddtl=%x W=%d", ntohl(r2t->itt),
101				ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl), opp->ipdu.scsi_req.W);
102			 sdebug(1, "npdu_max=%d npdu_alloc=%d", sp->isc->npdu_max, sp->isc->npdu_alloc);
103
104			 while((wpq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) {
105			      sdebug(2, "waiting...");
106#if __FreeBSD_version >= 700000
107			      pause("isc_r2t", 5*hz);
108#else
109			      tsleep(sp->isc, 0, "isc_r2t", 5*hz);
110#endif
111			 }
112		    }
113		    cmd = &wpq->pdu.ipdu.data_out;
114		    cmd->opcode = ISCSI_WRITE_DATA;
115		    cmd->lun[0]	= r2t->lun[0];
116		    cmd->lun[1]	= r2t->lun[1];
117		    cmd->ttt	= r2t->ttt;
118		    cmd->itt	= r2t->itt;
119
120		    cmd->dsn	= htonl(dsn);
121		    cmd->bo	= htonl(bo);
122
123		    cmd->F 	= (bs < bleft)? 0: 1; // is this the last one?
124		    bs = MIN(bs, bleft);
125
126		    wpq->pdu.ds_len	= bs;
127		    wpq->pdu.ds_addr	= bp;
128
129		    error = isc_qout(sp, wpq);
130		    sdebug(6, "bs=%x bo=%x bp=%p dsn=%x error=%d", bs, bo, bp, dsn, error);
131		    if(error)
132			 break;
133		    bo += bs;
134		    bp += bs;
135		    bleft -= bs;
136		    dsn++;
137	       }
138	  }
139	  break;
140
141     default:
142	  // XXX: should not happen ...
143	  xdebug("huh? opcode=0x%x", bhp->opcode);
144     }
145}
146
147static int
148getSenseData(u_int status, union ccb *ccb, pduq_t *pq)
149{
150     pdu_t		*pp = &pq->pdu;
151     struct		ccb_scsiio *scsi = (struct ccb_scsiio *)ccb;
152     struct		scsi_sense_data *sense = &scsi->sense_data;
153     struct mbuf	*m = pq->mp;
154     scsi_rsp_t		*cmd = &pp->ipdu.scsi_rsp;
155     caddr_t		bp;
156     int		sense_len, mustfree = 0;
157     int                error_code, sense_key, asc, ascq;
158
159     bp = mtod(pq->mp, caddr_t);
160     if((sense_len = scsi_2btoul(bp)) == 0)
161	  return 0;
162     debug(4, "sense_len=%d", sense_len);
163     /*
164      | according to the specs, the sense data cannot
165      | be larger than 252 ...
166      */
167     if(sense_len > m->m_len) {
168	  bp = malloc(sense_len, M_ISCSI, M_WAITOK);
169	  debug(3, "calling i_mbufcopy(len=%d)", sense_len);
170	  i_mbufcopy(pq->mp, bp, sense_len);
171	  mustfree++;
172     }
173     scsi->scsi_status = status;
174
175     bcopy(bp+2, sense, min(sense_len, scsi->sense_len));
176     scsi->sense_resid = 0;
177     if(cmd->flag & (BIT(1)|BIT(2)))
178	  scsi->sense_resid = ntohl(pp->ipdu.scsi_rsp.rcnt);
179     scsi_extract_sense_len(sense, scsi->sense_len - scsi->sense_resid,
180       &error_code, &sense_key, &asc, &ascq, /*show_errors*/ 1);
181
182     debug(3, "sense_len=%d rcnt=%d sense_resid=%d dsl=%d error_code=%x flags=%x",
183	   sense_len,
184	   ntohl(pp->ipdu.scsi_rsp.rcnt), scsi->sense_resid,
185	   pp->ds_len, error_code, sense_key);
186
187     if(mustfree)
188	  free(bp, M_ISCSI);
189
190     return 1;
191}
192
193/*
194 | Some information is from SAM draft.
195 */
196static void
197_scsi_done(isc_session_t *sp, u_int response, u_int status, union ccb *ccb, pduq_t *pq)
198{
199     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
200
201     debug_called(8);
202
203     if(status || response) {
204	  sdebug(3, "response=%x status=%x ccb=%p pq=%p", response, status, ccb, pq);
205	  if(pq != NULL)
206	       sdebug(3, "mp=%p buf=%p len=%d", pq->mp, pq->buf, pq->len);
207     }
208     ccb_h->status = 0;
209     switch(response) {
210     case 0: // Command Completed at Target
211	  switch(status) {
212	  case 0:	// Good, all is ok
213	       ccb_h->status = CAM_REQ_CMP;
214	       break;
215
216	  case 0x02: 	// Check Condition
217	       if((pq != NULL) && (pq->mp != NULL) && getSenseData(status, ccb, pq))
218		    ccb_h->status |= CAM_AUTOSNS_VALID;
219
220	  case 0x14:	// Intermediate-Condition Met
221	  case 0x10:	// Intermediate
222	  case 0x04:	// Condition Met
223	       ccb_h->status |= CAM_SCSI_STATUS_ERROR;
224	       break;
225
226	  case 0x08:
227	       ccb_h->status = CAM_BUSY;
228	       break;
229
230	  case 0x18: // Reservation Conflict
231	  case 0x28: // Task Set Full
232	       ccb_h->status = CAM_REQUEUE_REQ;
233	       break;
234	  default:
235	       //case 0x22: // Command Terminated
236	       //case 0x30: // ACA Active
237	       //case 0x40: // Task Aborted
238	       ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED;
239	  }
240	  break;
241
242     default:
243	  if((response >= 0x80) && (response <= 0xFF)) {
244	       // Vendor specific ...
245	  }
246     case 1: // target failure
247	  ccb_h->status = CAM_REQ_CMP_ERR; //CAM_REQ_ABORTED;
248	  break;
249     }
250     sdebug(5, "ccb_h->status=%x", ccb_h->status);
251
252     XPT_DONE(sp, ccb);
253}
254
255/*
256 | returns the lowest cmdseq that was not acked
257 */
258int
259iscsi_requeue(isc_session_t *sp)
260{
261     pduq_t	*pq;
262     u_int	i, n, last;
263
264     debug_called(8);
265     i = last = 0;
266     sp->flags |= ISC_HOLD;
267     while((pq = i_dqueue_hld(sp)) != NULL) {
268	  i++;
269	  if(pq->ccb != NULL) {
270	       _scsi_done(sp, 0, 0x28, pq->ccb, NULL);
271	       n = ntohl(pq->pdu.ipdu.bhs.CmdSN);
272	       if(last==0 || (last > n))
273		    last = n;
274	       sdebug(2, "last=%x n=%x", last, n);
275	  }
276	  pdu_free(sp->isc, pq);
277     }
278     sp->flags &= ~ISC_HOLD;
279     return i? last: sp->sn.cmd;
280}
281
282int
283i_pdu_flush(isc_session_t *sp)
284{
285     int	n = 0;
286     pduq_t	*pq;
287
288     debug_called(8);
289     while((pq = i_dqueue_rsp(sp)) != NULL) {
290	  pdu_free(sp->isc, pq);
291	  n++;
292     }
293     while((pq = i_dqueue_rsv(sp)) != NULL) {
294	  pdu_free(sp->isc, pq);
295	  n++;
296     }
297     while((pq = i_dqueue_snd(sp, -1)) != NULL) {
298	  pdu_free(sp->isc, pq);
299	  n++;
300     }
301     while((pq = i_dqueue_hld(sp)) != NULL) {
302	  pdu_free(sp->isc, pq);
303	  n++;
304     }
305     while((pq = i_dqueue_wsnd(sp)) != NULL) {
306	  pdu_free(sp->isc, pq);
307	  n++;
308     }
309     if(n != 0)
310	  xdebug("%d pdus recovered, should have been ZERO!", n);
311     return n;
312}
313/*
314 | called from ism_destroy.
315 */
316void
317iscsi_cleanup(isc_session_t *sp)
318{
319     pduq_t *pq, *pqtmp;
320
321     debug_called(8);
322
323     TAILQ_FOREACH_SAFE(pq, &sp->hld, pq_link, pqtmp) {
324	  sdebug(3, "hld pq=%p", pq);
325	  if(pq->ccb)
326	       _scsi_done(sp, 1, 0x40, pq->ccb, NULL);
327	  TAILQ_REMOVE(&sp->hld, pq, pq_link);
328	  if(pq->buf) {
329	       free(pq->buf, M_ISCSIBUF);
330	       pq->buf = NULL;
331	  }
332	  pdu_free(sp->isc, pq);
333     }
334     while((pq = i_dqueue_snd(sp, BIT(0)|BIT(1)|BIT(2))) != NULL) {
335	  sdebug(3, "pq=%p", pq);
336	  if(pq->ccb)
337	       _scsi_done(sp, 1, 0x40, pq->ccb, NULL);
338	  if(pq->buf) {
339	       free(pq->buf, M_ISCSIBUF);
340	       pq->buf = NULL;
341	  }
342	  pdu_free(sp->isc, pq);
343     }
344
345     wakeup(&sp->rsp);
346}
347
348void
349iscsi_done(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
350{
351     pdu_t		*pp = &pq->pdu;
352     scsi_rsp_t		*cmd = &pp->ipdu.scsi_rsp;
353
354     debug_called(8);
355
356     _scsi_done(sp, cmd->response, cmd->status, opq->ccb, pq);
357
358     pdu_free(sp->isc, opq);
359}
360
361// see RFC 3720, 10.9.1 page 146
362/*
363 | NOTE:
364 | the call to isc_stop_receiver is a kludge,
365 | instead, it should be handled by the userland controller,
366 | but that means that there should be a better way, other than
367 | sending a signal. Somehow, this packet should be supplied to
368 | the userland via read.
369 */
370void
371iscsi_async(isc_session_t *sp, pduq_t *pq)
372{
373     pdu_t		*pp = &pq->pdu;
374     async_t		*cmd = &pp->ipdu.async;
375
376     debug_called(8);
377
378     sdebug(3, "asyncevent=0x%x asyncVCode=0x%0x", cmd->asyncEvent, cmd->asyncVCode);
379     switch(cmd->asyncEvent) {
380     case 0: // check status ...
381	  break;
382
383     case 1: // target request logout
384	  isc_stop_receiver(sp);	// XXX: temporary solution
385	  break;
386
387     case 2: // target indicates it wants to drop connection
388	  isc_stop_receiver(sp);	// XXX: temporary solution
389	  break;
390
391     case 3: // target indicates it will drop all connections.
392	  isc_stop_receiver(sp);	// XXX: temporary solution
393	  break;
394
395     case 4: // target request parameter negotiation
396	  break;
397
398     default:
399	  break;
400     }
401}
402
403void
404iscsi_reject(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
405{
406     union ccb 		*ccb = opq->ccb;
407     //reject_t		*reject = &pq->pdu.ipdu.reject;
408
409     debug_called(8);
410     //XXX: check RFC 10.17.1 (page 176)
411     ccb->ccb_h.status = CAM_REQ_ABORTED;
412     XPT_DONE(sp, ccb);
413
414     pdu_free(sp->isc, opq);
415}
416
417/*
418 | deal with lun
419 */
420static int
421dwl(isc_session_t *sp, int lun, u_char *lp)
422{
423     debug_called(8);
424     sdebug(4, "lun=%d", lun);
425     /*
426      | mapping LUN to iSCSI LUN
427      | check the SAM-2 specs
428      | hint: maxLUNS is a small number, cam's LUN is 32bits
429      | iSCSI is 64bits, scsi is ?
430      */
431     // XXX: check if this will pass the endian test
432     if(lun < 256) {
433	  lp[0] = 0;
434	  lp[1] = lun;
435     } else
436     if(lun < 16384) {
437	  lp[0] = (1 << 5) | ((lun >> 8) & 0x3f);
438	  lp[1] = lun & 0xff;
439     }
440     else {
441	  xdebug("lun %d: is unsupported!", lun);
442	  return -1;
443     }
444
445     return 0;
446}
447
448/*
449 | encapsulate the scsi command and
450 */
451int
452scsi_encap(struct cam_sim *sim, union ccb *ccb)
453{
454     isc_session_t	*sp = cam_sim_softc(sim);
455     struct ccb_scsiio	*csio = &ccb->csio;
456     struct ccb_hdr	*ccb_h = &ccb->ccb_h;
457     pduq_t		*pq;
458     scsi_req_t		*cmd;
459
460     debug_called(8);
461
462     debug(4, "ccb->sp=%p", ccb_h->spriv_ptr0);
463     sp = ccb_h->spriv_ptr0;
464
465     if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) {
466	  debug(2, "ccb->sp=%p", ccb_h->spriv_ptr0);
467	  sdebug(1, "pdu_alloc failed sc->npdu_max=%d npdu_alloc=%d",
468		 sp->isc->npdu_max, sp->isc->npdu_alloc);
469	  while((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL) {
470	       sdebug(2, "waiting...");
471#if __FreeBSD_version >= 700000
472	       pause("isc_encap", 5*hz);
473#else
474	       tsleep(sp->isc, 0, "isc_encap", 5*hz);
475#endif
476	  }
477     }
478     cmd = &pq->pdu.ipdu.scsi_req;
479     cmd->opcode = ISCSI_SCSI_CMD;
480     cmd->F = 1;
481#if 0
482// this breaks at least Isilon's iscsi target.
483     /*
484      | map tag option, default is UNTAGGED
485      */
486     switch(csio->tag_action) {
487     case MSG_SIMPLE_Q_TAG:	cmd->attr = iSCSI_TASK_SIMPLE;	break;
488     case MSG_HEAD_OF_Q_TAG:	cmd->attr = iSCSI_TASK_HOFQ;	break;
489     case MSG_ORDERED_Q_TAG:	cmd->attr = iSCSI_TASK_ORDER;	break;
490     case MSG_ACA_TASK:		cmd->attr = iSCSI_TASK_ACA;	break;
491     }
492#else
493     cmd->attr = iSCSI_TASK_SIMPLE;
494#endif
495
496     dwl(sp, ccb_h->target_lun, (u_char *)&cmd->lun);
497
498     if((ccb_h->flags & CAM_CDB_POINTER) != 0) {
499	  if((ccb_h->flags & CAM_CDB_PHYS) == 0) {
500	       if(csio->cdb_len > 16) {
501		    sdebug(3, "oversize cdb %d > 16", csio->cdb_len);
502		    goto invalid;
503	       }
504	  }
505	  else {
506	       sdebug(3, "not phys");
507	       goto invalid;
508	  }
509     }
510
511     if(csio->cdb_len > sizeof(cmd->cdb))
512	  xdebug("guevalt! %d > %ld", csio->cdb_len, (long)sizeof(cmd->cdb));
513
514     memcpy(cmd->cdb,
515	    ccb_h->flags & CAM_CDB_POINTER? csio->cdb_io.cdb_ptr: csio->cdb_io.cdb_bytes,
516	    csio->cdb_len);
517
518     cmd->W = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_OUT;
519     cmd->R = (ccb->ccb_h.flags & CAM_DIR_MASK) == CAM_DIR_IN;
520     cmd->edtlen = htonl(csio->dxfer_len);
521
522     pq->ccb = ccb;
523     /*
524      | place it in the out queue
525      */
526     if(isc_qout(sp, pq) == 0)
527	  return 1;
528 invalid:
529     ccb->ccb_h.status = CAM_REQ_INVALID;
530     pdu_free(sp->isc, pq);
531
532     return 0;
533}
534
535int
536scsi_decap(isc_session_t *sp, pduq_t *opq, pduq_t *pq)
537{
538     union ccb 		*ccb = opq->ccb;
539     struct ccb_scsiio	*csio = &ccb->csio;
540     pdu_t		*opp = &opq->pdu;
541     bhs_t		*bhp = &opp->ipdu.bhs;
542
543     debug_called(8);
544     sdebug(6, "pq=%p opq=%p bhp->opcode=0x%x len=%d",
545	    pq, opq, bhp->opcode, pq->pdu.ds_len);
546     if(ccb == NULL) {
547	  sdebug(1, "itt=0x%x pq=%p opq=%p bhp->opcode=0x%x len=%d",
548		 ntohl(pq->pdu.ipdu.bhs.itt),
549		 pq, opq, bhp->opcode, pq->pdu.ds_len);
550	  xdebug("%d] ccb == NULL!", sp->sid);
551	  return 0;
552     }
553     if(pq->pdu.ds_len != 0) {
554	  switch(bhp->opcode) {
555	  case ISCSI_SCSI_CMD: {
556	       scsi_req_t *cmd = &opp->ipdu.scsi_req;
557	       sdebug(5, "itt=0x%x opcode=%x R=%d",
558		      ntohl(pq->pdu.ipdu.bhs.itt),
559		      pq->pdu.ipdu.bhs.opcode, cmd->R);
560
561	       switch(pq->pdu.ipdu.bhs.opcode) {
562	       case ISCSI_READ_DATA: // SCSI Data in
563	       {
564		    caddr_t	bp = NULL; // = mtod(pq->mp, caddr_t);
565		    data_in_t 	*rcmd = &pq->pdu.ipdu.data_in;
566
567		    if(cmd->R) {
568			 sdebug(5, "copy to=%p from=%p l1=%d l2=%d mp@%p",
569				csio->data_ptr, bp? mtod(pq->mp, caddr_t): 0,
570				ntohl(cmd->edtlen), pq->pdu.ds_len, pq->mp);
571			 if(ntohl(cmd->edtlen) >= pq->pdu.ds_len) {
572			      int	offset, len = pq->pdu.ds_len;
573
574			      if(pq->mp != NULL) {
575				   caddr_t		dp;
576
577				   offset = ntohl(rcmd->bo);
578				   dp = csio->data_ptr + offset;
579				   i_mbufcopy(pq->mp, dp, len);
580			      }
581			 }
582			 else {
583			      xdebug("edtlen=%d < ds_len=%d",
584				     ntohl(cmd->edtlen), pq->pdu.ds_len);
585			 }
586		    }
587		    if(rcmd->S) {
588			 /*
589			  | contains also the SCSI Status
590			  */
591			 _scsi_done(sp, 0, rcmd->status, opq->ccb, NULL);
592			 return 0;
593		    } else
594			 return 1;
595	       }
596	       break;
597	       }
598	  }
599	  default:
600	       sdebug(3, "opcode=%02x", bhp->opcode);
601	       break;
602	  }
603     }
604     /*
605      | XXX: error ...
606      */
607     return 1;
608}
609