741 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
			
		
		
	
	
			741 lines
		
	
	
		
			16 KiB
		
	
	
	
		
			C
		
	
	
	
| /*
 | |
|  *   Copyright (c) 2008 Vijay Kumar B. <vijaykumar@bravegnu.org>
 | |
|  *
 | |
|  *   Based on testcases/kernel/syscalls/waitpid/waitpid01.c
 | |
|  *   Original copyright message:
 | |
|  *
 | |
|  *   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 Street, Fifth Floor, Boston, MA 02110-1301 USA
 | |
|  */
 | |
| 
 | |
| /*
 | |
|  * NAME
 | |
|  *	eventfd01.c
 | |
|  *
 | |
|  * DESCRIPTION
 | |
|  *      Test cases for eventfd syscall.
 | |
|  *
 | |
|  * USAGE:  <for command-line>
 | |
|  *      eventfd01 [-c n] [-i n] [-I x] [-P x] [-t]
 | |
|  *      where,  -c n : Run n copies concurrently.
 | |
|  *              -i n : Execute test n times.
 | |
|  *              -I x : Execute test for x seconds.
 | |
|  *              -P x : Pause for x seconds between iterations.
 | |
|  *
 | |
|  * History
 | |
|  *	07/2008 Vijay Kumar
 | |
|  *		Initial Version.
 | |
|  *
 | |
|  * Restrictions
 | |
|  *	None
 | |
|  */
 | |
| 
 | |
| #include "config.h"
 | |
| 
 | |
| #include <sys/types.h>
 | |
| #include <sys/select.h>
 | |
| #include <sys/wait.h>
 | |
| #include <errno.h>
 | |
| #include <fcntl.h>
 | |
| #include <inttypes.h>
 | |
| #include <poll.h>
 | |
| #include <signal.h>
 | |
| #include <stdint.h>
 | |
| #include <string.h>
 | |
| #include <unistd.h>
 | |
| 
 | |
| #include "test.h"
 | |
| #define CLEANUP cleanup
 | |
| #include "lapi/syscalls.h"
 | |
| 
 | |
| TCID_DEFINE(eventfd01);
 | |
| int TST_TOTAL = 15;
 | |
| 
 | |
| #ifdef HAVE_LIBAIO
 | |
| #include <libaio.h>
 | |
| 
 | |
| static void setup(void);
 | |
| 
 | |
| static int myeventfd(unsigned int initval, int flags)
 | |
| {
 | |
| 	/* eventfd2 uses FLAGS but eventfd doesn't take FLAGS. */
 | |
| 	return ltp_syscall(__NR_eventfd, initval);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * clear_counter() - clears the counter by performing a dummy read
 | |
|  * @fd: the eventfd
 | |
|  *
 | |
|  * RETURNS:
 | |
|  * 0 on success, and -1 on failure
 | |
|  */
 | |
| static int clear_counter(int fd)
 | |
| {
 | |
| 	uint64_t dummy;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = read(fd, &dummy, sizeof(dummy));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno != EAGAIN) {
 | |
| 			tst_resm(TINFO | TERRNO, "error clearing counter");
 | |
| 			return -1;
 | |
| 		}
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * set_counter() - sets the count to specified value
 | |
|  * @fd: the eventfd
 | |
|  * @val: the value to be set
 | |
|  *
 | |
|  * Clears the counter and sets the counter to @val.
 | |
|  *
 | |
|  * RETURNS:
 | |
|  * 0 on success, -1 on failure
 | |
|  */
 | |
| static int set_counter(int fd, uint64_t val)
 | |
| {
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = clear_counter(fd);
 | |
| 	if (ret == -1)
 | |
| 		return -1;
 | |
| 
 | |
| 	ret = write(fd, &val, sizeof(val));
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TINFO | TERRNO, "error setting counter value");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether the current value of the counter matches @required.
 | |
|  */
 | |
| static void read_test(int fd, uint64_t required)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint64_t val;
 | |
| 
 | |
| 	ret = read(fd, &val, sizeof(val));
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK | TERRNO, "error reading eventfd");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (val == required)
 | |
