[LTP] [PATCH v3 2/2] syscalls/{send|recv}mmsg: add a test case for timeout and errno test

Cyril Hrubis chrubis@suse.cz
Tue Aug 18 16:37:15 CEST 2020


Hi!
> diff --git a/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c b/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> index 54febf661..d7d5d33cc 100644
> --- a/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> +++ b/testcases/kernel/syscalls/sendmmsg/sendmmsg01.c
> @@ -24,6 +24,36 @@ static int receive_sockfd;
>  static struct mmsghdr *snd_msg, *rcv_msg;
>  static struct iovec *snd1, *snd2, *rcv1, *rcv2;
>  
> +enum test_type {
> +	NORMAL,
> +	TIMEOUT,
> +};
> +
> +#define TYPE_NAME(x) .ttype = x, .desc = #x
> +
> +struct test_case {
> +	int ttype;
> +	const char *desc;
> +	long tv_sec;
> +	long tv_nsec;
> +	int exp_ret;
> +};
> +
> +static struct test_case tcase[] = {
> +	{
> +		TYPE_NAME(NORMAL),
> +		.tv_sec = 1,
> +		.tv_nsec = 0,
> +		.exp_ret = 2,
> +	},
> +	{
> +		TYPE_NAME(TIMEOUT),
> +		.tv_sec = 0,
> +		.tv_nsec = 1,
> +		.exp_ret = 1,

The manual states that the timeout is checked after each datagram is
received so I guess that we can as well set the timeout here to both sec
and nsec to zero which would be a bit more clear than nsec = 1.

> +	},
> +};
> +
>  static struct test_variants {
>  	int (*receive)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
>  		       unsigned int flags, void *timeout);
> @@ -43,14 +73,17 @@ static struct test_variants {
>  #endif
>  };
>  
> -static void run(void)
> +static void do_test(unsigned int i)
>  {
>  	struct test_variants *tv = &variants[tst_variant];
> +	struct test_case *tc = &tcase[i];
>  	struct tst_ts timeout;
> -	int retval;
>  
> -	retval = tv->send(send_sockfd, snd_msg, VLEN, 0);
> -	if (retval < 0 || snd_msg[0].msg_len != 6 || snd_msg[1].msg_len != 6) {
> +	tst_res(TINFO, "case %s", tc->desc);
> +
> +	TEST(tv->send(send_sockfd, snd_msg, VLEN, 0));
> +
> +	if (TST_RET < 0 || snd_msg[0].msg_len != 6 || snd_msg[1].msg_len != 6) {
>  		tst_res(TFAIL | TERRNO, "sendmmsg() failed");
>  		return;
>  	}
> @@ -59,18 +92,18 @@ static void run(void)
>  	memset(rcv2->iov_base, 0, rcv2->iov_len);
>  
>  	timeout.type = tv->type;
> -	tst_ts_set_sec(&timeout, 1);
> -	tst_ts_set_nsec(&timeout, 0);
> +	tst_ts_set_sec(&timeout, tc->tv_sec);
> +	tst_ts_set_nsec(&timeout, tc->tv_nsec);
>  
> -	retval = tv->receive(receive_sockfd, rcv_msg, VLEN, 0, tst_ts_get(&timeout));
> +	TEST(tv->receive(receive_sockfd, rcv_msg, VLEN, 0, tst_ts_get(&timeout)));
>  
> -	if (retval == -1) {
> +	if (TST_RET == -1) {
>  		tst_res(TFAIL | TERRNO, "recvmmsg() failed");
>  		return;
>  	}
> -	if (retval != 2) {
> +	if (tc->exp_ret != TST_RET) {
>  		tst_res(TFAIL, "Received unexpected number of messages (%d)",
> -			retval);
> +			TST_RET);
>  		return;
>  	}
>  
> @@ -79,10 +112,14 @@ static void run(void)
>  	else
>  		tst_res(TPASS, "First message received successfully");
>  
> -	if (memcmp(rcv2->iov_base, "three", 5))
> -		tst_res(TFAIL, "Error in second received message");
> -	else
> -		tst_res(TPASS, "Second message received successfully");
> +	if (tc->ttype == NORMAL) {
> +		if (memcmp(rcv2->iov_base, "three", 5))
> +			tst_res(TFAIL, "Error in second received message");
> +		else
> +			tst_res(TPASS, "Second message received successfully");
> +	} else {
> +		tst_res(TPASS, "Timeout successfully reached before second message");

	This actually breaks the test with -i 2 option, which runs the run()
	function twice.

	We have to receive the second message here as well for the timeout test.

> +	}
>  }
>  
>  static void setup(void)
> @@ -128,7 +165,8 @@ static void cleanup(void)
>  }
>  
>  static struct tst_test test = {
> -	.test_all = run,
> +	.tcnt = ARRAY_SIZE(tcase),
> +	.test = do_test,
>  	.setup = setup,
>  	.cleanup = cleanup,
>  	.test_variants = ARRAY_SIZE(variants),
> diff --git a/testcases/kernel/syscalls/sendmmsg/sendmmsg02.c b/testcases/kernel/syscalls/sendmmsg/sendmmsg02.c
> new file mode 100644
> index 000000000..37e6ea66e
> --- /dev/null
> +++ b/testcases/kernel/syscalls/sendmmsg/sendmmsg02.c
> @@ -0,0 +1,235 @@
> +// SPDX-License-Identifier: GPL-2.0-or-later

Can we please include a comment with a high level description of the
test here?

> +#define _GNU_SOURCE
> +#include <netinet/ip.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <sys/types.h>
> +
> +#include "tst_test.h"
> +#include "lapi/socket.h"
> +#include "tst_safe_macros.h"
> +#include "sendmmsg_var.h"
> +
> +#define BUFSIZE 16
> +#define VLEN 2
> +
> +static int send_sockfd;
> +static int receive_sockfd;
> +static struct mmsghdr *snd_msg, *rcv_msg;
> +static struct iovec *snd1, *snd2, *rcv1, *rcv2;
> +static struct tst_ts ts;
> +
> +enum test_type {
> +	BAD_SEND_FD,
> +	BAD_RECV_FD,
> +	BAD_SEND_MSGVEC,
> +	BAD_RECV_MSGVEC,
> +	BAD_TS_VALUE_1,
> +	BAD_TS_VALUE_2,
> +	BAD_TS_ADDR,
> +};
> +
> +#define TYPE_NAME(x) .ttype = x, .desc = #x
> +
> +static void *bad_addr;
> +
> +struct test_case {
> +	int ttype;
> +	const char *desc;
> +	int send_fd;
> +	int recv_fd;
> +	long tv_sec;
> +	long tv_nsec;
> +	int exp_send_ret;
> +	int exp_send_errno;
> +	int exp_recv_errno;
> +};
> +
> +static struct test_case tcase[] = {
> +	{
> +		TYPE_NAME(BAD_SEND_FD),
> +		.send_fd = -1,
> +		.exp_send_ret = -1,
> +		.exp_send_errno = EBADF,
> +	},
> +	{
> +		TYPE_NAME(BAD_RECV_FD),
> +		.exp_send_ret = VLEN,
> +		.recv_fd = -1,
> +		.exp_recv_errno = EBADF,
> +	},
> +	{
> +		TYPE_NAME(BAD_SEND_MSGVEC),
> +		.exp_send_ret = -1,
> +		.exp_send_errno = EFAULT,
> +	},
> +	{
> +		TYPE_NAME(BAD_RECV_MSGVEC),
> +		.exp_send_ret = VLEN,
> +		.exp_recv_errno = EFAULT,
> +	},
> +	{
> +		TYPE_NAME(BAD_TS_VALUE_1),
> +		.exp_send_ret = VLEN,
> +		.tv_sec = -1,
> +		.tv_nsec = 0,
> +		.exp_recv_errno = EINVAL,
> +	},
> +	{
> +		TYPE_NAME(BAD_TS_VALUE_2),
> +		.exp_send_ret = VLEN,
> +		.tv_sec = 1,
> +		.tv_nsec = 1000000001,
> +		.exp_recv_errno = EINVAL,
> +	},
> +	{
> +		TYPE_NAME(BAD_TS_ADDR),
> +		.exp_send_ret = VLEN,
> +		.exp_recv_errno = EFAULT,
> +	}
> +};
> +
> +static struct test_variants {
> +	int (*receive)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
> +		       unsigned int flags, void *timeout);
> +	int (*send)(int sockfd, struct mmsghdr *msgvec, unsigned int vlen,
> +		    unsigned int flags);
> +	enum tst_ts_type type;
> +	char *desc;
> +} variants[] = {
> +	{ .receive = libc_recvmmsg, .send = libc_sendmmsg, .type = TST_LIBC_TIMESPEC, .desc = "vDSO or syscall with libc spec"},
> +
> +#if (__NR_recvmmsg != __LTP__NR_INVALID_SYSCALL)
> +	{ .receive = sys_recvmmsg, .send = sys_sendmmsg, .type = TST_KERN_OLD_TIMESPEC, .desc = "syscall with old kernel spec"},
> +#endif
> +
> +#if (__NR_recvmmsg_time64 != __LTP__NR_INVALID_SYSCALL)
> +	{ .receive = sys_recvmmsg64, .send = sys_sendmmsg, .type = TST_KERN_TIMESPEC, .desc = "syscall time64 with kernel spec"},
> +#endif
> +};

Technically the recvmmsg tests should be in a recvmmsg01.c.

We can put the test_variants array to a common header though.

> +static void cleanup(void)
> +{
> +	if (send_sockfd > 0)
> +		SAFE_CLOSE(send_sockfd);
> +	if (receive_sockfd > 0)
> +		SAFE_CLOSE(receive_sockfd);
> +}
> +
> +static void do_test(unsigned int i)
> +{
> +	struct test_variants *tv = &variants[tst_variant];
> +	struct test_case *tc = &tcase[i];
> +	void *snd_msgvec, *rcv_msgvec, *timeout;
> +
> +	tst_res(TINFO, "case %s", tc->desc);
> +
> +	if (tc->ttype != BAD_SEND_FD)
> +		tc->send_fd = send_sockfd;
> +	if (tc->ttype != BAD_RECV_FD)
> +		tc->recv_fd = receive_sockfd;
> +
> +	if (tc->ttype == BAD_SEND_MSGVEC)
> +		snd_msgvec = bad_addr;
> +	else
> +		snd_msgvec = snd_msg;
> +
> +	TEST(tv->send(tc->send_fd, snd_msgvec, VLEN, 0));
> +
> +	if (TST_RET < 0) {
> +		if (tc->exp_send_ret != TST_RET || errno != tc->exp_send_errno)
> +			tst_res(TFAIL | TERRNO, "sendmmsg() failed unexpectedly");
> +		else
> +			tst_res(TPASS | TERRNO, "sendmmg() failed successfully");
> +		return;
> +	}
> +
> +	if (tc->exp_send_ret != TST_RET || snd_msg[0].msg_len != 6 ||
> +	    snd_msg[1].msg_len != 6) {
> +		tst_res(TFAIL | TERRNO, "sendmmsg() failed");
> +		return;
> +	}
> +
> +	memset(rcv1->iov_base, 0, rcv1->iov_len);
> +	memset(rcv2->iov_base, 0, rcv2->iov_len);
> +
> +	ts.type = tv->type;
> +	tst_ts_set_sec(&ts, tc->tv_sec);
> +	tst_ts_set_nsec(&ts, tc->tv_nsec);
> +
> +	if (tc->ttype == BAD_RECV_MSGVEC)
> +		rcv_msgvec = bad_addr;
> +	else
> +		rcv_msgvec = rcv_msg;
> +
> +	if (tc->ttype == BAD_TS_ADDR)
> +		timeout = bad_addr;
> +	else
> +		timeout = tst_ts_get(&ts);
> +
> +	TEST(tv->receive(tc->recv_fd, rcv_msgvec, VLEN, 0, timeout));
> +
> +	if (TST_RET < 0) {
> +		if (tc->exp_recv_errno == errno)
> +			tst_res(TPASS | TERRNO, "receivemmsg() failed successfully");
> +		else
> +			tst_res(TFAIL | TERRNO, "receivemmsg() failed unexpectedly");
> +	} else {
> +		tst_res(TFAIL | TERRNO, "receivemmsg() succeded unexpectedly");
> +	}
> +}
> +
> +static void setup(void)
> +{
> +	struct sockaddr_in addr;
> +	unsigned int port = TST_GET_UNUSED_PORT(AF_INET, SOCK_DGRAM);
> +
> +	send_sockfd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
> +	receive_sockfd = SAFE_SOCKET(AF_INET, SOCK_DGRAM, 0);
> +
> +	addr.sin_family = AF_INET;
> +	addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
> +	addr.sin_port = port;
> +
> +	SAFE_BIND(receive_sockfd, (struct sockaddr *)&addr, sizeof(addr));
> +	SAFE_CONNECT(send_sockfd, (struct sockaddr *)&addr, sizeof(addr));
> +
> +	memcpy(snd1[0].iov_base, "one", snd1[0].iov_len);
> +	memcpy(snd1[1].iov_base, "two", snd1[1].iov_len);
> +	memcpy(snd2->iov_base, "three3", snd2->iov_len);
> +
> +	memset(snd_msg, 0, VLEN * sizeof(*snd_msg));
> +	snd_msg[0].msg_hdr.msg_iov = snd1;
> +	snd_msg[0].msg_hdr.msg_iovlen = 2;
> +	snd_msg[1].msg_hdr.msg_iov = snd2;
> +	snd_msg[1].msg_hdr.msg_iovlen = 1;
> +
> +	memset(rcv_msg, 0, VLEN * sizeof(*rcv_msg));
> +	rcv_msg[0].msg_hdr.msg_iov = rcv1;
> +	rcv_msg[0].msg_hdr.msg_iovlen = 1;
> +	rcv_msg[1].msg_hdr.msg_iov = rcv2;
> +	rcv_msg[1].msg_hdr.msg_iovlen = 1;
> +
> +	bad_addr = tst_get_bad_addr(cleanup);
                                    ^
				    This must be NULL in new-library API
				    tests.

Since in the new-library API the cleanup is passed in the tst_test
structure and handled in the test library.


> +	tst_res(TINFO, "Testing variant: %s", variants[tst_variant].desc);
> +}
> +
> +static struct tst_test test = {
> +	.tcnt = ARRAY_SIZE(tcase),
> +	.test = do_test,
> +	.setup = setup,
> +	.cleanup = cleanup,
> +	.test_variants = ARRAY_SIZE(variants),
> +	.bufs = (struct tst_buffers []) {
> +		{&snd1, .iov_sizes = (int[]){3, 3, -1}},
> +		{&snd2, .iov_sizes = (int[]){6, -1}},
> +		{&rcv1, .iov_sizes = (int[]){6, -1}},
> +		{&rcv2, .iov_sizes = (int[]){5, -1}},
> +		{&snd_msg, .size = VLEN * sizeof(*snd_msg)},
> +		{&rcv_msg, .size = VLEN * sizeof(*rcv_msg)},
> +		{},
> +	}
> +};
> -- 
> 2.25.1
> 
> 
> -- 
> Mailing list info: https://lists.linux.it/listinfo/ltp

-- 
Cyril Hrubis
chrubis@suse.cz


More information about the ltp mailing list