[Devel] [PATCH 7/7] cr_tests: epoll: Add test passing epoll fd via SCM_RIGHTS
Matt Helsley
matthltc at us.ibm.com
Mon Oct 12 12:38:28 PDT 2009
This test passes an epoll fd and all the fds in its set over a UNIX
socket via SCM_RIGHTS. Then it does virtually the same things as
the pipe test. A special optional argument allows the second process
to be in a different cgroup so that we can test leak detection of epoll
fd sets in addition to SCM_RIGHTS.
Signed-off-by: Matt Helsley <matthltc at us.ibm.com>
---
epoll/Makefile | 2 +-
epoll/run.sh | 2 +
epoll/scm.c | 345 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 348 insertions(+), 1 deletions(-)
create mode 100644 epoll/scm.c
diff --git a/epoll/Makefile b/epoll/Makefile
index 84aec49..b3e3ca2 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 cycle
+PROGS=empty pipe sk10k cycle scm
.PHONY: all clean
diff --git a/epoll/run.sh b/epoll/run.sh
index 16ed76c..7e30e4f 100755
--- a/epoll/run.sh
+++ b/epoll/run.sh
@@ -111,6 +111,8 @@ while [ $CURTEST -lt $NUMTESTS ]; do
done
trap '' ERR EXIT
+# TODO add scm testcase to run.sh
+
rm -f ./checkpoint-{ready,done}
diff --git a/epoll/scm.c b/epoll/scm.c
new file mode 100644
index 0000000..7485916
--- /dev/null
+++ b/epoll/scm.c
@@ -0,0 +1,345 @@
+/*
+ * Open a pipe and an epoll set. Pass them across a unix socket to an
+ * "unrelated" task and test whether epoll retrieves events related to IO
+ * on the pipe.
+ *
+ * By varying the container or subtree checkpointed we can attempt to
+ * introduce and detect checkpoint leaks.
+ */
+
+/* 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>
+
+/* Stuff needed to use SCM_RIGHTS via a UNIX socket */
+#include <sys/socket.h>
+
+#include <sys/prctl.h>
+
+#include "libeptest.h"
+
+#define LOG_FILE "log.scm"
+
+void usage(FILE *pout)
+{
+ fprintf(pout, "\nscm [-L] [-N] [-h|--help] [-l LABEL] [-n NUM]\n"
+"Open a pipe and an epoll set. Pass them across a unix socket to an\n"
+"'unrelated' task and test whether epoll retrieves events related to IO\n"
+"on the pipe.\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"
+"\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'},
+ { "cgroup2", 2, 0, 'c'},
+ {0, 0, 0, 0},
+};
+
+char *cgroup2 = NULL;
+
+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: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 -n requires an argument in the range 0-%d. Got %d\n", num_labels - 1, ckpt_op_num);
+ usage(stderr);
+ exit(EXIT_FAILURE);
+ }
+ break;
+ case 'c':
+ if (optarg && (strlen(optarg) > 0))
+ cgroup2 = strdup(optarg);
+ else
+ cgroup2 = "2";
+ break;
+ case '?':
+ /* unkown option in optopt */
+ 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)
+{
+ struct epoll_event ev[2] = {
+ { .events = EPOLLIN, },
+ { .events = EPOLLOUT, },
+ };
+ pid_t kid;
+ int op_num = 0;
+ int tube[2];
+ int efd, sk[2];
+ int ec = EXIT_FAILURE;
+ int ret;
+ char rbuf[128];
+
+ 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);
+ }
+
+label(create,
+ efd, epoll_create(1));
+
+ ret = pipe(tube);
+ if (ret < 0)
+ goto out;
+
+ /*
+ * Now we have efd and the pipe set up. Time to pass file descriptors
+ * between two processes.
+ */
+ socketpair(PF_UNIX, SOCK_DGRAM, 0, sk);
+ kid = fork();
+ if (kid) {
+ char *msg_bytes = "efd,tube[0],tube[1]";
+ int status;
+ int *fdp;
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
+ struct iovec iobase;
+ char cbuf[CMSG_SPACE(sizeof(int)*3)];
+
+ iobase.iov_base = msg_bytes;
+ iobase.iov_len = strlen(msg_bytes) + 1;
+ msg.msg_iov = &iobase;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+ msg.msg_control = cbuf;
+ msg.msg_controllen = sizeof(cbuf);
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(int)*3);
+ fdp = (int*)CMSG_DATA(cmsg);
+ fdp[0] = efd;
+ fdp[1] = tube[0];
+ fdp[2] = tube[1];
+ msg.msg_controllen = cmsg->cmsg_len;
+ close(sk[1]);
+ sendmsg(sk[0], &msg, 0);
+ close(sk[0]);
+
+ /*
+ * Now the child and the parent share the open file description
+ * (aka handle) of efd, tube[0], and tube[1]. For more info
+ * see fork(2) and epoll(7).
+ */
+
+ waitpid(kid, &status, 0);
+ exit(status);
+ } else {
+ char msg_bytes[1024];
+ struct msghdr msg = {0};
+ struct cmsghdr *cmsg;
+ struct iovec iobase;
+ int *fdp;
+
+ close(sk[0]);
+ close(efd);
+ close(tube[0]);
+ close(tube[1]);
+ efd = tube[0] = tube[1] = -1;
+
+ /* Ensure that if the parent dies the child does too. */
+ prctl(PR_SET_PDEATHSIG, SIGINT);
+
+ /* Otherwise, distance ourself from parent */
+ setsid();
+ if (cgroup2 && !move_to_cgroup("freezer", cgroup2, getpid())) {
+ log_error("move_to_cgroup [kid]");
+ exit(2);
+ }
+
+ iobase.iov_base = msg_bytes;
+ iobase.iov_len = 1024;
+
+ msg.msg_iov = &iobase;
+ msg.msg_iovlen = 1;
+ msg.msg_flags = 0;
+
+ recvmsg(sk[1], &msg, MSG_CMSG_CLOEXEC|MSG_WAITALL);
+ for (cmsg = CMSG_FIRSTHDR(&msg); cmsg;
+ cmsg = CMSG_NXTHDR(&msg, cmsg)) {
+ if ((cmsg->cmsg_level != SOL_SOCKET) ||
+ (cmsg->cmsg_type != SCM_RIGHTS) ||
+ (cmsg->cmsg_len != CMSG_LEN(sizeof(int)*3)))
+ continue;
+ fdp = (int*)CMSG_DATA(cmsg);
+ efd = fdp[0];
+ tube[0] = fdp[1];
+ tube[1] = fdp[2];
+ }
+ close(sk[1]);
+ }
+
+ if (efd == -1 ||
+ tube[0] == -1 ||
+ tube[1] == -1) {
+ /* Failed to recv fds via SCM_RIGHTS */
+ log_error("failed to pass fds with SCM_RIGHTS");
+ exit(2);
+ }
+
+ ev[0].data.fd = tube[0];
+ ev[1].data.fd = tube[1];
+
+label(ctl_add_wfd,
+ ret, epoll_ctl(efd, EPOLL_CTL_ADD, tube[1], &ev[1]));
+
+label(wait_write,
+ ret, epoll_wait(efd, &ev[1], 1, 1000));
+ if (ret != 1) {
+ log_error("Expected epoll_wait() to return one event.\n");
+ goto out;
+ }
+ if (!(ev[1].events & EPOLLOUT)) {
+ log("FAIL", "Expected EPOLLOUT (0x%X) flag, got %s (0x%X)\n",
+ EPOLLOUT, eflags(ev[1].events), ev[1].events);
+ goto out;
+ }
+ if (tube[1] != ev[1].data.fd) {
+ log("FAIL", "Expected fd %d, got %d\n", tube[1], ev[1].data.fd);
+ goto out;
+ }
+
+label(do_write,
+ ret, write(tube[1], HELLO, strlen(HELLO) + 1));
+ if (ret < (strlen(HELLO) + 1)) {
+ log("FAIL", "Unable to write all %d bytes of \"%s\"\n",
+ strlen(HELLO) + 1, HELLO);
+ goto out;
+ }
+
+label(ctl_add_rfd,
+ ret, epoll_ctl(efd, EPOLL_CTL_ADD, tube[0], &ev[0]));
+
+label(ctl_rm_wfd,
+ ret, epoll_ctl(efd, EPOLL_CTL_DEL, tube[1], &ev[1]));
+
+label(wait_read,
+ ret, epoll_wait(efd, &ev[0], 1, 5000));
+ if (ret != 1) {
+ log_error("Expected epoll_wait() to return one event.\n");
+ goto out;
+ }
+ if (!(ev[0].events & EPOLLIN)) {
+ log("FAIL", "Expected EPOLLIN (0x%X) flag, got %s (0x%X)\n",
+ EPOLLIN, eflags(ev[0].events), ev[0].events);
+ goto out;
+ }
+ if (tube[0] != ev[0].data.fd) {
+ log("FAIL", "Expected fd %d, got %d\n", tube[0], ev[0].data.fd);
+ goto out;
+ }
+
+label(do_read,
+ ret, read(tube[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;
+ }
+ log("INFO", "read len ok\n");
+ if (strcmp(HELLO, rbuf)) {
+ log("FAIL", "Pipe buffer was corrupted. Expected: \"%s\" Got: \"%s\"\n",
+ HELLO, rbuf);
+ goto out;
+ }
+ log("INFO", "read buffer contents ok\n");
+ ec = EXIT_SUCCESS;
+ op_num = INT_MAX;
+
+out:
+ if ((efd >= 0) && close(efd) < 0) {
+ log_error("close()");
+ efd = -1;
+ goto out;
+ }
+ if (op_num != INT_MAX) {
+ log("FAIL", "error at label %s (op num: %d)\n",
+ labels(op_num), op_num);
+ }
+ close(tube[0]);
+ close(tube[1]);
+ fflush(logfp);
+ fclose(logfp);
+ exit(ec);
+}
+
+const char __attribute__((__section__(".LABELs"))) *last_label = "<end>";
--
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