| 		tst_resm(TPASS, "counter value matches required");
 | |
| 	else
 | |
| 		tst_resm(TFAIL, "counter value mismatch: "
 | |
| 			 "required: %" PRIu64 ", got: %" PRIu64, required, val);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether read returns with error EAGAIN when counter is at 0.
 | |
|  */
 | |
| static void read_eagain_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint64_t val;
 | |
| 
 | |
| 	ret = clear_counter(fd);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error clearing counter");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = read(fd, &val, sizeof(val));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno == EAGAIN)
 | |
| 			tst_resm(TPASS, "read failed with EAGAIN as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL | TERRNO, "read failed (wanted EAGAIN)");
 | |
| 	} else
 | |
| 		tst_resm(TFAIL, "read returned with %d", ret);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether writing to counter works.
 | |
|  */
 | |
| static void write_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint64_t val;
 | |
| 
 | |
| 	val = 12;
 | |
| 
 | |
| 	ret = set_counter(fd, val);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error setting counter value to %" PRIu64, val);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	read_test(fd, val);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether write returns with error EAGAIN when counter is at
 | |
|  * (UINT64_MAX - 1).
 | |
|  */
 | |
| static void write_eagain_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint64_t val;
 | |
| 
 | |
| 	ret = set_counter(fd, UINT64_MAX - 1);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error setting counter value to UINT64_MAX-1");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	val = 1;
 | |
| 	ret = write(fd, &val, sizeof(val));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno == EAGAIN)
 | |
| 			tst_resm(TPASS, "write failed with EAGAIN as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL, "write failed (wanted EAGAIN)");
 | |
| 	} else
 | |
| 		tst_resm(TFAIL, "write returned with %d", ret);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether read returns with error EINVAL, if buffer size is less
 | |
|  * than 8 bytes.
 | |
|  */
 | |
| static void read_einval_test(int fd)
 | |
| {
 | |
| 	uint32_t invalid;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = read(fd, &invalid, sizeof(invalid));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno == EINVAL)
 | |
| 			tst_resm(TPASS, "read failed with EINVAL as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL | TERRNO, "read failed (wanted EINVAL)");
 | |
| 	} else
 | |
| 		tst_resm(TFAIL, "read returned with %d", ret);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether write returns with error EINVAL, if buffer size is
 | |
|  * less than 8 bytes.
 | |
|  */
 | |
| static void write_einval_test(int fd)
 | |
| {
 | |
| 	uint32_t invalid;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = write(fd, &invalid, sizeof(invalid));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno == EINVAL)
 | |
| 			tst_resm(TPASS, "write failed with EINVAL as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL | TERRNO,
 | |
| 				 "write failed (wanted EINVAL)");
 | |
| 	} else
 | |
| 		tst_resm(TFAIL, "write returned with %d", ret);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test wheter write returns with error EINVAL, when the written value
 | |
|  * is 0xFFFFFFFFFFFFFFFF.
 | |
|  */
 | |
| static void write_einval2_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	uint64_t val;
 | |
| 
 | |
| 	ret = clear_counter(fd);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error clearing counter");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	val = 0xffffffffffffffffLL;
 | |
| 	ret = write(fd, &val, sizeof(val));
 | |
| 	if (ret == -1) {
 | |
| 		if (errno == EINVAL)
 | |
| 			tst_resm(TPASS, "write failed with EINVAL as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL | TERRNO,
 | |
| 				 "write failed (wanted EINVAL)");
 | |
| 	} else {
 | |
| 		tst_resm(TFAIL, "write returned with %d", ret);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether readfd is set by select when counter value is
 | |
|  * non-zero.
 | |
|  */
 | |
| static void readfd_set_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	fd_set readfds;
 | |
| 	struct timeval timeout = { 0, 0 };
 | |
| 	uint64_t non_zero = 10;
 | |
| 
 | |
| 	FD_ZERO(&readfds);
 | |
| 	FD_SET(fd, &readfds);
 | |
| 
 | |
| 	ret = set_counter(fd, non_zero);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error setting counter value to %" PRIu64,
 | |
| 			 non_zero);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
 | |
