From owner-freebsd-bugs@FreeBSD.ORG Wed Jan 15 21:10:00 2014 Return-Path: Delivered-To: freebsd-bugs@smarthost.ysv.freebsd.org Received: from mx1.freebsd.org (mx1.freebsd.org [8.8.178.115]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id 82045215 for ; Wed, 15 Jan 2014 21:10:00 +0000 (UTC) Received: from freefall.freebsd.org (freefall.freebsd.org [IPv6:2001:1900:2254:206c::16:87]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 5A66215CE for ; Wed, 15 Jan 2014 21:10:00 +0000 (UTC) Received: from freefall.freebsd.org (localhost [127.0.0.1]) by freefall.freebsd.org (8.14.7/8.14.7) with ESMTP id s0FLA0VF071435 for ; Wed, 15 Jan 2014 21:10:00 GMT (envelope-from gnats@freefall.freebsd.org) Received: (from gnats@localhost) by freefall.freebsd.org (8.14.7/8.14.7/Submit) id s0FLA0q9071434; Wed, 15 Jan 2014 21:10:00 GMT (envelope-from gnats) Resent-Date: Wed, 15 Jan 2014 21:10:00 GMT Resent-Message-Id: <201401152110.s0FLA0q9071434@freefall.freebsd.org> Resent-From: FreeBSD-gnats-submit@FreeBSD.org (GNATS Filer) Resent-To: freebsd-bugs@FreeBSD.org Resent-Reply-To: FreeBSD-gnats-submit@FreeBSD.org, Alan Somers Received: from mx1.freebsd.org (mx1.freebsd.org [IPv6:2001:1900:2254:206a::19:1]) (using TLSv1 with cipher ADH-AES256-SHA (256/256 bits)) (No client certificate requested) by hub.freebsd.org (Postfix) with ESMTPS id A9F16F11 for ; Wed, 15 Jan 2014 21:05:08 +0000 (UTC) Received: from oldred.freebsd.org (oldred.freebsd.org [IPv6:2001:1900:2254:206a::50:4]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.freebsd.org (Postfix) with ESMTPS id 89FB7159A for ; Wed, 15 Jan 2014 21:05:08 +0000 (UTC) Received: from oldred.freebsd.org ([127.0.1.6]) by oldred.freebsd.org (8.14.5/8.14.7) with ESMTP id s0FL584X081513 for ; Wed, 15 Jan 2014 21:05:08 GMT (envelope-from nobody@oldred.freebsd.org) Received: (from nobody@localhost) by oldred.freebsd.org (8.14.5/8.14.5/Submit) id s0FL58Qd081508; Wed, 15 Jan 2014 21:05:08 GMT (envelope-from nobody) Message-Id: <201401152105.s0FL58Qd081508@oldred.freebsd.org> Date: Wed, 15 Jan 2014 21:05:08 GMT From: Alan Somers To: freebsd-gnats-submit@FreeBSD.org X-Send-Pr-Version: www-3.1 Subject: kern/185812: send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN X-BeenThere: freebsd-bugs@freebsd.org X-Mailman-Version: 2.1.17 Precedence: list List-Id: Bug reports List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Wed, 15 Jan 2014 21:10:00 -0000 >Number: 185812 >Category: kern >Synopsis: send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN >Confidential: no >Severity: non-critical >Priority: low >Responsible: freebsd-bugs >State: open >Quarter: >Keywords: >Date-Required: >Class: sw-bug >Submitter-Id: current-users >Arrival-Date: Wed Jan 15 21:10:00 UTC 2014 >Closed-Date: >Last-Modified: >Originator: Alan Somers >Release: CURRENT >Organization: Spectra Logic >Environment: FreeBSD alans-fbsd-head 11.0-CURRENT FreeBSD 11.0-CURRENT #22 r260496: Thu Jan 9 16:39:42 MST 2014 alans@ns1.eng.sldomain.com:/vmpool/obj/usr/home/alans/freebsd/head/sys/GENERIC amd64 >Description: If you overflow the buffer of a nonblocking unix domain seqpacket socket, send(2) should return EAGAIN. Instead, it returns EMSGSIZE. >How-To-Repeat: The bug is reproduced by several testcases in the attached ATF test program. Most consise is unix_seqpacket:eagain_8k_8k >Fix: None known. But a clue is offered by this comment at sys/kern/uipc_usrreq.c:987 /* * XXXRW: While fine for SOCK_STREAM, this conflates maximum * datagram size and back-pressure for SOCK_SEQPACKET, which * can lead to undesired return of EMSGSIZE on send instead * of more desirable blocking. */ Patch attached with submission follows: Index: sys/kern/tests/Makefile =================================================================== --- sys/kern/tests/Makefile (revision 0) +++ sys/kern/tests/Makefile (working copy) @@ -0,0 +1,7 @@ +# $FreeBSD$ + +TESTSDIR= ${TESTSBASE}/sys/kern + +ATF_TESTS_C= unix_seqpacket + +.include Index: sys/kern/tests/unix_seqpacket.c =================================================================== --- sys/kern/tests/unix_seqpacket.c (revision 0) +++ sys/kern/tests/unix_seqpacket.c (working copy) @@ -0,0 +1,649 @@ +/*- + * Copyright (c) 2014 Spectra Logic Corporation. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +__FBSDID("$FreeBSD$"); + +#include +#include +#include +#include + +#include + +#include + +/* + * Helper functions + */ + +#define MIN(x, y) ((x) < (y) ? (x) : (y)) +#define MAX(x, y) ((x) > (y) ? (x) : (y)) + +void +do_socketpair(int *sv) +{ + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_REQUIRE_EQ(0, s); + ATF_REQUIRE(sv[0] >= 0); + ATF_REQUIRE(sv[1] >= 0); + ATF_REQUIRE(sv[0] != sv[1]); +} + +void +do_socketpair_nonblocking(int *sv) +{ + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_REQUIRE_EQ(0, s); + ATF_REQUIRE(sv[0] >= 0); + ATF_REQUIRE(sv[1] >= 0); + ATF_REQUIRE(sv[0] != sv[1]); + ATF_REQUIRE(-1 != fcntl(sv[0], F_SETFL, O_NONBLOCK)); + ATF_REQUIRE(-1 != fcntl(sv[1], F_SETFL, O_NONBLOCK)); +} + +/* + * Parameterized test function bodies + */ +void +test_eagain(size_t sndbufsize, size_t rcvbufsize) +{ + int i; + int sv[2]; + const size_t totalsize = (sndbufsize + rcvbufsize) * 2; + const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + bzero(sndbuf, pktsize); + /* Send data until we get EAGAIN */ + for(i=0; i < totalsize / pktsize; i++) { + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize == -1) { + if (errno == EAGAIN) + atf_tc_pass(); + else { + perror("send"); + atf_tc_fail("send returned < 0 but not EAGAIN"); + } + } + } + atf_tc_fail("Never got EAGAIN"); +} + +void +test_nonblocking_symmetric_buffers(size_t bufsize) { + int s; + int sv[2]; + const size_t pktsize = bufsize / 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + /* Setup the buffers */ + s = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &bufsize, sizeof(bufsize)); + ATF_REQUIRE_EQ(0, s); + s = setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &bufsize, sizeof(bufsize)); + ATF_REQUIRE_EQ(0, s); + + /* Fill the send buffer */ + bzero(sndbuf, pktsize); + + /* send and receive the packet */ + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, ssize, "expected %zd=send(...) but got %zd", + pktsize, ssize); + + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + if (rsize < 0) { + perror("recv"); + atf_tc_fail("recv returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, rsize, "expected %zd=send(...) but got %zd", + pktsize, rsize); +} + +void +test_pipe_simulator(size_t sndbufsize, size_t rcvbufsize) +{ + int s, num_sent, num_received; + int sv[2]; + const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4; + int numpkts; + char sndbuf[pktsize]; + char rcvbuf[pktsize]; + char comparebuf[pktsize]; + ssize_t ssize, rsize; + bool currently_sending = true; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + /* Send a total amount of data comfortably greater than the buffers */ + numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize; + for (num_sent=0, num_received=0; + num_sent < numpkts || num_received < numpkts; ) { + if (currently_sending && num_sent < numpkts) { + /* The simulated sending process */ + /* fill the buffer */ + memset(sndbuf, num_sent, pktsize); + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + /* + * XXX: This is bug-compatible with the kernel. + * The kernel returns EMSGSIZE when it should + * return EAGAIN + */ + if (errno == EAGAIN || errno == EMSGSIZE) + currently_sending = false; + else { + perror("send"); + atf_tc_fail("send failed"); + } + } else { + ATF_CHECK_EQ_MSG(pktsize, ssize, + "expected %zd=send(...) but got %zd", + pktsize, ssize); + num_sent++; + } + } else { + /* The simulated receiving process */ + rsize = recv(sv[1], rcvbuf, pktsize, MSG_WAITALL); + if (rsize < 0) { + if (errno == EAGAIN) { + currently_sending = true; + ATF_REQUIRE_MSG(num_sent < numpkts, + "Packets were lost!"); + } + else { + perror("recv"); + atf_tc_fail("recv failed"); + } + } else { + ATF_CHECK_EQ_MSG(pktsize, rsize, + "expected %zd=recv(...) but got %zd", + pktsize, rsize); + memset(comparebuf, num_received, pktsize); + ATF_CHECK_EQ_MSG(0, memcmp(comparebuf, rcvbuf, + pktsize), + "Received data miscompare"); + num_received++; + } + } + } +} + + +/* + * Test Cases + */ + +/* Create a SEQPACKET socket */ +ATF_TC_WITHOUT_HEAD(create_socket); +ATF_TC_BODY(create_socket, tc) +{ + int s; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_CHECK(s >= 0); +} + +/* Create SEQPACKET sockets using socketpair(2) */ +ATF_TC_WITHOUT_HEAD(create_socketpair); +ATF_TC_BODY(create_socketpair, tc) +{ + int sv[2]; + int s; + + s = socketpair(PF_LOCAL, SOCK_SEQPACKET, 0, sv); + ATF_CHECK_EQ(0, s); + ATF_CHECK(sv[0] >= 0); + ATF_CHECK(sv[1] >= 0); + ATF_CHECK(sv[0] != sv[1]); +} + +/* Call listen(2) without first calling bind(2). It should fail */ +ATF_TC_WITHOUT_HEAD(listen_unbound); +ATF_TC_BODY(listen_unbound, tc) +{ + int s, r; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s > 0); + r = listen(s, -1); + /* expect listen to fail since we haven't called bind(2) */ + ATF_CHECK(r != 0); +} + +/* Bind the socket to a file */ +ATF_TC_WITHOUT_HEAD(bind); +ATF_TC_BODY(bind, tc) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, r; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + r = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + ATF_CHECK_EQ(0, r); +} + +/* listen(2) a socket that is already bound(2) should succeed */ +ATF_TC_WITHOUT_HEAD(listen_bound); +ATF_TC_BODY(listen_bound, tc) +{ + struct sockaddr_un sun; + /* ATF's isolation mechanisms will guarantee uniqueness of this file */ + const char *path = "sock"; + int s, r, l; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + + bzero(&sun, sizeof(sun)); + sun.sun_family = AF_LOCAL; + sun.sun_len = sizeof(sun); + strlcpy(sun.sun_path, path, sizeof(sun.sun_path)); + r = bind(s, (struct sockaddr *)&sun, sizeof(sun)); + l = listen(s, -1); + ATF_CHECK_EQ(0, r); + ATF_CHECK_EQ(0, l); +} + +/* Set O_NONBLOCK on the socket */ +ATF_TC_WITHOUT_HEAD(fcntl_nonblock); +ATF_TC_BODY(fcntl_nonblock, tc) +{ + int s; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + if (fcntl(s, F_SETFL, O_NONBLOCK) == -1) { + perror("fcntl"); + atf_tc_fail("fcntl failed"); + } +} + +/* Resize the send and receive buffers */ +ATF_TC_WITHOUT_HEAD(resize_buffers); +ATF_TC_BODY(resize_buffers, tc) +{ + int s; + int sndbuf = 8192; + int rcvbuf = 8192; + + s = socket(PF_LOCAL, SOCK_SEQPACKET, 0); + ATF_REQUIRE(s >= 0); + if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_SNDBUF) failed"); + } + if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &sndbuf, sizeof(sndbuf)) != 0){ + perror("setsockopt"); + atf_tc_fail("setsockopt(SO_RCVBUF) failed"); + } +} + +/* send(2) and recv(2) a single short record */ +ATF_TC_WITHOUT_HEAD(send_recv); +ATF_TC_BODY(send_recv, tc) +{ + int s; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = send(sv[0], data, datalen, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(datalen, rsize); +} + +/* nonblocking send(2) and recv(2) a single short record */ +ATF_TC_WITHOUT_HEAD(send_recv_nonblocking); +ATF_TC_BODY(send_recv_nonblocking, tc) +{ + int s; + int sv[2]; + const int bufsize = 64; + const char *data = "data"; + char recv_buf[bufsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + + /* Verify that there is nothing to receive */ + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(EAGAIN, errno); + ATF_CHECK_EQ(-1, rsize); + + /* send and receive a small packet */ + datalen = strlen(data) + 1; /* +1 for the null */ + ssize = send(sv[0], data, datalen, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(datalen, ssize, "expected %zd=send(...) but got %zd", + datalen, ssize); + + rsize = recv(sv[1], recv_buf, bufsize, MSG_WAITALL); + ATF_CHECK_EQ(datalen, rsize); +} + +/* + * We should get EMSGSIZE if we try to send a message larger than the socket + * buffer, with blocking sockets + */ +ATF_TC_WITHOUT_HEAD(emsgsize); +ATF_TC_BODY(emsgsize, tc) +{ + int s; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 8192; + const size_t pktsize = (sndbufsize + rcvbufsize) * 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + ATF_CHECK_EQ(EMSGSIZE, errno); + ATF_CHECK_EQ(-1, ssize); +} + +/* + * We should get EMSGSIZE if we try to send a message larger than the socket + * buffer, with nonblocking sockets + */ +ATF_TC_WITHOUT_HEAD(emsgsize_nonblocking); +ATF_TC_BODY(emsgsize_nonblocking, tc) +{ + int s; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 8192; + const size_t pktsize = (sndbufsize + rcvbufsize) * 2; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + /* Setup the buffers */ + ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize, + sizeof(sndbufsize))); + ATF_REQUIRE_EQ(0, setsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rcvbufsize, + sizeof(rcvbufsize))); + + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + ATF_CHECK_EQ(EMSGSIZE, errno); + ATF_CHECK_EQ(-1, ssize); +} + + +/* + * We should get EAGAIN if we try to send a message larger than the socket + * buffer, with nonblocking sockets. Test with several different sockbuf sizes + */ +ATF_TC_WITHOUT_HEAD(eagain_8k_8k); +ATF_TC_BODY(eagain_8k_8k, tc) +{ + test_eagain(8192, 8192); +} +ATF_TC_WITHOUT_HEAD(eagain_8k_128k); +ATF_TC_BODY(eagain_8k_128k, tc) +{ + test_eagain(8192, 131072); +} +ATF_TC_WITHOUT_HEAD(eagain_128k_8k); +ATF_TC_BODY(eagain_128k_8k, tc) +{ + test_eagain(131072, 8192); +} +ATF_TC_WITHOUT_HEAD(eagain_128k_128k); +ATF_TC_BODY(eagain_128k_128k, tc) +{ + test_eagain(131072, 131072); +} + + +/* + * nonblocking send(2) and recv(2) of several records, which should collectively + * fill up the send buffer but not the receive buffer + */ +ATF_TC_WITHOUT_HEAD(rcvbuf_oversized); +ATF_TC_BODY(rcvbuf_oversized, tc) +{ + int s, i, j; + int sv[2]; + const size_t sndbufsize = 8192; + const size_t rcvbufsize = 131072; + const size_t geom_mean_bufsize = 32768; + const int pktsize = 1024; + char sndbuf[pktsize]; + char recv_buf[pktsize]; + size_t datalen; + ssize_t ssize, rsize; + + /* setup the socket pair */ + do_socketpair_nonblocking(sv); + + /* + * Send and receive packets that are collectively greater than the send + * buffer, but less than the receive buffer + */ + for (i=0; i < geom_mean_bufsize / pktsize; i++) { + /* Fill the buffer */ + memset(sndbuf, i, pktsize); + + /* send the packet */ + ssize = send(sv[0], sndbuf, pktsize, MSG_EOR); + if (ssize < 0) { + perror("send"); + atf_tc_fail("send returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, ssize, + "expected %zd=send(...) but got %zd", pktsize, ssize); + + /* Receive it */ + + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + if (rsize < 0) { + perror("recv"); + atf_tc_fail("recv returned < 0"); + } + ATF_CHECK_EQ_MSG(pktsize, rsize, + "expected %zd=send(...) but got %zd", pktsize, rsize); + + /* Verify the contents */ + ATF_CHECK_EQ_MSG(0, memcmp(sndbuf, recv_buf, pktsize), + "Received data miscompare"); + } + + /* Trying to receive again should return EAGAIN */ + rsize = recv(sv[1], recv_buf, pktsize, MSG_WAITALL); + ATF_CHECK_EQ(EAGAIN, errno); + ATF_CHECK_EQ(-1, rsize); +} + +/* + * Simulate the behavior of a blocking pipe. The sender will send until his + * buffer fills up, then we'll simulate a scheduler switch that will allow the + * receiver to read until his buffer empties. Repeat the process until the + * transfer is complete. + * Repeat the test with multiple send and receive buffer sizes + */ +ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_8k); +ATF_TC_BODY(pipe_simulator_8k_8k, tc) +{ + test_pipe_simulator(8192, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_8k_128k); +ATF_TC_BODY(pipe_simulator_8k_128k, tc) +{ + test_pipe_simulator(8192, 131072); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_8k); +ATF_TC_BODY(pipe_simulator_128k_8k, tc) +{ + test_pipe_simulator(131072, 8192); +} + +ATF_TC_WITHOUT_HEAD(pipe_simulator_128k_128k); +ATF_TC_BODY(pipe_simulator_128k_128k, tc) +{ + test_pipe_simulator(131072, 131072); +} + +/* Test nonblocking I/O with 8K buffers */ +ATF_TC_WITHOUT_HEAD(sendrecv_8k_nonblocking); +ATF_TC_BODY(sendrecv_8k_nonblocking, tc) +{ + test_nonblocking_symmetric_buffers(8 * 1024); +} + +/* Test nonblocking I/O with 16K buffers */ +ATF_TC_WITHOUT_HEAD(sendrecv_16k_nonblocking); +ATF_TC_BODY(sendrecv_16k_nonblocking, tc) +{ + test_nonblocking_symmetric_buffers(16 * 1024); +} + +/* Test nonblocking I/O with 32K buffers */ +ATF_TC_WITHOUT_HEAD(sendrecv_32k_nonblocking); +ATF_TC_BODY(sendrecv_32k_nonblocking, tc) +{ + test_nonblocking_symmetric_buffers(32 * 1024); +} + +/* Test nonblocking I/O with 64K buffers */ +ATF_TC_WITHOUT_HEAD(sendrecv_64k_nonblocking); +ATF_TC_BODY(sendrecv_64k_nonblocking, tc) +{ + test_nonblocking_symmetric_buffers(64 * 1024); +} + +/* Test nonblocking I/O with 128K buffers */ +ATF_TC_WITHOUT_HEAD(sendrecv_128k_nonblocking); +ATF_TC_BODY(sendrecv_128k_nonblocking, tc) +{ + test_nonblocking_symmetric_buffers(128 * 1024); +} + + +/* + * Main. + */ + +ATF_TP_ADD_TCS(tp) +{ + /* Basic creation and connection tests */ + ATF_TP_ADD_TC(tp, create_socket); + ATF_TP_ADD_TC(tp, create_socketpair); + ATF_TP_ADD_TC(tp, listen_unbound); + ATF_TP_ADD_TC(tp, bind); + ATF_TP_ADD_TC(tp, listen_bound); + ATF_TP_ADD_TC(tp, fcntl_nonblock); + ATF_TP_ADD_TC(tp, resize_buffers); + + /* Unthreaded I/O tests */ + ATF_TP_ADD_TC(tp, send_recv); + ATF_TP_ADD_TC(tp, send_recv_nonblocking); + ATF_TP_ADD_TC(tp, emsgsize); + ATF_TP_ADD_TC(tp, emsgsize_nonblocking); + ATF_TP_ADD_TC(tp, eagain_8k_8k); + ATF_TP_ADD_TC(tp, eagain_8k_128k); + ATF_TP_ADD_TC(tp, eagain_128k_8k); + ATF_TP_ADD_TC(tp, eagain_128k_128k); + ATF_TP_ADD_TC(tp, sendrecv_8k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_16k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_32k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_64k_nonblocking); + ATF_TP_ADD_TC(tp, sendrecv_128k_nonblocking); + ATF_TP_ADD_TC(tp, rcvbuf_oversized); + ATF_TP_ADD_TC(tp, pipe_simulator_8k_8k); + ATF_TP_ADD_TC(tp, pipe_simulator_8k_128k); + ATF_TP_ADD_TC(tp, pipe_simulator_128k_8k); + ATF_TP_ADD_TC(tp, pipe_simulator_128k_128k); + + return atf_no_error(); +} Index: sys/tests/Makefile =================================================================== --- sys/tests/Makefile (revision 0) +++ sys/tests/Makefile (working copy) @@ -0,0 +1,10 @@ +# $FreeBSD$ + +.include + +TESTSDIR= ${TESTSBASE}/sys + +.PATH: ${.CURDIR:H:H}/tests +KYUAFILE= yes + +.include Index: etc/mtree/BSD.tests.dist =================================================================== --- etc/mtree/BSD.tests.dist (revision 260696) +++ etc/mtree/BSD.tests.dist (working copy) @@ -78,6 +78,10 @@ .. .. .. + sys + kern + .. + .. usr.bin atf atf-sh >Release-Note: >Audit-Trail: >Unformatted: