diff mbox series

recvmmsg(2) system call tests

Message ID ecdea1f2-4931-5dc3-6695-4f8dd78d796d@google.com
State Changes Requested
Delegated to: Petr Vorel
Headers show
Series recvmmsg(2) system call tests | expand

Commit Message

Ramon Pantin Oct. 31, 2018, 11:57 p.m. UTC
Per Rafael's earlier email about how to proceed, find attached the path 
with the test cases.

Comments

Petr Vorel Nov. 1, 2018, 11:38 a.m. UTC | #1
Hi Ramon,

thank you for moving from github pull request to mailing list.

> Per Rafael's earlier email about how to proceed, find attached the path with
> the test cases.
You sent your patch as an attachment.
Please, next time, send is at plain text,
as although patchwork can work with that [1], mailman can't handle it [2].
Here are some instructions [3].
Please add your Signed-off-by: tag.

I've added some comments in github pull request [4], you didn't change them, so
I repeat some of it here. But please, read the doc to provide usable patch [5] [6].
2165 lines for one file suggest to split file into several.

Please have look into the to see code style (short code, easy to read, using
SAFE_*() helpers to prevent reinventing a wheal, avoiding macros, remove comments
which has nothing to say, ...).

LTP code varies, some is really old and ugly. But we try to include reasonably
clean code. You can have look at this as a good example of code style:
testcases/kernel/syscalls/inotify/inotify09.c
59e0a81c5 inotify07: Add test for kernel crash during event notification
It has also TST_TEST_TCONF

> From dea04e5946e8a30787943f347cf3861a8dca6008 Mon Sep 17 00:00:00 2001
> From: Ramon Pantin <pantin@google.com>
> Date: Wed, 31 Oct 2018 16:26:50 -0700
> Subject: [PATCH] recvmmsg(2) test cases
This should be removed.

> ---
>  testcases/kernel/syscalls/recvmmsg/Makefile   |   27 +
>  .../kernel/syscalls/recvmmsg/recvmmsg01.c     | 2165 +++++++++++++++++
>  2 files changed, 2192 insertions(+)
>  create mode 100644 testcases/kernel/syscalls/recvmmsg/Makefile
>  create mode 100644 testcases/kernel/syscalls/recvmmsg/recvmmsg01.c

> diff --git a/testcases/kernel/syscalls/recvmmsg/Makefile b/testcases/kernel/syscalls/recvmmsg/Makefile
> new file mode 100644
> index 000000000..61b3a55e6
> --- /dev/null
> +++ b/testcases/kernel/syscalls/recvmmsg/Makefile
> @@ -0,0 +1,27 @@
> +#  Copyright (c) Google LLC, 2018
> +#  Copyright (c) International Business Machines  Corp., 2001
^ Please delete this IBM copyright.

> +#  This program is free software;  you can redistribute it and/or modify
> +#  it under the terms of the GNU General Public License as published by
> +#  the Free Software Foundation; either version 2 of the License, or
> +#  (at your option) any later version.
> +#
> +#  This program is distributed in the hope that it will be useful,
> +#  but WITHOUT ANY WARRANTY;  without even the implied warranty of
> +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
> +#  the GNU General Public License for more details.
> +#
> +#  You should have received a copy of the GNU General Public License
> +#  along with this program;  if not, write to the Free Software
> +#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
Please use just SPDX identifier instead of address:
# SPDX-License-Identifier: GPL-2.0-or-later

> +#
> +
> +top_srcdir		?= ../../../..
> +
> +include $(top_srcdir)/include/mk/testcases.mk
> +
> +CPPFLAGS		+= -I$(abs_srcdir)/../utils
Why this include?

> +CFLAGS			= -g -std=c99
Please can you remove CFLAGS and produce code without warnings?
I'm also not sure about -std=c99. While you can grep for some -std=gnu99 in the
code, these are just some outdated comments in the source, not passed to make.

> +LDLIBS			+= -lpthread
> +
> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
> new file mode 100644
> index 000000000..131bcfd66
> --- /dev/null
> +++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
> @@ -0,0 +1,2165 @@
> + //  Copyright (c) Google LLC, 2018
> + //  SPDX-License-Identifier: GPL-2.0-or-later

> + //
> + //{
Why these lines?

> +#define _GNU_SOURCE
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdbool.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <errno.h>
> +#include <fcntl.h>
> +#include <pthread.h>
> +#include <assert.h>
> +#include <sys/types.h>
> +#include <sys/time.h>
> +#include <sys/signal.h>
> +#include <sys/uio.h>
> +#include <sys/file.h>
> +#include <sys/socket.h>
> +#include <sys/un.h>
> +#include <netinet/in.h>
Some of the includes will not be needed if you use API functions (SAFE_*, [5], [6]).

> +
> +// #define RECVMMSG_USE_SCTP
> +#ifdef RECVMMSG_USE_SCTP
> +#include <netinet/sctp.h>
> +#endif
Code related to RECVMMSG_USE_SCTP should be checked with autotools. There is our header
include/lapi/sctp.h, you can include it and add (in separate commit) definitions, which are needed.
If you really need netinet/sctp.h header, you can add exit clause with TST_TEST_TCONF

> +
> +#define TST_NO_DEFAULT_MAIN
You're not supposed to use this

> +#include "tst_test.h"
> +// #include "safe_macros.h"
Please remove all comments left

> +
> + //}
> + // 	Declarations.
> + //
> + //{	<-- (curly-brace match to move through major sections of this file)
Please comments like this.
Please use C style comments /* */

> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
> +int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
> +int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
> +				bool some_data, ssize_t controllen_delta);
> +int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
> +						    int type);
> +int con_receive_waits_for_message(con_t *conp, int tn);
> +int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
> +int con_receive_returns_what_is_available(con_t *conp, int tn);
> +int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
Function signatures aren't needed when used just in one file.
Actually, in this case we declare functions as static.

> + //  ensure() tests something that shouldn't fail, doesn't abort like assert()
> +
> +#define ensure(test_number, expr)					    \
> +	((expr) ? (test_verbose < 2 ||			    		    \
> +		   (print_prefix(test_number),				    \
> +		    printf("%s(): true: " #expr " \n", __FUNCTION__)))	    \
> +		: (++ensure_failures,					    \
> +		   printf("test_number = %d: %s(): false: "		    \
> +			  #expr " : failed\n",				    \
> +			  test_number, __FUNCTION__)))
We use tst_res(TINFO, ...) instead of printf.
We try to avoid macros.

> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov)
> +{
> +	size_t iov_max = iovec_max();
> +	int salt = tn + subtest_number();	// salt test data
> +
> +	int client_data[niov];
> +	iovec_t client_iov[niov];
> +	for (int i = 0; i < niov; ++i) {
int i should be defined before.

> +		client_data[i] = i + salt;
> +		iovec_init(&client_iov[i], &client_data[i],
> +			   sizeof(client_data[0]));

...


Kind regards,
Petr

[1] https://patchwork.ozlabs.org/patch/991864/
[2] http://lists.linux.it/pipermail/ltp/2018-November/009781.html
[3] https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#no-mime-no-links-no-compression-no-attachments-just-plain-text
[4] https://github.com/linux-test-project/ltp/pull/414
[5] https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2-writing-a-testcase)
[6] https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial
Ramon Pantin Nov. 2, 2018, 12:13 a.m. UTC | #2
Please see below. Before I email another patch please advise about my response below to each one of your individual items of feedback.

> On Nov 1, 2018, at 4:38 AM, Petr Vorel <pvorel@suse.cz> wrote:
> 
> Hi Ramon,
> 
> thank you for moving from github pull request to mailing list.
> 
>> Per Rafael's earlier email about how to proceed, find attached the path with
>> the test cases.
> You sent your patch as an attachment.
> Please, next time, send is at plain text,
> as although patchwork can work with that [1], mailman can't handle it [2].
> Here are some instructions [3].
> Please add your Signed-off-by: tag.

I am still confused where it is that you want that to be added and what is it that it is supposed to contain?

Should I have as the first line of the patch a line that says?
	Signed-off-by: pantin@google.com
If not what should it be?

> 
> I've added some comments in github pull request [4], you didn't change them, so
> I repeat some of it here. But please, read the doc to provide usable patch [5] [6].
> 2165 lines for one file suggest to split file into several.

That is unnecessary, it would then require a header file. Its needless complexity.

> 
> Please have look into the to see code style (short code, easy to read, using
> SAFE_*() helpers to prevent reinventing a wheal, avoiding macros, remove comments
> which has nothing to say, ...).
> 
> LTP code varies, some is really old and ugly. But we try to include reasonably
> clean code. You can have look at this as a good example of code style:
> testcases/kernel/syscalls/inotify/inotify09.c
> 59e0a81c5 inotify07: Add test for kernel crash during event notification
> It has also TST_TEST_TCONF
> 
>> From dea04e5946e8a30787943f347cf3861a8dca6008 Mon Sep 17 00:00:00 2001
>> From: Ramon Pantin <pantin@google.com>
>> Date: Wed, 31 Oct 2018 16:26:50 -0700
>> Subject: [PATCH] recvmmsg(2) test cases
> This should be removed.

In an earlier email you asked me to use this tool:
	git format-patch 
Which I used, which put that in the patch file. Confused. What harm does that cause?

In any case I will delete those lines. You mean delete the 4 lines right?

> 
>> ---
>> testcases/kernel/syscalls/recvmmsg/Makefile   |   27 +
>> .../kernel/syscalls/recvmmsg/recvmmsg01.c     | 2165 +++++++++++++++++
>> 2 files changed, 2192 insertions(+)
>> create mode 100644 testcases/kernel/syscalls/recvmmsg/Makefile
>> create mode 100644 testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
> 
>> diff --git a/testcases/kernel/syscalls/recvmmsg/Makefile b/testcases/kernel/syscalls/recvmmsg/Makefile
>> new file mode 100644
>> index 000000000..61b3a55e6
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/recvmmsg/Makefile
>> @@ -0,0 +1,27 @@
>> +#  Copyright (c) Google LLC, 2018
>> +#  Copyright (c) International Business Machines  Corp., 2001
> ^ Please delete this IBM copyright.

As I said I said in an earlier email I copied the Makefile from an IBM Makefile that had that copyright.
I will find another one to copy from that uses the SPDX license identifier instead and copy from that.

> 
>> +#  This program is free software;  you can redistribute it and/or modify
>> +#  it under the terms of the GNU General Public License as published by
>> +#  the Free Software Foundation; either version 2 of the License, or
>> +#  (at your option) any later version.
>> +#
>> +#  This program is distributed in the hope that it will be useful,
>> +#  but WITHOUT ANY WARRANTY;  without even the implied warranty of
>> +#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
>> +#  the GNU General Public License for more details.
>> +#
>> +#  You should have received a copy of the GNU General Public License
>> +#  along with this program;  if not, write to the Free Software
>> +#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
> Please use just SPDX identifier instead of address:
> # SPDX-License-Identifier: GPL-2.0-or-later
> 
>> +#
>> +
>> +top_srcdir		?= ../../../..
>> +
>> +include $(top_srcdir)/include/mk/testcases.mk
>> +
>> +CPPFLAGS		+= -I$(abs_srcdir)/../utils
> Why this include?

That was there in the file from the test case that I copied. I guess that was needed when using the original “test.h” (which in your earlier email you said was obsolete) so that came from that. I’ll see if it builds removing that.

> 
>> +CFLAGS			= -g -std=c99
> Please can you remove CFLAGS and produce code without warnings?

To be able to use ANSI C99 features on the builds from the continuous integration I have to have at least C99 enabled. The ANSI C99 standard is 19 years old, its silly to ask that the code be C89. I won’t change that. I’ll remove the “-g”

The test code uses variable declarations intermixed with statements, a feature of C99, reorganizing the code to be compatible with a 29 year old C standard would be silly.


> I'm also not sure about -std=c99. While you can grep for some -std=gnu99 in the
> code, these are just some outdated comments in the source, not passed to make.
> 
>> +LDLIBS			+= -lpthread
>> +
>> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
>> diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
>> new file mode 100644
>> index 000000000..131bcfd66
>> --- /dev/null
>> +++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
>> @@ -0,0 +1,2165 @@
>> + //  Copyright (c) Google LLC, 2018
>> + //  SPDX-License-Identifier: GPL-2.0-or-later
> 
>> + //
>> + //{
> Why these lines?

To navigate better within the source code within various areas in it. You go to the line and press ‘%’ in vi or whatever you use to go to the matching curly-brace in your source code editor.

> 
>> +#define _GNU_SOURCE
>> +#include <stdio.h>
>> +#include <stdlib.h>
>> +#include <stdbool.h>
>> +#include <unistd.h>
>> +#include <string.h>
>> +#include <errno.h>
>> +#include <fcntl.h>
>> +#include <pthread.h>
>> +#include <assert.h>
>> +#include <sys/types.h>
>> +#include <sys/time.h>
>> +#include <sys/signal.h>
>> +#include <sys/uio.h>
>> +#include <sys/file.h>
>> +#include <sys/socket.h>
>> +#include <sys/un.h>
>> +#include <netinet/in.h>
> Some of the includes will not be needed if you use API functions (SAFE_*, [5], [6]).

I’d rather base the test cases on standard UNIX system calls (Linux is mostly UNIX compliant), there is no value in using non standard APIs which I don’t want to have to bother learning about.

> 
>> +
>> +// #define RECVMMSG_USE_SCTP
>> +#ifdef RECVMMSG_USE_SCTP
>> +#include <netinet/sctp.h>
>> +#endif
> Code related to RECVMMSG_USE_SCTP should be checked with autotools. There is our header
> include/lapi/sctp.h, you can include it and add (in separate commit) definitions, which are needed.
> If you really need netinet/sctp.h header, you can add exit clause with TST_TEST_TCONF

I’ll address this later with a later patch. For now it causes no harm. Right?

> 
>> +
>> +#define TST_NO_DEFAULT_MAIN
> You're not supposed to use this
> 

Its there for a reason. I have my own main() program, main() is standard and part of C, don’t want to learn about some other stuff unrelated to what I am testing. In UNIX programs start in main() right?

>> +#include "tst_test.h"
>> +// #include "safe_macros.h"
> Please remove all comments left

I will remove that.

>> +
>> + //}
>> + // 	Declarations.
>> + //
>> + //{	<-- (curly-brace match to move through major sections of this file)
> Please comments like this.
> Please use C style comments /* */

C99 allows for // as comments. I don’t want to make the code bigger needlessly.

> 
>> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
>> +int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
>> +int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
>> +				bool some_data, ssize_t controllen_delta);
>> +int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
>> +						    int type);
>> +int con_receive_waits_for_message(con_t *conp, int tn);
>> +int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
>> +int con_receive_returns_what_is_available(con_t *conp, int tn);
>> +int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
> Function signatures aren't needed when used just in one file.
> Actually, in this case we declare functions as static.

The ARE required unless you reorganize you code to be all in the order of called functions preceding called functions, which is silly. Furthermore if there were mutually recursive functions you couldn’t do that either.

Having “static” for internal functions only matters if the test program is more than one source file. This is just one file. Its easier to debug when the symbols are visible to the debugger, with some compilers “static” functions can not be debugged because the symbols end up not being in the symbol table.

> 
>> + //  ensure() tests something that shouldn't fail, doesn't abort like assert()
>> +
>> +#define ensure(test_number, expr)					    \
>> +	((expr) ? (test_verbose < 2 ||			    		    \
>> +		   (print_prefix(test_number),				    \
>> +		    printf("%s(): true: " #expr " \n", __FUNCTION__)))	    \
>> +		: (++ensure_failures,					    \
>> +		   printf("test_number = %d: %s(): false: "		    \
>> +			  #expr " : failed\n",				    \
>> +			  test_number, __FUNCTION__)))
> We use tst_res(TINFO, ...) instead of printf.
> We try to avoid macros.

Because printf() is documented. You use macros when there is no other choice. For example when you need to stringify something for example with the #expr above, when you want to use __FUNCTION__, __FILE__, and __LINE__, etc.
> 
>> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov)
>> +{
>> +	size_t iov_max = iovec_max();
>> +	int salt = tn + subtest_number();	// salt test data
>> +
>> +	int client_data[niov];
>> +	iovec_t client_iov[niov];
>> +	for (int i = 0; i < niov; ++i) {
> int i should be defined before.

No it is not needed outside the loop.

> 
>> +		client_data[i] = i + salt;
>> +		iovec_init(&client_iov[i], &client_data[i],
>> +			   sizeof(client_data[0]));
> 
> …
> 

regards,
Ramon

> 
> Kind regards,
> Petr
> 
> [1] https://patchwork.ozlabs.org/patch/991864/
> [2] http://lists.linux.it/pipermail/ltp/2018-November/009781.html
> [3] https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#no-mime-no-links-no-compression-no-attachments-just-plain-text
> [4] https://github.com/linux-test-project/ltp/pull/414
> [5] https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2-writing-a-testcase)
> [6] https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial
<html><head><meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body style="word-wrap: break-word; -webkit-nbsp-mode: space; line-break: after-white-space;" class="">Please see below. Before I email another patch please advise about my response below to each one of your individual items of feedback.<br class=""><div><br class=""><blockquote type="cite" class=""><div class="">On Nov 1, 2018, at 4:38 AM, Petr Vorel &lt;<a href="mailto:pvorel@suse.cz" class="">pvorel@suse.cz</a>&gt; wrote:</div><br class="Apple-interchange-newline"><div class=""><div class="">Hi Ramon,<br class=""><br class="">thank you for moving from github pull request to mailing list.<br class=""><br class=""><blockquote type="cite" class="">Per Rafael's earlier email about how to proceed, find attached the path with<br class="">the test cases.<br class=""></blockquote>You sent your patch as an attachment.<br class="">Please, next time, send is at plain text,<br class="">as although patchwork can work with that [1], mailman can't handle it [2].<br class="">Here are some instructions [3].<br class="">Please add your Signed-off-by: tag.<br class=""></div></div></blockquote><div><br class=""></div><div>I am still confused where it is that you want that to be added and what is it that it is supposed to contain?</div><div><br class=""></div><div>Should I have as the first line of the patch a line that says?</div><div><span class="Apple-tab-span" style="white-space:pre">	</span>Signed-off-by: <a href="mailto:pantin@google.com" class="">pantin@google.com</a></div><div>If not what should it be?</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">I've added some comments in github pull request [4], you didn't change them, so<br class="">I repeat some of it here. But please, read the doc to provide usable patch [5] [6].<br class="">2165 lines for one file suggest to split file into several.<br class=""></div></div></blockquote><div><br class=""></div><div>That is unnecessary, it would then require a header file. Its needless complexity.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">Please have look into the to see code style (short code, easy to read, using<br class="">SAFE_*() helpers to prevent reinventing a wheal, avoiding macros, remove comments<br class="">which has nothing to say, ...).<br class=""><br class="">LTP code varies, some is really old and ugly. But we try to include reasonably<br class="">clean code. You can have look at this as a good example of code style:<br class="">testcases/kernel/syscalls/inotify/inotify09.c<br class="">59e0a81c5 inotify07: Add test for kernel crash during event notification<br class="">It has also TST_TEST_TCONF<br class=""><br class=""><blockquote type="cite" class="">From dea04e5946e8a30787943f347cf3861a8dca6008 Mon Sep 17 00:00:00 2001<br class="">From: Ramon Pantin &lt;<a href="mailto:pantin@google.com" class="">pantin@google.com</a>&gt;<br class="">Date: Wed, 31 Oct 2018 16:26:50 -0700<br class="">Subject: [PATCH] recvmmsg(2) test cases<br class=""></blockquote>This should be removed.<br class=""></div></div></blockquote><div><br class=""></div><div>In an earlier email you asked me to use this tool:</div><div><span class="Apple-tab-span" style="white-space:pre">	</span><code class="">git format-patch</code>&nbsp;</div><div>Which I used, which put that in the patch file. Confused. What harm does that cause?</div><div><br class=""></div><div>In any case I will delete those lines. You mean delete the 4 lines right?</div><div><br class=""></div><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">---<br class=""> testcases/kernel/syscalls/recvmmsg/Makefile &nbsp;&nbsp;| &nbsp;&nbsp;27 +<br class=""> .../kernel/syscalls/recvmmsg/recvmmsg01.c &nbsp;&nbsp;&nbsp;&nbsp;| 2165 +++++++++++++++++<br class=""> 2 files changed, 2192 insertions(+)<br class=""> create mode 100644 testcases/kernel/syscalls/recvmmsg/Makefile<br class=""> create mode 100644 testcases/kernel/syscalls/recvmmsg/recvmmsg01.c<br class=""></blockquote><br class=""><blockquote type="cite" class="">diff --git a/testcases/kernel/syscalls/recvmmsg/Makefile b/testcases/kernel/syscalls/recvmmsg/Makefile<br class="">new file mode 100644<br class="">index 000000000..61b3a55e6<br class="">--- /dev/null<br class="">+++ b/testcases/kernel/syscalls/recvmmsg/Makefile<br class="">@@ -0,0 +1,27 @@<br class="">+# &nbsp;Copyright (c) Google LLC, 2018<br class="">+# &nbsp;Copyright (c) International Business Machines &nbsp;Corp., 2001<br class=""></blockquote>^ Please delete this IBM copyright.<br class=""></div></div></blockquote><div><br class=""></div><div>As I said I said in an earlier email I copied the Makefile from an IBM Makefile that had that copyright.</div><div>I will find another one to copy from that uses the SPDX license identifier instead and copy from that.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+# &nbsp;This program is free software; &nbsp;you can redistribute it and/or modify<br class="">+# &nbsp;it under the terms of the GNU General Public License as published by<br class="">+# &nbsp;the Free Software Foundation; either version 2 of the License, or<br class="">+# &nbsp;(at your option) any later version.<br class="">+#<br class="">+# &nbsp;This program is distributed in the hope that it will be useful,<br class="">+# &nbsp;but WITHOUT ANY WARRANTY; &nbsp;without even the implied warranty of<br class="">+# &nbsp;MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. &nbsp;See<br class="">+# &nbsp;the GNU General Public License for more details.<br class="">+#<br class="">+# &nbsp;You should have received a copy of the GNU General Public License<br class="">+# &nbsp;along with this program; &nbsp;if not, write to the Free Software<br class="">+# &nbsp;Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA &nbsp;02110-1301 &nbsp;USA<br class=""></blockquote>Please use just SPDX identifier instead of address:<br class=""># SPDX-License-Identifier: GPL-2.0-or-later<br class=""><br class=""><blockquote type="cite" class="">+#<br class="">+<br class="">+top_srcdir<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>?= ../../../..<br class="">+<br class="">+include $(top_srcdir)/include/mk/testcases.mk<br class="">+<br class="">+CPPFLAGS<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>+= -I$(abs_srcdir)/../utils<br class=""></blockquote>Why this include?<br class=""></div></div></blockquote><div><br class=""></div><div>That was there in the file from the test case that I copied. I guess that was needed when using the original “test.h” (which in your earlier email you said was obsolete) so that came from that. I’ll see if it builds removing that.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+CFLAGS<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>= -g -std=c99<br class=""></blockquote>Please can you remove CFLAGS and produce code without warnings?<br class=""></div></div></blockquote><div><br class=""></div><div>To be able to use ANSI C99 features on the builds from the continuous integration I have to have at least C99 enabled. The ANSI C99 standard is 19 years old, its silly to ask that the code be C89. I won’t change that. I’ll remove the “-g”</div><div><br class=""></div><div>The test code uses variable declarations intermixed with statements, a feature of C99, reorganizing the code to be compatible with a 29 year old C standard would be silly.</div><div><br class=""></div><br class=""><blockquote type="cite" class=""><div class=""><div class="">I'm also not sure about -std=c99. While you can grep for some -std=gnu99 in the<br class="">code, these are just some outdated comments in the source, not passed to make.<br class=""><br class=""><blockquote type="cite" class="">+LDLIBS<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>+= -lpthread<br class="">+<br class="">+include $(top_srcdir)/include/mk/generic_leaf_target.mk<br class="">diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c<br class="">new file mode 100644<br class="">index 000000000..131bcfd66<br class="">--- /dev/null<br class="">+++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c<br class="">@@ -0,0 +1,2165 @@<br class="">+ // &nbsp;Copyright (c) Google LLC, 2018<br class="">+ // &nbsp;SPDX-License-Identifier: GPL-2.0-or-later<br class=""></blockquote><br class=""><blockquote type="cite" class="">+ //<br class="">+ //{<br class=""></blockquote>Why these lines?<br class=""></div></div></blockquote><div><br class=""></div>To navigate better within the source code within various areas in it. You go to the line and press ‘%’ in vi or whatever you use to go to the matching curly-brace in your source code editor.</div><div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+#define _GNU_SOURCE<br class="">+#include &lt;stdio.h&gt;<br class="">+#include &lt;stdlib.h&gt;<br class="">+#include &lt;stdbool.h&gt;<br class="">+#include &lt;unistd.h&gt;<br class="">+#include &lt;string.h&gt;<br class="">+#include &lt;errno.h&gt;<br class="">+#include &lt;fcntl.h&gt;<br class="">+#include &lt;pthread.h&gt;<br class="">+#include &lt;assert.h&gt;<br class="">+#include &lt;sys/types.h&gt;<br class="">+#include &lt;sys/time.h&gt;<br class="">+#include &lt;sys/signal.h&gt;<br class="">+#include &lt;sys/uio.h&gt;<br class="">+#include &lt;sys/file.h&gt;<br class="">+#include &lt;sys/socket.h&gt;<br class="">+#include &lt;sys/un.h&gt;<br class="">+#include &lt;netinet/in.h&gt;<br class=""></blockquote>Some of the includes will not be needed if you use API functions (SAFE_*, [5], [6]).<br class=""></div></div></blockquote><div><br class=""></div><div>I’d rather base the test cases on standard UNIX system calls (Linux is mostly UNIX compliant), there is no value in using non standard APIs which I don’t want to have to bother learning about.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+<br class="">+// #define RECVMMSG_USE_SCTP<br class="">+#ifdef RECVMMSG_USE_SCTP<br class="">+#include &lt;netinet/sctp.h&gt;<br class="">+#endif<br class=""></blockquote>Code related to RECVMMSG_USE_SCTP should be checked with autotools. There is our header<br class="">include/lapi/sctp.h, you can include it and add (in separate commit) definitions, which are needed.<br class="">If you really need netinet/sctp.h header, you can add exit clause with TST_TEST_TCONF<br class=""></div></div></blockquote><div><br class=""></div><div>I’ll address this later with a later patch. For now it causes no harm. Right?</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+<br class="">+#define TST_NO_DEFAULT_MAIN<br class=""></blockquote>You're not supposed to use this<br class=""><br class=""></div></div></blockquote><div><br class=""></div><div>Its there for a reason. I have my own main() program, main() is standard and part of C, don’t want to learn about some other stuff unrelated to what I am testing. In UNIX programs start in main() right?</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><blockquote type="cite" class="">+#include "tst_test.h"<br class="">+// #include "safe_macros.h"<br class=""></blockquote>Please remove all comments left<br class=""></div></div></blockquote><div><br class=""></div><div>I will remove that.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><blockquote type="cite" class="">+<br class="">+ //}<br class="">+ // <span class="Apple-tab-span" style="white-space:pre">	</span>Declarations.<br class="">+ //<br class="">+ //{<span class="Apple-tab-span" style="white-space:pre">	</span>&lt;-- (curly-brace match to move through major sections of this file)<br class=""></blockquote>Please comments like this.<br class="">Please use C style comments /* */<br class=""></div></div></blockquote><div><br class=""></div><div>C99 allows for // as comments. I don’t want to make the code bigger needlessly.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);<br class="">+int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);<br class="">+int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>bool some_data, ssize_t controllen_delta);<br class="">+int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;int type);<br class="">+int con_receive_waits_for_message(con_t *conp, int tn);<br class="">+int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);<br class="">+int con_receive_returns_what_is_available(con_t *conp, int tn);<br class="">+int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);<br class=""></blockquote>Function signatures aren't needed when used just in one file.<br class="">Actually, in this case we declare functions as static.<br class=""></div></div></blockquote><div><br class=""></div><div>The ARE required unless you reorganize you code to be all in the order of called functions preceding called functions, which is silly. Furthermore if there were mutually recursive functions you couldn’t do that either.</div><div><br class=""></div><div>Having “static” for internal functions only matters if the test program is more than one source file. This is just one file. Its easier to debug when the symbols are visible to the debugger, with some compilers “static” functions can not be debugged because the symbols end up not being in the symbol table.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+ // &nbsp;ensure() tests something that shouldn't fail, doesn't abort like assert()<br class="">+<br class="">+#define ensure(test_number, expr)<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>((expr) ? (test_verbose &lt; 2 ||<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;(print_prefix(test_number),<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;printf("%s(): true: " #expr " \n", __FUNCTION__)))<span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>: (++ensure_failures,<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;printf("test_number = %d: %s(): false: "<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;#expr " : failed\n",<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;&nbsp;\<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;test_number, __FUNCTION__)))<br class=""></blockquote>We use tst_res(TINFO, ...) instead of printf.<br class="">We try to avoid macros.<br class=""></div></div></blockquote><div><br class=""></div><div>Because printf() is documented. You use macros when there is no other choice. For example when you need to stringify something for example with the #expr above, when you want to use __FUNCTION__, __FILE__, and __LINE__, etc.</div><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov)<br class="">+{<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>size_t iov_max = iovec_max();<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>int salt = tn + subtest_number();<span class="Apple-tab-span" style="white-space:pre">	</span>// salt test data<br class="">+<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>int client_data[niov];<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>iovec_t client_iov[niov];<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span>for (int i = 0; i &lt; niov; ++i) {<br class=""></blockquote>int i should be defined before.<br class=""></div></div></blockquote><div><br class=""></div><div>No it is not needed outside the loop.</div><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class=""><blockquote type="cite" class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>client_data[i] = i + salt;<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span>iovec_init(&amp;client_iov[i], &amp;client_data[i],<br class="">+<span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span><span class="Apple-tab-span" style="white-space:pre">	</span> &nbsp;&nbsp;sizeof(client_data[0]));<br class=""></blockquote><br class="">…<br class=""><br class=""></div></div></blockquote><div><br class=""></div>regards,</div><div>Ramon<br class=""><br class=""><blockquote type="cite" class=""><div class=""><div class=""><br class="">Kind regards,<br class="">Petr<br class=""><br class="">[1] <a href="https://patchwork.ozlabs.org/patch/991864/" class="">https://patchwork.ozlabs.org/patch/991864/</a><br class="">[2] <a href="http://lists.linux.it/pipermail/ltp/2018-November/009781.html" class="">http://lists.linux.it/pipermail/ltp/2018-November/009781.html</a><br class="">[3] <a href="https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#no-mime-no-links-no-compression-no-attachments-just-plain-text" class="">https://www.kernel.org/doc/html/v4.17/process/submitting-patches.html#no-mime-no-links-no-compression-no-attachments-just-plain-text</a><br class="">[4] <a href="https://github.com/linux-test-project/ltp/pull/414" class="">https://github.com/linux-test-project/ltp/pull/414</a><br class="">[5] <a href="https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2-writing-a-testcase" class="">https://github.com/linux-test-project/ltp/wiki/Test-Writing-Guidelines#2-writing-a-testcase</a>)<br class="">[6] <a href="https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial" class="">https://github.com/linux-test-project/ltp/wiki/C-Test-Case-Tutorial</a><br class=""></div></div></blockquote></div><br class=""></body></html>
Cyril Hrubis Nov. 2, 2018, 10:52 a.m. UTC | #3
Hi!
> > You sent your patch as an attachment.
> > Please, next time, send is at plain text,
> > as although patchwork can work with that [1], mailman can't handle it [2].
> > Here are some instructions [3].
> > Please add your Signed-off-by: tag.
> 
> I am still confused where it is that you want that to be added and what is it that it is supposed to contain?
> 
> Should I have as the first line of the patch a line that says?
> 	Signed-off-by: pantin@google.com
> If not what should it be?

See:

https://www.kernel.org/doc/html/latest/process/submitting-patches.html#developer-s-certificate-of-origin-1-1

The process for LTP is more or less the same as for the linux kernel so
most of it applies here as well.

> >> +CFLAGS			= -g -std=c99
> > Please can you remove CFLAGS and produce code without warnings?
> 
> To be able to use ANSI C99 features on the builds from the continuous integration I have to have at least C99 enabled. The ANSI C99 standard is 19 years old, its silly to ask that the code be C89. I won???t change that. I???ll remove the ???-g???
> 
> The test code uses variable declarations intermixed with statements, a feature of C99, reorganizing the code to be compatible with a 29 year old C standard would be silly.

Actually the default for gcc is gnu90 which is close to c99 anyways, so
we do not have to do anything about it.

> > I'm also not sure about -std=c99. While you can grep for some -std=gnu99 in the
> > code, these are just some outdated comments in the source, not passed to make.
> > 
> >> +LDLIBS			+= -lpthread
> >> +
> >> +include $(top_srcdir)/include/mk/generic_leaf_target.mk
> >> diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
> >> new file mode 100644
> >> index 000000000..131bcfd66
> >> --- /dev/null
> >> +++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
> >> @@ -0,0 +1,2165 @@
> >> + //  Copyright (c) Google LLC, 2018
> >> + //  SPDX-License-Identifier: GPL-2.0-or-later
> > 
> >> + //
> >> + //{
> > Why these lines?
> 
> To navigate better within the source code within various areas in it.
> You go to the line and press ???%??? in vi or whatever you use to go
> to the matching curly-brace in your source code editor.

Please don't do that.

> >> +#define _GNU_SOURCE
> >> +#include <stdio.h>
> >> +#include <stdlib.h>
> >> +#include <stdbool.h>
> >> +#include <unistd.h>
> >> +#include <string.h>
> >> +#include <errno.h>
> >> +#include <fcntl.h>
> >> +#include <pthread.h>
> >> +#include <assert.h>
> >> +#include <sys/types.h>
> >> +#include <sys/time.h>
> >> +#include <sys/signal.h>
> >> +#include <sys/uio.h>
> >> +#include <sys/file.h>
> >> +#include <sys/socket.h>
> >> +#include <sys/un.h>
> >> +#include <netinet/in.h>
> > Some of the includes will not be needed if you use API functions (SAFE_*, [5], [6]).
> 
> I???d rather base the test cases on standard UNIX system calls (Linux is mostly UNIX compliant), there is no value in using non standard APIs which I don???t want to have to bother learning about.

These are not non-standard APIs, these are just tiny wrappers around
standard UNIX apis that handle failures automatically, which greatly
simplifies the test code.

> >> +// #define RECVMMSG_USE_SCTP
> >> +#ifdef RECVMMSG_USE_SCTP
> >> +#include <netinet/sctp.h>
> >> +#endif
> > Code related to RECVMMSG_USE_SCTP should be checked with autotools. There is our header
> > include/lapi/sctp.h, you can include it and add (in separate commit) definitions, which are needed.
> > If you really need netinet/sctp.h header, you can add exit clause with TST_TEST_TCONF
> 
> I???ll address this later with a later patch. For now it causes no harm. Right?

Sure.

> >> +
> >> +#define TST_NO_DEFAULT_MAIN
> > You're not supposed to use this
> > 
> 
> Its there for a reason. I have my own main() program, main() is
> standard and part of C, don???t want to learn about some other stuff
> unrelated to what I am testing. In UNIX programs start in main()
> right?

If you are writing LTP test you are not supposed to define main().

Seriously you just need to write the function that does the test and
that's it. Plese do not fight the infrastructure that is there to help
you and to make your life easier.

> >> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
> >> +int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
> >> +int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
> >> +				bool some_data, ssize_t controllen_delta);
> >> +int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
> >> +						    int type);
> >> +int con_receive_waits_for_message(con_t *conp, int tn);
> >> +int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
> >> +int con_receive_returns_what_is_available(con_t *conp, int tn);
> >> +int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
> > Function signatures aren't needed when used just in one file.
> > Actually, in this case we declare functions as static.
> 
> The ARE required unless you reorganize you code to be all in the order of called functions preceding called functions, which is silly. Furthermore if there were mutually recursive functions you couldn???t do that either.
> 
> Having ???static??? for internal functions only matters if the test program is more than one source file. This is just one file. Its easier to debug when the symbols are visible to the debugger, with some compilers ???static??? functions can not be debugged because the symbols end up not being in the symbol table.

Actually neither of that is true.

Having static defined tells the compiler that the function is used only
in the local file, which can for example tell you that some of the
functions are unused.

> >> + //  ensure() tests something that shouldn't fail, doesn't abort like assert()
> >> +
> >> +#define ensure(test_number, expr)					    \
> >> +	((expr) ? (test_verbose < 2 ||			    		    \
> >> +		   (print_prefix(test_number),				    \
> >> +		    printf("%s(): true: " #expr " \n", __FUNCTION__)))	    \
> >> +		: (++ensure_failures,					    \
> >> +		   printf("test_number = %d: %s(): false: "		    \
> >> +			  #expr " : failed\n",				    \
> >> +			  test_number, __FUNCTION__)))
> > We use tst_res(TINFO, ...) instead of printf.
> > We try to avoid macros.
> 
> Because printf() is documented. You use macros when there is no other choice. For example when you need to stringify something for example with the #expr above, when you want to use __FUNCTION__, __FILE__, and __LINE__, etc.

Plase do not use macros like that also the __FILE__ and __LINE__ is
printed by the tst_res() itself. Please do not reinvent the wheel and
use the standard LTP reporting API.
Ramon Pantin Nov. 3, 2018, 10:16 p.m. UTC | #4
Cyril,

Can you help me understand why you don't want to allow this test case to 
make use of the C99 programming language?

It is only a 19 year old standard.

If there is a problem with C99 use, can you indicate that the problem is?

thank you,
Ramon

On 11/02/18 03:52, Cyril Hrubis wrote:
> Actually the default for gcc is gnu90 which is close to c99 anyways, so
> we do not have to do anything about it.
Ramon Pantin Nov. 3, 2018, 10:18 p.m. UTC | #5
Pardon the typo, can you indicate *what* the problem is with using C99.

On 11/03/18 15:16, Ramon Pantin wrote:
> Cyril,
>
> Can you help me understand why you don't want to allow this test case 
> to make use of the C99 programming language?
>
> It is only a 19 year old standard.
>
> If there is a problem with C99 use, can you indicate that the problem is?
>
> thank you,
> Ramon
>
> On 11/02/18 03:52, Cyril Hrubis wrote:
>> Actually the default for gcc is gnu90 which is close to c99 anyways, so
>> we do not have to do anything about it.
>
Ramon Pantin Nov. 3, 2018, 10:37 p.m. UTC | #6
Hi Cyril,

About this statemement that you made:

    "Function signatures aren't needed when used just in one file." 

To whch I replied:

    "ARE required unless you reorganize you code to be all in the order
    of called functions preceding called functions"

To which you replied:

    "Actually neither of that is true"

If you don't use prototypes you do have to reorganize the functions in 
the order they are called.

Are you really questioning that? I don't want to have to explain that 
unless you don't understand why prototypes are required in some cases.

If you are not questioning that. Are you ok with me using prototypes?

I don't mind adding static, but do realize that that will indeed mean 
that in some compilation systems the sysmbols won't be in the symbol 
table and will make debugging harder. I don't care if you question this 
statment, don't have time to explain that.

regards,
Ramon Pantin

On 11/02/18 03:52, Cyril Hrubis wrote:
>>>> +int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
>>>> +int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
>>>> +int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
>>>> +				bool some_data, ssize_t controllen_delta);
>>>> +int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
>>>> +						    int type);
>>>> +int con_receive_waits_for_message(con_t *conp, int tn);
>>>> +int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
>>>> +int con_receive_returns_what_is_available(con_t *conp, int tn);
>>>> +int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
>>> Function signatures aren't needed when used just in one file.
>>> Actually, in this case we declare functions as static.
>> The ARE required unless you reorganize you code to be all in the order of called functions preceding called functions, which is silly. Furthermore if there were mutually recursive functions you couldn???t do that either.
>>
>> Having ???static??? for internal functions only matters if the test program is more than one source file. This is just one file. Its easier to debug when the symbols are visible to the debugger, with some compilers ???static??? functions can not be debugged because the symbols end up not being in the symbol table.
> Actually neither of that is true.
>
> Having static defined tells the compiler that the function is used only
> in the local file, which can for example tell you that some of the
> functions are unused.
>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
  </head>
  <body text="#000000" bgcolor="#FFFFFF">
    <p>Hi Cyril,</p>
    <p>About this statemement that you made:</p>
    <blockquote>"Function signatures aren't needed when used just in one
      file."
    </blockquote>
    <p>To whch I replied:</p>
    <blockquote>"ARE required unless you reorganize you code to be all
      in the order of called functions preceding called functions"<br>
    </blockquote>
    To which you replied:<br>
    <blockquote>"Actually neither of that is true"<br>
    </blockquote>
    If you don't use prototypes you do have to reorganize the functions
    in the order they are called.<br>
    <br>
    Are you really questioning that? I don't want to have to explain
    that unless you don't understand why prototypes are required in some
    cases.<br>
    <br>
    If you are not questioning that. Are you ok with me using
    prototypes?<br>
    <br>
    I don't mind adding static, but do realize that that will indeed
    mean that in some compilation systems the sysmbols won't be in the
    symbol table and will make debugging harder. I don't care if you
    question this statment, don't have time to explain that.<br>
    <br>
    regards,<br>
    Ramon Pantin<br>
    <br>
    <div class="moz-cite-prefix">On 11/02/18 03:52, Cyril Hrubis wrote:<br>
    </div>
    <blockquote type="cite" cite="mid:20181102105206.GA22411@rei">
      <blockquote type="cite" style="color: #000000;">
        <blockquote type="cite" style="color: #000000;">
          <blockquote type="cite" style="color: #000000;">
            <pre wrap="">+int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
+int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
+int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
+				bool some_data, ssize_t controllen_delta);
+int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
+						    int type);
+int con_receive_waits_for_message(con_t *conp, int tn);
+int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
+int con_receive_returns_what_is_available(con_t *conp, int tn);
+int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
</pre>
          </blockquote>
          <pre wrap="">Function signatures aren't needed when used just in one file.
Actually, in this case we declare functions as static.
</pre>
        </blockquote>
        <pre wrap="">The ARE required unless you reorganize you code to be all in the order of called functions preceding called functions, which is silly. Furthermore if there were mutually recursive functions you couldn???t do that either.

Having ???static??? for internal functions only matters if the test program is more than one source file. This is just one file. Its easier to debug when the symbols are visible to the debugger, with some compilers ???static??? functions can not be debugged because the symbols end up not being in the symbol table.
</pre>
      </blockquote>
      <pre wrap="">Actually neither of that is true.

Having static defined tells the compiler that the function is used only
in the local file, which can for example tell you that some of the
functions are unused.

</pre>
    </blockquote>
    <br>
  </body>
</html>
Cyril Hrubis Nov. 5, 2018, 9:40 a.m. UTC | #7
Hi!
> Can you help me understand why you don't want to allow this test case to 
> make use of the C99 programming language?
> 
> It is only a 19 year old standard.
> 
> If there is a problem with C99 use, can you indicate that the problem is?

What I'm trying to say is:

* Reasonable recent compilers support c99 features without -c99
  explicitelly passed on command line

* It's a very bad idea to hardcode switches in random leaf Makefiles
  as from maintenance point of view this is real disaster

If we ever want to stick to some C standard it has to be a top level
decision, but honestly I do prefer the compiler defaults.
Cyril Hrubis Nov. 5, 2018, 10:06 a.m. UTC | #8
Hi!
> About this statemement that you made:
> 
>     "Function signatures aren't needed when used just in one file." 
> 
> To whch I replied:
> 
>     "ARE required unless you reorganize you code to be all in the order
>     of called functions preceding called functions"
> 
> To which you replied:
> 
>     "Actually neither of that is true"

I actually answered only the part about 'static' being useless. Sorry
for not being clear on that.

> If you don't use prototypes you do have to reorganize the functions in 
> the order they are called.

That is indeed true.

> Are you really questioning that? I don't want to have to explain that 
> unless you don't understand why prototypes are required in some cases.

Not at all.

> If you are not questioning that. Are you ok with me using prototypes?

I do prefer to organize tests so that we don't have to use them, but I
can live with code that does not follow that as well.

> I don't mind adding static, but do realize that that will indeed mean 
> that in some compilation systems the sysmbols won't be in the symbol 
> table and will make debugging harder. I don't care if you question this 
> statment, don't have time to explain that.

For modern enough compilers and debuggers this is not true. Ancient
tooling may loose some debugging information when functions are inlined
though.
Ramon Pantin Nov. 5, 2018, 6:35 p.m. UTC | #9
Ok, so my test cases are using arrays on the stack dimensioned at 
run-time based on other values computed at run time. C99 is needed for 
that. I don't see any value in changing them to use alloca(), its a 
waste of time.

Is it really going to be an issue if I use a feature of a 19 year old 
language?


On 11/05/2018 01:40 AM, Cyril Hrubis wrote:
> Hi!
>> Can you help me understand why you don't want to allow this test case to
>> make use of the C99 programming language?
>>
>> It is only a 19 year old standard.
>>
>> If there is a problem with C99 use, can you indicate that the problem is?
> What I'm trying to say is:
>
> * Reasonable recent compilers support c99 features without -c99
>    explicitelly passed on command line
>
> * It's a very bad idea to hardcode switches in random leaf Makefiles
>    as from maintenance point of view this is real disaster
>
> If we ever want to stick to some C standard it has to be a top level
> decision, but honestly I do prefer the compiler defaults.
>
Cyril Hrubis Nov. 6, 2018, 10:25 a.m. UTC | #10
Hi!
> Ok, so my test cases are using arrays on the stack dimensioned at 
> run-time based on other values computed at run time. C99 is needed for 
> that. I don't see any value in changing them to use alloca(), its a 
> waste of time.

That is not true, at least for gcc variable arrays have been supported
as an extension in the default gnu89 mode for years now. I think that it
even predates the c99 standard, which is what I'm trying to explain for
quite some time now.

The only difference I'm aware of is how inline keyword behaves between
c99 and gnu89 mode, there may be some other minor differencies though.

The C accepted by default by a modern (10 years old+) compilers is much
closer than c99 than to the original c89.

Can we please stop this useless arguemnts and focus on the actual code
now?
diff mbox series

Patch

From dea04e5946e8a30787943f347cf3861a8dca6008 Mon Sep 17 00:00:00 2001
From: Ramon Pantin <pantin@google.com>
Date: Wed, 31 Oct 2018 16:26:50 -0700
Subject: [PATCH] recvmmsg(2) test cases

---
 testcases/kernel/syscalls/recvmmsg/Makefile   |   27 +
 .../kernel/syscalls/recvmmsg/recvmmsg01.c     | 2165 +++++++++++++++++
 2 files changed, 2192 insertions(+)
 create mode 100644 testcases/kernel/syscalls/recvmmsg/Makefile
 create mode 100644 testcases/kernel/syscalls/recvmmsg/recvmmsg01.c

diff --git a/testcases/kernel/syscalls/recvmmsg/Makefile b/testcases/kernel/syscalls/recvmmsg/Makefile
new file mode 100644
index 000000000..61b3a55e6
--- /dev/null
+++ b/testcases/kernel/syscalls/recvmmsg/Makefile
@@ -0,0 +1,27 @@ 
+#  Copyright (c) Google LLC, 2018
+#  Copyright (c) International Business Machines  Corp., 2001
+#
+#  This program is free software;  you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY;  without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
+#  the GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program;  if not, write to the Free Software
+#  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+#
+
+top_srcdir		?= ../../../..
+
+include $(top_srcdir)/include/mk/testcases.mk
+
+CPPFLAGS		+= -I$(abs_srcdir)/../utils
+CFLAGS			= -g -std=c99
+LDLIBS			+= -lpthread
+
+include $(top_srcdir)/include/mk/generic_leaf_target.mk
diff --git a/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
new file mode 100644
index 000000000..131bcfd66
--- /dev/null
+++ b/testcases/kernel/syscalls/recvmmsg/recvmmsg01.c
@@ -0,0 +1,2165 @@ 
+ //  Copyright (c) Google LLC, 2018
+ //  SPDX-License-Identifier: GPL-2.0-or-later
+ //
+ //{
+
+#define _GNU_SOURCE
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/signal.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+
+// #define RECVMMSG_USE_SCTP
+#ifdef RECVMMSG_USE_SCTP
+#include <netinet/sctp.h>
+#endif
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+// #include "safe_macros.h"
+
+ //}
+ // 	Declarations.
+ //
+ //{	<-- (curly-brace match to move through major sections of this file)
+
+ //  Test cases use ensure(expr) to indicate that an expression is expected
+ //  to be true.  Some test cases, depending on its arguments, tests for
+ //  the same condition and its opposite.  For easier reading of the output
+ //  extra ensure() uses are made to cause useful output to be produced, for
+ //  example on both branches of an if-else that has already tested the
+ //  condition. For example:
+ //
+ //	if (server_recv.msg_flags & MSG_EOR) {
+ //		ensure(server_recv.msg_flags & MSG_EOR);
+ //		...
+ //	} else {
+ //		ensure(! (server_recv.msg_flags & MSG_EOR));
+ //		...
+ //	}
+ //
+ //  This is not sloppy coding, its done purposely, don't delete such code.
+
+ //  contants
+
+enum { GUARD_CHAR = '+' };		// guard character to check overwrites
+enum { INVALID_SYSCALL_VALUE = -2 };	// syscalls values are all >= -1
+enum { TEST_IP = 0x7F000001 };
+enum { TEST_PORT = 6789 };
+enum { SENDMSG_SLEEP_MS = 10 };
+
+ //  Must be disjoint with errno values and non-zero, value returned by tests
+ //  that do multiple recvmsg() calls internally, or that have a non-zero flag,
+ //  or that require special treatment (sender needs to sleep).  Those tests
+ //  can not be bundled with other tests into a larger recvmmsg(2) multi-
+ //  receive message test, some can only be bundled with their own kind.
+
+enum non_error_errno {
+	TEST_HAS_MULTIPLE_RECVMSG = -1,		// can't be bundled at all
+	TEST_CLOEXEC_FLAG = -2,			// bundle these together
+	TEST_NEEDS_SLEEP_IN_SENDER = -3,	// bundle these together
+};
+
+ //  typedefs
+
+typedef struct timeval		timeval_t;
+typedef struct timespec		timespec_t;
+typedef struct iovec		iovec_t;
+typedef struct msghdr		msghdr_t;
+typedef struct mmsghdr		mmsghdr_t;
+typedef struct sockaddr		sockaddr_t;
+typedef struct sockaddr_in	sockaddr_in_t;
+typedef struct sctp_initmsg	sctp_initmsg_t;
+typedef struct cmsghdr		cmsghdr_t;
+
+typedef void *(*thread_start_t)(void *arg);
+
+typedef struct {
+	cmsghdr_t	cmf_cmsg;
+	int		cmf_fdspace;	// padding in cmf_cmsg implies fd might
+					// not be here, might be in cmf_cmsg!
+} ctlmsgfd_t;
+
+typedef struct {
+	pthread_t	 tc_thr;
+} thread_call_t;
+
+ //  sendmsg(2) call to be sent individually or bundled up with other such
+ //  calls with sendmmsg(2) by copying them into an mmsghdr_t array, the
+ //  sending is done by a pthread_create(3) created thread
+
+typedef struct {
+	thread_call_t	 smc_thread_call;	// must be first: inherits from
+	int		 smc_sockfd;
+	msghdr_t	*smc_msg;
+	int		 smc_flags;
+	ssize_t		 smc_value;
+	int		 smc_errno;
+} sendmsg_call_t;
+
+ //  sendmmsg(2) call, will be done as a single sendmmsg(2) call or vlen
+ //  individual sendmsg(2) calls
+
+typedef struct {
+	thread_call_t	 smmc_thread_call;	// must be first: inherits from
+	int		 smmc_sockfd;
+	unsigned	 smmc_vlen;
+	mmsghdr_t	*smmc_smm;
+	int		 smmc_flags;
+	bool		 smmc_call_sendmmsg;
+	bool		 smmc_sender_sleeps;
+	int		 smmc_value;
+	int		 smmc_errno;
+} sendmmsg_call_t;
+
+ //  recvmsg(2) call to be received individually or bundled up with other
+ //  such calls with recvmmsg(2) by copying them into an mmsghdr_t array
+
+typedef struct {
+	ssize_t		 rmc_value;
+	int		 rmc_errno;
+	msghdr_t	*rmc_msg;
+	int		 rmc_flags;
+} recvmsg_call_t;
+
+ //  client server connection
+
+typedef struct call_s call_t;
+
+ //  A con_t abstracts a connection between a client and a server, it is used
+ //  two different ways:
+ //
+ //  con_call_vec == NULL && con_call_n == 0 && con_call_sendmmsg == false
+ //
+ //  - Individual tests encapsulated in a single test function, these are
+ //    usually just one sendmsg(2) and one or maybe two sequential recvmsg(2)
+ //    calls, for example to peek then get the message or to test non-blocking
+ //    then blocking recvmsg, etc.
+ //	
+ //  con_call_vec != NULL && con_call_n >= 1
+ //
+ //  - Bundled unrelated tests each encapsulated in their own test function,
+ //    all of these tests have to be implemented with a 1 send and 1 recveive,
+ //    so that they can all run with a multi-receive message, recvmmsg(2), call.
+ //    In that case the con_t is used to hide the bundling of the tests from
+ //    each other and the tests, unsupectingly, pass the test vector via the
+ //    con_call_vec and con_call_n members.  Note that con_call_n == 1 is used
+ //    as a degenerate case to test recvmmsg(2) with a single message. Note
+ //    when recvmmsg(2) is used the messages can be sent individually or in
+ //    a single sendmmsg(2) for testing multi-message sending. Both ways of
+ //    sending are done controlled by con_call_sendmmsg == true to indicate
+ //    used of sendmmsg(2), otherwise multiple sendmsg(2) are used.
+
+typedef struct {
+	int		  con_client;
+	int		  con_server;
+	int		  con_type;
+	sockaddr_in_t	  con_server_sin;
+	bool		  con_call_sendmmsg;
+	bool		  con_sender_sleeps;
+	bool		  con_waitforone;
+	bool		  con_timeout;
+	size_t		  con_call_end;	// index of last+1 filled
+	size_t		  con_call_n;
+	call_t		**con_call_vec;	// points to array of con_call_n
+					// call_t pointers to be used in
+					// a recvmmsg(2) multi-message test
+	sendmsg_call_t	**con_smc_vec;
+	recvmsg_call_t	**con_rmc_vec;
+} con_t;
+
+typedef int (*calltest_t)(call_t *callp, con_t *conp, int tn);
+typedef void (*callconinit_t)(call_t *callp, con_t *conp);
+
+struct call_s {
+	calltest_t	call_test;
+	callconinit_t	call_con_init;
+	int		call_errno;	// set on first run
+};
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+	size_t	call_niov;
+} call_receive_iovec_boundary_checks_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+	size_t	call_niov;
+} call_receive_iovec_boundary_checks_peeking_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+	int	call_cloexec_flag;
+	bool	call_some_data;
+	ssize_t	call_controllen_delta;
+} call_receive_file_descriptor_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+	int	call_type;
+} call_message_too_long_to_fit_might_discard_bytes_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+} call_receive_waits_for_message_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+} call_receiver_doesnt_wait_for_messages_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+} call_receive_returns_what_is_available_t;
+
+typedef struct {
+	call_t	call_base;		// inherits from call_t
+} call_empty_datagram_received_as_empty_datagram_t;
+
+ //  prototypes
+
+void tests_run(void);
+void tests_wrap(void);
+size_t tests_select(callconinit_t cci, call_t *tests_src[],
+		    call_t *tests_dest[], size_t n, int error);
+
+void perror_exit_file_line(const char *s, const char *file,
+			   const char *func, int line);
+void strerror_exit_file_line(int e, const char *s,
+			     const char *file, const char *func, int line);
+int errno_value_is_error(int e);
+
+void sleep_ms(long ms);
+
+void thread_call(thread_call_t *tcp, thread_start_t func);
+void thread_join(thread_call_t *tcp);
+
+void tst_parse_opt(int argc, char *argv[]);
+
+void print_nest(int test_number);
+void print_prefix(int test_number);
+
+int test_number_get(void);
+
+int subtest_number(void);
+void subtest_nest(void);
+void subtest_unnest(void);
+
+bool mem_is_equal(void *a, void *b, size_t size);
+bool mem_is_zero(void *a, size_t size);
+
+void sockaddr_in_init(sockaddr_in_t *sinp, sa_family_t family,
+		      in_port_t port, uint32_t ip);
+void sockaddr_in_init_to_zero(sockaddr_in_t *sinp);
+
+char *sock_type_to_str(int type);
+
+size_t iovec_max(void);
+void iovec_init(iovec_t *iovecp, void *base, size_t len);
+
+void msghdr_init(msghdr_t *msgp, sockaddr_in_t *sinp,
+		 iovec_t *iovecp, size_t iovlen, int flags);
+void msghdr_init_with_control(msghdr_t *msgp, sockaddr_in_t *sinp,
+			      iovec_t *iovecp, size_t iovlen,
+			      void *control, size_t controllen,
+			      int flags);
+
+void mmsghdr_init(mmsghdr_t *mmsgp, msghdr_t *msgp);
+
+void recvmsg_call_init(recvmsg_call_t *rmcp,
+		       msghdr_t *msgp, int flags);
+
+void sendmsg_call_init(sendmsg_call_t *smcp, int sockfd,
+		       msghdr_t *msgp, int flags);
+void *sendmsg_call(sendmsg_call_t *smcp);
+void *sendmsg_call_after_sleep(sendmsg_call_t *smcp);
+
+void sendmmsg_init(sendmmsg_call_t *smmcp, int sockfd, unsigned vlen,
+		   mmsghdr_t *smm, int flags, bool call_sendmmsg,
+		   bool sender_sleeps);
+
+void ctlmsgfd_init(ctlmsgfd_t *cmfp, int fd);
+void ctlmsgfd_init_to_zero(ctlmsgfd_t *cmfp);
+int ctlmsgfd_get_fd(ctlmsgfd_t *cmfp);
+
+void con_init(con_t *conp, int client, int server,
+	      int type, sockaddr_in_t *sinp);
+void con_init_base(con_t *conp, int client, int server,
+	      int type, sockaddr_in_t *sinp);
+void con_make_vectored(con_t *conp, bool sendmulti,
+		       size_t n, call_t **call_vec,
+		       sendmsg_call_t **smc_vec, recvmsg_call_t **rmc_vec);
+void con_make_waitforone_tests(con_t *conp);
+void con_make_timeout_tests(con_t *conp);
+void con_init_with_type(con_t *conp, int type);
+void con_init_socketpair(con_t *conp);
+void con_init_seqpacket(con_t *conp);
+void con_init_dgram(con_t *conp);
+void con_deinit(con_t *conp);
+void con_sendmmsg_recvmmsg(con_t *conp, size_t n,
+			   int sflags, mmsghdr_t *smm,
+			   int rflags, mmsghdr_t *rmm);
+void con_do_multi_send_recv(con_t *conp);
+void con_add_send_recv_calls_vec(con_t *conp, sendmsg_call_t *smcp,
+				 recvmsg_call_t *rmcp, bool sender_sleeps);
+void con_add_send_recv_calls(con_t *conp, sendmsg_call_t *smcp,
+			     recvmsg_call_t *rmcp, bool sender_sleeps);
+
+int call_go(call_t *callp, con_t *conp);
+void call_do_con_init_socketpair(call_t *callp, con_t *conp);
+void call_do_con_init_seqpacket(call_t *callp, con_t *conp);
+void call_do_con_init_dgram(call_t *callp, con_t *conp);
+void call_base_init(call_t *callp, callconinit_t coninit, calltest_t test);
+
+void call_receive_iovec_boundary_checks_init(
+	call_receive_iovec_boundary_checks_t *callp, size_t niov);
+int call_receive_iovec_boundary_checks(
+	call_receive_iovec_boundary_checks_t *callp, con_t *conp, int tn);
+
+void call_receive_iovec_boundary_checks_peeking_init(
+	call_receive_iovec_boundary_checks_peeking_t *callp, size_t niov);
+int call_receive_iovec_boundary_checks_peeking(
+	call_receive_iovec_boundary_checks_peeking_t *callp,
+	con_t *conp, int tn);
+
+void call_receive_file_descriptor_init(
+	call_receive_file_descriptor_t *callp,
+	int cloexec_flag, bool some_data, ssize_t controllen_delta);
+int call_receive_file_descriptor(
+	call_receive_file_descriptor_t *callp, con_t *conp, int tn);
+
+void call_message_too_long_to_fit_might_discard_bytes_init(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp,
+	int type);
+int call_message_too_long_to_fit_might_discard_bytes(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp,
+	con_t *conp, int tn);
+
+void call_empty_datagram_received_as_empty_datagram_init(
+	call_empty_datagram_received_as_empty_datagram_t *callp);
+int call_empty_datagram_received_as_empty_datagram(
+	call_empty_datagram_received_as_empty_datagram_t *callp,
+	con_t *conp, int tn);
+
+void call_receive_waits_for_message_init(
+	call_receive_waits_for_message_t *callp);
+int call_receive_waits_for_message(
+	call_receive_waits_for_message_t *callp, con_t *conp, int tn);
+
+void call_receiver_doesnt_wait_for_messages_init(
+	call_receiver_doesnt_wait_for_messages_t *callp);
+int call_receiver_doesnt_wait_for_messages(
+	call_receiver_doesnt_wait_for_messages_t *callp, con_t *conp, int tn);
+
+void call_receive_returns_what_is_available_init(
+	call_receive_returns_what_is_available_t *callp);
+int call_receive_returns_what_is_available(
+	call_receive_returns_what_is_available_t *callp, con_t *conp, int tn);
+
+int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov);
+int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov);
+int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
+				bool some_data, ssize_t controllen_delta);
+int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
+						    int type);
+int con_receive_waits_for_message(con_t *conp, int tn);
+int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn);
+int con_receive_returns_what_is_available(con_t *conp, int tn);
+int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn);
+
+ //  misc
+
+char *prog_name;
+
+ //  error reporting and exiting
+
+int test_verbose = 0;	// 0 not verbose, 1 verbose, 2 even more verbose
+int ensure_failures;
+
+#define perror_exit(s) \
+	perror_exit_file_line(s, __FILE__, __FUNCTION__, __LINE__)
+
+#define strerror_exit(e, s) \
+	strerror_exit_file_line(e, s, __FILE__, __FUNCTION__, __LINE__)
+
+ //  ensure() tests something that shouldn't fail, doesn't abort like assert()
+
+#define ensure(test_number, expr)					    \
+	((expr) ? (test_verbose < 2 ||			    		    \
+		   (print_prefix(test_number),				    \
+		    printf("%s(): true: " #expr " \n", __FUNCTION__)))	    \
+		: (++ensure_failures,					    \
+		   printf("test_number = %d: %s(): false: "		    \
+			  #expr " : failed\n",				    \
+			  test_number, __FUNCTION__)))
+
+ //}
+ //{	Test functions
+ //
+
+ //  Various boundary tests related to use of more than one iovec
+
+int con_receive_iovec_boundary_checks(con_t *conp, int tn, size_t niov)
+{
+	size_t iov_max = iovec_max();
+	int salt = tn + subtest_number();	// salt test data
+
+	int client_data[niov];
+	iovec_t client_iov[niov];
+	for (int i = 0; i < niov; ++i) {
+		client_data[i] = i + salt;
+		iovec_init(&client_iov[i], &client_data[i],
+			   sizeof(client_data[0]));
+	}
+
+	int server_data[niov];
+	iovec_t server_iov[niov];
+	for (int i = 0; i < niov; ++i) {
+		server_data[i] = 0;
+		iovec_init(&server_iov[i], &server_data[i],
+			   sizeof(server_data[0]));
+	}
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, NULL, client_iov, niov, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, NULL, server_iov, niov, 0);
+
+	recvmsg_call_t rmc;
+	recvmsg_call_init(&rmc, &server_recv, 0);
+
+	con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+	ssize_t received = rmc.rmc_value;
+	if (niov > iov_max) {
+		ensure(tn, smc.smc_value == -1);
+		ensure(tn, smc.smc_errno == EMSGSIZE);
+		ensure(tn, received == -1);
+		ensure(tn, rmc.rmc_errno == EMSGSIZE);
+		ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+		return errno;
+	}
+
+	if (smc.smc_value < 0)
+		strerror_exit(smc.smc_errno, "sendmsg");
+	if (received < 0)
+		strerror_exit(rmc.rmc_errno, "recvmsg_or_1_msg_in_recvmmsg");
+	ensure(tn, received == sizeof(client_data));
+	ensure(tn, mem_is_equal(client_data, server_data, received));
+	return 0;
+}
+
+ //  Various boundary tests related to use of more than one iovec,
+ //  peek at the data first, then receive it
+
+int con_receive_iovec_boundary_checks_peeking(con_t *conp, int tn, size_t niov)
+{
+	size_t iov_max = iovec_max();
+
+	int client_data[niov];
+	iovec_t client_iov[niov];
+	for (int i = 0; i < niov; ++i) {
+		client_data[i] = i;
+		iovec_init(&client_iov[i], &client_data[i],
+			   sizeof(client_data[0]));
+	}
+
+	int server_data[niov];
+	iovec_t server_iov[niov];
+	for (int i = 0; i < niov; ++i) {
+		server_data[i] = 0;
+		iovec_init(&server_iov[i], &server_data[i],
+			   sizeof(server_data[0]));
+	}
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, NULL, client_iov, niov, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, NULL, server_iov, niov, 0);
+
+	thread_call(&smc.smc_thread_call, (thread_start_t) sendmsg_call);
+	ssize_t received = recvmsg(conp->con_server, &server_recv, MSG_PEEK);
+	thread_join(&smc.smc_thread_call);
+
+	if (niov > iov_max) {
+		ensure(tn, smc.smc_value == -1);
+		ensure(tn, smc.smc_errno == EMSGSIZE);
+		ensure(tn, received == -1);
+		ensure(tn, errno == EMSGSIZE);
+		ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+	} else {
+		if (smc.smc_value < 0)
+			strerror_exit(smc.smc_errno, "sendmsg");
+		if (received < 0)
+			perror_exit("recvmsg");
+		ensure(tn, received == sizeof(client_data));
+		ensure(tn, mem_is_equal(client_data, server_data, received));
+	}
+
+	received = recvmsg(conp->con_server, &server_recv, 0);
+	if (niov > iov_max) {
+		ensure(tn, smc.smc_value == -1);
+		ensure(tn, smc.smc_errno == EMSGSIZE);
+		ensure(tn, received == -1);
+		ensure(tn, errno == EMSGSIZE);
+		ensure(tn, mem_is_zero(server_data, sizeof(server_data)));
+		return errno;
+	}
+
+	if (smc.smc_value < 0)
+		strerror_exit(smc.smc_errno, "sendmsg");
+	if (received < 0)
+		perror_exit("recvmsg");
+	ensure(tn, received == sizeof(client_data));
+	ensure(tn, mem_is_equal(client_data, server_data, received));
+	return TEST_HAS_MULTIPLE_RECVMSG;
+}
+
+ //  Test that file descriptor is received over a unix domain socket.
+ //  Variations to test the close on exec flag, whether some amount of
+ //  data is to be sent as regular data, and missing bytes of space for
+ //  the control message to receive the file descriptor.  The missing
+ //  bytes are expressed as a (signed) ssize_t controllen_delta.  Passing
+ //  in cmsghdr_t requires a negative delta to be small enough so as to
+ //  actually cause trancation of the space for an int sized file descriptor,
+ //  a negative controllen_delta must be <= -(ssize_t)(sizeof(size_t)).
+
+int con_receive_file_descriptor(con_t *conp, int tn, int cloexec_flag,
+				bool some_data, ssize_t controllen_delta)
+{
+	assert(controllen_delta >= 0 ||
+	       controllen_delta <= -(ssize_t) (sizeof(size_t)));
+	assert(cloexec_flag == 0 || cloexec_flag == MSG_CMSG_CLOEXEC);
+
+	int pipefd[2];
+	if (pipe(pipefd) < 0)	// something to send that's easy to check
+		perror_exit("pipe");
+	int pipe_read = pipefd[0];
+	int pipe_write = pipefd[1];
+
+	ctlmsgfd_t client_ctlmsgfd;
+	ctlmsgfd_init(&client_ctlmsgfd, pipe_write);
+
+	iovec_t client_iov;
+	char server_data = 0;
+	iovec_init(&client_iov, "m", 1);
+	iovec_t server_iov;
+	iovec_init(&server_iov, &server_data, 1);
+
+	iovec_t *client_iovp = NULL;
+	iovec_t *server_iovp = NULL;
+	size_t client_iovlen = 0;
+	size_t server_iovlen = 0;
+	size_t expected = 0;
+	if (some_data) {
+		client_iovp = &client_iov;
+		server_iovp = &server_iov;
+		client_iovlen = 1;
+		server_iovlen = 1;
+		expected = 1;
+	}
+
+	msghdr_t client_send;
+	msghdr_init_with_control(&client_send, NULL, client_iovp, client_iovlen,
+				&client_ctlmsgfd, sizeof(client_ctlmsgfd), 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	ctlmsgfd_t server_ctlmsgfd;
+	ctlmsgfd_init_to_zero(&server_ctlmsgfd);
+
+	msghdr_t server_recv;
+	msghdr_init_with_control(&server_recv, NULL, server_iovp, server_iovlen,
+				&server_ctlmsgfd,
+				(size_t) ((ssize_t) sizeof(server_ctlmsgfd)
+					  + controllen_delta),
+				0);
+
+	recvmsg_call_t rmc;
+	recvmsg_call_init(&rmc, &server_recv, cloexec_flag);
+
+	con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+	ssize_t received = rmc.rmc_value;
+	if (received < 0)
+		strerror_exit(rmc.rmc_errno, "recvmsg_or_1_msg_in_recvmmsg");
+
+	ensure(tn, received == expected);
+	if (some_data)
+		ensure(tn, server_data == 'm');
+
+	if (controllen_delta < 0)
+		ensure(tn, server_recv.msg_flags & MSG_CTRUNC);
+	else {
+		ensure(tn, ! (server_recv.msg_flags & MSG_CTRUNC));
+		ensure(tn, server_ctlmsgfd.cmf_cmsg.cmsg_len >=
+		       sizeof(ctlmsgfd_t));
+		int received_fd = ctlmsgfd_get_fd(&server_ctlmsgfd);
+		ensure(tn, received_fd > 0);
+
+		size_t nwrote = write(received_fd, "p", 1);
+		if (nwrote < 0)
+			perror_exit("write");
+		ensure(tn, nwrote == 1);
+
+		char c;
+		size_t nread = read(pipe_read, &c, 1);
+		if (nread < 0)
+			perror_exit("read");
+		ensure(tn, nread == 1);
+		ensure(tn, c == 'p');
+
+		int fdflags = fcntl(received_fd, F_GETFD, 0);
+		if (fdflags == -1)
+			perror_exit("fcntl");
+		if (cloexec_flag)
+			ensure(tn, fdflags & FD_CLOEXEC);
+		else
+			ensure(tn, ! (fdflags & FD_CLOEXEC));
+		close(received_fd);
+	}
+
+	close(pipe_read);
+	close(pipe_write);
+	if (cloexec_flag)
+		return TEST_CLOEXEC_FLAG;	// can not mix flags
+	return 0;
+}
+
+ //  To be able to test this specification in the recvmsg(2) man page:
+ //
+ //	"If a message is too long to fit in the supplied buffer,
+ //	 excess bytes may be discarded depending on the type of
+ //	 socket the message is received from."
+ //
+ //  The udp(7) protocol discards the excess bytes, the sctp(7) does not.
+ //  This test is meant to be used with both, tests for the excess bytes being
+ //  discarded or for MSG_EOR being set together with the non-discarded bytes.
+ //  The test sends a 16 byte message, but receives it with an 8 byte buffer,
+ //  a second recvmsg(2) is done to see if the next 8 bytes are of a 2nd
+ //  identical 16 byte packet sent or if they belong to the first packet.
+
+int con_message_too_long_to_fit_might_discard_bytes(con_t *conp, int tn,
+						    int type)
+{
+	assert(type == SOCK_SEQPACKET || type == SOCK_DGRAM);
+
+	size_t total = 16;
+	assert(total % 2 == 0);				// must be even
+	size_t half = total / 2;
+
+	iovec_t client_iov;
+	iovec_init(&client_iov, "0123456789ABCDEF", total);
+	assert(strlen(client_iov.iov_base) == total);
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	char server_data[half + 1];	// client sends total, receive half
+	memset(&server_data, 0, half);	// 1 byte of space for GUARD_CHAR
+	server_data[half] = GUARD_CHAR;
+	iovec_t server_iov;
+	iovec_init(&server_iov, server_data, half);
+
+	sockaddr_in_t client_sin;
+	sockaddr_in_init_to_zero(&client_sin);
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+	thread_call(&smc.smc_thread_call, (thread_start_t) sendmsg_call);
+	ssize_t received = recvmsg(conp->con_server, &server_recv, 0);
+	thread_join(&smc.smc_thread_call);
+
+	if (received < 0)
+		perror_exit("recvmsg");
+
+	ensure(tn, received == half);
+	ensure(tn, server_data[half] == GUARD_CHAR);
+	ensure(tn, mem_is_equal(server_data, client_iov.iov_base, half));
+	if (type == SOCK_SEQPACKET)
+		ensure(tn, type == SOCK_SEQPACKET &&
+		       server_recv.msg_flags == 0);
+	else
+		ensure(tn, type == SOCK_DGRAM &&
+		       server_recv.msg_flags & MSG_TRUNC);
+
+	 //  send the same data in a second from client, should receive
+	 //  the same data, not the second half of the first message, if
+	 //  this test passes, then the excess bytes are being discarded,
+	 //  but if it receives the second half, then they are not being
+	 //  discarded and MSG_EOR indicates the end of the first record
+
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	thread_call(&smc.smc_thread_call, (thread_start_t) sendmsg_call);
+	received = recvmsg(conp->con_server, &server_recv, MSG_TRUNC);
+	thread_join(&smc.smc_thread_call);
+
+	if (received < 0)
+		perror_exit("recvmsg");
+
+	 //  MSG_TRUNC in this recvmsg() asks for real size of the datagram,
+	 //  not the read size, for SOCK_DGRAM this should be total not half
+	 //  the GUARD_CHAR check below ensures that no more than half were
+	 //  read, and the check on the read bytes ensures half were returned
+	 //
+	 //     "MSG_TRUNC"
+	 //	"For raw (AF_PACKET), Internet datagram, netlink, and UNIX
+	 //	 datagram sockets: return the real length of the packet or
+	 //	 datagram, even when it was longer than the passed buffer."
+	 //							-- recvmsg(2)
+
+	if (type == SOCK_DGRAM)
+		ensure(tn, received == total);
+	else
+		ensure(tn, received == half);
+	ensure(tn, server_data[half] == GUARD_CHAR);
+	if (server_recv.msg_flags & MSG_EOR) {
+		ensure(tn, server_recv.msg_flags & MSG_EOR);
+		ensure(tn, mem_is_equal(server_data,
+					client_iov.iov_base + half, half));
+	} else {
+		ensure(tn, ! (server_recv.msg_flags & MSG_EOR));
+		ensure(tn,
+		       mem_is_equal(server_data, client_iov.iov_base, half));
+	}
+	return TEST_HAS_MULTIPLE_RECVMSG;
+}
+
+ //  recvmsg(2):
+ //
+ //	"If no messages are available at the socket, the
+ //	 receive calls wait for a message to arrive"
+ //
+ //  The sending thread waits a second prior to sending to see if the
+ //  recvmsg(2) indeed waits for the message to arrive.
+
+int con_receive_waits_for_message(con_t *conp, int tn)
+{
+	size_t total = 16;
+	iovec_t client_iov;
+	iovec_init(&client_iov, "0123456789ABCDEF", total);
+	assert(strlen(client_iov.iov_base) == total);
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	char server_data[total + 1];	
+	memset(&server_data, 0, total);	// 1 byte of space for GUARD_CHAR
+	server_data[total] = GUARD_CHAR;
+	iovec_t server_iov;
+	iovec_init(&server_iov, server_data, total);
+
+	sockaddr_in_t client_sin;
+	sockaddr_in_init_to_zero(&client_sin);
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+	recvmsg_call_t rmc;
+	recvmsg_call_init(&rmc, &server_recv, 0);
+
+	con_add_send_recv_calls(conp, &smc, &rmc, true);
+
+	ssize_t received = rmc.rmc_value;
+	if (received < 0)
+		strerror_exit(rmc.rmc_errno, "recvmsg_or_1_msg_in_recvmmsg");
+
+	ensure(tn, received == total);
+	ensure(tn, server_data[total] == GUARD_CHAR);
+	ensure(tn, mem_is_equal(server_data, client_iov.iov_base, total));
+	ensure(tn, server_recv.msg_flags & MSG_EOR);
+	return TEST_NEEDS_SLEEP_IN_SENDER;
+}
+
+ //  recvmsg(2):
+ //
+ //	"If no messages are available at the socket, the
+ //	 receive calls wait for a message to arrive, unless 
+ //	 the socket is nonblocking (see fcntl(2)), in which
+ //	 case the value -1 is returned and the external variable 
+ //	 errno is set to EAGAIN or EWOULDBLOCK."
+ //
+ //  If a message is never sent, the receiver should not block.
+
+int con_receiver_doesnt_wait_for_messages(con_t *conp, int tn)
+{
+	size_t total = 16;
+	iovec_t client_iov;
+	iovec_init(&client_iov, "0123456789ABCDEF", total);
+	assert(strlen(client_iov.iov_base) == total);
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	char server_data[total + 1];	
+	memset(&server_data, 0, total);	// 1 byte of space for GUARD_CHAR
+	server_data[total] = GUARD_CHAR;
+	iovec_t server_iov;
+	iovec_init(&server_iov, server_data, total);
+
+	sockaddr_in_t client_sin;
+	sockaddr_in_init_to_zero(&client_sin);
+	msghdr_t server_recv;
+	memset(&server_recv, 0, sizeof(server_recv));
+	msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+	thread_call(&smc.smc_thread_call,
+		    (thread_start_t) sendmsg_call_after_sleep);
+	ssize_t received = recvmsg(conp->con_server,
+				   &server_recv, MSG_DONTWAIT);
+	ensure(tn, received == -1 && (errno == EAGAIN || errno == EWOULDBLOCK));
+	received = recvmsg(conp->con_server, &server_recv, 0);	// now wait
+	thread_join(&smc.smc_thread_call);
+
+	if (received < 0)
+		perror_exit("recvmsg");
+
+	ensure(tn, received == total);
+	ensure(tn, server_data[total] == GUARD_CHAR);
+	ensure(tn, mem_is_equal(server_data, client_iov.iov_base, total));
+	ensure(tn, server_recv.msg_flags & MSG_EOR);
+	return TEST_HAS_MULTIPLE_RECVMSG;
+}
+
+ //  recvmsg(2):
+ //
+ //	"The receive calls normally return any data available,
+ //	 up to the requested amount, rather than waiting for
+ //	 receipt of the full amount requested."
+
+int con_receive_returns_what_is_available(con_t *conp, int tn)
+{
+	size_t total = 8;
+	iovec_t client_iov;
+	iovec_init(&client_iov, "01234567", total);
+	assert(strlen(client_iov.iov_base) == total);
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	size_t twice = 2 * total;
+	char server_data[twice + 1];	// client sends total, want twice
+	memset(&server_data, 0, twice);	// 1 byte of space for GUARD_CHAR
+	server_data[total] = GUARD_CHAR;
+	server_data[twice] = GUARD_CHAR;	// set two guards
+	iovec_t server_iov;
+	iovec_init(&server_iov, server_data, twice);
+
+	sockaddr_in_t client_sin;
+	sockaddr_in_init_to_zero(&client_sin);
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+	recvmsg_call_t rmc;
+	recvmsg_call_init(&rmc, &server_recv, 0);
+
+	con_add_send_recv_calls(conp, &smc, &rmc, false);
+
+	ssize_t received = rmc.rmc_value;
+	if (received < 0)
+		strerror_exit(rmc.rmc_errno, "recvmsg_or_1_msg_in_recvmmsg");
+
+	ensure(tn, received == total);
+	ensure(tn, server_data[total] == GUARD_CHAR);
+	ensure(tn, server_data[twice] == GUARD_CHAR);
+	ensure(tn, mem_is_equal(server_data, client_iov.iov_base, total));
+	ensure(tn, server_recv.msg_flags & MSG_EOR);
+	return 0;
+}
+
+ //  recvmsg(2):
+ //
+ //	"Datagram sockets in various domains (e.g., the UNIX and Internet
+ //	 domains) permit zero-length datagrams. When such a datagram is
+ //	 received, the return value is 0."
+
+int con_empty_datagram_received_as_empty_datagram(con_t *conp, int tn)
+{
+	iovec_t client_iov;
+	iovec_init(&client_iov, "", 0);
+
+	msghdr_t client_send;
+	msghdr_init(&client_send, &conp->con_server_sin, &client_iov, 1, 0);
+
+	sendmsg_call_t smc;
+	sendmsg_call_init(&smc, conp->con_client, &client_send, 0);
+
+	char server_data[1];		// client sends nothing
+	server_data[0] = GUARD_CHAR;	// set guard
+	iovec_t server_iov;
+	iovec_init(&server_iov, server_data, 1);
+
+	sockaddr_in_t client_sin;
+	sockaddr_in_init_to_zero(&client_sin);
+	msghdr_t server_recv;
+	msghdr_init(&server_recv, &client_sin, &server_iov, 1, 0);
+
+	recvmsg_call_t rmc;
+	recvmsg_call_init(&rmc, &server_recv, 0);
+
+	con_add_send_recv_calls(conp, &smc, &rmc, true);
+
+	ssize_t received = rmc.rmc_value;
+	if (received < 0)
+		strerror_exit(rmc.rmc_errno, "recvmsg_or_1_msg_in_recvmmsg");
+
+	ensure(tn, received == 0);
+	ensure(tn, server_data[0] == GUARD_CHAR);
+	return TEST_NEEDS_SLEEP_IN_SENDER;
+}
+
+ //}
+ //{	The test functions above have variations based on their arguments.
+ //
+ //	The variations are wrapped with specific argument values into objects
+ //	that derive from call_t, the wrapped objects don't change so they
+ //	can be used to repeat a test multiple times for a multi-message call.
+ //
+
+call_empty_datagram_received_as_empty_datagram_t edraed_0;
+call_receive_iovec_boundary_checks_t ribc_0, ribc_1, ribc_2, ribc_3,
+				     ribc_4, ribc_5, ribc_6, ribc_7;
+call_receive_iovec_boundary_checks_peeking_t ribcp_0, ribcp_1, ribcp_2, ribcp_3,
+					     ribcp_4, ribcp_5, ribcp_6, ribcp_7;
+call_receive_file_descriptor_t rfd_0, rfd_1, rfd_2, rfd_3,
+			       rfd_4, rfd_5, rfd_6, rfd_7,
+			       rfd_8, rfd_9, rfd_10, rfd_11,
+			       rfd_12, rfd_13, rfd_14, rfd_15;
+call_message_too_long_to_fit_might_discard_bytes_t mtltfmdb_1;
+
+#ifdef RECVMMSG_USE_SCTP
+call_message_too_long_to_fit_might_discard_bytes_t mtltfmdb_0;
+call_receive_waits_for_message_t rwfm_0;
+call_receiver_doesnt_wait_for_messages_t rdwfm_0;
+call_receive_returns_what_is_available_t rwis_0;
+#endif
+
+call_t *call_tests[] = {
+	(call_t *) &edraed_0,
+	(call_t *) &mtltfmdb_1,
+	(call_t *) &ribc_0, (call_t *) &ribc_1,
+	(call_t *) &ribc_2, (call_t *) &ribc_3,
+	(call_t *) &ribc_4, (call_t *) &ribc_5,
+	(call_t *) &ribc_6, (call_t *) &ribc_7,
+	(call_t *) &ribcp_0, (call_t *) &ribcp_1,
+	(call_t *) &ribcp_2, (call_t *) &ribcp_3,
+	(call_t *) &ribcp_4, (call_t *) &ribcp_5,
+	(call_t *) &ribcp_6, (call_t *) &ribcp_7,
+	(call_t *) &rfd_0, (call_t *) &rfd_1,
+	(call_t *) &rfd_2, (call_t *) &rfd_3,
+	(call_t *) &rfd_4, (call_t *) &rfd_5,
+	(call_t *) &rfd_6, (call_t *) &rfd_7,
+	(call_t *) &rfd_8, (call_t *) &rfd_9,
+	(call_t *) &rfd_10, (call_t *) &rfd_11,
+	(call_t *) &rfd_12, (call_t *) &rfd_13,
+	(call_t *) &rfd_14, (call_t *) &rfd_15,
+#ifdef RECVMMSG_USE_SCTP
+	(call_t *) &mtltfmdb_0,
+	(call_t *) &rwfm_0,
+	(call_t *) &rdwfm_0,
+	(call_t *) &rwis_0,
+#endif
+};
+
+void tests_wrap(void)
+{
+	size_t iov_max = iovec_max();
+
+	call_empty_datagram_received_as_empty_datagram_init(&edraed_0);
+
+#ifdef RECVMMSG_USE_SCTP
+	call_receive_waits_for_message_init(&rwfm_0);
+	call_receiver_doesnt_wait_for_messages_init(&rdwfm_0);
+	call_receive_returns_what_is_available_init(&rwis_0);
+	call_message_too_long_to_fit_might_discard_bytes_init(&mtltfmdb_0,
+							      SOCK_SEQPACKET);
+#endif
+
+	call_message_too_long_to_fit_might_discard_bytes_init(&mtltfmdb_1,
+							      SOCK_DGRAM);
+
+	call_receive_iovec_boundary_checks_init(&ribc_0, 1);
+	call_receive_iovec_boundary_checks_init(&ribc_1, 2);
+	call_receive_iovec_boundary_checks_init(&ribc_2, 3);
+	call_receive_iovec_boundary_checks_init(&ribc_3, iov_max - 2);
+	call_receive_iovec_boundary_checks_init(&ribc_4, iov_max - 1);
+	call_receive_iovec_boundary_checks_init(&ribc_5, iov_max);
+	call_receive_iovec_boundary_checks_init(&ribc_6, iov_max + 1);
+	call_receive_iovec_boundary_checks_init(&ribc_7, iov_max + 2);
+
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_0, 1);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_1, 2);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_2, 3);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_3, iov_max - 2);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_4, iov_max - 1);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_5, iov_max);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_6, iov_max + 1);
+	call_receive_iovec_boundary_checks_peeking_init(&ribcp_7, iov_max + 2);
+
+	bool some_data = true;
+	bool no_data = false;
+
+	call_receive_file_descriptor_init(&rfd_0, 0, some_data, 0);
+	call_receive_file_descriptor_init(&rfd_1, 0, no_data, 0);
+	call_receive_file_descriptor_init(&rfd_2, MSG_CMSG_CLOEXEC,
+					  some_data, 0);
+	call_receive_file_descriptor_init(&rfd_3, MSG_CMSG_CLOEXEC, no_data, 0);
+
+	 //  ctlmsgfd_t (with 64 bit size_t) gets an extra int of padding
+	 //  missing bytes then has to be sizeof(size_t) not sizeof(int),
+	 //  otherwise there is still space for a sizeof(int) sized fd
+
+	call_receive_file_descriptor_init(&rfd_4, 0, some_data,
+					  -sizeof(size_t));
+	call_receive_file_descriptor_init(&rfd_5, 0, no_data,
+					  -sizeof(size_t));
+	call_receive_file_descriptor_init(&rfd_6, MSG_CMSG_CLOEXEC, some_data,
+					  -sizeof(size_t));
+	call_receive_file_descriptor_init(&rfd_7, MSG_CMSG_CLOEXEC, no_data,
+					  -sizeof(size_t));
+
+	 //  missing actual fields in the underlying cmsghdr_t
+
+	call_receive_file_descriptor_init(&rfd_8, 0, some_data,
+					  -(ssize_t) (2 * sizeof(size_t)));
+	call_receive_file_descriptor_init(&rfd_9, 0, no_data,
+					  -(ssize_t) (2 * sizeof(size_t)));
+	call_receive_file_descriptor_init(&rfd_10, MSG_CMSG_CLOEXEC, some_data,
+					  -(ssize_t) (2 * sizeof(size_t)));
+	call_receive_file_descriptor_init(&rfd_11, MSG_CMSG_CLOEXEC, no_data,
+					  -(ssize_t) (2 * sizeof(size_t)));
+
+	 //  just one byte in the underlying cmsghdr_t
+
+	call_receive_file_descriptor_init(&rfd_12, 0, some_data,
+					  -(ssize_t) (sizeof(ctlmsgfd_t) - 1));
+	call_receive_file_descriptor_init(&rfd_13, 0, no_data,
+					  -(ssize_t) (sizeof(ctlmsgfd_t) - 1));
+	call_receive_file_descriptor_init(&rfd_14, MSG_CMSG_CLOEXEC, some_data,
+					  -(ssize_t) (sizeof(ctlmsgfd_t) - 1));
+	call_receive_file_descriptor_init(&rfd_15, MSG_CMSG_CLOEXEC, no_data,
+					  -(ssize_t) (sizeof(ctlmsgfd_t) - 1));
+}
+
+ //}
+ //{	Test selection and combinations of them into multi-message tests.
+ //
+
+size_t tests_select(callconinit_t cci, call_t *tests_src[],
+			 call_t *tests_dest[], size_t n, int error)
+{
+	size_t d = 0;
+	for (size_t s = 0; s < n; ++s) {
+		call_t *callp = tests_src[s];
+		if (callp->call_con_init == cci && callp->call_errno == error) {
+			tests_dest[d] = callp;
+			++d;
+		}
+	}
+	return d;
+}
+
+enum { N_TESTS = sizeof(call_tests) / sizeof(call_tests[1]) };
+enum { N_REPEAT = 4 };
+
+void tests_run(void)
+{
+	int error;
+	size_t iov_max = iovec_max();
+	size_t max_tests = iov_max;
+	sendmsg_call_t *smc_vec[max_tests];
+	recvmsg_call_t *rmc_vec[max_tests];
+
+	 //  Run all the tests individually.
+
+	if (test_verbose > 1)
+		printf("Running %d individual sendmsg(2) tests:\n", N_TESTS);
+	call_t *callp;
+	for (int i = 0; i < N_TESTS; ++i) {
+		callp = call_tests[i];
+		con_t con;
+		callp->call_con_init(callp, &con);
+		callp->call_errno = call_go(callp, &con);
+		con_deinit(&con);
+	}
+
+	 //  Select all the socketpair() based test cases a test array so
+	 //  they can be used together in single recvmmsg() multi-message test
+	 //  that uses the same connection.
+
+	call_t *call_tests_selected[max_tests + 1];
+	assert(iov_max > N_TESTS);
+	size_t n_selected = tests_select(call_do_con_init_socketpair,
+					 call_tests, call_tests_selected,
+					 N_TESTS, 0);
+
+	 //  First call them individually sharing the connection one after
+	 //  the other to ensure there are no dependencies, for example left-
+	 //  over data leakage left in the socket between individual tests
+	 //  that might break the tests
+
+	if (test_verbose > 1)
+		printf("\nRunning %d non-error socketpair() "
+		       "tests sharing a connection:\n", n_selected);
+	con_t con;
+	con_init_socketpair(&con);
+	for (int i = 0; i < n_selected; ++i) {
+		callp = call_tests_selected[i];
+		int error = call_go(callp, &con);
+		assert(!error);
+	}
+	con_deinit(&con);
+
+	 //  We have n_selected tests that can work over the same type of
+	 //  connection and don't have any failures. Use them for mult-receive
+	 //  recvmmsg(2) testing.
+	 //
+	 //  Calling the first test causes it to call in the middle of it
+	 //  (unknown to it) the next one in a nested recursive call, the
+	 //  same occurs for each one of them, when there are no more to
+	 //  recurse all the sendmsg_call_t and recvmsg_call_t have been
+	 //  gathered and they can be used to craft the single recvmmsg(2)
+	 //  call which is tested both with n_selected sendmsg(2) calls or
+	 //  a single call to sendmmsg(2).
+	 //
+	 //  The recursion occurs, unknown by the tests, in their call to:
+	 //	con_add_send_recv_calls()
+
+	for (int sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+		con_init_socketpair(&con);
+		if (test_verbose > 1)
+			printf("\nRunning %d non-error socketpair() tests "
+			       "with a single recvmmsg(2) call and %s:\n",
+			       n_selected,
+			       sendmulti ? "a single sendmmsg(2) call"
+					 : "multiple sendmsg(2) calls");
+		con_make_vectored(&con, sendmulti > 0, n_selected,
+				  call_tests_selected, smc_vec, rmc_vec);
+		callp = call_tests_selected[0];
+		error = call_go(callp, &con);
+		assert(!error);					
+		con_deinit(&con);
+	}
+
+	 //  Run the recvmmsg(2) test from above with sleeps in the
+	 //  sender and MSG_WAITFORONE in the receiver, the receiver
+	 //  keeps on on re-trying until they are all received.
+
+	if (test_verbose > 1)
+		printf("\nRunning %d non-error socketpair() tests with as "
+		       "many MSG_WAITFORONE recvmmsg(2) calls as needed and "
+		       "individual sleeping sendmsg(2) calls\n", n_selected);
+	con_init_socketpair(&con);
+	con_make_vectored(&con, false, n_selected,
+			  call_tests_selected, smc_vec, rmc_vec);
+	con_make_waitforone_tests(&con);
+	callp = call_tests_selected[0];
+	error = call_go(callp, &con);
+	assert(!error);					
+	con_deinit(&con);
+
+	 //  Run the recvmmsg(2) test from above with sleeps in the
+	 //  sender and timeouts in the receiver, the receiver keeps
+	 //  on on re-trying until they are all received.
+
+	if (test_verbose > 1)
+		printf("\nRunning %d non-error socketpair() tests with as "
+		       "many recvmmsg(2) calls with a timeout as needed and "
+		       "individual sleeping sendmsg(2) calls\n", n_selected);
+	con_init_socketpair(&con);
+	con_make_vectored(&con, false, n_selected,
+			  call_tests_selected, smc_vec, rmc_vec);
+	con_make_timeout_tests(&con);
+	callp = call_tests_selected[0];
+	error = call_go(callp, &con);
+	assert(!error);					
+	con_deinit(&con);
+
+	 //  Get the socketpair tests that returned TEST_CLOEXEC_FLAG
+	 //  and run them sequentially to ensure there are no issues with
+	 //  the connection sharing.
+
+	n_selected = tests_select(call_do_con_init_socketpair, call_tests,
+				  call_tests_selected,
+				  N_TESTS, TEST_CLOEXEC_FLAG);
+	if (test_verbose > 1)
+		printf("\nRunning %d TEST_CLOEXEC_FLAG socketpair() "
+		       "tests sharing a connection:\n",
+		       n_selected);
+	con_init_socketpair(&con);
+	for (int i = 0; i < n_selected; ++i) {
+		callp = call_tests_selected[i];
+		int error = call_go(callp, &con);
+		assert(error == TEST_CLOEXEC_FLAG);
+	}
+	con_deinit(&con);
+
+	 //  Now run them with recvmmsg(2)
+
+	for (int sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+		if (test_verbose > 1)
+			printf("\nRunning %d TEST_CLOEXEC_FLAG socketpair() "
+			       "tests with a single recvmmsg(2) call "
+			       "and %s:\n", n_selected,
+			       sendmulti ? "a single sendmmsg(2) call"
+					 : "multiple sendmsg(2) calls");
+		con_init_socketpair(&con);
+		con_make_vectored(&con, sendmulti > 0, n_selected,
+				  call_tests_selected, smc_vec, rmc_vec);
+		callp = call_tests_selected[0];
+		error = call_go(callp, &con);
+		assert(error == TEST_CLOEXEC_FLAG);
+		con_deinit(&con);
+	}
+
+#ifdef RECVMMSG_USE_SCTP //{
+	 //  Select the SOCK_SEQPACKET tests, run them sequentially sharing
+	 //  the connection to ensure there are no issues sharing it.
+
+	n_selected = tests_select(call_do_con_init_seqpacket, call_tests,
+				  call_tests_selected, N_TESTS, 0);
+	if (test_verbose > 1)
+		printf("\nRunning %d non-error SOCK_SEQPACKET "
+		       "tests sharing a connection:\n", n_selected);
+	con_init_seqpacket(&con);
+	for (int i = 0; i < n_selected; ++i) {
+		callp = call_tests_selected[i];
+		int error = call_go(callp, &con);
+		assert(!error);
+	}
+	con_deinit(&con);
+
+	 //  Run the SOCK_SEQPACKET tests with multiple sendmsg(2) calls
+	 //  (or a single sendmmsg(2) call) and recvmmsg(2).
+
+	for (int sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+		if (test_verbose > 1)
+			printf("\nRunning %d non-error SOCK_SEQPACKET tests "
+			       "with a single recvmmsg(2) call and %s:\n",
+			       n_selected,
+			       sendmulti ? "a single sendmmsg(2) call"
+					 : "multiple sendmsg(2) calls");
+		con_init_seqpacket(&con);
+		con_make_vectored(&con, sendmulti > 0, n_selected,
+				  call_tests_selected, smc_vec, rmc_vec);
+		callp = call_tests_selected[0];
+		error = call_go(callp, &con);
+		assert(!error);					
+		con_deinit(&con);
+	}
+
+	 //  Get the SOCK_SEQPACKET tests again, repeat them N_REPEAT times
+	 //  and run them sequentially to ensure there are no issues with the
+	 //  connection sharing.
+
+	n_selected = tests_select(call_do_con_init_seqpacket, call_tests,
+				  call_tests_selected, N_TESTS, 0);
+	assert(N_REPEAT * n_selected < N_TESTS);
+	for (int i = 0; i < n_selected; ++i) {
+		callp = call_tests_selected[i];
+		for (int skip = n_selected, r = 0; r < N_REPEAT;
+		     ++r, skip += n_selected)
+			call_tests_selected[i + skip] = callp;
+	}
+
+	n_selected *= N_REPEAT;
+	if (test_verbose > 1)
+		printf("\nRunning %d non-error SOCK_SEQPACKET "
+		       "tests sharing a connection (N_REPEAT = %d):\n",
+		       n_selected, N_REPEAT);
+	con_init_seqpacket(&con);
+	for (int i = 0; i < n_selected; ++i) {
+		callp = call_tests_selected[i];
+		int error = call_go(callp, &con);
+		assert(!error);
+	}
+	con_deinit(&con);
+
+	 //  Now run them with recvmmsg(2)
+
+	for (int sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+		if (test_verbose > 1)
+			printf("\nRunning %d non-error SOCK_SEQPACKET tests "
+			       "with a single recvmmsg(2) call (N_REPEAT = %d) "
+			       "and %s:\n",
+			       n_selected, N_REPEAT,
+			       sendmulti ? "a single sendmmsg(2) call"
+					 : "multiple sendmsg(2) calls");
+		con_init_seqpacket(&con);
+		con_make_vectored(&con, sendmulti > 0, n_selected,
+				  call_tests_selected, smc_vec, rmc_vec);
+		callp = call_tests_selected[0];
+		error = call_go(callp, &con);
+		assert(!error);					
+		con_deinit(&con);
+	}
+
+	 //  Repeat the first test iov_max with recvmmsg(2)
+
+	n_selected = iov_max;
+	callp = call_tests_selected[0];
+	for (int i = 1; i < n_selected; ++i)
+		call_tests_selected[i] = callp;
+
+	for (int sendmulti = 0; sendmulti <= 1; ++sendmulti) {
+		if (test_verbose > 1)
+			printf("\nRunning %d non-error SOCK_SEQPACKET tests "
+			       "with a single recvmmsg(2) call and %s:\n",
+			       n_selected,
+			       sendmulti ? "a single sendmmsg(2) call"
+					 : "multiple sendmsg(2) calls");
+		con_init_seqpacket(&con);
+		con_make_vectored(&con, sendmulti > 0, n_selected,
+				  call_tests_selected, smc_vec, rmc_vec);
+		callp = call_tests_selected[0];
+		error = call_go(callp, &con);
+		assert(!error);					
+		con_deinit(&con);
+	}
+#endif //}
+}
+
+ //}
+ //{	Wrap and run the tests.
+ //
+
+int main(int argc, char *argv[])
+{
+	tst_parse_opt(argc, argv);
+	tests_wrap();
+	tests_run();
+	exit(ensure_failures > 0 ? 1 : 0);
+}
+
+ //}
+ //{	Utility functions and mechanism for test combinations and for
+ //	turning individual sendmsg(2) / recvmsg(2) based tests into
+ //	multi-message sendmmsg(2) / recvmmsg(2) calls are in this code.
+ //
+ //	Test functions that use a single sendmsg() and a single recvmsg() use:
+ //		con_add_send_recv_calls()
+ //
+ //	To either run the test with an individual sendmsg() and recvmsg(),
+ //	or to have those combined with the send and receive of unrelated
+ //	tests into a multi-message test.
+ //
+ //	The combination happens in:
+ //		con_do_multi_send_recv()
+
+void con_init_base(con_t *conp, int client, int server,
+	      int type, sockaddr_in_t *sinp)
+{
+	conp->con_type = type;
+	conp->con_client = client;
+	conp->con_server = server;
+	if (!sinp)
+		memset(&conp->con_server_sin, 0 , sizeof(conp->con_server_sin));
+	else
+		conp->con_server_sin = *sinp;
+}
+
+void con_init(con_t *conp, int client, int server,
+	      int type, sockaddr_in_t *sinp)
+{
+	con_init_base(conp, client, server, type, sinp);
+	conp->con_call_sendmmsg = false;
+	conp->con_sender_sleeps = false;	// determined later
+	conp->con_waitforone = false;
+	conp->con_timeout = false;
+	conp->con_call_end = 0;
+	conp->con_call_n = 0;
+	conp->con_call_vec = NULL;
+	conp->con_smc_vec = NULL;
+	conp->con_rmc_vec = NULL;
+}
+
+void con_make_vectored(con_t *conp, bool sendmulti,
+		       size_t n, call_t **call_vec,
+		       sendmsg_call_t **smc_vec, recvmsg_call_t **rmc_vec)
+{
+	assert(n >= 1 && call_vec && smc_vec && rmc_vec);
+	assert(conp->con_client >= 0 &&
+	       conp->con_server >= 0 &&
+	       !conp->con_call_sendmmsg &&
+	       !conp->con_call_end &&
+	       !conp->con_call_n &&
+	       !conp->con_call_vec &&
+	       !conp->con_smc_vec &&
+	       !conp->con_rmc_vec);
+	conp->con_call_sendmmsg = sendmulti;
+	conp->con_call_end = 0;
+	conp->con_call_n = n;
+	conp->con_call_vec = call_vec;
+	conp->con_smc_vec = smc_vec;
+	conp->con_rmc_vec = rmc_vec;
+}
+
+void con_make_waitforone_tests(con_t *conp)
+{
+	assert(conp->con_call_vec);
+	assert(!conp->con_call_sendmmsg);
+	conp->con_timeout = true;
+}
+
+void con_make_timeout_tests(con_t *conp)
+{
+	assert(conp->con_call_vec);
+	assert(!conp->con_call_sendmmsg);
+	conp->con_waitforone = true;
+}
+
+void con_add_send_recv_calls_vec(con_t *conp, sendmsg_call_t *smcp,
+				 recvmsg_call_t *rmcp, bool sender_sleeps)
+{
+	assert(conp->con_call_end < conp->con_call_n);
+	int i = conp->con_call_end;
+
+	 //  All senders must either: need to sleep or must not sleep.
+
+	if (i == 0)
+		conp->con_sender_sleeps = sender_sleeps;
+	else
+		assert(conp->con_sender_sleeps == sender_sleeps);
+	conp->con_smc_vec[i] = smcp;
+	conp->con_rmc_vec[i] = rmcp;
+
+	++i;
+	conp->con_call_end = i;
+	if (i == conp->con_call_n)
+		con_do_multi_send_recv(conp);
+	else {
+		subtest_nest();
+		call_t *callp = conp->con_call_vec[i];
+		int error = call_go(callp, conp);
+		assert(!errno_value_is_error(error));
+		subtest_unnest();
+	}
+}
+
+void *sendmmsg_call(sendmmsg_call_t *smmcp)
+{
+	errno = 0;
+
+	int sockfd = smmcp->smmc_sockfd;
+	int vlen = smmcp->smmc_vlen;
+	mmsghdr_t *smm = smmcp->smmc_smm;;
+	int flags = smmcp->smmc_flags;
+	bool call_sendmmsg = smmcp->smmc_call_sendmmsg;
+	bool sender_sleeps = smmcp->smmc_sender_sleeps;
+
+	int nsent;
+	if (call_sendmmsg)
+		nsent = sendmmsg(sockfd, smm, vlen, flags);
+	else {
+		for (nsent = 0; nsent < vlen; ++nsent) {
+			if (sender_sleeps)
+				sleep_ms(SENDMSG_SLEEP_MS);
+			int value = sendmsg(sockfd, &smm[nsent].msg_hdr, flags);
+			assert(value >= 0);
+			smm[nsent].msg_len = value;
+		}
+	}
+
+	smmcp->smmc_value = nsent;
+	smmcp->smmc_errno = errno;
+	return NULL;
+}
+
+void con_sendmmsg_recvmmsg(con_t *conp, size_t n,
+			   int sflags, mmsghdr_t *smm,
+			   int rflags, mmsghdr_t *rmm)
+{
+	bool waitforone = conp->con_waitforone;
+	bool timeout = conp->con_timeout;
+	assert(!waitforone || !timeout);
+
+	sendmmsg_call_t smmc;
+	sendmmsg_init(&smmc, conp->con_client, (unsigned) n,
+		      smm, sflags, conp->con_call_sendmmsg,
+		      waitforone || timeout || conp->con_sender_sleeps);
+
+	thread_call(&smmc.smmc_thread_call, (thread_start_t) sendmmsg_call);
+
+	int nrecv;
+	if (!waitforone && !timeout) {
+		nrecv = recvmmsg(conp->con_server, rmm, n, rflags, NULL);
+		assert(nrecv == n);
+	} else {
+		timespec_t ts, *tsp = NULL;
+		if (timeout) {
+			ts.tv_sec = 0;
+			ts.tv_nsec = 100 * 1000 * 1000;
+			tsp = &ts;
+		}
+		if (waitforone)
+			rflags |= MSG_WAITFORONE;
+		int nleft = n;
+		do {
+			nrecv = recvmmsg(conp->con_server, rmm,
+					 nleft, rflags, tsp);
+			if (waitforone)
+				assert(nrecv > 0);
+			if (timeout && nrecv == -1) {
+				nrecv = 0;
+				assert(errno == ETIMEDOUT ||
+				       errno == EAGAIN ||
+				       errno == EWOULDBLOCK);
+			}
+			nleft -= nrecv;
+			rmm += nrecv;
+		} while (nleft > 0);
+	}
+
+	thread_join(&smmc.smmc_thread_call);
+
+	int nsent = smmc.smmc_value;
+	assert(nsent == n);
+}
+
+void con_do_multi_send_recv(con_t *conp)
+{
+	assert(conp->con_call_n >= 1 && conp->con_call_n <= iovec_max());
+	size_t n = conp->con_call_n;
+	mmsghdr_t smm[n];
+	mmsghdr_t rmm[n];
+
+	 //  Gather the arguments from all the sendmsg_call_t and the
+	 //  recvmsg_call_t into the smm[] and rmm[] vectors for the
+	 //  multi-message send and receive syscalls.
+
+	 //  All the send and recv flags must be the same, there is
+	 //  a single argument for the flags in sendmmsg(2) and in
+	 //  recvmmsg(2), there is not an argument per message.
+
+	int sflags = conp->con_smc_vec[0]->smc_flags;
+	int rflags = conp->con_rmc_vec[0]->rmc_flags;
+	for (int i = 0; i < n; i++) {
+		assert(conp->con_smc_vec[i]->smc_flags == sflags);
+		assert(conp->con_rmc_vec[i]->rmc_flags == rflags);
+		mmsghdr_init(&smm[i], conp->con_smc_vec[i]->smc_msg);
+		mmsghdr_init(&rmm[i], conp->con_rmc_vec[i]->rmc_msg);
+	}
+
+	con_sendmmsg_recvmmsg(conp, n, sflags, smm, rflags, rmm);
+
+	 //  Scatter the results from smm[] and rmm[].
+
+	for (int i = 0; i < n; i++) {
+		conp->con_smc_vec[i]->smc_value = smm[i].msg_len;
+		conp->con_smc_vec[i]->smc_msg->msg_flags =
+						  smm[i].msg_hdr.msg_flags;
+		conp->con_rmc_vec[i]->rmc_value = rmm[i].msg_len;
+		conp->con_rmc_vec[i]->rmc_msg->msg_flags =
+						  rmm[i].msg_hdr.msg_flags;
+	}
+}
+
+void con_init_socketpair(con_t *conp)
+{
+	int pair[2];
+	if (socketpair(AF_UNIX, SOCK_SEQPACKET, 0, pair) < 0)
+		perror_exit("socketpair");
+	con_init(conp, pair[0], pair[1], SOCK_SEQPACKET, NULL);
+}
+
+void con_init_seqpacket(con_t *conp)
+{
+	con_init_with_type(conp, SOCK_SEQPACKET);
+}
+
+void con_init_dgram(con_t *conp)
+{
+	con_init_with_type(conp, SOCK_DGRAM);
+}
+
+void con_init_with_type(con_t *conp, int type)
+{
+#ifndef RECVMMSG_USE_SCTP
+	assert(type != SOCK_SEQPACKET);
+#endif
+	int protocol = (type == SOCK_SEQPACKET) ? IPPROTO_SCTP : 0;
+	int server = socket(PF_INET, type, protocol);
+	if (server < 0)
+		perror_exit("socket");
+
+	sockaddr_in_t sin;
+	sockaddr_in_init(&sin, AF_INET, TEST_PORT, TEST_IP);
+	if (bind(server, (sockaddr_t *) &sin, sizeof(sin)) < 0)
+		perror_exit("bind");
+
+	int client = socket(PF_INET, type, protocol);
+	if (client < 0)
+		perror_exit("socket");
+
+	if (type == SOCK_SEQPACKET && listen(server, 1) < 0)
+		perror_exit("listen");
+
+	con_init(conp, client, server, type, &sin);
+}
+
+void con_deinit(con_t *conp)
+{
+	close(conp->con_client);
+	close(conp->con_server);
+}
+
+void con_add_send_recv_calls(con_t *conp, sendmsg_call_t *smcp,
+			     recvmsg_call_t *rmcp, bool sender_sleeps)
+{
+	if (conp->con_call_n > 0) {
+		con_add_send_recv_calls_vec(conp, smcp, rmcp, sender_sleeps);
+		return;
+	}
+
+	thread_call(&smcp->smc_thread_call, (thread_start_t)
+		    (sender_sleeps ? sendmsg_call_after_sleep : sendmsg_call));
+	timeval_t pretv, postv;
+	if (sender_sleeps && gettimeofday(&pretv, NULL) < 0)
+		perror_exit("gettimeofday()");
+	ssize_t val = recvmsg(conp->con_server, rmcp->rmc_msg, rmcp->rmc_flags);
+	if (val < 0)
+		rmcp->rmc_errno = errno;
+	if (sender_sleeps) {
+
+		 //  If the sender is supposed to sleep prior to sending then
+		 //  the receiver is supposed to sleep too because its receive
+		 //  should block for the sender, testing that sender blocked
+		 //  for at least half as long as the sender sleeps is good
+		 //  enough to ensure that scheduling noise doesn't affect test.
+
+		if (gettimeofday(&postv, NULL) < 0)
+			perror_exit("gettimeofday()");
+		unsigned long long pre = pretv.tv_sec * 1000;
+		pre += pretv.tv_usec * 1000;
+		unsigned long long pos = postv.tv_sec * 1000;
+		pos += postv.tv_usec * 1000;
+		assert((pos - pre) > SENDMSG_SLEEP_MS / 2);
+	}
+
+	rmcp->rmc_value = val;
+	thread_join(&smcp->smc_thread_call);
+}
+
+ //}
+ //{	Initialization of test functions into wrapped tests into classes
+ //	that dervice from call_t. For each test function there is an init
+ //	function and a test running function. All the test running funcitons
+ //	have the same signature, so the calling mechainsm doesn't have to
+ //	know about their arguments which are wrapped into the classes that
+ //	inherit from call_t.
+ //
+
+ //  Tests bundled into their own call_t types so they can be intermixed
+ //  in multi-message sends and receives (sendmmsg(2) and recvmmsg(2)
+
+void call_receive_iovec_boundary_checks_init(
+	call_receive_iovec_boundary_checks_t *callp, size_t niov)
+{
+	call_base_init(&callp->call_base, call_do_con_init_socketpair,
+		       (calltest_t) call_receive_iovec_boundary_checks);
+	callp->call_niov = niov;
+}
+int call_receive_iovec_boundary_checks(
+	call_receive_iovec_boundary_checks_t *callp, con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receive_iovec_boundary_checks(niov = %d)"
+		       " // iov_max = %d\n",
+		       (int) callp->call_niov, (int) iovec_max());
+	return con_receive_iovec_boundary_checks(conp, tn, callp->call_niov);
+}
+
+void call_receive_iovec_boundary_checks_peeking_init(
+	call_receive_iovec_boundary_checks_peeking_t *callp, size_t niov)
+{
+	call_base_init(&callp->call_base, call_do_con_init_socketpair,
+		       (calltest_t) call_receive_iovec_boundary_checks_peeking);
+	callp->call_niov = niov;
+}
+int call_receive_iovec_boundary_checks_peeking(
+	call_receive_iovec_boundary_checks_peeking_t *callp,
+	con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receive_iovec_boundary_checks_peeking(niov = %d)"
+		       " // iov_max = %d\n",
+		       (int) callp->call_niov, (int) iovec_max());
+	return con_receive_iovec_boundary_checks_peeking(conp, tn,
+							 callp->call_niov);
+}
+
+void call_receive_file_descriptor_init(
+	call_receive_file_descriptor_t *callp,
+	int cloexec_flag, bool some_data, ssize_t controllen_delta)
+{
+	call_base_init(&callp->call_base, call_do_con_init_socketpair,
+		       (calltest_t) call_receive_file_descriptor);
+	callp->call_cloexec_flag = cloexec_flag;
+	callp->call_some_data = some_data;
+	callp->call_controllen_delta = controllen_delta;
+}
+int call_receive_file_descriptor(
+	call_receive_file_descriptor_t *callp, con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receive_file_descriptor("
+		       "cloexec_flag = %s, some_data = %d, "
+		       "controllen_delta = %ld)\n",
+		       (callp->call_cloexec_flag & MSG_CMSG_CLOEXEC) ?
+					"MSG_CMSG_CLOEXEC" : "0",
+		       callp->call_some_data,
+		       (long) callp->call_controllen_delta);
+	return con_receive_file_descriptor(conp, tn, callp->call_cloexec_flag,
+					   callp->call_some_data,
+					   callp->call_controllen_delta);
+}
+
+void call_msgtoolong_do_con_init_from_type(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp, con_t *conp);
+
+void call_msgtoolong_do_con_init_from_type(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp, con_t *conp)
+{
+	con_init_with_type(conp, callp->call_type);
+}
+
+void call_message_too_long_to_fit_might_discard_bytes_init(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp,
+	int type)
+{
+	call_base_init(&callp->call_base,
+		       (callconinit_t) call_msgtoolong_do_con_init_from_type,
+		       (calltest_t)
+		       call_message_too_long_to_fit_might_discard_bytes);
+	callp->call_type = type;
+}
+int call_message_too_long_to_fit_might_discard_bytes(
+	call_message_too_long_to_fit_might_discard_bytes_t *callp,
+	con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_message_too_long_to_fit_might_discard_bytes("
+		       "type = %s)\n",
+		       sock_type_to_str(callp->call_type));
+	return con_message_too_long_to_fit_might_discard_bytes(conp, tn,
+							callp->call_type);
+}
+
+void call_empty_datagram_received_as_empty_datagram_init(
+	call_empty_datagram_received_as_empty_datagram_t *callp)
+{
+	call_base_init(&callp->call_base, call_do_con_init_dgram,
+		       (calltest_t)
+		       call_empty_datagram_received_as_empty_datagram);
+}
+int call_empty_datagram_received_as_empty_datagram(
+	call_empty_datagram_received_as_empty_datagram_t *callp,
+	con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_empty_datagram_received_as_empty_datagram()\n");
+	return con_empty_datagram_received_as_empty_datagram(conp, tn);
+}
+
+#ifdef RECVMMSG_USE_SCTP //{
+
+void call_receive_waits_for_message_init(
+	call_receive_waits_for_message_t *callp)
+{
+	call_base_init(&callp->call_base, call_do_con_init_seqpacket,
+		       (calltest_t) call_receive_waits_for_message);
+}
+int call_receive_waits_for_message(
+	call_receive_waits_for_message_t *callp, con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receive_waits_for_message()\n");
+	return con_receive_waits_for_message(conp, tn);
+}
+
+void call_receiver_doesnt_wait_for_messages_init(
+	call_receiver_doesnt_wait_for_messages_t *callp)
+{
+	call_base_init(&callp->call_base, call_do_con_init_seqpacket,
+		       (calltest_t) call_receiver_doesnt_wait_for_messages);
+}
+int call_receiver_doesnt_wait_for_messages(
+	call_receiver_doesnt_wait_for_messages_t *callp, con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receiver_doesnt_wait_for_messages()\n");
+	return con_receiver_doesnt_wait_for_messages(conp, tn);
+}
+
+void call_receive_returns_what_is_available_init(
+	call_receive_returns_what_is_available_t *callp)
+{
+	call_base_init(&callp->call_base, call_do_con_init_seqpacket,
+		       (calltest_t) call_receive_returns_what_is_available);
+}
+int call_receive_returns_what_is_available(
+	call_receive_returns_what_is_available_t *callp, con_t *conp, int tn)
+{
+	print_nest(tn);
+	if (test_verbose)
+		printf("con_receive_returns_what_is_available()\n");
+	return con_receive_returns_what_is_available(conp, tn);
+}
+
+#endif //}
+
+ //}
+ //{	Miscellaneous supporting code is in this section.
+ //
+
+void perror_exit_file_line(const char *s, const char *file,
+			   const char *func, int line)
+{
+	fprintf(stderr, "%s: %s:%d %s() ", prog_name, file, line, func);
+	perror(s);
+	exit(1);
+}
+
+void strerror_exit_file_line(int e, const char *s,
+			     const char *file, const char *func, int line)
+{
+	fprintf(stderr, "%s: %s:%d %s() %s: %s\n",
+		prog_name, file, line, func, s, strerror(e));
+	exit(1);
+}
+
+void tst_parse_opt(int argc, char *argv[])
+{
+	prog_name = argv[0];
+	char *lasts = strrchr(prog_name, '/');
+	if (lasts)
+		prog_name = lasts + 1;
+	TCID = prog_name;
+	while (--argc >= 1) {
+		char *arg = *++argv;
+		if (strcmp(arg, "-v") == 0)
+			++test_verbose;
+		else {
+			fprintf(stderr, "usage: %s [-v]\n", prog_name);
+			exit(1);
+		}
+	}
+}
+
+int errno_value_is_error(int e)
+{
+	return e > 0;	// negative values are not errors, see non_error_errno
+}
+
+void sleep_ms(long ms)
+{
+	assert(ms < 1000);
+	timespec_t ts;
+	ts.tv_sec = 0;
+	ts.tv_nsec = ms * 1000 * 1000;
+	nanosleep(&ts, NULL);
+}
+
+void print_nest(int test_number)
+{
+	if (test_verbose) {
+		if (subtest_number() == 1)
+			printf("\n");
+		print_prefix(test_number);
+	}
+}
+
+void print_prefix(int test_number)
+{
+	if (test_verbose) {
+		printf("%s%5d        :  [subtest=%d] ",
+		       TCID, test_number, subtest_number());
+	}
+}
+
+int test_number_get(void)
+{
+	static int test_number_gen;
+
+	 //  Nested subtests share the same test number, these are multi
+	 //  message tests, i.e. recvmmsg() and sendmmsg(), where each message
+	 //  is treated as a subtest
+
+	if (subtest_number() > 1)
+		return test_number_gen;
+	return ++test_number_gen;
+}
+
+ //  subtests numbers
+
+int subtest_level = 1;
+
+int subtest_number(void)
+{
+	return subtest_level;
+}
+
+void subtest_nest(void)
+{
+	++subtest_level;
+}
+
+void subtest_unnest(void)
+{
+	--subtest_level;
+}
+
+ //  compare that memory is equal, makes the output of various ensure() nicer
+
+bool mem_is_equal(void *a, void *b, size_t size)
+{
+	return !memcmp(a, b, size);
+}
+
+bool mem_is_zero(void *a, size_t size)
+{
+	unsigned char set = 0;
+	unsigned char *p = a;
+	unsigned char *endp = p + size;
+	while (p < endp) set |= *p++;
+	return !set;
+}
+
+void thread_call(thread_call_t *tcp, thread_start_t func)
+{
+	int error = pthread_create(&tcp->tc_thr, NULL, func, tcp);
+	if (error)
+		strerror_exit(error, "pthread_create");
+}
+
+void thread_join(thread_call_t *tcp)
+{
+	int error = pthread_join(tcp->tc_thr, NULL);
+	if (error)
+		strerror_exit(error, "pthread_join");
+}
+
+void sendmmsg_init(sendmmsg_call_t *smmcp, int sockfd, unsigned vlen,
+		   mmsghdr_t *smm, int flags, bool call_sendmmsg,
+		   bool sender_sleeps)
+{
+	smmcp->smmc_sockfd = sockfd;
+	smmcp->smmc_vlen = vlen;
+	smmcp->smmc_smm = smm;
+	smmcp->smmc_flags = flags;
+	smmcp->smmc_call_sendmmsg = call_sendmmsg;
+	smmcp->smmc_sender_sleeps = sender_sleeps;
+	smmcp->smmc_value = INVALID_SYSCALL_VALUE;
+	smmcp->smmc_errno = 0;
+}
+
+ //  various init and member functions for various types
+
+void sockaddr_in_init(sockaddr_in_t *sinp, sa_family_t family,
+		      in_port_t port, uint32_t ip)
+{
+	sockaddr_in_init_to_zero(sinp);
+	sinp->sin_family = family;
+	sinp->sin_port = htons(port);
+	sinp->sin_addr.s_addr = htonl(ip);
+}
+
+void sockaddr_in_init_to_zero(sockaddr_in_t *sinp)
+{
+	memset(sinp, 0, sizeof(*sinp));
+}
+
+char *sock_type_to_str(int type)
+{
+	switch (type) {
+	case SOCK_DCCP:		return "SOCK_DCCP";
+	case SOCK_DGRAM:	return "SOCK_DGRAM";
+	case SOCK_PACKET:	return "SOCK_PACKET";
+	case SOCK_RAW:		return "SOCK_RAW";
+	case SOCK_RDM:		return "SOCK_RDM";
+	case SOCK_SEQPACKET:	return "SOCK_SEQPACKET";
+	case SOCK_STREAM:	return "SOCK_STREAM";
+	default:		return "socket_type_unknown!";
+	}
+}
+
+size_t iovec_max(void)
+{
+	long iov_max = sysconf(_SC_IOV_MAX);
+	if (iov_max == -1)
+		perror_exit("sysconf");
+	assert(iov_max > 2);
+	return (size_t) iov_max;
+}
+
+void iovec_init(iovec_t *iovecp, void *base, size_t len)
+{
+	iovecp->iov_base = base;
+        iovecp->iov_len = len;
+}
+
+void msghdr_init(msghdr_t *msgp, sockaddr_in_t *sinp,
+		 iovec_t *iovecp, size_t iovlen, int flags)
+{
+	msghdr_init_with_control(msgp, sinp, iovecp, iovlen, NULL, 0, flags);
+}
+
+void msghdr_init_with_control(msghdr_t *msgp, sockaddr_in_t *sinp,
+			      iovec_t *iovecp, size_t iovlen,
+			      void *control, size_t controllen,
+			      int flags)
+{
+	memset(msgp, 0, sizeof(*msgp));
+	msgp->msg_name = sinp;
+	msgp->msg_namelen = sinp ? sizeof(*sinp) : 0;
+	msgp->msg_iov =iovecp;
+	msgp->msg_iovlen = iovlen;
+	msgp->msg_control = control;
+	msgp->msg_controllen = controllen;
+	msgp->msg_flags = flags;
+}
+
+void mmsghdr_init(mmsghdr_t *mmsgp, msghdr_t *msgp)
+{
+	mmsgp->msg_hdr = *msgp;
+	mmsgp->msg_len = 0;
+}
+
+void recvmsg_call_init(recvmsg_call_t *rmcp, msghdr_t *msgp, int flags)
+{
+	rmcp->rmc_value = INVALID_SYSCALL_VALUE;
+	rmcp->rmc_errno = 0;
+	rmcp->rmc_msg = msgp;
+	rmcp->rmc_flags = flags;
+}
+
+void sendmsg_call_init(sendmsg_call_t *smcp, int sockfd,
+		       msghdr_t *msgp, int flags)
+{
+	smcp->smc_value = INVALID_SYSCALL_VALUE;
+	smcp->smc_errno = 0;
+	smcp->smc_sockfd = sockfd;
+	smcp->smc_msg = msgp;
+	smcp->smc_flags = flags;
+}
+
+ //  functions that can be called directly through pthread_create(3)
+
+void *sendmsg_call(sendmsg_call_t *smcp)
+{
+	errno = 0;
+	smcp->smc_value = sendmsg(smcp->smc_sockfd, smcp->smc_msg,
+				  smcp->smc_flags);
+	smcp->smc_errno = errno;
+	return NULL;
+}
+
+void *sendmsg_call_after_sleep(sendmsg_call_t *smcp)
+{
+	sleep_ms(SENDMSG_SLEEP_MS);
+	return sendmsg_call(smcp);
+}
+
+void ctlmsgfd_init(ctlmsgfd_t *cmfp, int fd)
+{
+	memset(cmfp, 0, sizeof(*cmfp));
+	cmfp->cmf_cmsg.cmsg_len = sizeof(*cmfp);
+        cmfp->cmf_cmsg.cmsg_level = SOL_SOCKET;
+        cmfp->cmf_cmsg.cmsg_type = SCM_RIGHTS;
+	*(int *) CMSG_DATA(&cmfp->cmf_cmsg) = fd;
+}
+
+void ctlmsgfd_init_to_zero(ctlmsgfd_t *cmfp)
+{
+	memset(cmfp, 0, sizeof(*cmfp));
+}
+
+int ctlmsgfd_get_fd(ctlmsgfd_t *cmfp)
+{
+	return *(int *) CMSG_DATA(&cmfp->cmf_cmsg);
+}
+
+int call_go(call_t *callp, con_t *conp)
+{
+	static int max_subtest_level = 1;
+
+	int failures = ensure_failures;
+	int tn = test_number_get();
+	int error = callp->call_test(callp, conp, tn);
+	if (subtest_number() > 1) {
+		if (subtest_number() > max_subtest_level)
+			max_subtest_level = subtest_number();
+	} else {
+		bool pass = failures == ensure_failures;
+		if (test_verbose > 0)
+			printf("%s%5d  %s  :  [%d subtests]\n", TCID, tn,
+			       pass ? "PASS" : "FAIL", max_subtest_level);
+		else
+			tst_res(pass ? TPASS : TFAIL, "[%d subtests]",
+				max_subtest_level);
+		max_subtest_level = 1;
+	}
+	return error;
+}
+
+void call_do_con_init_socketpair(call_t *callp, con_t *conp)
+{
+	con_init_socketpair(conp);
+}
+void call_do_con_init_seqpacket(call_t *callp, con_t *conp)
+{
+	con_init_seqpacket(conp);
+}
+void call_do_con_init_dgram(call_t *callp, con_t *conp)
+{
+	con_init_dgram(conp);
+}
+
+void call_base_init(call_t *callp, callconinit_t coninit, calltest_t test)
+{
+	callp->call_con_init = coninit;
+	callp->call_test = test;
+	callp->call_errno = 0;
+}
+
+ //}
-- 
2.19.1.568.g152ad8e336-goog