xref: /dragonfly/sys/dev/disk/iscsi/initiator/isc_soc.c (revision b3f5eba6)
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_soc.c,v 1.6 2009/06/25 18:46:30 kib Exp $
27  */
28 /*
29  | iSCSI
30  | $Id: isc_soc.c,v 1.26 2007/05/19 06:09:01 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/eventhandler.h>
54 #include <sys/socketops.h>
55 
56 #include <sys/mutex2.h>
57 #include <sys/mplock2.h>
58 
59 #include <bus/cam/cam.h>
60 #include <bus/cam/cam_ccb.h>
61 
62 #include <dev/disk/iscsi/initiator/iscsi.h>
63 #include <dev/disk/iscsi/initiator/iscsivar.h>
64 
65 #ifndef NO_USE_MBUF
66 #define USE_MBUF
67 #endif
68 
69 #ifdef USE_MBUF
70 
71 static int ou_refcnt = 0;
72 
73 /*
74  | function for counting refs on external storage for mbuf
75  */
76 static void
77 ext_ref(void *arg)
78 {
79      pduq_t *a = arg;
80 
81      debug(3, "ou_refcnt=%d arg=%p b=%p", ou_refcnt, a, a->buf);
82      atomic_add_int(&a->refcnt, 1);
83 }
84 
85 /*
86  | function for freeing external storage for mbuf
87  */
88 static void
89 ext_free(void *arg)
90 {
91      pduq_t *a = arg;
92 
93      if (atomic_fetchadd_int(&a->refcnt, -1) == 1)
94 	  if (a->buf != NULL) {
95 	       debug(3, "ou_refcnt=%d a=%p b=%p", ou_refcnt, a, a->buf);
96 	       kfree(a->buf, M_ISCSI);
97 	       a->buf = NULL;
98 	  }
99 }
100 
101 int
102 isc_sendPDU(isc_session_t *sp, pduq_t *pq)
103 {
104      struct mbuf *mh, **mp;
105      pdu_t		*pp = &pq->pdu;
106      int		len, error;
107 
108      debug_called(8);
109      /*
110       | mbuf for the iSCSI header
111       */
112      MGETHDR(mh, M_WAITOK, MT_DATA);
113      mh->m_len = mh->m_pkthdr.len = sizeof(union ipdu_u);
114      mh->m_pkthdr.rcvif = NULL;
115      MH_ALIGN(mh, sizeof(union ipdu_u));
116      bcopy(&pp->ipdu, mh->m_data, sizeof(union ipdu_u));
117      mh->m_next = NULL;
118 
119      if(sp->hdrDigest)
120 	  pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
121      if(pp->ahs_len) {
122           /*
123 	   | Add any AHS to the iSCSI hdr mbuf
124            |  XXX Assert: (mh->m_pkthdr.len + pp->ahs_len) < MHLEN
125 	   */
126           bcopy(pp->ahs, (mh->m_data + mh->m_len), pp->ahs_len);
127           mh->m_len += pp->ahs_len;
128           mh->m_pkthdr.len += pp->ahs_len;
129 
130 	  if(sp->hdrDigest)
131 	       pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
132      }
133      if(sp->hdrDigest) {
134 	  debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
135           /*
136 	   | Add header digest to the iSCSI hdr mbuf
137 	   | XXX Assert: (mh->m_pkthdr.len + 4) < MHLEN
138 	   */
139           bcopy(&pp->hdr_dig, (mh->m_data + mh->m_len), sizeof(int));
140           mh->m_len += sizeof(int);
141           mh->m_pkthdr.len += sizeof(int);
142      }
143      mp = &mh->m_next;
144      if(pq->pdu.ds) {
145           struct mbuf   *md;
146           int           off = 0;
147 
148           len = pp->ds_len;
149 	  while(len & 03) // the specs say it must be int alligned
150 	       len++;
151           while(len > 0) {
152                 int       l;
153 
154 	       MGET(md, M_WAITOK, MT_DATA);
155 	       pq->refcnt++;
156 
157                 l = min(MCLBYTES, len);
158 	       debug(5, "setting ext_free(arg=%p len/l=%d/%d)", pq->buf, len, l);
159 	       md->m_ext.ext_buf = pq->buf;
160 	       md->m_ext.ext_free = ext_free;
161 	       md->m_ext.ext_ref = ext_ref;
162 	       md->m_ext.ext_arg = pq;
163 	       md->m_ext.ext_size = l;
164 	       md->m_flags |= M_EXT;
165 	       md->m_data = pp->ds + off;
166 	       md->m_len = l;
167 	       md->m_next = NULL;
168 	       mh->m_pkthdr.len += l;
169 	       *mp = md;
170 	       mp = &md->m_next;
171 	       len -= l;
172 	       off += l;
173           }
174      }
175      if(sp->dataDigest) {
176           struct mbuf   *me;
177 
178 	  pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
179 
180 	  MGET(me, M_WAITOK, MT_DATA);
181           me->m_len = sizeof(int);
182           MH_ALIGN(mh, sizeof(int));
183           bcopy(&pp->ds_dig, me->m_data, sizeof(int));
184           me->m_next = NULL;
185           mh->m_pkthdr.len += sizeof(int);
186           *mp = me;
187      }
188      if((error = sosend(sp->soc, NULL, NULL, mh, 0, 0, curthread)) != 0) {
189 	  sdebug(3, "error=%d", error);
190 	  return error;
191      }
192      sp->stats.nsent++;
193      getmicrouptime(&sp->stats.t_sent);
194      return 0;
195 }
196 #else /* NO_USE_MBUF */
197 int
198 isc_sendPDU(isc_session_t *sp, pduq_t *pq)
199 {
200      struct uio *uio = &pq->uio;
201      struct iovec *iv;
202      pdu_t	*pp = &pq->pdu;
203      int	len, error;
204 
205      debug_called(8);
206 
207      bzero(uio, sizeof(struct uio));
208      uio->uio_rw = UIO_WRITE;
209      uio->uio_segflg = UIO_SYSSPACE;
210      uio->uio_td = curthread;
211      uio->uio_iov = iv = pq->iov;
212 
213      iv->iov_base = &pp->ipdu;
214      iv->iov_len = sizeof(union ipdu_u);
215      uio->uio_resid = pq->len;
216      iv++;
217      if(sp->hdrDigest)
218 	  pq->pdu.hdr_dig = sp->hdrDigest(&pp->ipdu, sizeof(union ipdu_u), 0);
219      if(pp->ahs_len) {
220 	  iv->iov_base = pp->ahs;
221 	  iv->iov_len = pp->ahs_len;
222 	  iv++;
223 
224 	  if(sp->hdrDigest)
225 	       pq->pdu.hdr_dig = sp->hdrDigest(&pp->ahs, pp->ahs_len, pq->pdu.hdr_dig);
226      }
227      if(sp->hdrDigest) {
228 	  debug(2, "hdr_dig=%x", pq->pdu.hdr_dig);
229 	  iv->iov_base = &pp->hdr_dig;
230 	  iv->iov_len = sizeof(int);
231 	  iv++;
232      }
233      if(pq->pdu.ds) {
234 	  iv->iov_base = pp->ds;
235 	  iv->iov_len = pp->ds_len;
236 	  while(iv->iov_len & 03) // the specs say it must be int alligned
237 	       iv->iov_len++;
238 	  iv++;
239      }
240      if(sp->dataDigest) {
241 	  pp->ds_dig = sp->dataDigest(pp->ds, pp->ds_len, 0);
242 	  iv->iov_base = &pp->ds_dig;
243 	  iv->iov_len = sizeof(int);
244 	  iv++;
245      }
246      uio->uio_iovcnt	= iv - pq->iov;
247      sdebug(5, "opcode=%x iovcnt=%d uio_resid=%d itt=%x",
248 	    pp->ipdu.bhs.opcode, uio->uio_iovcnt, uio->uio_resid,
249 	    ntohl(pp->ipdu.bhs.itt));
250      sdebug(5, "sp=%p sp->soc=%p uio=%p sp->td=%p",
251 	    sp, sp->soc, uio, sp->td);
252      do {
253 	  len = uio->uio_resid;
254 	  error = sosend(sp->soc, NULL, uio, 0, 0, 0, curthread);
255 	  if(uio->uio_resid == 0 || error || len == uio->uio_resid) {
256 	       if(uio->uio_resid) {
257 		    sdebug(2, "uio->uio_resid=%d uio->uio_iovcnt=%d error=%d len=%d",
258 			   uio->uio_resid, uio->uio_iovcnt, error, len);
259 		    if(error == 0)
260 			 error = EAGAIN; // 35
261 	       }
262 	       break;
263 	  }
264 	  /*
265 	   | XXX: untested code
266 	   */
267 	  sdebug(1, "uio->uio_resid=%d uio->uio_iovcnt=%d",
268 		uio->uio_resid, uio->uio_iovcnt);
269 	  iv = uio->uio_iov;
270 	  len -= uio->uio_resid;
271 	  while(uio->uio_iovcnt > 0) {
272 	       if(iv->iov_len > len) {
273 		    caddr_t	bp = (caddr_t)iv->iov_base;
274 
275 		    iv->iov_len -= len;
276 		    iv->iov_base = (void *)&bp[len];
277 		    break;
278 	       }
279 	       len -= iv->iov_len;
280 	       uio->uio_iovcnt--;
281 	       uio->uio_iov++;
282 	       iv++;
283 	  }
284      } while(uio->uio_resid);
285 
286      if(error == 0) {
287 	  sp->stats.nsent++;
288 	  getmicrouptime(&sp->stats.t_sent);
289 
290      }
291 
292      return error;
293 }
294 #endif /* USE_MBUF */
295 
296 /*
297  | wait till a PDU header is received
298  | from the socket.
299  */
300 /*
301    The format of the BHS is:
302 
303    Byte/     0       |       1       |       2       |       3       |
304       /              |               |               |               |
305      |0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|0 1 2 3 4 5 6 7|
306      +---------------+---------------+---------------+---------------+
307     0|.|I| Opcode    |F|  Opcode-specific fields                     |
308      +---------------+---------------+---------------+---------------+
309     4|TotalAHSLength | DataSegmentLength                             |
310      +---------------+---------------+---------------+---------------+
311     8| LUN or Opcode-specific fields                                 |
312      +                                                               +
313    12|                                                               |
314      +---------------+---------------+---------------+---------------+
315    16| Initiator Task Tag                                            |
316      +---------------+---------------+---------------+---------------+
317    20/ Opcode-specific fields                                        /
318     +/                                                               /
319      +---------------+---------------+---------------+---------------+
320    48
321  */
322 static __inline int
323 so_getbhs(isc_session_t *sp)
324 {
325      bhs_t *bhs		= &sp->bhs;
326      struct uio		*uio = &sp->uio;
327      struct iovec	*iov = &sp->iov;
328      int		error, flags;
329 
330      debug_called(8);
331 
332      iov->iov_base	= bhs;
333      iov->iov_len	= sizeof(bhs_t);
334 
335      uio->uio_iov	= iov;
336      uio->uio_iovcnt	= 1;
337      uio->uio_rw	= UIO_READ;
338      uio->uio_segflg	= UIO_SYSSPACE;
339      uio->uio_td	= curthread; // why ...
340      uio->uio_resid	= sizeof(bhs_t);
341 
342      flags = MSG_WAITALL;
343      error = so_pru_soreceive(sp->soc, NULL, uio, NULL, NULL, &flags);
344 
345      if(error)
346 	  debug(2, "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd",
347 		error,
348 		sp->soc->so_error, uio->uio_resid, iov->iov_len);
349      if(!error && (uio->uio_resid > 0)) {
350 	  error = EPIPE; // was EAGAIN
351 	  debug(2, "error=%d so_error=%d uio->uio_resid=%zd iov.iov_len=%zd "
352 		   "so_state=%x",
353 		error,
354 		sp->soc->so_error, uio->uio_resid, iov->iov_len,
355 		sp->soc->so_state);
356      }
357 
358      return error;
359 }
360 
361 /*
362  | so_recv gets called when there is at least
363  | an iSCSI header in the queue
364  */
365 static int
366 so_recv(isc_session_t *sp, pduq_t *pq)
367 {
368      struct socket	*so = sp->soc;
369      sn_t		*sn = &sp->sn;
370      struct uio		*uio = &pq->uio;
371      struct sockbuf	sbp;
372      pdu_t		*pp;
373      int		error;
374      size_t		n, len;
375      bhs_t		*bhs;
376      u_int		max, exp;
377 
378      debug_called(8);
379      /*
380       | now calculate how much data should be in the buffer
381       | NOTE: digest is not verified/calculated - yet
382       */
383      pp = &pq->pdu;
384      bhs = &pp->ipdu.bhs;
385 
386      sbinit(&sbp, 0);
387      len = 0;
388      if(bhs->AHSLength) {
389 	  pp->ahs_len = bhs->AHSLength * 4;
390 	  len += pp->ahs_len;
391      }
392      if(sp->hdrDigest)
393 	  len += 4;
394      if(bhs->DSLength) {
395 	  n = bhs->DSLength;
396 #if BYTE_ORDER == LITTLE_ENDIAN
397 	  pp->ds_len = ((n & 0x00ff0000) >> 16)
398 	       | (n & 0x0000ff00)
399 	       | ((n & 0x000000ff) << 16);
400 #else
401 	  pp->ds_len = n;
402 #endif
403 	  len += pp->ds_len;
404 	  while(len & 03)
405 	       len++;
406 	  if(sp->dataDigest)
407 	       len += 4;
408      }
409 
410      if((sp->opt.maxRecvDataSegmentLength > 0) && (len > sp->opt.maxRecvDataSegmentLength)) {
411 #if 0
412 	  xdebug("impossible PDU length(%d) opt.maxRecvDataSegmentLength=%d",
413 		 len, sp->opt.maxRecvDataSegmentLength);
414 	  // deep trouble here, probably all we can do is
415 	  // force a disconnect, XXX: check RFC ...
416 	  log(LOG_ERR,
417 	      "so_recv: impossible PDU length(%ld) from iSCSI %s/%s\n",
418 	      len, sp->opt.targetAddress, sp->opt.targetName);
419 #endif
420 	  /*
421 	   | XXX: this will really screwup the stream.
422 	   | should clear up the buffer till a valid header
423 	   | is found, or just close connection ...
424 	   | should read the RFC.
425 	   */
426 	  error = E2BIG;
427 	  goto out;
428      }
429      if(len) {
430 	  int	flags;
431 
432 	  sbp.sb_climit = len;
433 	  uio->uio_td = curthread; // why ...
434 	  if(sp->douio) {
435 	       // it's more efficient to use mbufs -- why?
436 	       if(bhs->opcode == ISCSI_READ_DATA) {
437 		    pduq_t	*opq;
438 
439 		    opq = i_search_hld(sp, pq->pdu.ipdu.bhs.itt, 1);
440 		    if(opq != NULL) {
441 			 union ccb *ccb 		= opq->ccb;
442 			 struct ccb_scsiio *csio	= &ccb->csio;
443 			 pdu_t *opp			= &opq->pdu;
444 			 scsi_req_t *cmd		= &opp->ipdu.scsi_req;
445 			 data_in_t *rcmd		= &pq->pdu.ipdu.data_in;
446 			 bhs_t *bhp			= &opp->ipdu.bhs;
447 			 int	r;
448 
449 			 if(bhp->opcode == ISCSI_SCSI_CMD
450 			    && cmd->R
451 			    && (ntohl(cmd->edtlen) >= pq->pdu.ds_len)) {
452 			      struct iovec *iov = pq->iov;
453 			      iov->iov_base = csio->data_ptr + ntohl(rcmd->bo);
454 			      iov->iov_len = pq->pdu.ds_len;
455 
456 			      uio->uio_rw = UIO_READ;
457 			      uio->uio_segflg = UIO_SYSSPACE;
458 			      uio->uio_iov = iov;
459 			      uio->uio_iovcnt = 1;
460 			      if(len > pq->pdu.ds_len) {
461 				   pq->iov[1].iov_base = &r;
462 				   pq->iov[1].iov_len = len - pq->pdu.ds_len;
463 				   uio->uio_iovcnt++;
464 			      }
465 
466 			      sdebug(4, "uio_resid=0x%zx itt=0x%x bp=%p bo=%x len=%x/%x",
467 				     uio->uio_resid,
468 				     ntohl(pq->pdu.ipdu.bhs.itt),
469 				     csio->data_ptr, ntohl(rcmd->bo), ntohl(cmd->edtlen), pq->pdu.ds_len);
470 			 }
471 		    }
472 	       }
473 	  }
474 	  /*
475 	   * Here we call so_pru_receive with a sockbuf so we can obtain
476 	   * the mbuf chain that can be assigned later to the pq->mp,
477 	   * which is the mbuf wanted.
478 	   * For the moment, resid will be saved in the uio.
479 	   */
480 	  flags = MSG_WAITALL;
481 	  error = so_pru_soreceive(so, NULL, NULL, &sbp, NULL, &flags);
482 	  pq->mp = sbp.sb_mb;
483 	  uio->uio_resid = sbp.sb_climit - sbp.sb_cc;
484 	  //if(error == EAGAIN)
485 	  // XXX: this needs work! it hangs iscontrol
486 	  if(error || uio->uio_resid)
487 	       goto out;
488      }
489      pq->len += len;
490      sdebug(6, "len=%d] opcode=0x%x ahs_len=0x%x ds_len=0x%x",
491 	    pq->len, bhs->opcode, pp->ahs_len, pp->ds_len);
492 
493      max = ntohl(bhs->MaxCmdSN);
494      exp = ntohl(bhs->ExpStSN);
495 
496      if(max < exp - 1 &&
497 	max > exp - _MAXINCR) {
498 	  sdebug(2,  "bad cmd window size");
499 	  error = EIO; // XXX: for now;
500 	  goto out; // error
501      }
502 
503      if(SNA_GT(max, sn->maxCmd))
504 	  sn->maxCmd = max;
505 
506      if(SNA_GT(exp, sn->expCmd))
507 	  sn->expCmd = exp;
508 
509      sp->cws = sn->maxCmd - sn->expCmd + 1;
510 
511      return 0;
512 
513  out:
514      // XXX: need some work here
515      xdebug("have a problem, error=%d", error);
516      pdu_free(sp->isc, pq);
517      if(!error && uio->uio_resid > 0)
518 	  error = EPIPE;
519      return error;
520 }
521 
522 /*
523  | wait for something to arrive.
524  | and if the pdu is without errors, process it.
525  */
526 static int
527 so_input(isc_session_t *sp)
528 {
529      pduq_t		*pq;
530      int		error;
531 
532      debug_called(8);
533      /*
534       | first read in the iSCSI header
535       */
536      error = so_getbhs(sp);
537      if(error == 0) {
538 	  /*
539 	   | now read the rest.
540 	   */
541 	  pq = pdu_alloc(sp->isc, M_NOWAIT);
542 	  if(pq == NULL) { // XXX: might cause a deadlock ...
543 	       debug(3, "out of pdus, wait");
544 	       pq = pdu_alloc(sp->isc, M_NOWAIT);  // OK to WAIT
545 	  }
546 	  pq->pdu.ipdu.bhs = sp->bhs;
547 	  pq->len = sizeof(bhs_t);	// so far only the header was read
548 	  error = so_recv(sp, pq);
549 	  if(error != 0) {
550 	       error += 0x800; // XXX: just to see the error.
551 	       // terminal error
552 	       // XXX: close connection and exit
553 	  }
554 	  else {
555 	       sp->stats.nrecv++;
556 	       getmicrouptime(&sp->stats.t_recv);
557 	       ism_recv(sp, pq);
558 	  }
559      }
560      return error;
561 }
562 
563 /*
564  | one per active (connected) session.
565  | this thread is responsible for reading
566  | in packets from the target.
567  */
568 static void
569 isc_soc(void *vp)
570 {
571      isc_session_t	*sp = (isc_session_t *)vp;
572      struct socket	*so = sp->soc;
573      int		error;
574 
575      get_mplock();
576      debug_called(8);
577 
578      sp->td = curthread;
579      if(sp->cam_path)
580 	  ic_release(sp);
581 
582      error = 0;
583      while((sp->flags & (ISC_CON_RUN | ISC_LINK_UP)) == (ISC_CON_RUN | ISC_LINK_UP)) {
584 	  // XXX: hunting ...
585 	  if(sp->soc == NULL || !(so->so_state & SS_ISCONNECTED)) {
586 	       debug(2, "sp->soc=%p", sp->soc);
587 	       break;
588 	  }
589 	  error = so_input(sp);
590 	  if(error == 0) {
591 	       iscsi_lock_ex(&sp->io_mtx);
592 	       if(sp->flags & ISC_OWAITING) {
593 	       wakeup(&sp->flags);
594 	       }
595 	       iscsi_unlock_ex(&sp->io_mtx);
596 	  } else if(error == EPIPE) {
597 	       break;
598 	  }
599 	  else if(error == EAGAIN) {
600 	       if(so->so_state & SS_ISCONNECTED)
601 		    // there seems to be a problem in 6.0 ...
602 		    tsleep(sp, 0, "iscsoc", 2*hz);
603 	  }
604      }
605      sdebug(2, "terminated, flags=%x so_state=%x error=%d proc=%p",
606 	    sp->flags, so ? so->so_state : 0, error, sp->proc);
607      if((sp->proc != NULL) && sp->signal) {
608 	  PROC_LOCK(sp->proc);
609 	  ksignal(sp->proc, sp->signal);
610 	  PROC_UNLOCK(sp->proc);
611 	  sp->flags |= ISC_SIGNALED;
612 	  sdebug(2, "pid=%d signaled(%d)", sp->proc->p_pid, sp->signal);
613      }
614      else {
615 	  // we have to do something ourselves
616 	  // like closing this session ...
617      }
618      /*
619       | we've been terminated
620       */
621      // do we need this mutex ...?
622      //iscsi_lock_ex(&sp->io_mtx);
623      sp->flags &= ~(ISC_CON_RUNNING | ISC_LINK_UP);
624      wakeup(&sp->soc);
625      //iscsi_unlock_ex(&sp->io_mtx);
626 
627      sdebug(2, "dropped ISC_CON_RUNNING");
628 
629      rel_mplock();
630 }
631 
632 void
633 isc_stop_receiver(isc_session_t *sp)
634 {
635      debug_called(8);
636      debug(3, "sp=%p sp->sid=%d sp->soc=%p", sp, sp ? sp->sid : 0,
637 	  sp ? sp->soc : NULL);
638      iscsi_lock_ex(&sp->io_mtx);
639      sp->flags &= ~ISC_LINK_UP;
640      if (sp->flags & ISC_CON_RUNNING) {
641 	     issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", 5*hz);
642      }
643      iscsi_unlock_ex(&sp->io_mtx);
644 
645      if (sp->soc)
646 	  soshutdown(sp->soc, SHUT_RD);
647 
648      iscsi_lock_ex(&sp->io_mtx);
649      sdebug(3, "soshutdown");
650      sp->flags &= ~ISC_CON_RUN;
651      while(sp->flags & ISC_CON_RUNNING) {
652 	  sdebug(3, "waiting flags=%x", sp->flags);
653 	  issleep(&sp->soc, &sp->io_mtx, 0, "iscstpc", hz);
654      }
655      iscsi_unlock_ex(&sp->io_mtx);
656 
657      if (sp->fp != NULL) {
658 	  fdrop(sp->fp);
659 	  sp->fp = NULL;
660      }
661      /* sofree(sp->soc); fp deals with socket termination */
662      sp->soc = NULL;
663 
664      sdebug(3, "done");
665 }
666 
667 void
668 isc_start_receiver(isc_session_t *sp)
669 {
670      debug_called(8);
671 
672      sp->flags |= ISC_CON_RUN | ISC_LINK_UP;
673      sp->flags |= ISC_CON_RUNNING;
674 
675      kthread_create(isc_soc, sp, &sp->soc_thr, "iscsi%d", sp->sid);
676 }
677