From nobody@FreeBSD.org  Wed Jan 15 21:05:08 2014
Return-Path: <nobody@FreeBSD.org>
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 <freebsd-gnats-submit@FreeBSD.org>; 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 <freebsd-gnats-submit@FreeBSD.org>; 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 <freebsd-gnats-submit@FreeBSD.org>; 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 <asomers@FreeBSD.org>
To: freebsd-gnats-submit@FreeBSD.org
Subject: send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN
X-Send-Pr-Version: www-3.1
X-GNATS-Notify:

>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:    asomers
>State:          closed
>Quarter:        
>Keywords:       
>Date-Required:  
>Class:          sw-bug
>Submitter-Id:   current-users
>Arrival-Date:   Wed Jan 15 21:10:00 UTC 2014
>Closed-Date:    Mon Apr 21 16:36:06 UTC 2014
>Last-Modified:  Mon Apr 21 16:36:06 UTC 2014
>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 <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:

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/185812: commit references a PR
Date: Thu, 23 Jan 2014 17:26:41 +0000 (UTC)

 Author: asomers
 Date: Thu Jan 23 17:26:28 2014
 New Revision: 261081
 URL: http://svnweb.freebsd.org/changeset/base/261081
 
 Log:
   Replace the old unix_seqpacket and unix_seqpacket_exercise tests, which
   were a little broken and not automatable, with unix_seqpacket_test.
   It's coverage is a superset of the old tests and it uses ATF.  It
   includes test cases for bugs kern/185813 and kern/185812.
   
   PR:		kern/185812
   PR:		kern/185813
   Sponsored by:	Spectra Logic
   MFC after:	2 weeks
 
 Added:
   head/tests/sys/
   head/tests/sys/Makefile   (contents, props changed)
   head/tests/sys/kern/
   head/tests/sys/kern/Makefile   (contents, props changed)
   head/tests/sys/kern/unix_seqpacket_test.c   (contents, props changed)
 Deleted:
   head/tools/regression/sockets/unix_seqpacket/
   head/tools/regression/sockets/unix_seqpacket_exercise/
 Modified:
   head/Makefile.inc1
   head/etc/mtree/BSD.tests.dist
 
 Modified: head/Makefile.inc1
 ==============================================================================
 --- head/Makefile.inc1	Thu Jan 23 17:24:26 2014	(r261080)
 +++ head/Makefile.inc1	Thu Jan 23 17:26:28 2014	(r261081)
 @@ -417,7 +417,7 @@ LIB32WMAKEFLAGS+=	\
  		-DNO_LINT
  
  LIB32WMAKE=	${LIB32WMAKEENV} ${MAKE} ${LIB32WMAKEFLAGS} \
 -		-DWITHOUT_MAN -DWITHOUT_INFO -DWITHOUT_HTML
 +		-DWITHOUT_MAN -DWITHOUT_INFO -DWITHOUT_HTML -DNO_TESTS
  LIB32IMAKE=	${LIB32WMAKE:NINSTALL=*:NDESTDIR=*:N_LDSCRIPTROOT=*} -DNO_INCS \
  		${IMAKE_INSTALL}
  .endif
 
 Modified: head/etc/mtree/BSD.tests.dist
 ==============================================================================
 --- head/etc/mtree/BSD.tests.dist	Thu Jan 23 17:24:26 2014	(r261080)
 +++ head/etc/mtree/BSD.tests.dist	Thu Jan 23 17:26:28 2014	(r261081)
 @@ -78,6 +78,10 @@
                  ..
              ..
          ..
 +        sys
 +            kern
 +            ..
 +        ..
          usr.bin
              atf
                  atf-sh
 
 Added: head/tests/sys/Makefile
 ==============================================================================
 --- /dev/null	00:00:00 1970	(empty, because file is newly added)
 +++ head/tests/sys/Makefile	Thu Jan 23 17:26:28 2014	(r261081)
 @@ -0,0 +1,13 @@
 +# $FreeBSD$
 +
 +.include <bsd.own.mk>
 +
 +TESTSDIR= ${TESTSBASE}/sys
 +
 +KYUAFILE= yes
 +
 +CLEANFILES+= Kyuafile
 +Kyuafile: ${.CURDIR}/../Kyuafile
 +	cp -f ${.CURDIR}/../Kyuafile .
 +
 +.include <bsd.test.mk>
 
 Added: head/tests/sys/kern/Makefile
 ==============================================================================
 --- /dev/null	00:00:00 1970	(empty, because file is newly added)
 +++ head/tests/sys/kern/Makefile	Thu Jan 23 17:26:28 2014	(r261081)
 @@ -0,0 +1,10 @@
 +# $FreeBSD$
 +
 +TESTSDIR=	${TESTSBASE}/sys/kern
 +
 +ATF_TESTS_C=	unix_seqpacket_test
 +TEST_METADATA.unix_seqpacket_test+=	timeout="15"
 +
 +LDADD+=		-lpthread
 +
 +.include <atf.test.mk>
 
 Added: head/tests/sys/kern/unix_seqpacket_test.c
 ==============================================================================
 --- /dev/null	00:00:00 1970	(empty, because file is newly added)
 +++ head/tests/sys/kern/unix_seqpacket_test.c	Thu Jan 23 17:26:28 2014	(r261081)
 @@ -0,0 +1,1117 @@
 +/*-
 + * 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 <pthread.h>
 +#include <signal.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));
 +}
 +
 +/* 
 + * Returns a pair of sockets made the hard way: bind, listen, connect & accept
 + * @return	const char* The path to the socket
 + */
 +const char*
 +mk_pair_of_sockets(int *sv)
 +{
 +	struct sockaddr_un sun;
 +	/* ATF's isolation mechanisms will guarantee uniqueness of this file */
 +	const char *path = "sock";
 +	int s, err, s2, s1;
 +
 +	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));
 +	err = bind(s, (struct sockaddr *)&sun, sizeof(sun));
 +	err = listen(s, -1);
 +	ATF_CHECK_EQ(0, err);
 +	ATF_CHECK_EQ(0, err);
 +
 +	/* Create the other socket */
 +	s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
 +	ATF_REQUIRE(s2 >= 0);
 +	err = connect(s2, (struct sockaddr*)&sun, sizeof(sun));
 +	if (err != 0) {
 +		perror("connect");
 +		atf_tc_fail("connect(2) failed");
 +	}
 +	
 +	/* Accept it */
 +	s1 = accept(s, NULL, NULL);
 +	if (s1 == -1) {
 +		perror("accept");
 +		atf_tc_fail("accept(2) failed");
 +	}
 +
 +	sv[0] = s1;
 +	sv[1] = s2;
 +	return (path);
 +}
 +
 +static volatile sig_atomic_t got_sigpipe = 0;
 +static void
 +shutdown_send_sigpipe_handler(int x)
 +{
 +	got_sigpipe = 1;
 +}
 +
 +/*
 + * 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_sendrecv_symmetric_buffers(size_t bufsize, int blocking) {
 +	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 */
 +	if (blocking)
 +		do_socketpair(sv);
 +	else
 +		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++;
 +			}
 +		}
 +	}
 +}
 +
 +typedef struct {
 +	ssize_t	pktsize;
 +	int	numpkts;
 +	int	so;
 +} test_pipe_thread_data_t;
 +
 +static void*
 +test_pipe_writer(void* args)
 +{
 +	test_pipe_thread_data_t* td = args;
 +	char sndbuf[td->pktsize];
 +	ssize_t ssize;
 +	int i;
 +
 +	for(i=0; i < td->numpkts; i++) {
 +			memset(sndbuf, i, td->pktsize);
 +			ssize = send(td->so, sndbuf, td->pktsize, MSG_EOR);
 +			if (ssize < 0) {
 +				perror("send");
 +				atf_tc_fail("send returned < 0");
 +			}
 +			ATF_CHECK_EQ_MSG(td->pktsize, ssize,
 +			    		 "expected %zd=send(...) but got %zd",
 +			    		  td->pktsize, ssize);
 +	}
 +	return (0);
 +}
 +
 +static void*
 +test_pipe_reader(void* args)
 +{
 +	test_pipe_thread_data_t* td = args;
 +	char rcvbuf[td->pktsize];
 +	char comparebuf[td->pktsize];
 +	ssize_t rsize;
 +	int i, d;
 +
 +	for(i=0; i < td->numpkts; i++) {
 +		memset(comparebuf, i, td->pktsize);
 +		rsize = recv(td->so, rcvbuf, td->pktsize, MSG_WAITALL);
 +		if (rsize < 0) {
 +			perror("recv");
 +			atf_tc_fail("recv returned < 0");
 +		}
 +		ATF_CHECK_EQ_MSG(td->pktsize, rsize,
 +		    		 "expected %zd=send(...) but got %zd",
 +				 td->pktsize, rsize);
 +		d = memcmp(comparebuf, rcvbuf, td->pktsize);
 +		ATF_CHECK_EQ_MSG(0, d, 
 +		    		 "Received data miscompare on packet %d", i);
 +	}
 +	return (0);
 +}
 +
 +
 +void
 +test_pipe(size_t sndbufsize, size_t rcvbufsize)
 +{
 +	test_pipe_thread_data_t writer_data, reader_data;
 +	pthread_t writer, reader;
 +	int num_sent, num_received;
 +	int sv[2];
 +	const size_t pktsize = MIN(sndbufsize, rcvbufsize) / 4;
 +	int numpkts;
 +
 +	/* 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)));
 +
 +	/* Send a total amount of data comfortably greater than the buffers */
 +	numpkts = MAX(sndbufsize, rcvbufsize) * 8 / pktsize;
 +
 +	/* Start the child threads */
 +	writer_data.pktsize = pktsize;
 +	writer_data.numpkts = numpkts;
 +	writer_data.so = sv[0];
 +	reader_data.pktsize = pktsize;
 +	reader_data.numpkts = numpkts;
 +	reader_data.so = sv[1];
 +	ATF_REQUIRE_EQ(0, pthread_create(&writer, NULL, test_pipe_writer,
 +	    				 (void*)&writer_data));
 +	ATF_REQUIRE_EQ(0, pthread_create(&reader, NULL, test_pipe_reader,
 +	    				 (void*)&reader_data));
 +
 +	/* Join the children */
 +	ATF_REQUIRE_EQ(0, pthread_join(writer, NULL));
 +	ATF_REQUIRE_EQ(0, pthread_join(reader, NULL));
 +}
 +
 +
 +/*
 + * 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);
 +}
 +
 +/* connect(2) can make a connection */
 +ATF_TC_WITHOUT_HEAD(connect);
 +ATF_TC_BODY(connect, tc)
 +{
 +	struct sockaddr_un sun;
 +	/* ATF's isolation mechanisms will guarantee uniqueness of this file */
 +	const char *path = "sock";
 +	int s, r, err, l, s2;
 +
 +	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);
 +
 +	/* Create the other socket */
 +	s2 = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
 +	ATF_REQUIRE(s2 >= 0);
 +	err = connect(s2, (struct sockaddr*)&sun, sizeof(sun));
 +	if (err != 0) {
 +		perror("connect");
 +		atf_tc_fail("connect(2) failed");
 +	}
 +}
 +
 +/* accept(2) can receive a connection */
 +ATF_TC_WITHOUT_HEAD(accept);
 +ATF_TC_BODY(accept, tc)
 +{
 +	int sv[2];
 +
 +	mk_pair_of_sockets(sv);
 +}
 +
 +
 +/* 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 = 12345;
 +	int rcvbuf = 23456;
 +	int xs, xr;
 +	socklen_t sl = sizeof(xs);
 +
 +	s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
 +	ATF_REQUIRE(s >= 0);
 +
 +	printf("                       Socket Buffer Sizes\n");
 +	printf("                              | SNDBUF  | RCVBUF  |\n");
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
 +	printf("Default                       | %7d | %7d |\n", xs, xr);
 +
 +	if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf)) != 0){
 +		perror("setsockopt");
 +		atf_tc_fail("setsockopt(SO_SNDBUF) failed");
 +	}
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
 +	printf("After changing SNDBUF         | %7d | %7d |\n", xs, xr);
 +	
 +	if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf)) != 0){
 +		perror("setsockopt");
 +		atf_tc_fail("setsockopt(SO_RCVBUF) failed");
 +	}
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_SNDBUF, &xs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(s, SOL_SOCKET, SO_RCVBUF, &xr, &sl));
 +	printf("After changing RCVBUF         | %7d | %7d |\n", xs, xr);
 +}
 +
 +/*
 + * Resize the send and receive buffers of a connected socketpair
 + * Print some useful debugging info too
 + */
 +ATF_TC_WITHOUT_HEAD(resize_connected_buffers);
 +ATF_TC_BODY(resize_connected_buffers, tc)
 +{
 +	int sv[2];
 +	int sndbuf = 12345;
 +	int rcvbuf = 23456;
 +	int err;
 +	int ls, lr, rs, rr;
 +	socklen_t sl = sizeof(ls);
 +
 +	/* setup the socket pair */
 +	do_socketpair(sv);
 +
 +	printf("                       Socket Buffer Sizes\n");
 +	printf("                              | Left Socket       | Right Socket      |\n");
 +	printf("                              | SNDBUF  | RCVBUF  | SNDBUF  | RCVBUF  |\n");
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
 +	printf("Default                       | %7d | %7d | %7d | %7d |\n",
 +	    ls, lr, rs, rr);
 +
 +	/* Update one side's send buffer */
 +	err = setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbuf, sizeof(sndbuf));
 +	if (err != 0){
 +		perror("setsockopt");
 +		atf_tc_fail("setsockopt(SO_SNDBUF) failed");
 +	}
 +
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
 +	printf("After changing Left's SNDBUF  | %7d | %7d | %7d | %7d |\n",
 +	    ls, lr, rs, rr);
 +
 +	/* Update the same side's receive buffer */
 +	err = setsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &rcvbuf, sizeof(rcvbuf));
 +	if (err != 0){
 +		perror("setsockopt");
 +		atf_tc_fail("setsockopt(SO_RCVBUF) failed");
 +	}
 +
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &ls, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[0], SOL_SOCKET, SO_RCVBUF, &lr, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_SNDBUF, &rs, &sl));
 +	ATF_CHECK_EQ(0, getsockopt(sv[1], SOL_SOCKET, SO_RCVBUF, &rr, &sl));
 +	printf("After changing Left's RCVBUF  | %7d | %7d | %7d | %7d |\n",
 +	    ls, lr, rs, rr);
 +}
 +
 +
 +/* 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);
 +}
 +
 +/* sendto(2) and recvfrom(2) a single short record
 + * According to The Open Group Base Specifications Issue 6 IEEE Std 1003.1, 2004
 + * Edition, sendto(2) is exactly the same as send(2) on a connection-mode socket
 + *
 + * According to the same spec, not all protocols are required to provide the
 + * source addres in recvfrom(2).
 + */
 +ATF_TC_WITHOUT_HEAD(sendto_recvfrom);
 +ATF_TC_BODY(sendto_recvfrom, tc)
 +{
 +	const char* path;
 +	struct sockaddr_storage from;
 +	int s;
 +	int sv[2];
 +	const int bufsize = 64;
 +	const char *data = "data";
 +	char recv_buf[bufsize];
 +	size_t datalen;
 +	ssize_t ssize, rsize;
 +	socklen_t fromlen;
 +
 +	/* setup the socket pair */
 +	path = mk_pair_of_sockets(sv);
 +
 +	/* send and receive a small packet */
 +	datalen = strlen(data) + 1;	/* +1 for the null */
 +	ssize = sendto(sv[0], data, datalen, MSG_EOR, NULL, 0);
 +	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);
 +
 +	fromlen = sizeof(from);
 +	rsize = recvfrom(sv[1], recv_buf, bufsize, MSG_WAITALL,
 +	    (struct sockaddr*)&from, &fromlen);
 +	if (ssize < 0) {
 +		perror("recvfrom");
 +		atf_tc_fail("recvfrom returned < 0");
 +	}
 +	ATF_CHECK_EQ(datalen, rsize);
 +
 +	/* 
 +	 * FreeBSD does not currently provide the source address for SEQ_PACKET
 +	 * AF_UNIX sockets, and POSIX does not require it, so these two checks
 +	 * are disabled.  If FreeBSD gains that feature in the future, then
 +	 * these checks may be reenabled
 +	 */
 +	/* ATF_CHECK_EQ(PF_LOCAL, from.ss_family); */
 +	/* ATF_CHECK_STREQ(path, ((struct sockaddr_un*)&from)->sun_path); */
 +}
 +
 +/* 
 + * send(2) and recv(2) a single short record with sockets created the
 + * traditional way, involving bind, listen, connect, and accept
 + */
 +ATF_TC_WITHOUT_HEAD(send_recv_with_connect);
 +ATF_TC_BODY(send_recv_with_connect, tc)
 +{
 +	const char* path;
 +	int sv[2];
 +	const int bufsize = 64;
 +	const char *data = "data";
 +	char recv_buf[bufsize];
 +	size_t datalen;
 +	ssize_t ssize, rsize;
 +
 +	mk_pair_of_sockets(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);
 +}
 +
 +/* send(2) should fail on a shutdown socket */
 +ATF_TC_WITHOUT_HEAD(shutdown_send);
 +ATF_TC_BODY(shutdown_send, tc)
 +{
 +	int s;
 +	const char *data = "data";
 +	ssize_t ssize;
 +
 +	s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
 +	ATF_CHECK(s >= 0);
 +	ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR));
 +	/* USE MSG_NOSIGNAL so we don't get SIGPIPE */
 +	ssize = send(s, data, sizeof(data), MSG_EOR | MSG_NOSIGNAL);
 +	ATF_CHECK_EQ(EPIPE, errno);
 +	ATF_CHECK_EQ(-1, ssize);
 +}
 +
 +/* send(2) should cause SIGPIPE on a shutdown socket */
 +ATF_TC_WITHOUT_HEAD(shutdown_send_sigpipe);
 +ATF_TC_BODY(shutdown_send_sigpipe, tc)
 +{
 +	int s;
 +	const char *data = "data";
 +	ssize_t ssize;
 +
 +	s = socket(PF_LOCAL, SOCK_SEQPACKET, 0);
 +	ATF_CHECK(s >= 0);
 +	ATF_CHECK_EQ(0, shutdown(s, SHUT_RDWR));
 +	ATF_REQUIRE(SIG_ERR != signal(SIGPIPE, shutdown_send_sigpipe_handler));
 +	ssize = send(s, data, sizeof(data), MSG_EOR);
 +	ATF_CHECK_EQ(1, got_sigpipe);
 +}
 +
 +/* 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)
 +{
 +	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
 +	test_eagain(8192, 8192);
 +}
 +ATF_TC_WITHOUT_HEAD(eagain_8k_128k);
 +ATF_TC_BODY(eagain_8k_128k, tc)
 +{
 +	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
 +	test_eagain(8192, 131072);
 +}
 +ATF_TC_WITHOUT_HEAD(eagain_128k_8k);
 +ATF_TC_BODY(eagain_128k_8k, tc)
 +{
 +	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
 +	test_eagain(131072, 8192);
 +}
 +ATF_TC_WITHOUT_HEAD(eagain_128k_128k);
 +ATF_TC_BODY(eagain_128k_128k, tc)
 +{
 +	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
 +	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);
 +}
 +
 
 *** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/185812: commit references a PR
Date: Mon, 17 Feb 2014 22:07:00 +0000 (UTC)

 Author: asomers
 Date: Mon Feb 17 22:06:52 2014
 New Revision: 262133
 URL: http://svnweb.freebsd.org/changeset/base/262133
 
 Log:
   test_eagain_*_* should've been using nonblocking sockets instead of
   blocking sockets.  The error was not exposed as long as the kernel
   suffered from PR kern/185812.  Now corrected, these tests pass on
   DragonFlyBSD 3.6.0.
   
   PR:		kern/185812
   Sponsored by:	Spectra Logic Corporation
   MFC after:	2 weeks
 
 Modified:
   head/tests/sys/kern/unix_seqpacket_test.c
 
 Modified: head/tests/sys/kern/unix_seqpacket_test.c
 ==============================================================================
 --- head/tests/sys/kern/unix_seqpacket_test.c	Mon Feb 17 20:45:39 2014	(r262132)
 +++ head/tests/sys/kern/unix_seqpacket_test.c	Mon Feb 17 22:06:52 2014	(r262133)
 @@ -136,7 +136,7 @@ test_eagain(size_t sndbufsize, size_t rc
  	ssize_t ssize, rsize;
  
  	/* setup the socket pair */
 -	do_socketpair(sv);
 +	do_socketpair_nonblocking(sv);
  	/* Setup the buffers */
  	ATF_REQUIRE_EQ(0, setsockopt(sv[0], SOL_SOCKET, SO_SNDBUF, &sndbufsize,
  	    sizeof(sndbufsize)));
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
Responsible-Changed-From-To: freebsd-bugs->asomers 
Responsible-Changed-By: asomers 
Responsible-Changed-When: Thu Feb 20 16:12:20 UTC 2014 
Responsible-Changed-Why:  
Testing patch now 