| 	if (ret == -1) {
 | |
| 		/* EINTR cannot occur, since we don't block. */
 | |
| 		tst_resm(TBROK | TERRNO, "select() failed");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (FD_ISSET(fd, &readfds))
 | |
| 		tst_resm(TPASS, "fd is set in readfds");
 | |
| 	else
 | |
| 		tst_resm(TFAIL, "fd is not set in readfds");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether readfd is not set by select when counter value is
 | |
|  * zero.
 | |
|  */
 | |
| static void readfd_not_set_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	fd_set readfds;
 | |
| 	struct timeval timeout = { 0, 0 };
 | |
| 
 | |
| 	FD_ZERO(&readfds);
 | |
| 	FD_SET(fd, &readfds);
 | |
| 
 | |
| 	ret = clear_counter(fd);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error clearing counter");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = select(fd + 1, &readfds, NULL, NULL, &timeout);
 | |
| 	if (ret == -1) {
 | |
| 		/* EINTR cannot occur, since we don't block. */
 | |
| 		tst_resm(TBROK | TERRNO, "select() failed");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!FD_ISSET(fd, &readfds))
 | |
| 		tst_resm(TPASS, "fd is not set in readfds");
 | |
| 	else
 | |
| 		tst_resm(TFAIL, "fd is set in readfds");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether writefd is set by select when counter value is not the
 | |
|  * maximum counter value.
 | |
|  */
 | |
| static void writefd_set_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	fd_set writefds;
 | |
| 	struct timeval timeout = { 0, 0 };
 | |
| 	uint64_t non_max = 10;
 | |
| 
 | |
| 	FD_ZERO(&writefds);
 | |
| 	FD_SET(fd, &writefds);
 | |
| 
 | |
| 	ret = set_counter(fd, non_max);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error setting counter value to %" PRIu64,
 | |
| 			 non_max);
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = select(fd + 1, NULL, &writefds, NULL, &timeout);
 | |
