diff options
author | Andres Freund <andres@anarazel.de> | 2020-02-02 06:26:07 -0800 |
---|---|---|
committer | Andres Freund <andres@anarazel.de> | 2020-02-02 06:40:57 -0800 |
commit | 754b9acb9abf1ce646a9394262a8cd58726793c5 (patch) | |
tree | c7d0c7adb4f23d4bcc8f59459ac579eebbfaf892 /test | |
parent | 9416351377f04211f859667f39a58d2a223cbd21 (diff) | |
download | liburing-754b9acb9abf1ce646a9394262a8cd58726793c5.tar.gz liburing-754b9acb9abf1ce646a9394262a8cd58726793c5.tar.bz2 |
Add test for sharing uring across fork
Signed-off-by: Andres Freund <andres@anarazel.de>
Diffstat (limited to 'test')
-rw-r--r-- | test/Makefile | 6 | ||||
-rw-r--r-- | test/across-fork.c | 278 |
2 files changed, 282 insertions, 2 deletions
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); +} |