http://www.freebsd.org/cgi/query-pr.cgi?pr=185812 

From: dfilter@FreeBSD.ORG (dfilter service)
To: bug-followup@FreeBSD.org
Cc:  
Subject: Re: kern/185812: commit references a PR
Date: Thu, 13 Mar 2014 18:42:22 +0000 (UTC)

 Author: asomers
 Date: Thu Mar 13 18:42:12 2014
 New Revision: 263116
 URL: http://svnweb.freebsd.org/changeset/base/263116
 
 Log:
   Replace 4.4BSD Lite's unix domain socket backpressure hack with a cleaner
   mechanism, based on the new SB_STOP sockbuf flag.  The old hack dynamically
   changed the sending sockbuf's high water mark whenever adding or removing
   data from the receiving sockbuf.  It worked for stream sockets, but it never
   worked for SOCK_SEQPACKET sockets because of their atomic nature.  If the
   sockbuf was partially full, it might return EMSGSIZE instead of blocking.
   
   The new solution is based on DragonFlyBSD's fix from commit
   3a6117bbe0ed6a87605c1e43e12a1438d8844380 on 2008-05-27.  It adds an SB_STOP
   flag to sockbufs.  Whenever uipc_send surpasses the socket's size limit, it
   sets SB_STOP on the sending sockbuf.  sbspace() will then return 0 for that
   sockbuf, causing sosend_generic and friends to block.  uipc_rcvd will
   likewise clear SB_STOP.  There are two fringe benefits: uipc_{send,rcvd} no
   longer need to call chgsbsize() on every send and receive because they don't
   change the sockbuf's high water mark.  Also, uipc_sense no longer needs to
   acquire the UIPC linkage lock, because it's simpler to compute the
   st_blksizes.
   
   There is one drawback: since sbspace() will only ever return 0 or the
   maximum, sosend_generic will allow the sockbuf to exceed its nominal maximum
   size by at most one packet of size less than the max.  I don't think that's
   a serious problem.  In fact, I'm not even positive that FreeBSD guarantees a
   socket will always stay within its nominal size limit.
   
   sys/sys/sockbuf.h
   	Add the SB_STOP flag and adjust sbspace()
   
   sys/sys/unpcb.h
   	Delete the obsolete unp_cc and unp_mbcnt fields from struct unpcb.
   
   sys/kern/uipc_usrreq.c
   	Adjust uipc_rcvd, uipc_send, and uipc_sense to use the SB_STOP
   	backpressure mechanism.  Removing obsolete unpcb fields from
   	db_show_unpcb.
   
   tests/sys/kern/unix_seqpacket_test.c
   	Clear expected failures from ATF.
   
   Obtained from:	DragonFly BSD
   PR:		kern/185812
   Reviewed by:	silence from freebsd-net@ and rwatson@
   MFC after:	3 weeks
   Sponsored by:	Spectra Logic Corporation
 
 Modified:
   head/sys/kern/uipc_usrreq.c
   head/sys/sys/sockbuf.h
   head/sys/sys/unpcb.h
   head/tests/sys/kern/unix_seqpacket_test.c
 
 Modified: head/sys/kern/uipc_usrreq.c
 ==============================================================================
 --- head/sys/kern/uipc_usrreq.c	Thu Mar 13 18:17:18 2014	(r263115)
 +++ head/sys/kern/uipc_usrreq.c	Thu Mar 13 18:42:12 2014	(r263116)
 @@ -51,7 +51,6 @@
   *
   * TODO:
   *	RDM
 - *	distinguish datagram size limits from flow control limits in SEQPACKET
   *	rethink name space problems
   *	need a proper out-of-band
   */
 @@ -789,7 +788,6 @@ uipc_rcvd(struct socket *so, int flags)
  	struct unpcb *unp, *unp2;
  	struct socket *so2;
  	u_int mbcnt, sbcc;
 -	u_long newhiwat;
  
  	unp = sotounpcb(so);
  	KASSERT(unp != NULL, ("uipc_rcvd: unp == NULL"));
 @@ -811,6 +809,15 @@ uipc_rcvd(struct socket *so, int flags)
  	mbcnt = so->so_rcv.sb_mbcnt;
  	sbcc = so->so_rcv.sb_cc;
  	SOCKBUF_UNLOCK(&so->so_rcv);
 +	/*
 +	 * There is a benign race condition at this point.  If we're planning to
 +	 * clear SB_STOP, but uipc_send is called on the connected socket at
 +	 * this instant, it might add data to the sockbuf and set SB_STOP.  Then
 +	 * we would erroneously clear SB_STOP below, even though the sockbuf is
 +	 * full.  The race is benign because the only ill effect is to allow the
 +	 * sockbuf to exceed its size limit, and the size limits are not
 +	 * strictly guaranteed anyway.
 +	 */
  	UNP_PCB_LOCK(unp);
  	unp2 = unp->unp_conn;
  	if (unp2 == NULL) {
 @@ -819,13 +826,9 @@ uipc_rcvd(struct socket *so, int flags)
  	}
  	so2 = unp2->unp_socket;
  	SOCKBUF_LOCK(&so2->so_snd);
 -	so2->so_snd.sb_mbmax += unp->unp_mbcnt - mbcnt;
 -	newhiwat = so2->so_snd.sb_hiwat + unp->unp_cc - sbcc;
 -	(void)chgsbsize(so2->so_cred->cr_uidinfo, &so2->so_snd.sb_hiwat,
 -	    newhiwat, RLIM_INFINITY);
 +	if (sbcc < so2->so_snd.sb_hiwat && mbcnt < so2->so_snd.sb_mbmax)
 +		so2->so_snd.sb_flags &= ~SB_STOP;
  	sowwakeup_locked(so2);
 -	unp->unp_mbcnt = mbcnt;
 -	unp->unp_cc = sbcc;
  	UNP_PCB_UNLOCK(unp);
  	return (0);
  }
 @@ -836,8 +839,7 @@ uipc_send(struct socket *so, int flags, 
  {
  	struct unpcb *unp, *unp2;
  	struct socket *so2;
 -	u_int mbcnt_delta, sbcc;
 -	u_int newhiwat;
 +	u_int mbcnt, sbcc;
  	int error = 0;
  
  	unp = sotounpcb(so);
 @@ -991,27 +993,21 @@ uipc_send(struct socket *so, int flags, 
  			}
  		}
  
 -		/*
 -		 * 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.
 -		 */
 -		mbcnt_delta = so2->so_rcv.sb_mbcnt - unp2->unp_mbcnt;
 -		unp2->unp_mbcnt = so2->so_rcv.sb_mbcnt;
 +		mbcnt = so2->so_rcv.sb_mbcnt;
  		sbcc = so2->so_rcv.sb_cc;
  		sorwakeup_locked(so2);
  
 +		/*
 +		 * The PCB lock on unp2 protects the SB_STOP flag.  Without it,
 +		 * it would be possible for uipc_rcvd to be called at this
 +		 * point, drain the receiving sockbuf, clear SB_STOP, and then
 +		 * we would set SB_STOP below.  That could lead to an empty
 +		 * sockbuf having SB_STOP set
 +		 */
  		SOCKBUF_LOCK(&so->so_snd);
 -		if ((int)so->so_snd.sb_hiwat >= (int)(sbcc - unp2->unp_cc))
 -			newhiwat = so->so_snd.sb_hiwat - (sbcc - unp2->unp_cc);
 -		else
 -			newhiwat = 0;
 -		(void)chgsbsize(so->so_cred->cr_uidinfo, &so->so_snd.sb_hiwat,
 -		    newhiwat, RLIM_INFINITY);
 -		so->so_snd.sb_mbmax -= mbcnt_delta;
 +		if (sbcc >= so->so_snd.sb_hiwat || mbcnt >= so->so_snd.sb_mbmax)
 +			so->so_snd.sb_flags |= SB_STOP;
  		SOCKBUF_UNLOCK(&so->so_snd);
 -		unp2->unp_cc = sbcc;
  		UNP_PCB_UNLOCK(unp2);
  		m = NULL;
  		break;
 @@ -1049,27 +1045,18 @@ release:
  static int
  uipc_sense(struct socket *so, struct stat *sb)
  {
 -	struct unpcb *unp, *unp2;
 -	struct socket *so2;
 +	struct unpcb *unp;
  
  	unp = sotounpcb(so);
  	KASSERT(unp != NULL, ("uipc_sense: unp == NULL"));
  
  	sb->st_blksize = so->so_snd.sb_hiwat;
 -	UNP_LINK_RLOCK();
  	UNP_PCB_LOCK(unp);
 -	unp2 = unp->unp_conn;
 -	if ((so->so_type == SOCK_STREAM || so->so_type == SOCK_SEQPACKET) &&
 -	    unp2 != NULL) {
 -		so2 = unp2->unp_socket;
 -		sb->st_blksize += so2->so_rcv.sb_cc;
 -	}
  	sb->st_dev = NODEV;
  	if (unp->unp_ino == 0)
  		unp->unp_ino = (++unp_ino == 0) ? ++unp_ino : unp_ino;
  	sb->st_ino = unp->unp_ino;
  	UNP_PCB_UNLOCK(unp);
 -	UNP_LINK_RUNLOCK();
  	return (0);
  }
  
 @@ -2497,8 +2484,7 @@ DB_SHOW_COMMAND(unpcb, db_show_unpcb)
  	/* XXXRW: Would be nice to print the full address, if any. */
  	db_printf("unp_addr: %p\n", unp->unp_addr);
  
 -	db_printf("unp_cc: %d   unp_mbcnt: %d   unp_gencnt: %llu\n",
 -	    unp->unp_cc, unp->unp_mbcnt,
 +	db_printf("unp_gencnt: %llu\n",
  	    (unsigned long long)unp->unp_gencnt);
  
  	db_printf("unp_flags: %x (", unp->unp_flags);
 
 Modified: head/sys/sys/sockbuf.h
 ==============================================================================
 --- head/sys/sys/sockbuf.h	Thu Mar 13 18:17:18 2014	(r263115)
 +++ head/sys/sys/sockbuf.h	Thu Mar 13 18:42:12 2014	(r263116)
 @@ -52,6 +52,7 @@
  #define	SB_NOCOALESCE	0x200		/* don't coalesce new data into existing mbufs */
  #define	SB_IN_TOE	0x400		/* socket buffer is in the middle of an operation */
  #define	SB_AUTOSIZE	0x800		/* automatically size socket buffer */
 +#define	SB_STOP		0x1000		/* backpressure indicator */
  
  #define	SBS_CANTSENDMORE	0x0010	/* can't send more data to peer */
  #define	SBS_CANTRCVMORE		0x0020	/* can't receive more data from peer */
 @@ -168,9 +169,19 @@ void	sbunlock(struct sockbuf *sb);
   * still be negative (cc > hiwat or mbcnt > mbmax).  Should detect
   * overflow and return 0.  Should use "lmin" but it doesn't exist now.
   */
 -#define	sbspace(sb) \
 -    ((long) imin((int)((sb)->sb_hiwat - (sb)->sb_cc), \
 -	 (int)((sb)->sb_mbmax - (sb)->sb_mbcnt)))
 +static __inline
 +long
 +sbspace(struct sockbuf *sb)
 +{
 +	long bleft;
 +	long mleft;
 +
 +	if (sb->sb_flags & SB_STOP)
 +		return(0);
 +	bleft = sb->sb_hiwat - sb->sb_cc;
 +	mleft = sb->sb_mbmax - sb->sb_mbcnt;
 +	return((bleft < mleft) ? bleft : mleft);
 +}
  
  /* adjust counters in sb reflecting allocation of m */
  #define	sballoc(sb, m) { \
 
 Modified: head/sys/sys/unpcb.h
 ==============================================================================
 --- head/sys/sys/unpcb.h	Thu Mar 13 18:17:18 2014	(r263115)
 +++ head/sys/sys/unpcb.h	Thu Mar 13 18:42:12 2014	(r263116)
 @@ -74,8 +74,8 @@ struct unpcb {
  	struct	unp_head unp_refs;	/* referencing socket linked list */
  	LIST_ENTRY(unpcb) unp_reflink;	/* link in unp_refs list */
  	struct	sockaddr_un *unp_addr;	/* bound address of socket */
 -	int	unp_cc;			/* copy of rcv.sb_cc */
 -	int	unp_mbcnt;		/* copy of rcv.sb_mbcnt */
 +	int	reserved1;
 +	int	reserved2;
  	unp_gen_t unp_gencnt;		/* generation count of this instance */
  	short	unp_flags;		/* flags */
  	short	unp_gcflag;		/* Garbage collector flags. */
 
 Modified: head/tests/sys/kern/unix_seqpacket_test.c
 ==============================================================================
 --- head/tests/sys/kern/unix_seqpacket_test.c	Thu Mar 13 18:17:18 2014	(r263115)
 +++ head/tests/sys/kern/unix_seqpacket_test.c	Thu Mar 13 18:42:12 2014	(r263116)
 @@ -844,25 +844,21 @@ ATF_TC_BODY(emsgsize_nonblocking, tc)
  ATF_TC_WITHOUT_HEAD(eagain_8k_8k);
  ATF_TC_BODY(eagain_8k_8k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_eagain(8192, 8192);
  }
  ATF_TC_WITHOUT_HEAD(eagain_8k_128k);
  ATF_TC_BODY(eagain_8k_128k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_eagain(8192, 131072);
  }
  ATF_TC_WITHOUT_HEAD(eagain_128k_8k);
  ATF_TC_BODY(eagain_128k_8k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_eagain(131072, 8192);
  }
  ATF_TC_WITHOUT_HEAD(eagain_128k_128k);
  ATF_TC_BODY(eagain_128k_128k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_eagain(131072, 131072);
  }
  
 @@ -969,37 +965,24 @@ ATF_TC_BODY(pipe_simulator_128k_128k, tc
  ATF_TC_WITHOUT_HEAD(pipe_8k_8k);
  ATF_TC_BODY(pipe_8k_8k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_pipe(8192, 8192);
  }
  
  ATF_TC_WITHOUT_HEAD(pipe_8k_128k);
  ATF_TC_BODY(pipe_8k_128k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_pipe(8192, 131072);
  }
  
  ATF_TC_WITHOUT_HEAD(pipe_128k_8k);
  ATF_TC_BODY(pipe_128k_8k, tc)
  {
 -	/* 
 -	 * kern/185812 causes this test case to both fail and timeout.  The
 -	 * atf-c-api(3) doesn't have a way to set such an expectation.
 -	 * If you use atf_tc_expect_fail, then it will timeout.  If you use
 -	 * atf_tc_expect_timeout, then it will fail.  If you use both, then it
 -	 * will show up as an unexpected pass, which is much worse
 -	 *
 -	 * https://code.google.com/p/kyua/issues/detail?id=76
 -	 */
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_pipe(131072, 8192);
  }
  
  ATF_TC_WITHOUT_HEAD(pipe_128k_128k);
  ATF_TC_BODY(pipe_128k_128k, tc)
  {
 -	atf_tc_expect_fail("PR kern/185812 send(2) on a UNIX domain SEQPACKET socket returns EMSGSIZE instead of EAGAIN");
  	test_pipe(131072, 131072);
  }
  
 _______________________________________________
 svn-src-all@freebsd.org mailing list
 http://lists.freebsd.org/mailman/listinfo/svn-src-all
 To unsubscribe, send any mail to "svn-src-all-unsubscribe@freebsd.org"
 
State-Changed-From-To: open->patched 
State-Changed-By: asomers 
State-Changed-When: Thu Mar 13 19:03:32 UTC 2014 
State-Changed-Why:  
Fixed in head by r263116 

http://www.freebsd.org/cgi/query-pr.cgi?pr=185812 
State-Changed-From-To: patched->closed 
State-Changed-By: asomers 
State-Changed-When: Mon Apr 21 16:36:06 UTC 2014 
State-Changed-Why:  
MFCed by change 264080 

http://www.freebsd.org/cgi/query-pr.cgi?pr=185812 
>Unformatted:
