1 /* $NetBSD: kttcp.c,v 1.33 2014/05/18 14:46:15 rmind 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.33 2014/05/18 14:46:15 rmind 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_usrreqs->pr_generic)(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_usrreqs->pr_generic)(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_usrreqs->pr_generic)(so, PRU_RCVD, NULL, 636 (struct mbuf *)(long)flags, NULL, NULL); 637 } 638 SBLASTRECORDCHK(&so->so_rcv, 639 "kttcp_soreceive sbwait 2"); 640 SBLASTMBUFCHK(&so->so_rcv, 641 "kttcp_soreceive sbwait 2"); 642 error = sbwait(&so->so_rcv); 643 if (error) { 644 sbunlock(&so->so_rcv); 645 sounlock(so); 646 return (0); 647 } 648 if ((m = so->so_rcv.sb_mb) != NULL) 649 nextrecord = m->m_nextpkt; 650 } 651 } 652 653 if (m && pr->pr_flags & PR_ATOMIC) { 654 flags |= MSG_TRUNC; 655 if ((flags & MSG_PEEK) == 0) 656 (void) sbdroprecord(&so->so_rcv); 657 } 658 if ((flags & MSG_PEEK) == 0) { 659 if (m == NULL) { 660 /* 661 * First part is an SB_EMPTY_FIXUP(). Second part 662 * makes sure sb_lastrecord is up-to-date if 663 * there is still data in the socket buffer. 664 */ 665 so->so_rcv.sb_mb = nextrecord; 666 if (so->so_rcv.sb_mb == NULL) { 667 so->so_rcv.sb_mbtail = NULL; 668 so->so_rcv.sb_lastrecord = NULL; 669 } else if (nextrecord->m_nextpkt == NULL) 670 so->so_rcv.sb_lastrecord = nextrecord; 671 } 672 SBLASTRECORDCHK(&so->so_rcv, "kttcp_soreceive 4"); 673 SBLASTMBUFCHK(&so->so_rcv, "kttcp_soreceive 4"); 674 if (pr->pr_flags & PR_WANTRCVD && so->so_pcb) { 675 (*pr->pr_usrreqs->pr_generic)(so, PRU_RCVD, NULL, 676 (struct mbuf *)(long)flags, NULL, NULL); 677 } 678 } 679 if (orig_resid == resid && orig_resid && 680 (flags & MSG_EOR) == 0 && (so->so_state & SS_CANTRCVMORE) == 0) { 681 sbunlock(&so->so_rcv); 682 goto restart; 683 } 684 685 if (flagsp) 686 *flagsp |= flags; 687 release: 688 sbunlock(&so->so_rcv); 689 sounlock(so); 690 *done = slen - resid; 691 #if 0 692 printf("soreceive: error %d slen %llu resid %lld\n", error, slen, resid); 693 #endif 694 return (error); 695 } 696