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