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