Skip site navigation (1)Skip section navigation (2)
Date:      Wed, 15 Jan 2014 20:59:54 GMT
From:      Alan Somers <asomers@FreeBSD.org>
To:        freebsd-gnats-submit@FreeBSD.org
Subject:   kern/185813: SOCK_SEQPACKET AF_UNIX sockets with asymmetrical buffers drop packets
Message-ID:  <201401152059.s0FKxsS8059023@oldred.freebsd.org>
Resent-Message-ID: <201401152110.s0FLA1FZ071508@freefall.freebsd.org>

next in thread | raw e-mail | index | archive | help

>Number:         185813
>Category:       kern
>Synopsis:       SOCK_SEQPACKET AF_UNIX sockets with asymmetrical buffers drop packets
>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:01 UTC 2014
>Closed-Date:
>Last-Modified:
>Originator:     Alan Somers
>Release:        CURRENT
>Organization:
SpectraLogic
>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:
Unix domain sequential packet sockets can silently drop packets.

In fact, the problem is even worse than it appears.  Every time a packet is lost, the kernel leaks mbufs.  Eventually, the system will run out of mbufs, causing some processes to lock up.

The cause is in sbappendaddr_locked() in sys/kern/uipc_sockbuf.c:644.  That line checks whether the receiving sockbuf would overflow before appending a new mbuf.  If it would overflow, the function fails.  However, uipc_send() in sys/kern/uipc_usrreq.c:980 does not free the mbuf, nor does it return an error to the caller.  That causes the packet to disappear and the mbufs to leak.

The space check shouldn't fail because we've enlarged the send buffer.  However, there is an optimization for unix domain sockets that does not apply to AF_INET sockets: send() writes packets directly to the receiving buffer, skipping the sending buffer.  Despite that optimization, sosend_generic() in kern/uipc_socket.c:1230 uses the _sending_ sockbuf for its space calculation.  The mismatched calculations in sosend_generic() and sbappendaddr_locked() cause the bug.

The exact same optimization applies to SOCK_STREAM sockets, but they don't exhibit this bug.  Why not?  Because they don't use sbappendaddr_locked().  They go through a parallel code path: sbappend_locked().  That function does not do a space check at all.

>How-To-Repeat:
1) Create a pair of sequential packet unix domain sockets, either using socketpair(2), or by using socket(2), bind(2), listen(2) and accept(2).

2) Increase the sending buffer with setsockopt(2), but do not change the receiving buffer

3) Send one or more packets with size totalling 8kB or greater.

4) Read packets using receive.  All packets whose cumulative size was > 8kB will be lost.

The attached patch contains an ATF test program for SEQPACKET unix domain sockets.  This problem is most clearly reproduced with the testcase "unix_seqpacket:pipe_simulator_128k_8k", but most concisely reproduced with "unix_seqpacket:eagain_128k_8k"
>Fix:
The simple solution would be to simply delete the space check from sbappendaddr_locked().  I've tested it and it works.  However, sbappendaddr_locked() is called by many other functions than just uipc_send().

A better fix might be that setsockopt(s, SOL_SOCKET, SO_SNDBUF, &x, sizeof(x)) should set both the sending buffer size, _and_ the receiving buffer size of the companion socket.  Conversely, setsockopt(s, SOL_SOCKET, SO_RCVBUF, &x, sizeof(x)) would have to set both the receiving buffer size, _and_ the sending buffer size of the companion socket.  I haven't tested that solution yet.

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 <atf.test.mk>
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 <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+
+#include <stdio.h>
+
+#include <atf-c.h>
+
+/*
+ * 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 <bsd.own.mk>
+
+TESTSDIR=	${TESTSBASE}/sys
+
+.PATH:		${.CURDIR:H:H}/tests
+KYUAFILE=	yes
+
+.include <bsd.test.mk>
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:



Want to link to this message? Use this URL: <https://mail-archive.FreeBSD.org/cgi/mid.cgi?201401152059.s0FKxsS8059023>