| 	if (ret == -1) {
 | |
| 		/* EINTR cannot occur, since we don't block. */
 | |
| 		tst_resm(TBROK | TERRNO, "select: error getting fd status");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (FD_ISSET(fd, &writefds))
 | |
| 		tst_resm(TPASS, "fd is set in writefds");
 | |
| 	else
 | |
| 		tst_resm(TFAIL, "fd is not set in writefds");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether writefd is not set by select when counter value is at
 | |
|  * (UINT64_MAX - 1).
 | |
|  */
 | |
| static void writefd_not_set_test(int fd)
 | |
| {
 | |
| 	int ret;
 | |
| 	fd_set writefds;
 | |
| 	struct timeval timeout = { 0, 0 };
 | |
| 
 | |
| 	FD_ZERO(&writefds);
 | |
| 	FD_SET(fd, &writefds);
 | |
| 
 | |
| 	ret = set_counter(fd, UINT64_MAX - 1);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error setting counter value to UINT64_MAX-1");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = select(fd + 1, NULL, &writefds, NULL, &timeout);
 | |
| 	if (ret == -1) {
 | |
| 		/* EINTR cannot occur, since we don't block. */
 | |
| 		tst_resm(TBROK | TERRNO, "select: error getting fd status");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	if (!FD_ISSET(fd, &writefds))
 | |
| 		tst_resm(TPASS, "fd is not set in writefds");
 | |
| 	else
 | |
| 		tst_resm(TFAIL, "fd is set in writefds");
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Test whether counter update in child is reflected in the parent.
 | |
|  */
 | |
| static void child_inherit_test(int fd)
 | |
| {
 | |
| 	uint64_t val;
 | |
| 	pid_t cpid;
 | |
| 	int ret;
 | |
| 	int status;
 | |
| 	uint64_t to_parent = 0xdeadbeef;
 | |
| 	uint64_t dummy;
 | |
| 
 | |
| 	cpid = fork();
 | |
| 	if (cpid == -1)
 | |
| 		tst_resm(TBROK | TERRNO, "fork failed");
 | |
| 	else if (cpid != 0) {
 | |
| 		ret = wait(&status);
 | |
| 		if (ret == -1) {
 | |
| 			tst_resm(TBROK, "error getting child exit status");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (WEXITSTATUS(status) == 1) {
 | |
| 			tst_resm(TBROK, "counter value write not "
 | |
| 				 "successful in child");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		ret = read(fd, &val, sizeof(val));
 | |
| 		if (ret == -1) {
 | |
| 			tst_resm(TBROK | TERRNO, "error reading eventfd");
 | |
| 			return;
 | |
| 		}
 | |
| 
 | |
| 		if (val == to_parent)
 | |
| 			tst_resm(TPASS, "counter value write from "
 | |
| 				 "child successful");
 | |
| 		else
 | |
| 			tst_resm(TFAIL, "counter value write in child "
 | |
| 				 "failed");
 | |
| 	} else {
 | |
| 		/* Child */
 | |
| 		ret = read(fd, &dummy, sizeof(dummy));
 | |
| 		if (ret == -1 && errno != EAGAIN) {
 | |
| 			tst_resm(TWARN | TERRNO, "error clearing counter");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		ret = write(fd, &to_parent, sizeof(to_parent));
 | |
| 		if (ret == -1) {
 | |
| 			tst_resm(TWARN | TERRNO, "error writing eventfd");
 | |
| 			exit(1);
 | |
| 		}
 | |
| 
 | |
| 		exit(0);
 | |
| 	}
 | |
| }
 | |
| 
 | |
| #ifdef HAVE_IO_SET_EVENTFD
 | |
| /*
 | |
|  * Test whether counter overflow is detected and handled correctly.
 | |
|  *
 | |
|  * It is not possible to directly overflow the counter using the
 | |
|  * write() syscall. Overflows occur when the counter is incremented
 | |
|  * from kernel space, in an irq context, when it is not possible to
 | |
|  * block the calling thread of execution.
 | |
|  *
 | |
|  * The AIO subsystem internally uses eventfd mechanism for
 | |
|  * notification of completion of read or write requests. In this test
 | |
|  * we trigger a counter overflow, by setting the counter value to the
 | |
|  * max possible value initially. When the AIO subsystem notifies
 | |
|  * through the eventfd counter, the counter overflows.
 | |
|  *
 | |
|  * NOTE: If the the counter starts from an initial value of 0, it will
 | |
|  * take decades for an overflow to occur. But since we set the initial
 | |
|  * value to the max possible counter value, we are able to cause it to
 | |
|  * overflow with a single increment.
 | |
|  *
 | |
|  * When the counter overflows, the following are tested
 | |
|  *   1. Check whether POLLERR event occurs in poll() for the eventfd.
 | |
|  *   2. Check whether readfd_set/writefd_set is set in select() for the
 | |
|         eventfd.
 | |
|  *   3. The counter value is UINT64_MAX.
 | |
|  */
 | |
| static int trigger_eventfd_overflow(int evfd, int *fd, io_context_t * ctx)
 | |
| {
 | |
| 	int ret;
 | |
| 	struct iocb iocb;
 | |
| 	struct iocb *iocbap[1];
 | |
| 	struct io_event ioev;
 | |
| 	static char buf[4 * 1024];
 | |
| 
 | |
| 	*ctx = 0;
 | |
| 	ret = io_setup(16, ctx);
 | |
| 	if (ret < 0) {
 | |
| 		errno = -ret;
 | |
| 		if (errno == ENOSYS) {
 | |
| 			tst_brkm(TCONF | TERRNO, cleanup,
 | |
| 				 "io_setup(): AIO not supported by kernel");
 | |
| 		}
 | |
| 
 | |
| 		tst_resm(TINFO | TERRNO, "io_setup error");
 | |
| 		return -1;
 | |
| 	}
 | |
| 
 | |
| 	*fd = open("testfile", O_RDWR | O_CREAT, 0644);
 | |
| 	if (*fd == -1) {
 | |
| 		tst_resm(TINFO | TERRNO, "open(testfile) failed");
 | |
| 		goto err_io_destroy;
 | |
| 	}
 | |
| 
 | |
| 	ret = set_counter(evfd, UINT64_MAX - 1);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TINFO, "error setting counter to UINT64_MAX-1");
 | |
| 		goto err_close_file;
 | |
| 	}
 | |
| 
 | |
| 	io_prep_pwrite(&iocb, *fd, buf, sizeof(buf), 0);
 | |
| 	io_set_eventfd(&iocb, evfd);
 | |
| 
 | |
| 	iocbap[0] = &iocb;
 | |
| 	ret = io_submit(*ctx, 1, iocbap);
 | |
| 	if (ret < 0) {
 | |
| 		errno = -ret;
 | |
| 		tst_resm(TINFO | TERRNO, "error submitting iocb");
 | |
| 		goto err_close_file;
 | |
| 	}
 | |
| 
 | |
| 	ret = io_getevents(*ctx, 1, 1, &ioev, NULL);
 | |
| 	if (ret < 0) {
 | |
| 		errno = -ret;
 | |
| 		tst_resm(TINFO | TERRNO, "error waiting for event");
 | |
| 		goto err_close_file;
 | |
| 	}
 | |
| 
 | |
| 	return 0;
 | |
| 
 | |
| err_close_file:
 | |
| 	close(*fd);
 | |
| 
 | |
| err_io_destroy:
 | |
| 	io_destroy(*ctx);
 | |
| 
 | |
| 	return -1;
 | |
| }
 | |
| 
 | |
| static void cleanup_overflow(int fd, io_context_t ctx)
 | |
| {
 | |
| 	close(fd);
 | |
| 	io_destroy(ctx);
 | |
| }
 | |
| 
 | |
| static void overflow_select_test(int evfd)
 | |
| {
 | |
| 	struct timeval timeout = { 10, 0 };
 | |
| 	fd_set readfds;
 | |
| 	int fd;
 | |
| 	io_context_t ctx;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = trigger_eventfd_overflow(evfd, &fd, &ctx);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error triggering eventfd overflow");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	FD_ZERO(&readfds);
 | |
| 	FD_SET(evfd, &readfds);
 | |
| 	ret = select(evfd + 1, &readfds, NULL, NULL, &timeout);
 | |
| 	if (ret == -1)
 | |
| 		tst_resm(TBROK | TERRNO,
 | |
| 			 "error getting evfd status with select");
 | |
| 	else {
 | |
| 		if (FD_ISSET(evfd, &readfds))
 | |
| 			tst_resm(TPASS, "read fd set as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL, "read fd not set");
 | |
| 	}
 | |
| 	cleanup_overflow(fd, ctx);
 | |
| }
 | |
| 
 | |
| static void overflow_poll_test(int evfd)
 | |
| {
 | |
| 	struct pollfd pollfd;
 | |
| 	int fd;
 | |
| 	io_context_t ctx;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = trigger_eventfd_overflow(evfd, &fd, &ctx);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error triggering eventfd overflow");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	pollfd.fd = evfd;
 | |
| 	pollfd.events = POLLIN;
 | |
| 	pollfd.revents = 0;
 | |
| 	ret = poll(&pollfd, 1, 10000);
 | |
| 	if (ret == -1)
 | |
| 		tst_resm(TBROK | TERRNO, "error getting evfd status with poll");
 | |
| 	else {
 | |
| 		if (pollfd.revents & POLLERR)
 | |
| 			tst_resm(TPASS, "POLLERR occurred as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL, "POLLERR did not occur");
 | |
| 	}
 | |
| 	cleanup_overflow(fd, ctx);
 | |
| }
 | |
| 
 | |
| static void overflow_read_test(int evfd)
 | |
| {
 | |
| 	uint64_t count;
 | |
| 	io_context_t ctx;
 | |
| 	int fd;
 | |
| 	int ret;
 | |
| 
 | |
| 	ret = trigger_eventfd_overflow(evfd, &fd, &ctx);
 | |
| 	if (ret == -1) {
 | |
| 		tst_resm(TBROK, "error triggering eventfd overflow");
 | |
| 		return;
 | |
| 	}
 | |
| 
 | |
| 	ret = read(evfd, &count, sizeof(count));
 | |
| 	if (ret == -1)
 | |
| 		tst_resm(TBROK | TERRNO, "error reading eventfd");
 | |
| 	else {
 | |
| 
 | |
| 		if (count == UINT64_MAX)
 | |
| 			tst_resm(TPASS, "overflow occurred as expected");
 | |
| 		else
 | |
| 			tst_resm(TFAIL, "overflow did not occur");
 | |
| 	}
 | |
| 	cleanup_overflow(fd, ctx);
 | |
| }
 | |
| #else
 | |
| static void overflow_select_test(int evfd)
 | |
| {
 | |
| 	tst_resm(TCONF, "eventfd support is not available in AIO subsystem");
 | |
| }
 | |
| 
 | |
| static void overflow_poll_test(int evfd)
 | |
| {
 | |
| 	tst_resm(TCONF, "eventfd support is not available in AIO subsystem");
 | |
| }
 | |
| 
 | |
| static void overflow_read_test(int evfd)
 | |
| {
 | |
| 	tst_resm(TCONF, "eventfd support is not available in AIO subsystem");
 | |
| }
 | |
| #endif
 | |
| 
 | |
| int main(int argc, char **argv)
 | |
| {
 | |
| 	int lc;
 | |
| 	int fd;
 | |
| 
 | |
| 	tst_parse_opts(argc, argv, NULL, NULL);
 | |
| 
 | |
| 	setup();
 | |
| 
 | |
| 	for (lc = 0; TEST_LOOPING(lc); lc++) {
 | |
| 		int ret;
 | |
| 		uint64_t einit = 10;
 | |
| 
 | |
| 		tst_count = 0;
 | |
| 
 | |
| 		fd = myeventfd(einit, 0);
 | |
| 		if (fd == -1)
 | |
| 			tst_brkm(TBROK | TERRNO, CLEANUP,
 | |
| 				 "error creating eventfd");
 | |
| 
 | |
| 		ret = fcntl(fd, F_SETFL, O_NONBLOCK);
 | |
| 		if (ret == -1)
 | |
| 			tst_brkm(TBROK | TERRNO, CLEANUP,
 | |
| 				 "error setting non-block mode");
 | |
| 
 | |
| 		read_test(fd, einit);
 | |
| 		read_eagain_test(fd);
 | |
| 		write_test(fd);
 | |
| 		write_eagain_test(fd);
 | |
| 		read_einval_test(fd);
 | |
| 		write_einval_test(fd);
 | |
| 		write_einval2_test(fd);
 | |
| 		readfd_set_test(fd);
 | |
| 		readfd_not_set_test(fd);
 | |
| 		writefd_set_test(fd);
 | |
| 		writefd_not_set_test(fd);
 | |
| 		child_inherit_test(fd);
 | |
| 		overflow_select_test(fd);
 | |
| 		overflow_poll_test(fd);
 | |
| 		overflow_read_test(fd);
 | |
| 
 | |
| 		close(fd);
 | |
| 	}
 | |
| 
 | |
| 	cleanup();
 | |
| 
 | |
| 	tst_exit();
 | |
| }
 | |
| 
 | |
| static void setup(void)
 | |
| {
 | |
| 
 | |
| 	tst_sig(FORK, DEF_HANDLER, cleanup);
 | |
| 
 | |
| 	if (tst_kvercmp(2, 6, 22) < 0)
 | |
| 		tst_brkm(TCONF, NULL, "2.6.22 or greater kernel required");
 | |
| 
 | |
| 	tst_tmpdir();
 | |
| 
 | |
| 	TEST_PAUSE;
 | |
| }
 | |
| 
 | |
| static void cleanup(void)
 | |
| {
 | |
| 	tst_rmdir();
 | |
| }
 | |
| 
 | |
| #else
 | |
| int main(void)
 | |
| {
 | |
| 	tst_brkm(TCONF, NULL, "test requires libaio and it's development packages");
 | |
| }
 | |
| #endif
 |