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