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