summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAndres Freund <andres@anarazel.de>2020-02-02 06:26:07 -0800
committerAndres Freund <andres@anarazel.de>2020-02-02 06:40:57 -0800
commit754b9acb9abf1ce646a9394262a8cd58726793c5 (patch)
treec7d0c7adb4f23d4bcc8f59459ac579eebbfaf892
parent9416351377f04211f859667f39a58d2a223cbd21 (diff)
downloadliburing-754b9acb9abf1ce646a9394262a8cd58726793c5.tar.gz
liburing-754b9acb9abf1ce646a9394262a8cd58726793c5.tar.bz2
Add test for sharing uring across fork
Signed-off-by: Andres Freund <andres@anarazel.de>
-rw-r--r--.gitignore1
-rw-r--r--test/Makefile6
-rw-r--r--test/across-fork.c278
3 files changed, 283 insertions, 2 deletions
diff --git a/.gitignore b/.gitignore
index fc0531e..85be532 100644
--- a/.gitignore
+++ b/.gitignore
@@ -26,6 +26,7 @@
/test/accept-link
/test/accept-reuse
/test/accept-test
+/test/across-fork
/test/b19062a56726-test
/test/b5837bd5311d-test
/test/connect
diff --git a/test/Makefile b/test/Makefile
index 4ee68d2..965403b 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -19,7 +19,8 @@ all_targets += poll poll-cancel ring-leak fsync io_uring_setup io_uring_register
poll-many b5837bd5311d-test accept-test d77a67ed5f27-test \
connect 7ad0e4b2f83c-test submit-reuse fallocate open-close \
file-update statx accept-reuse poll-v-poll fadvise madvise \
- short-read openat2 probe shared-wq personality eventfd send_recv
+ short-read openat2 probe shared-wq personality eventfd send_recv \
+ across-fork
include ../Makefile.quiet
@@ -46,7 +47,7 @@ test_srcs := poll.c poll-cancel.c ring-leak.c fsync.c io_uring_setup.c \
7ad0e4b2f83c-test.c submit-reuse.c fallocate.c open-close.c \
file-update.c statx.c accept-reuse.c poll-v-poll.c fadvise.c \
madvise.c short-read.c openat2.c probe.c shared-wq.c \
- personality.c eventfd.c
+ personality.c eventfd.c across-fork.c
test_objs := $(patsubst %.c,%.ol,$(test_srcs))
@@ -58,6 +59,7 @@ poll-link: XCFLAGS = -lpthread
accept-link: XCFLAGS = -lpthread
submit-reuse: XCFLAGS = -lpthread
poll-v-poll: XCFLAGS = -lpthread
+across-fork: XCFLAGS = -lpthread
install: $(all_targets) runtests.sh runtests-loop.sh
$(INSTALL) -D -d -m 755 $(datadir)/liburing-test/
diff --git a/test/across-fork.c b/test/across-fork.c
new file mode 100644
index 0000000..440d6f2
--- /dev/null
+++ b/test/across-fork.c
@@ -0,0 +1,278 @@
+/*
+ * Description: test sharing a ring across a fork
+ */
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "liburing.h"
+
+
+struct forktestmem
+{
+ struct io_uring ring;
+ pthread_barrier_t barrier;
+ pthread_barrierattr_t barrierattr;
+};
+
+static int open_tempfile(const char *dir, const char *fname)
+{
+ int fd;
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ dir, fname);
+ fd = open(buf, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ return fd;
+}
+
+static int submit_write(struct io_uring *ring, int fd, const char *str)
+{
+ struct io_uring_sqe *sqe;
+ struct iovec iovec;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ {
+ fprintf(stderr, "could not get sqe\n");
+ return 1;
+ }
+
+ iovec.iov_base = (char *) str;
+ iovec.iov_len = strlen(str);
+ io_uring_prep_writev(sqe, fd, &iovec, 1, 0);
+ if ((ret = io_uring_submit(ring) < 0))
+ {
+ fprintf(stderr, "submit failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wait_cqe(struct io_uring *ring, const char *stage)
+{
+ struct io_uring_cqe *cqe;
+
+ io_uring_wait_cqe(ring, &cqe);
+ if (cqe->res < 0)
+ {
+ fprintf(stderr, "%s cqe failed\n", stage);
+ return 1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+
+ return 0;
+}
+
+static int verify_file(const char *tmpdir, const char *fname, const char* expect)
+{
+ int fd;
+ char buf[512];
+ int err = 0;
+
+ memset(buf, 0, sizeof(buf));
+
+ fd = open_tempfile(tmpdir, fname);
+ if (fd < 0)
+ return 1;
+
+ if (read(fd, buf, sizeof(buf) - 1) < 0)
+ return 1;
+
+ if (strcmp(buf, expect) != 0)
+ {
+ fprintf(stderr, "content mismatch for %s\n"
+ "got:\n%s\n"
+ "expected:\n%s\n",
+ fname, buf, expect);
+ err = 1;
+ }
+
+ close(fd);
+
+ return err;
+}
+
+static void cleanup(const char *tmpdir)
+{
+ char buf[32];
+
+ /* don't check errors, called during partial runs */
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ tmpdir, "shared");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ tmpdir, "parent1");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ tmpdir, "parent2");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ tmpdir, "child");
+ unlink(buf);
+
+ rmdir(tmpdir);
+}
+
+int main(int argc, char *argv[])
+{
+ struct forktestmem *shmem;
+ char tmpdir[] = "forktmpXXXXXX";
+ int shared_fd;
+ int ret;
+ pid_t p;
+
+ shmem = mmap(0, sizeof(struct forktestmem), PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+ if (!shmem)
+ {
+ fprintf(stderr, "mmap failed\n");
+ exit(1);
+ }
+
+ pthread_barrierattr_init(&shmem->barrierattr);
+ pthread_barrierattr_setpshared(&shmem->barrierattr, 1);
+ pthread_barrier_init(&shmem->barrier, &shmem->barrierattr, 2);
+
+ ret = io_uring_queue_init(10, &shmem->ring, 0);
+ if (ret < 0)
+ {
+ fprintf(stderr, "queue init failed\n");
+ exit(1);
+ }
+
+ if (mkdtemp(tmpdir) == NULL)
+ {
+ fprintf(stderr, "temp directory creation failed\n");
+ exit(1);
+ }
+
+ shared_fd = open_tempfile(tmpdir, "shared");
+
+ /*
+ * First do a write before the fork, to test whether child can
+ * reap that
+ */
+ if (submit_write(&shmem->ring, shared_fd, "before fork: write shared fd\n"))
+ goto errcleanup;
+
+
+ p = fork();
+ switch (p) {
+ case -1:
+ fprintf(stderr, "fork failed\n");
+ goto errcleanup;
+
+ default: {
+ /* parent */
+ int parent_fd1;
+ int parent_fd2;
+ int wstatus;
+
+ /* wait till fork is started up */
+ pthread_barrier_wait(&shmem->barrier);
+
+ parent_fd1 = open_tempfile(tmpdir, "parent1");
+ parent_fd2 = open_tempfile(tmpdir, "parent2");
+
+ /* do a parent write to the shared fd */
+ if (submit_write(&shmem->ring, shared_fd, "parent: write shared fd\n"))
+ goto errcleanup;
+
+ /* do a parent write to an fd where same numbered fd exists in child */
+ if (submit_write(&shmem->ring, parent_fd1, "parent: write parent fd 1\n"))
+ goto errcleanup;
+
+ /* do a parent write to an fd where no same numbered fd exists in child */
+ if (submit_write(&shmem->ring, parent_fd2, "parent: write parent fd 2\n"))
+ goto errcleanup;
+
+ /* wait to switch read/writ roles with child */
+ pthread_barrier_wait(&shmem->barrier);
+
+ /* now wait for child to exit, to ensure we still can read completion */
+ waitpid(p, &wstatus, 0);
+ if (WEXITSTATUS(wstatus) != 0)
+ {
+ fprintf(stderr, "child failed\n");
+ goto errcleanup;
+ }
+
+ if (wait_cqe(&shmem->ring, "p cqe 1"))
+ goto errcleanup;
+
+ if (wait_cqe(&shmem->ring, "p cqe 2"))
+ goto errcleanup;
+
+ break;
+ }
+ case 0: {
+ /* child */
+ int child_fd;
+
+ /* wait till fork is started up */
+ pthread_barrier_wait(&shmem->barrier);
+
+ child_fd = open_tempfile(tmpdir, "child");
+
+ if (wait_cqe(&shmem->ring, "c cqe shared"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 1"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 2"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 3"))
+ exit(1);
+
+ /* wait to switch read/writ roles with parent */
+ pthread_barrier_wait(&shmem->barrier);
+
+ if (submit_write(&shmem->ring, child_fd, "child: write child fd\n"))
+ exit(1);
+
+ if (submit_write(&shmem->ring, shared_fd, "child: write shared fd\n"))
+ exit(1);
+
+ exit(0);
+ }
+ }
+
+ if (verify_file(tmpdir, "shared",
+ "before fork: write shared fd\n"
+ "parent: write shared fd\n"
+ "child: write shared fd\n") ||
+ verify_file(tmpdir, "parent1", "parent: write parent fd 1\n") ||
+ verify_file(tmpdir, "parent2", "parent: write parent fd 2\n") ||
+ verify_file(tmpdir, "child", "child: write child fd\n"))
+ goto errcleanup;
+
+ cleanup(tmpdir);
+ exit(0);
+
+errcleanup:
+ cleanup(tmpdir);
+ exit(1);
+}