[Devel] [PATCH 6/7] cr_tests: epoll: Add cycle in epoll sets
Matt Helsley
matthltc at us.ibm.com
Mon Oct 12 12:38:27 PDT 2009
This test creates a cycle of epoll sets and uses a pipe to trigger
events which we can then verify. This testcase is important because
epoll set cycles could trigger bugs in the way epoll handles recursive
sets.
Signed-off-by: Matt Helsley <matthltc at us.ibm.com>
---
epoll/Makefile | 2 +-
epoll/cycle.c | 328 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
epoll/run.sh | 2 +-
3 files changed, 330 insertions(+), 2 deletions(-)
create mode 100644 epoll/cycle.c
diff --git a/epoll/Makefile b/epoll/Makefile
index c94f5f5..84aec49 100644
--- a/epoll/Makefile
+++ b/epoll/Makefile
@@ -3,7 +3,7 @@
LIBS := ../libcrtest/libcrtest.a ./libeptest.a
CFLAGS := -Wall $(ARCHOPTS) -I../ -I../libcrtest
-PROGS=empty pipe sk10k
+PROGS=empty pipe sk10k cycle
.PHONY: all clean
diff --git a/epoll/cycle.c b/epoll/cycle.c
new file mode 100644
index 0000000..e3799af
--- /dev/null
+++ b/epoll/cycle.c
@@ -0,0 +1,328 @@
+/*
+ * Open a number of epoll sets, link them into a cycle, add a pipe to one,
+ * write sample content to the pipe, and verify that the write events
+ * arrived.
+ *
+ */
+
+/* pretty standard stuff really */
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+#include <string.h>
+#include <limits.h>
+#include <getopt.h>
+
+/* open() */
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+/* waitpid() and W* status macros */
+#include <sys/wait.h>
+
+#include "libeptest.h"
+
+#define LOG_FILE "log.cycle"
+
+void usage(FILE *pout)
+{
+ fprintf(pout, "\ncycle [-L] [-N] [-h|--help] [-l LABEL] [-n NUM] [-c NUM]\n\n"
+"Open several epoll sets and link them in a cycle.\n"
+"This means that each successive epoll fd waits for events from another epoll\n"
+"file descriptor. To test event propagation we also use a pipe in one epoll\n"
+"set and do some IO with the pipe.\n"
+"\n"
+"\t-L\tPrint the valid LABELs in order and exit.\n"
+"\t-l\tWait for checkpoint at LABEL.\n"
+"\t-N\tPrint the maximum label number and exit.\n"
+"\t-n\tWait for checkpoint at NUM.\n"
+"\t-c\tCicumference of the epoll set cycle in NUM epoll fds.\n"
+"\n"
+"You may only specify one LABEL or NUM and you may not specify both.\n"
+"Label numbers are integers in the range 0-%d\n"
+"Valid label numbers and their corresponding LABELs are:\n", num_labels - 1);
+ print_labels(pout);
+}
+
+const struct option long_options[] = {
+ { "print-labels", 0, 0, 'L'},
+ { "print-max-label-no", 0, 0, 'N'},
+ { "help", 0, 0, 'h'},
+ { "label", 1, 0, 'l'},
+ { "num", 1, 0, 'n'},
+ { "circumference", 1, 0, 'c'},
+ {0, 0, 0, 0},
+};
+
+int num_efd = 3;
+
+void parse_args(int argc, char **argv)
+{
+ ckpt_label = last_label;
+ ckpt_op_num = num_labels;
+ while (1) {
+ char c;
+ c = getopt_long(argc, argv, "LNhl:n:s:c:", long_options, NULL);
+ if (c == -1)
+ break;
+ switch(c) {
+ case 'L':
+ print_labels(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'N':
+ printf("%d\n", num_labels - 1);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'h':
+ usage(stdout);
+ exit(EXIT_SUCCESS);
+ break;
+ case 'l':
+ ckpt_label = optarg;
+ break;
+ case 'n':
+ if ((sscanf(optarg, "%d", &ckpt_op_num) < 1) ||
+ (ckpt_op_num < 0) ||
+ (ckpt_op_num >= num_labels)) {
+ fprintf(stderr, "Option -%c requires an argument in the range 0-%d. Got %d\n", c, num_labels - 1, ckpt_op_num);
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'c':
+ if (sscanf(optarg, "%u", &num_efd) < 1) {
+ fprintf(stderr, "Option -%c requires an argument in the range 1-INT_MAX. Got %d\n", c, num_efd);
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+
+ {
+ /* rlimit restricts max fd */
+ struct rlimit lim;
+ getrlimit(RLIMIT_NOFILE, &lim);
+ fprintf(stdout, "INFO: RLIMIT_NOFILE: soft (cur): %ld hard (max): %ld\n", lim.rlim_cur, lim.rlim_max);
+ if (num_efd >= lim.rlim_cur) {
+ fprintf(stderr, "WARN: process is restricted from opening %d sockets. Opening %ld instead.\n", num_efd, lim.rlim_cur);
+ num_efd = lim.rlim_cur;
+ }
+ }
+ break;
+ default: /* unknown option */
+ break;
+ }
+ }
+}
+
+/*
+ * A LABEL is a point in the program we can goto where it's interesting to
+ * checkpoint. These enable us to have a set of labels that can be specified
+ * on the commandline.
+ */
+const char __attribute__((__section__(".LABELs"))) *first_label = "<start>";
+
+int main(int argc, char **argv)
+{
+ char rbuf[128];
+ struct epoll_event ev;
+ int op_num = 0;
+ int pfd[2];
+ int efd[3];
+ int ec = EXIT_FAILURE;
+ int ret, i;
+
+ parse_args(argc, argv);
+
+ /* FIXME eventually stdio streams should be harmless */
+ close(0);
+ logfp = fopen(LOG_FILE, "w+");
+ if (!logfp) {
+ perror("could not open logfile");
+ exit(1);
+ }
+ /* redirect stdout and stderr to the log file */
+ if (dup2(fileno(logfp), 1) < 0) {
+ log_error("dup2(logfp, 1)");
+ goto out;
+ }
+ if (dup2(fileno(logfp), 2) < 0) {
+ log_error("dup2(logfp, 2)");
+ goto out;
+ }
+ /*if (!move_to_cgroup("freezer", "1", getpid())) {
+ log_error("move_to_cgroup");
+ exit(2);
+ }*/
+
+ ret = pipe(pfd);
+ if (ret < 0)
+ goto out;
+ log("INFO", "pipe read fd: %d, pipe write fd: %d\n",
+ pfd[0], pfd[1]);
+
+label(create_efd, ret, ret + 0);
+ for (i = 0; i < num_efd; i++) {
+ efd[i] = epoll_create(3);
+ if (ret < 0) {
+ log("FAIL", "efd[i] = epoll_create(3);");
+ goto out;
+ }
+ }
+
+label(link_cycle, ret, ret + 0);
+ /* Link the epoll fds together into a simple cycle */
+ ev.events = EPOLLOUT|EPOLLIN|EPOLLET;
+ for (--i; i >= 0; i--) {
+ ev.data.fd = efd[i + 1];
+ ret = epoll_ctl(efd[i], EPOLL_CTL_ADD, ev.data.fd, &ev);
+ if (ret < 0) {
+ log("FAIL", "epoll_ctl(efd[i], EPOLL_CTL_ADD, ev.data.fd, &ev);");
+ goto out;
+ }
+ }
+
+ /* Close the cycle */
+ ev.data.fd = 0;
+ ret = epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, ev.data.fd, &ev);
+ if (ret < 0) {
+ log("FAIL",
+ "epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, ev.data.fd, &ev);");
+ goto out;
+ }
+
+label(link_pipe, ret, ret + 0);
+ /*
+ * Now put the pipe fds "last" set of the cycle. For example:
+ *
+ * /---------------------------------\
+ * | |
+ * \-> efd[0] --> efd[1] --> efd[2] -/
+ * | |
+ * | \--> pfd[0]
+ * \----> pfd[1]
+ *
+ * Where foo --> bar means that foo has bar in its set.
+ */
+ ev.events = EPOLLIN;
+ ev.data.fd = pfd[0];
+ ret = epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, ev.data.fd, &ev);
+ if (ret < 0) {
+ log("FAIL",
+ "epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, pfd[0], &ev);");
+ goto out;
+ }
+ ev.events = EPOLLOUT;
+ ev.data.fd = pfd[1];
+ ret = epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, ev.data.fd, &ev);
+ if (ret < 0) {
+ log("FAIL",
+ "epoll_ctl(efd[num_efd - 1], EPOLL_CTL_ADD, pfd[1], &ev);");
+ goto out;
+ }
+
+ ev.events = 0;
+label(wait_write,
+ ret, epoll_wait(efd[0], &ev, 1, 1000));
+ if (ret != 1) {
+ log_error("Expected epoll_wait() to return an event.\n");
+ goto out;
+ }
+
+ /*
+ * Since it's a cycle of epoll sets, we have to wait on the
+ * other epoll sets to get the event that triggered EPOLLIN
+ * on this set.
+ */
+ for (i = 1; i < num_efd; i++) {
+ if (!(ev.events & EPOLLIN)) {
+ log("FAIL", "Expected EPOLLIN (0x%X) flag, got %s (0x%X)\n",
+ EPOLLOUT, eflags(ev.events), ev.events);
+ goto out;
+ }
+ if (ev.data.fd != efd[i]) {
+ log("FAIL", "Expected event fd == %d, got %d\n",
+ efd[i], ev.data.fd);
+ goto out;
+ }
+ ret = epoll_wait(efd[i], &ev, 1, 1000);
+ }
+ /*
+ * Now we expect the actual event indicating it's ok to write
+ * output.
+ */
+ if (!(ev.events & EPOLLOUT)) {
+ log("FAIL", "Expected EPOLLOUT (0x%X) flag, got %s (0x%X)\n",
+ EPOLLOUT, eflags(ev.events), ev.events);
+ goto out;
+ }
+ if (ev.data.fd != pfd[1]) {
+ log("FAIL", "Expected event fd == %d, got %d\n",
+ pfd[1], ev.data.fd);
+ goto out;
+ }
+
+label(do_write,
+ ret, write(pfd[1], HELLO, strlen(HELLO) + 1));
+ if (ret < (strlen(HELLO) + 1)) {
+ log("FAIL", "Unable to write all %d bytes of \"%s\" to %d\n",
+ strlen(HELLO) + 1, HELLO, pfd[0]);
+ goto out;
+ }
+
+label(wait_read,
+ ret, epoll_wait(efd[i], &ev, 1, 1000));
+ if (ret != 1) {
+ log_error("Expected epoll_wait() to return an event.\n");
+ goto out;
+ }
+ if (!(ev.events & EPOLLIN)) {
+ log("FAIL", "Expected EPOLLIN (0x%X) flag, got %s (0x%X)\n",
+ EPOLLIN, eflags(ev.events), ev.events);
+ goto out;
+ }
+ if (ev.data.fd != pfd[0]) {
+ log("FAIL", "Expected event fd == %d, got %d\n",
+ pfd[0], ev.data.fd);
+ goto out;
+ }
+
+label(do_read, ret, ret + 0);
+ ret = read(pfd[0], rbuf, strlen(HELLO) + 1);
+ if (ret < (strlen(HELLO) + 1)) {
+ log("FAIL", "Unable to read all %d bytes of \"%s\"\n",
+ strlen(HELLO) + 1, HELLO);
+ goto out;
+ }
+ if (strcmp(HELLO, rbuf)) {
+ log("FAIL", "File was corrupted. Expected: \"%s\" Got: \"%s\"\n",
+ HELLO, rbuf);
+ goto out;
+ }
+ log("INFO", "read len ok\n");
+ log("INFO", "read pipe contents ok\n");
+ ec = EXIT_SUCCESS;
+ op_num = INT_MAX;
+
+out:
+ if (op_num != INT_MAX) {
+ log("FAIL", "error at label %s (op num: %d)\n",
+ labels(op_num), op_num);
+ }
+ for (i = 0; i < num_efd; i++) {
+ ret = close(efd[i]);
+ efd[i] = -1;
+ if (ret < 0)
+ log_error("close(efd[i])");
+ }
+ if (pfd[0]) {
+ close(pfd[0]);
+ close(pfd[1]);
+ }
+ fflush(logfp);
+ fclose(logfp);
+ exit(ec);
+}
+
+const char __attribute__((__section__(".LABELs"))) *last_label = "<end>";
diff --git a/epoll/run.sh b/epoll/run.sh
index 6f64ee8..16ed76c 100755
--- a/epoll/run.sh
+++ b/epoll/run.sh
@@ -20,7 +20,7 @@ fi ) | grep -E '^[[:space:]]*CONFIG_EPOLL=y' > /dev/null 2>&1
exit 1
}
-TESTS=( empty pipe sk10k )
+TESTS=( empty pipe sk10k cycle )
#make ${TESTS[@]}
# mount -t cgroup foo /cg
--
1.5.6.3
_______________________________________________
Containers mailing list
Containers at lists.linux-foundation.org
https://lists.linux-foundation.org/mailman/listinfo/containers
More information about the Devel
mailing list