123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- #include "../config-host.h"
- /* SPDX-License-Identifier: MIT */
- /*
- * 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"
- #include "helpers.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,
- int wait)
- {
- 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);
- ret = io_uring_submit_and_wait(ring, wait);
- if (ret < 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;
- int ret;
- ret = io_uring_wait_cqe(ring, &cqe);
- if (ret) {
- fprintf(stderr, "%s wait_cqe failed %d\n", stage, ret);
- return 1;
- }
- if (cqe->res < 0) {
- fprintf(stderr, "%s cqe failed %d\n", stage, cqe->res);
- 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;
- if (argc > 1)
- return T_EXIT_SKIP;
- shmem = mmap(0, sizeof(struct forktestmem), PROT_READ|PROT_WRITE,
- MAP_SHARED | MAP_ANONYMOUS, 0, 0);
- if (!shmem) {
- fprintf(stderr, "mmap failed\n");
- exit(T_EXIT_FAIL);
- }
- 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(T_EXIT_FAIL);
- }
- if (mkdtemp(tmpdir) == NULL) {
- fprintf(stderr, "temp directory creation failed\n");
- exit(T_EXIT_FAIL);
- }
- 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", 0))
- 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", 0))
- 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", 0))
- 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", 0))
- 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;
- /* check that IO can still be submitted after child exited */
- if (submit_write(&shmem->ring, shared_fd, "parent: write shared fd after child exit\n", 0))
- goto errcleanup;
- if (wait_cqe(&shmem->ring, "p cqe 3"))
- 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", 0))
- exit(1);
- /* ensure both writes have finished before child exits */
- if (submit_write(&shmem->ring, shared_fd, "child: write shared fd\n", 2))
- 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"
- "parent: write shared fd after child exit\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(T_EXIT_PASS);
- errcleanup:
- cleanup(tmpdir);
- exit(T_EXIT_FAIL);
- }
|