1 /* $NetBSD: kttcp.c,v 1.38 2015/08/20 14:40:17 christos 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.38 2015/08/20 14:40:17 christos 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 #include "ioconf.h" 67 68 static int kttcp_send(struct lwp *l, struct kttcp_io_args *); 69 static int kttcp_recv(struct lwp *l, struct kttcp_io_args *); 70 static int kttcp_sosend(struct socket *, unsigned long long, 71 unsigned long long *, struct lwp *, int); 72 static int kttcp_soreceive(struct socket *, unsigned long long, 73 unsigned long long *, struct lwp *, 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_discard = nodiscard, 89 .d_flag = D_OTHER 90 }; 91 92 void 93 kttcpattach(int count) 94 { 95 /* Do nothing. */ 96 } 97 98 int 99 kttcpioctl(dev_t dev, u_long cmd, void *data, int flag, 100 struct lwp *l) 101 { 102 int error; 103 104 if ((flag & FWRITE) == 0) 105 return EPERM; 106 107 switch (cmd) { 108 case KTTCP_IO_SEND: 109 error = kttcp_send(l, (struct kttcp_io_args *) data); 110 break; 111 112 case KTTCP_IO_RECV: 113 error = kttcp_recv(l, (struct kttcp_io_args *) data); 114 break; 115 116 default: 117 return EINVAL; 118 } 119 120 return error; 121 } 122 123 static int 124 kttcp_send(struct lwp *l, struct kttcp_io_args *kio) 125 { 126 struct socket *so; 127 int error; 128 struct timeval t0, t1; 129 unsigned long long len, done; 130 131 if (kio->kio_totalsize >= KTTCP_MAX_XMIT) 132 return EINVAL; 133 134 if ((error = fd_getsock(kio->kio_socket, &so)) != 0) 135 return error; 136 137 len = kio->kio_totalsize; 138 microtime(&t0); 139 do { 140 error = kttcp_sosend(so, len, &done, l, 0); 141 len -= done; 142 } while (error == 0 && len > 0); 143 144 fd_putfile(kio->kio_socket); 145 146 microtime(&t1); 147 if (error != 0) 148 return error; 149 timersub(&t1, &t0, &kio->kio_elapsed); 150 151 kio->kio_bytesdone = kio->kio_totalsize - len; 152 153 return 0; 154 } 155 156 static int 157 kttcp_recv(struct lwp *l, struct kttcp_io_args *kio) 158 { 159 struct socket *so; 160 int error; 161 struct timeval t0, t1; 162 unsigned long long len, done; 163 164 done = 0; /* XXX gcc */ 165 166 if (kio->kio_totalsize > KTTCP_MAX_XMIT) 167 return EINVAL; 168 169 if ((error = fd_getsock(kio->kio_socket, &so)) != 0) 170 return error; 171 len = kio->kio_totalsize; 172 microtime(&t0); 173 do { 174 error = kttcp_soreceive(so, len, &done, l, NULL); 175 len -= done; 176 } while (error == 0 && len > 0 && done > 0); 177 178 fd_putfile(kio->kio_socket); 179 180 microtime(&t1); 181 if (error == EPIPE) 182 error = 0; 183 if (error != 0) 184 return error; 185 timersub(&t1, &t0, &kio->kio_elapsed); 186 187 kio->kio_bytesdone = kio->kio_totalsize - len; 188 189 return 0; 190 } 191 192 #define SBLOCKWAIT(f) (((f) & MSG_DONTWAIT) ? M_NOWAIT : M_WAITOK) 193 194 /* 195 * Slightly changed version of sosend() 196 */ 197 static int 198 kttcp_sosend(struct socket *so, unsigned long long slen, 199 unsigned long long *done, struct lwp *l, int flags) 200 { 201 struct mbuf **mp, *m, *top; 202 long space, len, mlen; 203 int error, dontroute, atomic; 204 long long resid; 205 206 atomic = sosendallatonce(so); 207 resid = slen; 208 top = NULL; 209 /* 210 * In theory resid should be unsigned. 211 * However, space must be signed, as it might be less than 0 212 * if we over-committed, and we must use a signed comparison 213 * of space and resid. On the other hand, a negative resid 214 * causes us to loop sending 0-length segments to the protocol. 215 */ 216 if (resid < 0) { 217 error = EINVAL; 218 goto out; 219 } 220 dontroute = 221 (flags & MSG_DONTROUTE) && (so->so_options & SO_DONTROUTE) == 0 && 222 (so->so_proto->pr_flags & PR_ATOMIC); 223 l->l_ru.ru_msgsnd++; 224 #define snderr(errno) { error = errno; goto release; } 225 solock(so); 226 restart: 227 if ((error = sblock(&so->so_snd, SBLOCKWAIT(flags))) != 0) 228 goto out; 229 do { 230 if (so->so_state & SS_CANTSENDMORE) 231 snderr(EPIPE); 232 if (so->so_error) { 233 error = so->so_error; 234 so->so_error = 0; 235 goto release; 236 } 237 if ((so->so_state & SS_ISCONNECTED) == 0) { 238 if (so->so_proto->pr_flags & PR_CONNREQUIRED) { 239 snderr(ENOTCONN); 240 } else { 241 snderr(EDESTADDRREQ); 242 } 243 } 244 space = sbspace(&so->so_snd); 245 if (flags & MSG_OOB) 246 space += 1024; 247 if ((atomic && resid > so->so_snd.sb_hiwat)) 248 snderr(EMSGSIZE); 249 if (space < resid && (atomic || space < so->so_snd.sb_lowat)) { 250 if (so->so_state & SS_NBIO) 251 snderr(EWOULDBLOCK); 252 SBLASTRECORDCHK(&so->so_rcv, 253 "kttcp_soreceive sbwait 1"); 254 SBLASTMBUFCHK(&so->so_rcv, 255 "kttcp_soreceive sbwait 1"); 256 sbunlock(&so->so_snd); 257 error = sbwait(&so->so_snd); 258 if (error) 259 goto out; 260 goto restart; 261 } 262 mp = ⊤ 263 do { 264 sounlock(so); 265 do { 266 if (top == 0) { 267 m = m_gethdr(M_WAIT, MT_DATA); 268 mlen = MHLEN; 269 m->m_pkthdr.len = 0; 270 m->m_pkthdr.rcvif = NULL; 271 } else { 272 m = m_get(M_WAIT, MT_DATA); 273 mlen = MLEN; 274 } 275 if (resid >= MINCLSIZE && space >= MCLBYTES) { 276 m_clget(m, M_WAIT); 277 if ((m->m_flags & M_EXT) == 0) 278 goto nopages; 279 mlen = MCLBYTES; 280 #ifdef MAPPED_MBUFS 281 len = lmin(MCLBYTES, resid); 282 #else 283 if (atomic && top == 0) { 284 len = lmin(MCLBYTES - max_hdr, 285 resid); 286 m->m_data += max_hdr; 287 } else 288 len = lmin(MCLBYTES, resid); 289 #endif 290 space -= len; 291 } else { 292 nopages: 293 len = lmin(lmin(mlen, resid), space); 294 space -= len; 295 /* 296 * For datagram protocols, leave room 297 * for protocol headers in first mbuf. 298 */ 299 if (atomic && top == 0 && len < mlen) 300 MH_ALIGN(m, len); 301 } 302 resid -= len; 303 m->m_len = len; 304 *mp = m; 305 top->m_pkthdr.len += len; 306 if (error) 307 goto release; 308 mp = &m->m_next; 309 if (resid <= 0) { 310 if (flags & MSG_EOR) 311 top->m_flags |= M_EOR; 312 break; 313 } 314 } while (space > 0 && atomic); 315 solock(so); 316 317 if (so->so_state & SS_CANTSENDMORE) 318 snderr(EPIPE); 319 if (dontroute) 320 so->so_options |= SO_DONTROUTE; 321 if (resid > 0) 322 so->so_state |= SS_MORETOCOME; 323 if (flags & MSG_OOB) 324 error = (*so->so_proto->pr_usrreqs->pr_sendoob)(so, 325 top, NULL); 326 else 327 error = (*so->so_proto->pr_usrreqs->pr_send)(so, 328 top, NULL, NULL, l); 329 if (dontroute) 330 so->so_options &= ~SO_DONTROUTE; 331 if (resid > 0) 332 so->so_state &= ~SS_MORETOCOME; 333 top = 0; 334 mp = ⊤ 335 if (error) 336 goto release; 337 } while (resid && space > 0); 338 } while (resid); 339 340 release: 341 sbunlock(&so->so_snd); 342 out: 343 sounlock(so); 344 if (top) 345 m_freem(top); 346 *done = slen - resid; 347 #if 0 348 printf("sosend: error %d slen %llu resid %lld\n", error, slen, resid); 349 #endif 350 return (error); 351 } 352 353 static int 354 kttcp_soreceive(struct socket *so, unsigned long long slen, 355 unsigned long long *done, struct lwp *l, int *flagsp) 356 { 357 struct mbuf *m, **mp; 358 int flags, len, error, offset, moff, type; 359 long long orig_resid, resid; 360 const struct protosw *pr; 361 struct mbuf *nextrecord; 362 363 pr = so->so_proto; 364 mp = NULL; 365 type = 0; 366 resid = orig_resid = slen; 367 if (flagsp) 368 flags = *flagsp &~ MSG_EOR; 369 else 370 flags = 0; 371 if (flags & MSG_OOB) { 372 m = m_get(M_WAIT, MT_DATA); 373 solock(so); 374 error = (*pr->pr_usrreqs->pr_recvoob)(so, m, flags & MSG_PEEK); 375 sounlock(so); 376 if (error) 377 goto bad; 378 do { 379 resid -= min(resid, m->m_len); 380 m = m_free(m); 381 } while (resid && error == 0 && m); 382 bad: 383 if (m) 384 m_freem(m); 385 return (error); 386 } 387 if (mp) 388 *mp = NULL; 389 solock(so); 390 restart: 391 if ((error = sblock(&so->so_rcv, SBLOCKWAIT(flags))) != 0) 392 return (error); 393 m = so->so_rcv.sb_mb; 394 /* 395 * If we have less data than requested, block awaiting more 396 * (subject to any timeout) if: 397 * 1. the current count is less than the low water mark, 398 * 2. MSG_WAITALL is set, and it is possible to do the entire 399 * receive operation at once if we block (resid <= hiwat), or 400 * 3. MSG_DONTWAIT is not set. 401 * If MSG_WAITALL is set but resid is larger than the receive buffer, 402 * we have to do the receive in sections, and thus risk returning 403 * a short count if a timeout or signal occurs after we start. 404 */ 405 if (m == NULL || (((flags & MSG_DONTWAIT) == 0 && 406 so->so_rcv.sb_cc < resid) && 407 (so->so_rcv.sb_cc < so->so_rcv.sb_lowat || 408 ((flags & MSG_WAITALL) && resid <= so->so_rcv.sb_hiwat)) && 409 m->m_nextpkt == NULL && (pr->pr_flags & PR_ATOMIC) == 0)) { 410 #ifdef DIAGNOSTIC 411 if (m == NULL && so->so_rcv.sb_cc) 412 panic("receive 1"); 413 #endif 414 if (so->so_error) { 415 if (m) 416 goto dontblock; 417 error = so->so_error; 418 if ((flags & MSG_PEEK) == 0) 419 so->so_error = 0; 420 goto release; 421 } 422 if (so->so_state & SS_CANTRCVMORE) { 423 if (m) 424 goto dontblock; 425 else 426 goto release; 427 } 428 for (; m; m = m->m_next) 429 if (m->m_type == MT_OOBDATA || (m->m_flags & M_EOR)) { 430 m = so->so_rcv.sb_mb; 431 goto dontblock; 432 } 433 if ((so->so_state & (SS_ISCONNECTED|SS_ISCONNECTING)) == 0 && 434 (so->so_proto->pr_flags & PR_CONNREQUIRED)) { 435 error = ENOTCONN; 436 goto release; 437 } 438 if (resid == 0) 439 goto release; 440 if ((so->so_state & SS_NBIO) || 441 (flags & (MSG_DONTWAIT|MSG_NBIO))) { 442 error = EWOULDBLOCK; 443 goto release; 444 } 445 sbunlock(&so->so_rcv); 446 error = sbwait(&so->so_rcv); 447 if (error) { 448 sounlock(so); 449 return (error); 450 } 451 goto restart; 452 } 453 dontblock: 454 /* 455 * On entry here, m points to the first record of the socket buffer. 456 * While we process the initial mbufs containing address and control 457 * info, we save a copy of m->m_nextpkt into nextrecord. 458 */ 459 #ifdef notyet /* XXXX */ 460 if (uio->uio_lwp) 461 uio->uio_lwp->l_ru.ru_msgrcv++; 462 #endif 463 KASSERT(m == so->so_rcv.sb_mb); 464 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 1"); 465 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 1"); 466 nextrecord = m->m_nextpkt; 467 if (pr->pr_flags & PR_ADDR) { 468 #ifdef DIAGNOSTIC 469 if (m->m_type != MT_SONAME) 470 panic("receive 1a"); 471 #endif 472 orig_resid = 0; 473 if (flags & MSG_PEEK) { 474 m = m->m_next; 475 } else { 476 sbfree(&so->so_rcv, m); 477 MFREE(m, so->so_rcv.sb_mb); 478 m = so->so_rcv.sb_mb; 479 } 480 } 481 while (m && m->m_type == MT_CONTROL && error == 0) { 482 if (flags & MSG_PEEK) { 483 m = m->m_next; 484 } else { 485 sbfree(&so->so_rcv, m); 486 MFREE(m, so->so_rcv.sb_mb); 487 m = so->so_rcv.sb_mb; 488 } 489 } 490 491 /* 492 * If m is non-NULL, we have some data to read. From now on, 493 * make sure to keep sb_lastrecord consistent when working on 494 * the last packet on the chain (nextrecord == NULL) and we 495 * change m->m_nextpkt. 496 */ 497 if (m) { 498 if ((flags & MSG_PEEK) == 0) { 499 m->m_nextpkt = nextrecord; 500 /* 501 * If nextrecord == NULL (this is a single chain), 502 * then sb_lastrecord may not be valid here if m 503 * was changed earlier. 504 */ 505 if (nextrecord == NULL) { 506 KASSERT(so->so_rcv.sb_mb == m); 507 so->so_rcv.sb_lastrecord = m; 508 } 509 } 510 type = m->m_type; 511 if (type == MT_OOBDATA) 512 flags |= MSG_OOB; 513 } else { 514 if ((flags & MSG_PEEK) == 0) { 515 KASSERT(so->so_rcv.sb_mb == m); 516 so->so_rcv.sb_mb = nextrecord; 517 SB_EMPTY_FIXUP(&so->so_rcv); 518 } 519 } 520 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 2"); 521 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 2"); 522 523 moff = 0; 524 offset = 0; 525 while (m && resid > 0 && error == 0) { 526 if (m->m_type == MT_OOBDATA) { 527 if (type != MT_OOBDATA) 528 break; 529 } else if (type == MT_OOBDATA) 530 break; 531 #ifdef DIAGNOSTIC 532 else if (m->m_type != MT_DATA && m->m_type != MT_HEADER) 533 panic("receive 3"); 534 #endif 535 so->so_state &= ~SS_RCVATMARK; 536 len = resid; 537 if (so->so_oobmark && len > so->so_oobmark - offset) 538 len = so->so_oobmark - offset; 539 if (len > m->m_len - moff) 540 len = m->m_len - moff; 541 /* 542 * If mp is set, just pass back the mbufs. 543 * Otherwise copy them out via the uio, then free. 544 * Sockbuf must be consistent here (points to current mbuf, 545 * it points to next record) when we drop priority; 546 * we must note any additions to the sockbuf when we 547 * block interrupts again. 548 */ 549 resid -= len; 550 if (len == m->m_len - moff) { 551 if (m->m_flags & M_EOR) 552 flags |= MSG_EOR; 553 if (flags & MSG_PEEK) { 554 m = m->m_next; 555 moff = 0; 556 } else { 557 nextrecord = m->m_nextpkt; 558 sbfree(&so->so_rcv, m); 559 if (mp) { 560 *mp = m; 561 mp = &m->m_next; 562 so->so_rcv.sb_mb = m = m->m_next; 563 *mp = NULL; 564 } else { 565 MFREE(m, so->so_rcv.sb_mb); 566 m = so->so_rcv.sb_mb; 567 } 568 /* 569 * If m != NULL, we also know that 570 * so->so_rcv.sb_mb != NULL. 571 */ 572 KASSERT(so->so_rcv.sb_mb == m); 573 if (m) { 574 m->m_nextpkt = nextrecord; 575 if (nextrecord == NULL) 576 so->so_rcv.sb_lastrecord = m; 577 } else { 578 so->so_rcv.sb_mb = nextrecord; 579 SB_EMPTY_FIXUP(&so->so_rcv); 580 } 581 SBLASTRECORDCHK(&so->so_rcv, 582 "kttcp_soreceive 3"); 583 SBLASTMBUFCHK(&so->so_rcv, 584 "kttcp_soreceive 3"); 585 } 586 } else { 587 if (flags & MSG_PEEK) 588 moff += len; 589 else { 590 if (mp) { 591 sounlock(so); 592 *mp = m_copym(m, 0, len, M_WAIT); 593 solock(so); 594 } 595 m->m_data += len; 596 m->m_len -= len; 597 so->so_rcv.sb_cc -= len; 598 } 599 } 600 if (so->so_oobmark) { 601 if ((flags & MSG_PEEK) == 0) { 602 so->so_oobmark -= len; 603 if (so->so_oobmark == 0) { 604 so->so_state |= SS_RCVATMARK; 605 break; 606 } 607 } else { 608 offset += len; 609 if (offset == so->so_oobmark) 610 break; 611 } 612 } 613 if (flags & MSG_EOR) 614 break; 615 /* 616 * If the MSG_WAITALL flag is set (for non-atomic socket), 617 * we must not quit until "uio->uio_resid == 0" or an error 618 * termination. If a signal/timeout occurs, return 619 * with a short count but without error. 620 * Keep sockbuf locked against other readers. 621 */ 622 while (flags & MSG_WAITALL && m == NULL && resid > 0 && 623 !sosendallatonce(so) && !nextrecord) { 624 if (so->so_error || so->so_state & SS_CANTRCVMORE) 625 break; 626 /* 627 * If we are peeking and the socket receive buffer is 628 * full, stop since we can't get more data to peek at. 629 */ 630 if ((flags & MSG_PEEK) && sbspace(&so->so_rcv) <= 0) 631 break; 632 /* 633 * If we've drained the socket buffer, tell the 634 * protocol in case it needs to do something to 635 * get it filled again. 636 */ 637 if ((pr->pr_flags & PR_WANTRCVD) && so->so_pcb) { 638 (*pr->pr_usrreqs->pr_rcvd)(so, flags, l); 639 } 640 SBLASTRECORDCHK(&so->so_rcv, 641 "kttcp_soreceive sbwait 2"); 642 SBLASTMBUFCHK(&so->so_rcv, 643 "kttcp_soreceive sbwait 2"); 644 error = sbwait(&so->so_rcv); 645 if (error) { 646 sbunlock(&so->so_rcv); 647 sounlock(so); 648 return (0); 649 } 650 if ((m = so->so_rcv.sb_mb) != NULL) 651 nextrecord = m->m_nextpkt; 652 } 653 } 654 655 if (m && pr->pr_flags & PR_ATOMIC) { 656 flags |= MSG_TRUNC; 657 if ((flags & MSG_PEEK) == 0) 658 (void) sbdroprecord(&so->so_rcv); 659 } 660 if ((flags & MSG_PEEK) == 0) { 661 if (m == NULL) { 662 /* 663 * First part is an SB_EMPTY_FIXUP(). Second part 664 * makes sure sb_lastrecord is up-to-date if 665 * there is still data in the socket buffer. 666 */ 667 so->so_rcv.sb_mb = nextrecord; 668 if (so->so_rcv.sb_mb == NULL) { 669 so->so_rcv.sb_mbtail = NULL; 670 so->so_rcv.sb_lastrecord = NULL; 671 } else if (nextrecord->m_nextpkt == NULL) 672 so->so_rcv.sb_lastrecord = nextrecord; 673 } 674 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 4"); 675 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 4"); 676 if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) { 677 (*pr->pr_usrreqs->pr_rcvd)(so, flags, l); 678 } 679 } 680 if (orig_resid == resid && orig_resid && 681 (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { 682 sbunlock(&so->so_rcv); 683 goto restart; 684 } 685 686 if (flagsp) 687 *flagsp |= flags; 688 release: 689 sbunlock(&so->so_rcv); 690 sounlock(so); 691 *done = slen - resid; 692 #if 0 693 printf("soreceive: error %d slen %llu resid %lld\n", error, slen, resid); 694 #endif 695 return (error); 696 } 697