kttcp.c revision 1.32 1 /* $NetBSD: kttcp.c,v 1.32 2014/03/16 05:20:26 dholland Exp $ */
2
3 /*
4 * Copyright (c) 2002 Wasabi Systems, Inc.
5 * All rights reserved.
6 *
7 * Written by Frank van der Linden and Jason R. Thorpe for
8 * Wasabi Systems, Inc.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 * must display the following acknowledgement:
20 * This product includes software developed for the NetBSD Project by
21 * Wasabi Systems, Inc.
22 * 4. The name of Wasabi Systems, Inc. may not be used to endorse
23 * or promote products derived from this software without specific prior
24 * written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY WASABI SYSTEMS, INC. ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL WASABI SYSTEMS, INC
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39 /*
40 * kttcp.c -- provides kernel support for testing network testing,
41 * see kttcp(4)
42 */
43
44 #include <sys/cdefs.h>
45 __KERNEL_RCSID(0, "$NetBSD: kttcp.c,v 1.32 2014/03/16 05:20:26 dholland Exp $");
46
47 #include <sys/param.h>
48 #include <sys/types.h>
49 #include <sys/ioctl.h>
50 #include <sys/file.h>
51 #include <sys/filedesc.h>
52 #include <sys/conf.h>
53 #include <sys/systm.h>
54 #include <sys/protosw.h>
55 #include <sys/proc.h>
56 #include <sys/resourcevar.h>
57 #include <sys/signal.h>
58 #include <sys/socketvar.h>
59 #include <sys/socket.h>
60 #include <sys/mbuf.h>
61 #include <sys/mount.h>
62 #include <sys/syscallargs.h>
63
64 #include <dev/kttcpio.h>
65
66 static int kttcp_send(struct lwp *l, struct kttcp_io_args *);
67 static int kttcp_recv(struct lwp *l, struct kttcp_io_args *);
68 static int kttcp_sosend(struct socket *, unsigned long long,
69 unsigned long long *, struct lwp *, int);
70 static int kttcp_soreceive(struct socket *, unsigned long long,
71 unsigned long long *, struct lwp *, int *);
72
73 void kttcpattach(int);
74
75 dev_type_ioctl(kttcpioctl);
76
77 const struct cdevsw kttcp_cdevsw = {
78 .d_open = nullopen,
79 .d_close = nullclose,
80 .d_read = noread,
81 .d_write = nowrite,
82 .d_ioctl = kttcpioctl,
83 .d_stop = nostop,
84 .d_tty = notty,
85 .d_poll = nopoll,
86 .d_mmap = nommap,
87 .d_kqfilter = nokqfilter,
88 .d_flag = D_OTHER
89 };
90
91 void
92 kttcpattach(int count)
93 {
94 /* Do nothing. */
95 }
96
97 int
98 kttcpioctl(dev_t dev, u_long cmd, void *data, int flag,
99 struct lwp *l)
100 {
101 int error;
102
103 if ((flag & FWRITE) == 0)
104 return EPERM;
105
106 switch (cmd) {
107 case KTTCP_IO_SEND:
108 error = kttcp_send(l, (struct kttcp_io_args *) data);
109 break;
110
111 case KTTCP_IO_RECV:
112 error = kttcp_recv(l, (struct kttcp_io_args *) data);
113 break;
114
115 default:
116 return EINVAL;
117 }
118
119 return error;
120 }
121
122 static int
123 kttcp_send(struct lwp *l, struct kttcp_io_args *kio)
124 {
125 struct socket *so;
126 int error;
127 struct timeval t0, t1;
128 unsigned long long len, done;
129
130 if (kio->kio_totalsize >= KTTCP_MAX_XMIT)
131 return EINVAL;
132
133 if ((error = fd_getsock(kio->kio_socket, &so)) != 0)
134 return error;
135
136 len = kio->kio_totalsize;
137 microtime(&t0);
138 do {
139 error = kttcp_sosend(so, len, &done, l, 0);
140 len -= done;
141 } while (error == 0 && len > 0);
142
143 fd_putfile(kio->kio_socket);
144
145 microtime(&t1);
146 if (error != 0)
147 return error;
148 timersub(&t1, &t0, &kio->kio_elapsed);
149
150 kio->kio_bytesdone = kio->kio_totalsize - len;
151
152 return 0;
153 }
154
155 static int
156 kttcp_recv(struct lwp *l, struct kttcp_io_args *kio)
157 {
158 struct socket *so;
159 int error;
160 struct timeval t0, t1;
161 unsigned long long len, done;
162
163 done = 0; /* XXX gcc */
164
165 if (kio->kio_totalsize > KTTCP_MAX_XMIT)
166 return EINVAL;
167
168 if ((error = fd_getsock(kio->kio_socket, &so)) != 0)
169 return error;
170 len = kio->kio_totalsize;
171 microtime(&t0);
172 do {
173 error = kttcp_soreceive(so, len, &done, l, NULL);
174 len -= done;
175 } while (error == 0 && len > 0 && done > 0);
176
177 fd_putfile(kio->kio_socket);
178
179 microtime(&t1);
180 if (error == EPIPE)
181 error = 0;
182 if (error != 0)
183 return error;
184 timersub(&t1, &t0, &kio->kio_elapsed);
185
186 kio->kio_bytesdone = kio->kio_totalsize - len;
187
188 return 0;
189 }
190
191 #define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK)
192
193 /*
194 * Slightly changed version of sosend()
195 */
196 static int
197 kttcp_sosend(struct socket *so, unsigned long long slen,
198 unsigned long long *done, struct lwp *l, int flags)
199 {
200 struct mbuf **mp, *m, *top;
201 long space, len, mlen;
202 int error, dontroute, atomic;
203 long long resid;
204
205 atomic = sosendallatonce(so);
206 resid = slen;
207 top = NULL;
208 /*
209 * In theory resid should be unsigned.
210 * However, space must be signed, as it might be less than 0
211 * if we over-committed, and we must use a signed comparison
212 * of space and resid. On the other hand, a negative resid
213 * causes us to loop sending 0-length segments to the protocol.
214 */
215 if (resid < 0) {
216 error = EINVAL;
217 goto out;
218 }
219 dontroute =
220 (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 &&
221 (so->so_proto->pr_flags & PR_ATOMIC);
222 l->l_ru.ru_msgsnd++;
223 #define snderr(errno) { error = errno; goto release; }
224 solock(so);
225 restart:
226 if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0)
227 goto out;
228 do {
229 if (so->so_state & SS_CANTSENDMORE)
230 snderr(EPIPE);
231 if (so->so_error) {
232 error = so->so_error;
233 so->so_error = 0;
234 goto release;
235 }
236 if ((so->so_state & SS_ISCONNECTED) == 0) {
237 if (so->so_proto->pr_flags & PR_CONNREQUIRED) {
238 snderr(ENOTCONN);
239 } else {
240 snderr(EDESTADDRREQ);
241 }
242 }
243 space = sbspace(&so->so_snd);
244 if (flags & MSG_OOB)
245 space += 1024;
246 if ((atomic && resid > so->so_snd.sb_hiwat))
247 snderr(EMSGSIZE);
248 if (space < resid && (atomic || space < so->so_snd.sb_lowat)) {
249 if (so->so_state & SS_NBIO)
250 snderr(EWOULDBLOCK);
251 SBLASTRECORDCHK(&so->so_rcv,
252 "kttcp_soreceive sbwait 1");
253 SBLASTMBUFCHK(&so->so_rcv,
254 "kttcp_soreceive sbwait 1");
255 sbunlock(&so->so_snd);
256 error = sbwait(&so->so_snd);
257 if (error)
258 goto out;
259 goto restart;
260 }
261 mp = ⊤
262 do {
263 sounlock(so);
264 do {
265 if (top == 0) {
266 m = m_gethdr(M_WAIT, MT_DATA);
267 mlen = MHLEN;
268 m->m_pkthdr.len = 0;
269 m->m_pkthdr.rcvif = NULL;
270 } else {
271 m = m_get(M_WAIT, MT_DATA);
272 mlen = MLEN;
273 }
274 if (resid >= MINCLSIZE && space >= MCLBYTES) {
275 m_clget(m, M_WAIT);
276 if ((m->m_flags & M_EXT) == 0)
277 goto nopages;
278 mlen = MCLBYTES;
279 #ifdef MAPPED_MBUFS
280 len = lmin(MCLBYTES, resid);
281 #else
282 if (atomic && top == 0) {
283 len = lmin(MCLBYTES - max_hdr,
284 resid);
285 m->m_data += max_hdr;
286 } else
287 len = lmin(MCLBYTES, resid);
288 #endif
289 space -= len;
290 } else {
291 nopages:
292 len = lmin(lmin(mlen, resid), space);
293 space -= len;
294 /*
295 * For datagram protocols, leave room
296 * for protocol headers in first mbuf.
297 */
298 if (atomic && top == 0 && len < mlen)
299 MH_ALIGN(m, len);
300 }
301 resid -= len;
302 m->m_len = len;
303 *mp = m;
304 top->m_pkthdr.len += len;
305 if (error)
306 goto release;
307 mp = &m->m_next;
308 if (resid <= 0) {
309 if (flags & MSG_EOR)
310 top->m_flags |= M_EOR;
311 break;
312 }
313 } while (space > 0 && atomic);
314 solock(so);
315
316 if (so->so_state & SS_CANTSENDMORE)
317 snderr(EPIPE);
318 if (dontroute)
319 so->so_options |= SO_DONTROUTE;
320 if (resid > 0)
321 so->so_state |= SS_MORETOCOME;
322 error = (*so->so_proto->pr_usrreq)(so,
323 (flags & MSG_OOB) ? PRU_SENDOOB : PRU_SEND,
324 top, NULL, NULL, l);
325 if (dontroute)
326 so->so_options &= ~SO_DONTROUTE;
327 if (resid > 0)
328 so->so_state &= ~SS_MORETOCOME;
329 top = 0;
330 mp = ⊤
331 if (error)
332 goto release;
333 } while (resid && space > 0);
334 } while (resid);
335
336 release:
337 sbunlock(&so->so_snd);
338 out:
339 sounlock(so);
340 if (top)
341 m_freem(top);
342 *done = slen - resid;
343 #if 0
344 printf("sosend: error %d slen %llu resid %lld\n", error, slen, resid);
345 #endif
346 return (error);
347 }
348
349 static int
350 kttcp_soreceive(struct socket *so, unsigned long long slen,
351 unsigned long long *done, struct lwp *l, int *flagsp)
352 {
353 struct mbuf *m, **mp;
354 int flags, len, error, offset, moff, type;
355 long long orig_resid, resid;
356 const struct protosw *pr;
357 struct mbuf *nextrecord;
358
359 pr = so->so_proto;
360 mp = NULL;
361 type = 0;
362 resid = orig_resid = slen;
363 if (flagsp)
364 flags = *flagsp &~ MSG_EOR;
365 else
366 flags = 0;
367 if (flags & MSG_OOB) {
368 m = m_get(M_WAIT, MT_DATA);
369 solock(so);
370 error = (*pr->pr_usrreq)(so, PRU_RCVOOB, m,
371 (struct mbuf *)(long)(flags & MSG_PEEK), NULL, NULL);
372 sounlock(so);
373 if (error)
374 goto bad;
375 do {
376 resid -= min(resid, m->m_len);
377 m = m_free(m);
378 } while (resid && error == 0 && m);
379 bad:
380 if (m)
381 m_freem(m);
382 return (error);
383 }
384 if (mp)
385 *mp = NULL;
386 solock(so);
387 restart:
388 if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0)
389 return (error);
390 m = so->so_rcv.sb_mb;
391 /*
392 * If we have less data than requested, block awaiting more
393 * (subject to any timeout) if:
394 * 1. the current count is less than the low water mark,
395 * 2. MSG_WAITALL is set, and it is possible to do the entire
396 * receive operation at once if we block (resid <= hiwat), or
397 * 3. MSG_DONTWAIT is not set.
398 * If MSG_WAITALL is set but resid is larger than the receive buffer,
399 * we have to do the receive in sections, and thus risk returning
400 * a short count if a timeout or signal occurs after we start.
401 */
402 if (m == NULL || (((flags & MSG_DONTWAIT) == 0 &&
403 so->so_rcv.sb_cc < resid) &&
404 (so->so_rcv.sb_cc < so->so_rcv.sb_lowat ||
405 ((flags & MSG_WAITALL) && resid <= so->so_rcv.sb_hiwat)) &&
406 m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) {
407 #ifdef DIAGNOSTIC
408 if (m == NULL && so->so_rcv.sb_cc)
409 panic("receive 1");
410 #endif
411 if (so->so_error) {
412 if (m)
413 goto dontblock;
414 error = so->so_error;
415 if ((flags & MSG_PEEK) == 0)
416 so->so_error = 0;
417 goto release;
418 }
419 if (so->so_state & SS_CANTRCVMORE) {
420 if (m)
421 goto dontblock;
422 else
423 goto release;
424 }
425 for (; m; m = m->m_next)
426 if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) {
427 m = so->so_rcv.sb_mb;
428 goto dontblock;
429 }
430 if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 &&
431 (so->so_proto->pr_flags & PR_CONNREQUIRED)) {
432 error = ENOTCONN;
433 goto release;
434 }
435 if (resid == 0)
436 goto release;
437 if ((so->so_state & SS_NBIO) ||
438 (flags & (MSG_DONTWAIT|MSG_NBIO))) {
439 error = EWOULDBLOCK;
440 goto release;
441 }
442 sbunlock(&so->so_rcv);
443 error = sbwait(&so->so_rcv);
444 if (error) {
445 sounlock(so);
446 return (error);
447 }
448 goto restart;
449 }
450 dontblock:
451 /*
452 * On entry here, m points to the first record of the socket buffer.
453 * While we process the initial mbufs containing address and control
454 * info, we save a copy of m->m_nextpkt into nextrecord.
455 */
456 #ifdef notyet /* XXXX */
457 if (uio->uio_lwp)
458 uio->uio_lwp->l_ru.ru_msgrcv++;
459 #endif
460 KASSERT(m == so->so_rcv.sb_mb);
461 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 1");
462 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 1");
463 nextrecord = m->m_nextpkt;
464 if (pr->pr_flags & PR_ADDR) {
465 #ifdef DIAGNOSTIC
466 if (m->m_type != MT_SONAME)
467 panic("receive 1a");
468 #endif
469 orig_resid = 0;
470 if (flags & MSG_PEEK) {
471 m = m->m_next;
472 } else {
473 sbfree(&so->so_rcv, m);
474 MFREE(m, so->so_rcv.sb_mb);
475 m = so->so_rcv.sb_mb;
476 }
477 }
478 while (m && m->m_type == MT_CONTROL && error == 0) {
479 if (flags & MSG_PEEK) {
480 m = m->m_next;
481 } else {
482 sbfree(&so->so_rcv, m);
483 MFREE(m, so->so_rcv.sb_mb);
484 m = so->so_rcv.sb_mb;
485 }
486 }
487
488 /*
489 * If m is non-NULL, we have some data to read. From now on,
490 * make sure to keep sb_lastrecord consistent when working on
491 * the last packet on the chain (nextrecord == NULL) and we
492 * change m->m_nextpkt.
493 */
494 if (m) {
495 if ((flags & MSG_PEEK) == 0) {
496 m->m_nextpkt = nextrecord;
497 /*
498 * If nextrecord == NULL (this is a single chain),
499 * then sb_lastrecord may not be valid here if m
500 * was changed earlier.
501 */
502 if (nextrecord == NULL) {
503 KASSERT(so->so_rcv.sb_mb == m);
504 so->so_rcv.sb_lastrecord = m;
505 }
506 }
507 type = m->m_type;
508 if (type == MT_OOBDATA)
509 flags |= MSG_OOB;
510 } else {
511 if ((flags & MSG_PEEK) == 0) {
512 KASSERT(so->so_rcv.sb_mb == m);
513 so->so_rcv.sb_mb = nextrecord;
514 SB_EMPTY_FIXUP(&so->so_rcv);
515 }
516 }
517 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 2");
518 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 2");
519
520 moff = 0;
521 offset = 0;
522 while (m && resid > 0 && error == 0) {
523 if (m->m_type == MT_OOBDATA) {
524 if (type != MT_OOBDATA)
525 break;
526 } else if (type == MT_OOBDATA)
527 break;
528 #ifdef DIAGNOSTIC
529 else if (m->m_type != MT_DATA && m->m_type != MT_HEADER)
530 panic("receive 3");
531 #endif
532 so->so_state &= ~SS_RCVATMARK;
533 len = resid;
534 if (so->so_oobmark && len > so->so_oobmark - offset)
535 len = so->so_oobmark - offset;
536 if (len > m->m_len - moff)
537 len = m->m_len - moff;
538 /*
539 * If mp is set, just pass back the mbufs.
540 * Otherwise copy them out via the uio, then free.
541 * Sockbuf must be consistent here (points to current mbuf,
542 * it points to next record) when we drop priority;
543 * we must note any additions to the sockbuf when we
544 * block interrupts again.
545 */
546 resid -= len;
547 if (len == m->m_len - moff) {
548 if (m->m_flags & M_EOR)
549 flags |= MSG_EOR;
550 if (flags & MSG_PEEK) {
551 m = m->m_next;
552 moff = 0;
553 } else {
554 nextrecord = m->m_nextpkt;
555 sbfree(&so->so_rcv, m);
556 if (mp) {
557 *mp = m;
558 mp = &m->m_next;
559 so->so_rcv.sb_mb = m = m->m_next;
560 *mp = NULL;
561 } else {
562 MFREE(m, so->so_rcv.sb_mb);
563 m = so->so_rcv.sb_mb;
564 }
565 /*
566 * If m != NULL, we also know that
567 * so->so_rcv.sb_mb != NULL.
568 */
569 KASSERT(so->so_rcv.sb_mb == m);
570 if (m) {
571 m->m_nextpkt = nextrecord;
572 if (nextrecord == NULL)
573 so->so_rcv.sb_lastrecord = m;
574 } else {
575 so->so_rcv.sb_mb = nextrecord;
576 SB_EMPTY_FIXUP(&so->so_rcv);
577 }
578 SBLASTRECORDCHK(&so->so_rcv,
579 "kttcp_soreceive 3");
580 SBLASTMBUFCHK(&so->so_rcv,
581 "kttcp_soreceive 3");
582 }
583 } else {
584 if (flags & MSG_PEEK)
585 moff += len;
586 else {
587 if (mp) {
588 sounlock(so);
589 *mp = m_copym(m, 0, len, M_WAIT);
590 solock(so);
591 }
592 m->m_data += len;
593 m->m_len -= len;
594 so->so_rcv.sb_cc -= len;
595 }
596 }
597 if (so->so_oobmark) {
598 if ((flags & MSG_PEEK) == 0) {
599 so->so_oobmark -= len;
600 if (so->so_oobmark == 0) {
601 so->so_state |= SS_RCVATMARK;
602 break;
603 }
604 } else {
605 offset += len;
606 if (offset == so->so_oobmark)
607 break;
608 }
609 }
610 if (flags & MSG_EOR)
611 break;
612 /*
613 * If the MSG_WAITALL flag is set (for non-atomic socket),
614 * we must not quit until "uio->uio_resid == 0" or an error
615 * termination. If a signal/timeout occurs, return
616 * with a short count but without error.
617 * Keep sockbuf locked against other readers.
618 */
619 while (flags & MSG_WAITALL && m == NULL && resid > 0 &&
620 !sosendallatonce(so) && !nextrecord) {
621 if (so->so_error || so->so_state & SS_CANTRCVMORE)
622 break;
623 /*
624 * If we are peeking and the socket receive buffer is
625 * full, stop since we can't get more data to peek at.
626 */
627 if ((flags & MSG_PEEK) && sbspace(&so->so_rcv) <= 0)
628 break;
629 /*
630 * If we've drained the socket buffer, tell the
631 * protocol in case it needs to do something to
632 * get it filled again.
633 */
634 if ((pr->pr_flags & PR_WANTRCVD) && so->so_pcb)
635 (*pr->pr_usrreq)(so, PRU_RCVD, NULL,
636 (struct mbuf *)(long)flags, NULL, NULL);
637 SBLASTRECORDCHK(&so->so_rcv,
638 "kttcp_soreceive sbwait 2");
639 SBLASTMBUFCHK(&so->so_rcv,
640 "kttcp_soreceive sbwait 2");
641 error = sbwait(&so->so_rcv);
642 if (error) {
643 sbunlock(&so->so_rcv);
644 sounlock(so);
645 return (0);
646 }
647 if ((m = so->so_rcv.sb_mb) != NULL)
648 nextrecord = m->m_nextpkt;
649 }
650 }
651
652 if (m && pr->pr_flags & PR_ATOMIC) {
653 flags |= MSG_TRUNC;
654 if ((flags & MSG_PEEK) == 0)
655 (void) sbdroprecord(&so->so_rcv);
656 }
657 if ((flags & MSG_PEEK) == 0) {
658 if (m == NULL) {
659 /*
660 * First part is an SB_EMPTY_FIXUP(). Second part
661 * makes sure sb_lastrecord is up-to-date if
662 * there is still data in the socket buffer.
663 */
664 so->so_rcv.sb_mb = nextrecord;
665 if (so->so_rcv.sb_mb == NULL) {
666 so->so_rcv.sb_mbtail = NULL;
667 so->so_rcv.sb_lastrecord = NULL;
668 } else if (nextrecord->m_nextpkt == NULL)
669 so->so_rcv.sb_lastrecord = nextrecord;
670 }
671 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 4");
672 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 4");
673 if (pr->pr_flags & PR_WANTRCVD && so->so_pcb)
674 (*pr->pr_usrreq)(so, PRU_RCVD, NULL,
675 (struct mbuf *)(long)flags, NULL, NULL);
676 }
677 if (orig_resid == resid && orig_resid &&
678 (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) {
679 sbunlock(&so->so_rcv);
680 goto restart;
681 }
682
683 if (flagsp)
684 *flagsp |= flags;
685 release:
686 sbunlock(&so->so_rcv);
687 sounlock(so);
688 *done = slen - resid;
689 #if 0
690 printf("soreceive: error %d slen %llu resid %lld\n", error, slen, resid);
691 #endif
692 return (error);
693 }
694