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