xref: /dragonfly/sys/dev/disk/iscsi/initiator/isc_sm.c (revision 0ca59c34)
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/isc_sm.c,v 1.3 2008/11/25 07:17:11 scottl Exp $
27  */
28 /*
29  | iSCSI - Session Manager
30  | $Id: isc_sm.c,v 1.30 2007/04/22 09:53:09 danny Exp danny $
31  */
32 
33 #include "opt_iscsi_initiator.h"
34 
35 #include <sys/param.h>
36 #include <sys/kernel.h>
37 #include <sys/conf.h>
38 #include <sys/systm.h>
39 #include <sys/malloc.h>
40 #include <sys/ctype.h>
41 #include <sys/errno.h>
42 #include <sys/sysctl.h>
43 #include <sys/file.h>
44 #include <sys/uio.h>
45 #include <sys/socketvar.h>
46 #include <sys/socket.h>
47 #include <sys/protosw.h>
48 #include <sys/proc.h>
49 #include <sys/queue.h>
50 #include <sys/kthread.h>
51 #include <sys/syslog.h>
52 #include <sys/mbuf.h>
53 #include <sys/bus.h>
54 #include <sys/eventhandler.h>
55 
56 #include <sys/thread2.h>
57 #include <sys/mutex2.h>
58 #include <sys/mplock2.h>
59 
60 #include <bus/cam/cam.h>
61 #include <bus/cam/cam_ccb.h>
62 #include <bus/cam/cam_sim.h>
63 #include <bus/cam/cam_xpt_sim.h>
64 #include <bus/cam/cam_periph.h>
65 
66 #include <dev/disk/iscsi/initiator/iscsi.h>
67 #include <dev/disk/iscsi/initiator/iscsivar.h>
68 
69 static void
70 _async(isc_session_t *sp, pduq_t *pq)
71 {
72      debug_called(8);
73 
74      iscsi_async(sp, pq);
75 
76      pdu_free(sp->isc, pq);
77 }
78 
79 static void
80 _reject(isc_session_t *sp, pduq_t *pq)
81 {
82      pduq_t	*opq;
83      pdu_t	*pdu;
84      reject_t	*reject;
85      int	itt;
86 
87      debug_called(8);
88      pdu = mtod(pq->mp, pdu_t *);
89      itt = pdu->ipdu.bhs.itt;
90      reject = &pq->pdu.ipdu.reject;
91      sdebug(2, "itt=%x reason=0x%x", ntohl(itt), reject->reason);
92      opq = i_search_hld(sp, itt, 0);
93      if(opq != NULL)
94 	  iscsi_reject(sp, opq, pq);
95      else {
96 	  switch(pq->pdu.ipdu.bhs.opcode) {
97 	  case ISCSI_LOGOUT_CMD: // XXX: wasabi does this - can't figure out why
98 	       sdebug(2, "ISCSI_LOGOUT_CMD ...");
99 	       break;
100 	  default:
101 	       xdebug("%d] we lost something itt=%x",
102 		      sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
103 	  }
104      }
105      pdu_free(sp->isc, pq);
106 }
107 
108 static void
109 _r2t(isc_session_t *sp, pduq_t *pq)
110 {
111      pduq_t	*opq;
112 
113      debug_called(8);
114      opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
115      if(opq != NULL) {
116 	  iscsi_r2t(sp, opq, pq);
117      }
118      else {
119 	  r2t_t		*r2t = &pq->pdu.ipdu.r2t;
120 
121 	  xdebug("%d] we lost something itt=%x r2tSN=%d bo=%x ddtl=%x",
122 		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt),
123 		 ntohl(r2t->r2tSN), ntohl(r2t->bo), ntohl(r2t->ddtl));
124      }
125      pdu_free(sp->isc, pq);
126 }
127 
128 static void
129 _scsi_rsp(isc_session_t *sp, pduq_t *pq)
130 {
131      pduq_t	*opq;
132 
133      debug_called(8);
134      opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 0);
135      debug(5, "itt=%x pq=%p opq=%p", ntohl(pq->pdu.ipdu.bhs.itt), pq, opq);
136      if(opq != NULL)
137 	  iscsi_done(sp, opq, pq);
138      else
139 	  xdebug("%d] we lost something itt=%x",
140 		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
141      pdu_free(sp->isc, pq);
142 }
143 
144 static void
145 _read_data(isc_session_t *sp, pduq_t *pq)
146 {
147      pduq_t		*opq;
148 
149      debug_called(8);
150      opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
151      if(opq != NULL) {
152 	  if(scsi_decap(sp, opq, pq) != 1) {
153 	       i_remove_hld(sp, opq); // done
154 	       pdu_free(sp->isc, opq);
155 	  }
156      }
157      else
158 	  xdebug("%d] we lost something itt=%x",
159 		 sp->sid, ntohl(pq->pdu.ipdu.bhs.itt));
160      pdu_free(sp->isc, pq);
161 }
162 /*
163  | this is a kludge,
164  | the jury is not back with a veredict, user or kernel
165  */
166 static void
167 _nop_out(isc_session_t *sp)
168 {
169      pduq_t	*pq;
170      nop_out_t	*nop_out;
171 
172      debug_called(8);
173 
174      sdebug(4, "cws=%d", sp->cws);
175      if(sp->cws == 0) {
176 	  /*
177 	   | only send a nop if window is closed.
178 	   */
179 	  if((pq = pdu_alloc(sp->isc, M_NOWAIT)) == NULL)
180 	       // I guess we ran out of resources
181 	       return;
182 	  nop_out = &pq->pdu.ipdu.nop_out;
183 	  nop_out->opcode = ISCSI_NOP_OUT;
184 	  nop_out->itt = htonl(sp->sn.itt);
185 	  nop_out->ttt = -1;
186 	  nop_out->I = 1;
187 	  nop_out->F = 1;
188 	  if(isc_qout(sp, pq) != 0) {
189 	       sdebug(1, "failed");
190 	       pdu_free(sp->isc, pq);
191 	  }
192      }
193 }
194 
195 static void
196 _nop_in(isc_session_t *sp, pduq_t *pq)
197 {
198      pdu_t	*pp = &pq->pdu;
199      nop_in_t	*nop_in = &pp->ipdu.nop_in;
200      bhs_t	*bhs = &pp->ipdu.bhs;
201 
202      debug_called(8);
203 
204      sdebug(5, "itt=%x ttt=%x", htonl(nop_in->itt), htonl(nop_in->ttt));
205      if(nop_in->itt == -1) {
206 	  if(pp->ds_len != 0) {
207 	       /*
208 		| according to RFC 3720 this should be zero
209 		| what to do if not?
210 		*/
211 	       xdebug("%d] dslen not zero", sp->sid);
212 	  }
213 	  if(nop_in->ttt != -1) {
214 	       nop_out_t	*nop_out;
215 	       /*
216 		| target wants a nop_out
217 	        */
218 	       bhs->opcode = ISCSI_NOP_OUT;
219 	       bhs->I = 1;
220 	       bhs->F = 1;
221 	       /*
222 		| we are reusing the pdu, so bhs->ttt == nop_in->ttt;
223 		| and need to zero out 'Reserved'
224 		| small cludge here.
225 	        */
226 	       nop_out = &pp->ipdu.nop_out;
227 	       nop_out->sn.maxcmd = 0;
228 	       memset(nop_out->mbz, 0, sizeof(nop_out->mbz));
229 	       (void)isc_qout(sp, pq); //XXX: should check return?
230 	       return;
231 	  }
232 	  //else {
233 	       // just making noise?
234 	       // see 10.9.1: target does not want and answer.
235 	  //}
236 
237      } else
238      if(nop_in->ttt == -1) {
239 	  /*
240 	   | it is an answer to a nop_in from us
241 	   */
242 	  if(nop_in->itt != -1) {
243 #ifdef ISC_WAIT4PING
244 	       // XXX: MUTEX please
245 	       if(sp->flags & ISC_WAIT4PING) {
246 		    i_nqueue_rsp(sp, pq);
247 		    wakeup(&sp->rsp);
248 		    return;
249 	       }
250 #endif
251 	  }
252      }
253      /*
254       | drop it
255       */
256      pdu_free(sp->isc, pq);
257      return;
258 }
259 
260 int
261 i_prepPDU(isc_session_t *sp, pduq_t *pq)
262 {
263      size_t	len, n;
264      pdu_t	*pp = &pq->pdu;
265      bhs_t	*bhp = &pp->ipdu.bhs;
266 
267      len = sizeof(bhs_t);
268      if(pp->ahs_len) {
269 	  len += pp->ahs_len;
270 	  bhp->AHSLength =  pp->ahs_len / 4;
271      }
272      if(sp->hdrDigest)
273 	  len += 4;
274      if(pp->ds_len) {
275 	  n = pp->ds_len;
276 	  len += n;
277 #if BYTE_ORDER == LITTLE_ENDIAN
278 	  bhp->DSLength = ((n & 0x00ff0000) >> 16)
279 	       | (n & 0x0000ff00)
280 	       | ((n & 0x000000ff) << 16);
281 #else
282 	  bhp->DSLength = n;
283 #endif
284 	  if(len & 03) {
285 	       n = 4 - (len & 03);
286 	       len += n;
287 	  }
288 	  if(sp->dataDigest)
289 	       len += 4;
290      }
291 
292      pq->len = len;
293      len -= sizeof(bhs_t);
294      if(sp->opt.maxBurstLength && (len > sp->opt.maxBurstLength)) {
295 	  xdebug("%d] pdu len=%zd > %d",
296 		 sp->sid, len, sp->opt.maxBurstLength);
297 	  // XXX: when this happens it used to hang ...
298 	  return E2BIG;
299      }
300      return 0;
301 }
302 
303 int
304 isc_qout(isc_session_t *sp, pduq_t *pq)
305 {
306      int error = 0;
307 
308      debug_called(8);
309 
310      if(pq->len == 0 && (error = i_prepPDU(sp, pq)))
311 	  return error;
312 
313      if(pq->pdu.ipdu.bhs.I)
314 	  i_nqueue_isnd(sp, pq);
315      else
316      if(pq->pdu.ipdu.data_out.opcode == ISCSI_WRITE_DATA)
317 	  i_nqueue_wsnd(sp, pq);
318      else
319 	  i_nqueue_csnd(sp, pq);
320 
321      sdebug(5, "enqued: pq=%p", pq);
322 
323      iscsi_lock_ex(&sp->io_mtx);
324      sp->flags |= ISC_OQNOTEMPTY;
325      if(sp->flags & ISC_OWAITING)
326 	  wakeup(&sp->flags);
327      iscsi_unlock_ex(&sp->io_mtx);
328 
329      return error;
330 }
331 /*
332  | called when a fullPhase is restarted
333  */
334 static void
335 ism_restart(isc_session_t *sp)
336 {
337      int lastcmd;
338 
339      sdebug(2, "restart ...");
340      lastcmd = iscsi_requeue(sp);
341 #if 0
342      if(lastcmd != sp->sn.cmd) {
343 	  sdebug(1, "resetting CmdSN to=%d (from %d)", lastcmd, sp->sn.cmd);
344 	  sp->sn.cmd = lastcmd;
345      }
346 #endif
347      iscsi_lock_ex(&sp->io_mtx);
348      if(sp->flags & ISC_OWAITING) {
349 	  wakeup(&sp->flags);
350      }
351      iscsi_unlock_ex(&sp->io_mtx);
352 
353      sdebug(2, "restarted lastcmd=0x%x", lastcmd);
354 }
355 
356 int
357 ism_fullfeature(struct cdev *dev, int flag)
358 {
359      isc_session_t *sp = (isc_session_t *)dev->si_drv2;
360      int	error;
361 
362      sdebug(2, "flag=%d", flag);
363 
364      error = 0;
365      switch(flag) {
366      case 0: // stop
367 	  sp->flags &= ~ISC_FFPHASE;
368 	  break;
369      case 1: // start
370 	  error = ic_fullfeature(dev);
371 	  break;
372      case 2: // restart
373 	  ism_restart(sp);
374 	  break;
375      }
376      return error;
377 }
378 
379 void
380 ism_recv(isc_session_t *sp, pduq_t *pq)
381 {
382      bhs_t	*bhs;
383      int	statSN;
384 
385      debug_called(8);
386 
387      bhs = &pq->pdu.ipdu.bhs;
388      statSN = ntohl(bhs->OpcodeSpecificFields[1]);
389 #if 0
390      {
391 	  /*
392 	   | this code is only for debugging.
393 	   */
394 	  sn_t	*sn = &sp->sn;
395 	  if(sp->cws == 0) {
396 	       if((sp->flags & ISC_STALLED) == 0) {
397 		    sdebug(4, "window closed: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
398 			   sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
399 		    sp->flags |= ISC_STALLED;
400 	       } else
401 	       if(sp->flags & ISC_STALLED) {
402 		    sdebug(4, "window opened: max=0x%x exp=0x%x opcode=0x%x cmd=0x%x cws=%d.",
403 			   sn->maxCmd, sn->expCmd, bhs->opcode, sn->cmd, sp->cws);
404 		    sp->flags &= ~ISC_STALLED;
405 	       }
406 	  }
407      }
408 #endif
409 
410 #ifdef notyet
411      if(sp->sn.expCmd != sn->cmd) {
412 	  sdebug(1, "we lost something ... exp=0x%x cmd=0x%x",
413 		 sn->expCmd, sn->cmd);
414      }
415 #endif
416      sdebug(5, "opcode=0x%x itt=0x%x stat#0x%x maxcmd=0x%0x",
417 	    bhs->opcode, ntohl(bhs->itt), statSN, sp->sn.maxCmd);
418 
419      switch(bhs->opcode) {
420      case ISCSI_READ_DATA: {
421 	  data_in_t 	*cmd = &pq->pdu.ipdu.data_in;
422 
423 	  if(cmd->S == 0)
424 	       break;
425      }
426 
427      default:
428 	  if(statSN > (sp->sn.stat + 1)) {
429 	       sdebug(1, "we lost some rec=0x%x exp=0x%x",
430 		      statSN, sp->sn.stat);
431 	       // XXX: must do some error recovery here.
432 	  }
433 	  sp->sn.stat = statSN;
434      }
435 
436      switch(bhs->opcode) {
437      case ISCSI_LOGIN_RSP:
438      case ISCSI_TEXT_RSP:
439      case ISCSI_LOGOUT_RSP:
440 	  i_nqueue_rsp(sp, pq);
441 	  wakeup(&sp->rsp);
442 	  sdebug(3, "wakeup rsp");
443 	  break;
444 
445      case ISCSI_NOP_IN:		_nop_in(sp, pq);	break;
446      case ISCSI_SCSI_RSP:	_scsi_rsp(sp, pq);	break;
447      case ISCSI_READ_DATA:	_read_data(sp, pq);	break;
448      case ISCSI_R2T:		_r2t(sp, pq);		break;
449      case ISCSI_REJECT:		_reject(sp, pq);	break;
450      case ISCSI_ASYNC:		_async(sp, pq);		break;
451 
452      case ISCSI_TASK_RSP:
453      default:
454 	  sdebug(1, "opcode=0x%x itt=0x%x not implemented yet",
455 		 bhs->opcode, ntohl(bhs->itt));
456 	  break;
457      }
458 }
459 
460 /*
461  | go through the out queues looking for work
462  | if either nothing to do, or window is closed
463  | return.
464  */
465 static int
466 proc_out(isc_session_t *sp)
467 {
468      sn_t	*sn = &sp->sn;
469      pduq_t	*pq;
470      int	error, ndone;
471      int	which;
472 
473      debug_called(8);
474      error = ndone = 0;
475 
476      while(sp->flags & ISC_LINK_UP) {
477 	  pdu_t *pp;
478 	  bhs_t	*bhs;
479 	  /*
480 	   | check if there is outstanding work in:
481 	   | 1- the Immediate queue
482 	   | 2- the R2T queue
483 	   | 3- the cmd queue, only if the command window allows it.
484 	   */
485 	  which = BIT(0) | BIT(1);
486 	  if(SNA_GT(sn->cmd, sn->maxCmd) == 0) // if(sn->maxCmd - sn->smc + 1) > 0
487 	       which |= BIT(2);
488 
489 	  sdebug(4, "which=%d sn->maxCmd=%d sn->cmd=%d", which, sn->maxCmd, sn->cmd);
490 
491 	  if((pq = i_dqueue_snd(sp, which)) == NULL)
492 	       break;
493 	  sdebug(4, "pq=%p", pq);
494 
495 	  pp = &pq->pdu;
496 	  bhs = &pp->ipdu.bhs;
497 	  switch(bhs->opcode) {
498 	  case ISCSI_SCSI_CMD:
499 	       sn->itt++;
500 	       bhs->itt = htonl(sn->itt);
501 
502 	  case ISCSI_LOGIN_CMD:
503 	  case ISCSI_TEXT_CMD:
504 	  case ISCSI_LOGOUT_CMD:
505 	  case ISCSI_SNACK:
506 	  case ISCSI_NOP_OUT:
507 	  case ISCSI_TASK_CMD:
508 	       bhs->CmdSN = htonl(sn->cmd);
509 	       if(bhs->I == 0)
510 		    sn->cmd++;
511 
512 	  case ISCSI_WRITE_DATA:
513 	       bhs->ExpStSN = htonl(sn->stat);
514 	       break;
515 
516 	  default:
517 	       // XXX: can this happen?
518 	       xdebug("bad opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
519 		      bhs->opcode,
520 		      sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
521 	       // XXX: and now?
522 	  }
523 
524 	  sdebug(4, "opcode=0x%x sn(cmd=0x%x expCmd=0x%x maxCmd=0x%x expStat=0x%x itt=0x%x)",
525 		bhs->opcode,
526 		sn->cmd, sn->expCmd, sn->maxCmd, sn->expStat, sn->itt);
527 
528 	  if(pq->ccb)
529 	       i_nqueue_hld(sp, pq);
530 
531 	  if((error = isc_sendPDU(sp, pq)) == 0) {
532 	       ndone++;
533 	       if(pq->ccb == NULL)
534 		    pdu_free(sp->isc, pq);
535 	  }
536 	  else {
537 	       xdebug("error=%d ndone=%d opcode=0x%x ccb=%p itt=%x",
538 		      error, ndone, bhs->opcode, pq->ccb, ntohl(bhs->itt));
539 	       if(pq->ccb)
540 		    i_remove_hld(sp, pq);
541 	       switch(error) {
542 	       case EPIPE:
543 		    sp->flags &= ~ISC_LINK_UP;
544 
545 	       case EAGAIN:
546 		    xdebug("requed");
547 		    i_rqueue_pdu(sp, pq);
548 		    break;
549 
550 	       default:
551 	       if(pq->ccb) {
552 			 xdebug("back to cam");
553 			 pq->ccb->ccb_h.status |= CAM_REQUEUE_REQ; // some better error?
554 			 XPT_DONE(sp->isc, pq->ccb);
555 			 pdu_free(sp->isc, pq);
556 	       }
557 		    else
558 			 xdebug("we lost it!");
559 	       }
560 	  }
561      }
562      return error;
563 }
564 
565 /*
566  | survives link breakdowns.
567  */
568 static void
569 ism_proc(void *vp)
570 {
571      isc_session_t 	*sp = (isc_session_t *)vp;
572      int		error;
573 
574      get_mplock();
575      debug_called(8);
576 
577      sdebug(3, "started sp->flags=%x", sp->flags);
578      do {
579 	  if((sp->flags & ISC_HOLD) == 0) {
580 	       error = proc_out(sp);
581 	       if(error) {
582 		    sdebug(3, "error=%d", error);
583 	       }
584 	  }
585 	  iscsi_lock_ex(&sp->io_mtx);
586 	  if((sp->flags & ISC_LINK_UP) == 0) {
587 	       wakeup(&sp->soc);
588 	  }
589 
590 	  if((sp->flags & (ISC_OQNOTEMPTY | ISC_SM_RUN)) == ISC_SM_RUN) {
591 	       sp->flags |= ISC_OWAITING;
592 	       if(issleep(&sp->flags, &sp->io_mtx, 0, "iscproc", hz*30) == EWOULDBLOCK) {
593 		    if(sp->flags & ISC_CON_RUNNING)
594 		    _nop_out(sp);
595 	       }
596 	       sp->flags &= ~ISC_OWAITING;
597 	  }
598 	  sp->flags &= ~ISC_OQNOTEMPTY;
599 	  iscsi_unlock_ex(&sp->io_mtx);
600      } while(sp->flags & ISC_SM_RUN);
601 
602      sp->flags &= ~ISC_SM_RUNNING;
603      sdebug(3, "dropped ISC_SM_RUNNING");
604 
605      wakeup(sp);
606 
607      debug(3, "terminated sp=%p sp->sid=%d", sp, sp->sid);
608 
609      rel_mplock();
610 }
611 
612 #if 0
613 static int
614 isc_dump_options(SYSCTL_HANDLER_ARGS)
615 {
616      int error;
617      isc_session_t *sp;
618      char	buf[1024], *bp;
619 
620      sp = (isc_session_t *)arg1;
621      bp = buf;
622      ksprintf(bp, "targetname='%s'", sp->opt.targetName);
623      bp += strlen(bp);
624      ksprintf(bp, " targetname='%s'", sp->opt.targetAddress);
625      error = SYSCTL_OUT(req, buf, strlen(buf));
626      return error;
627 }
628 #endif
629 
630 static int
631 isc_dump_stats(SYSCTL_HANDLER_ARGS)
632 {
633      isc_session_t	*sp;
634      struct isc_softc	*sc;
635      char	buf[1024], *bp;
636      int 	error, n;
637 
638      sp = (isc_session_t *)arg1;
639      sc = sp->isc;
640 
641      bp = buf;
642      n = sizeof(buf);
643      ksnprintf(bp, n, "recv=%d sent=%d", sp->stats.nrecv, sp->stats.nsent);
644      bp += strlen(bp);
645      n -= strlen(bp);
646      ksnprintf(bp, n, " flags=0x%08x pdus-alloc=%d pdus-max=%d",
647 		  sp->flags, sc->npdu_alloc, sc->npdu_max);
648      bp += strlen(bp);
649      n -= strlen(bp);
650      ksnprintf(bp, n, " cws=%d cmd=%x exp=%x max=%x stat=%x itt=%x",
651 		  sp->cws, sp->sn.cmd, sp->sn.expCmd, sp->sn.maxCmd, sp->sn.stat, sp->sn.itt);
652      error = SYSCTL_OUT(req, buf, strlen(buf));
653      return error;
654 }
655 
656 static int
657 isc_sysctl_targetName(SYSCTL_HANDLER_ARGS)
658 {
659      char	buf[128], **cp;
660      int 	error;
661 
662      cp = (char **)arg1;
663      ksnprintf(buf, sizeof(buf), "%s", *cp);
664      error = SYSCTL_OUT(req, buf, strlen(buf));
665      return error;
666 }
667 static int
668 isc_sysctl_targetAddress(SYSCTL_HANDLER_ARGS)
669 {
670      char	buf[128], **cp;
671      int 	error;
672 
673      cp = (char **)arg1;
674      ksnprintf(buf, sizeof(buf), "%s", *cp);
675      error = SYSCTL_OUT(req, buf, strlen(buf));
676      return error;
677 }
678 static void
679 isc_add_sysctls(isc_session_t *sp)
680 {
681      debug_called(8);
682      sdebug(6, "sid=%d %s", sp->sid, sp->dev->si_name);
683 
684      sysctl_ctx_init(&sp->clist);
685      sp->oid = SYSCTL_ADD_NODE(&sp->clist,
686 			       SYSCTL_CHILDREN(sp->isc->oid),
687 			       OID_AUTO,
688 			       sp->dev->si_name+5, // iscsi0
689 			       CTLFLAG_RD,
690 			       0,
691 			       "initiator");
692      SYSCTL_ADD_PROC(&sp->clist,
693 		     SYSCTL_CHILDREN(sp->oid),
694 		     OID_AUTO,
695 		     "targetname",
696 		     CTLFLAG_RD,
697 		     (void *)&sp->opt.targetName, 0,
698 		     isc_sysctl_targetName, "A", "target name");
699 
700      SYSCTL_ADD_PROC(&sp->clist,
701 		     SYSCTL_CHILDREN(sp->oid),
702 		     OID_AUTO,
703 		     "targeaddress",
704 		     CTLFLAG_RD,
705 		     (void *)&sp->opt.targetAddress, 0,
706 		     isc_sysctl_targetAddress, "A", "target address");
707 
708      SYSCTL_ADD_PROC(&sp->clist,
709 		     SYSCTL_CHILDREN(sp->oid),
710 		     OID_AUTO,
711 		     "stats",
712 		     CTLFLAG_RD,
713 		     (void *)sp, 0,
714 		     isc_dump_stats, "A", "statistics");
715 
716      SYSCTL_ADD_INT(&sp->clist,
717 		     SYSCTL_CHILDREN(sp->oid),
718 		     OID_AUTO,
719 		     "douio",
720 		     CTLFLAG_RW,
721 		     &sp->douio, 0, "enable uio on read");
722 }
723 
724 void
725 ism_stop(isc_session_t *sp)
726 {
727      struct isc_softc *sc = sp->isc;
728      cdev_t	dev;
729 
730      debug_called(8);
731      sdebug(2, "terminating");
732      /*
733       | first stop the receiver
734       */
735      isc_stop_receiver(sp);
736 
737      /*
738       | now stop the xmitter
739       */
740      sp->flags &= ~ISC_SM_RUN;
741      while(sp->flags & ISC_SM_RUNNING) {
742 	  sdebug(2, "waiting for ism to stop");
743 	  wakeup(&sp->flags);
744 	  tsleep(sp, 0, "-", hz);
745      }
746      sdebug(2, "ism stopped");
747      sp->flags &= ~ISC_FFPHASE;
748 
749      iscsi_cleanup(sp);
750 
751      (void)i_pdu_flush(sp);
752 
753      ic_lost_target(sp, sp->sid);
754 
755      lockmgr(&sc->lock, LK_EXCLUSIVE);
756      TAILQ_REMOVE(&sc->isc_sess, sp, sp_link);
757      sc->nsess--;
758      lockmgr(&sc->lock, LK_RELEASE);
759 
760      dev = sp->dev;
761      sp->dev = NULL;
762 
763      release_dev(dev);
764      destroy_dev(dev);
765 
766      mtx_uninit(&sp->rsp_mtx);
767      mtx_uninit(&sp->rsv_mtx);
768      mtx_uninit(&sp->hld_mtx);
769      mtx_uninit(&sp->snd_mtx);
770      mtx_uninit(&sp->io_mtx);
771 
772      i_freeopt(&sp->opt);
773      sc->sessions[sp->sid] = NULL;
774 
775      if(sysctl_ctx_free(&sp->clist))
776 	  xdebug("sysctl_ctx_free failed");
777 
778      kfree(sp, M_ISCSI);
779 }
780 
781 int
782 ism_start(isc_session_t *sp)
783 {
784      debug_called(8);
785     /*
786      | now is a good time to do some initialization
787      */
788      TAILQ_INIT(&sp->rsp);
789      TAILQ_INIT(&sp->rsv);
790      TAILQ_INIT(&sp->csnd);
791      TAILQ_INIT(&sp->isnd);
792      TAILQ_INIT(&sp->wsnd);
793      TAILQ_INIT(&sp->hld);
794 
795      mtx_init(&sp->rsv_mtx, "irsv");
796      mtx_init(&sp->rsp_mtx, "irsp");
797      mtx_init(&sp->snd_mtx, "isnd");
798      mtx_init(&sp->hld_mtx, "ihld");
799 
800      mtx_init(&sp->io_mtx, "isio");
801 
802      isc_add_sysctls(sp);
803 
804      sp->flags |= ISC_SM_RUN;
805      sp->flags |= ISC_SM_RUNNING;
806 
807      debug(4, "starting ism_proc: sp->sid=%d", sp->sid);
808      return kthread_create(ism_proc, sp, &sp->stp, "ism_%d", sp->sid);
809 }
810