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