123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392 |
- #include "../config-host.h"
- // SPDX-License-Identifier: MIT
- #include <errno.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <stdlib.h>
- #include <string.h>
- #include <sys/eventfd.h>
- #include <signal.h>
- #include <poll.h>
- #include <assert.h>
- #include <pthread.h>
- #include <sys/types.h>
- #include <sys/wait.h>
- #include "liburing.h"
- #include "test.h"
- #include "helpers.h"
- #define EXEC_FILENAME ".defer-taskrun"
- #define EXEC_FILESIZE (1U<<20)
- static bool can_read_t(int fd, int time)
- {
- int ret;
- struct pollfd p = {
- .fd = fd,
- .events = POLLIN,
- };
- ret = poll(&p, 1, time);
- return ret == 1;
- }
- static bool can_read(int fd)
- {
- return can_read_t(fd, 0);
- }
- static void eventfd_clear(int fd)
- {
- uint64_t val;
- int ret;
- assert(can_read(fd));
- ret = read(fd, &val, 8);
- assert(ret == 8);
- }
- static void eventfd_trigger(int fd)
- {
- uint64_t val = 1;
- int ret;
- ret = write(fd, &val, sizeof(val));
- assert(ret == sizeof(val));
- }
- #define CHECK(x) \
- do { \
- if (!(x)) { \
- fprintf(stderr, "%s:%d %s failed\n", __FILE__, __LINE__, #x); \
- return -1; \
- } \
- } while (0)
- static int test_eventfd(void)
- {
- struct io_uring ring;
- int ret;
- int fda, fdb;
- struct io_uring_cqe *cqe;
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN);
- if (ret)
- return ret;
- fda = eventfd(0, EFD_NONBLOCK);
- fdb = eventfd(0, EFD_NONBLOCK);
- CHECK(fda >= 0 && fdb >= 0);
- ret = io_uring_register_eventfd(&ring, fda);
- if (ret)
- return ret;
- CHECK(!can_read(fda));
- CHECK(!can_read(fdb));
- io_uring_prep_poll_add(io_uring_get_sqe(&ring), fdb, POLLIN);
- io_uring_submit(&ring);
- CHECK(!can_read(fda)); /* poll should not have completed */
- io_uring_prep_nop(io_uring_get_sqe(&ring));
- io_uring_submit(&ring);
- CHECK(can_read(fda)); /* nop should have */
- CHECK(io_uring_peek_cqe(&ring, &cqe) == 0);
- CHECK(cqe->res == 0);
- io_uring_cqe_seen(&ring, cqe);
- eventfd_clear(fda);
- eventfd_trigger(fdb);
- /* can take time due to rcu_call */
- CHECK(can_read_t(fda, 1000));
- /* should not have processed the cqe yet */
- CHECK(io_uring_cq_ready(&ring) == 0);
- io_uring_get_events(&ring);
- CHECK(io_uring_cq_ready(&ring) == 1);
- io_uring_queue_exit(&ring);
- return 0;
- }
- struct thread_data {
- struct io_uring ring;
- int efd;
- char buff[8];
- };
- static void *thread(void *t)
- {
- struct thread_data *td = t;
- io_uring_enable_rings(&td->ring);
- io_uring_prep_read(io_uring_get_sqe(&td->ring), td->efd, td->buff, sizeof(td->buff), 0);
- io_uring_submit(&td->ring);
- return NULL;
- }
- static int test_thread_shutdown(void)
- {
- pthread_t t1;
- int ret;
- struct thread_data td;
- struct io_uring_cqe *cqe;
- uint64_t val = 1;
- ret = io_uring_queue_init(8, &td.ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN |
- IORING_SETUP_R_DISABLED);
- if (ret)
- return ret;
- CHECK(io_uring_get_events(&td.ring) == -EBADFD);
- td.efd = eventfd(0, 0);
- CHECK(td.efd >= 0);
- CHECK(pthread_create(&t1, NULL, thread, &td) == 0);
- CHECK(pthread_join(t1, NULL) == 0);
- CHECK(io_uring_get_events(&td.ring) == -EEXIST);
- CHECK(write(td.efd, &val, sizeof(val)) == sizeof(val));
- CHECK(io_uring_wait_cqe(&td.ring, &cqe) == -EEXIST);
- close(td.efd);
- io_uring_queue_exit(&td.ring);
- return 0;
- }
- static int test_exec(const char *filename)
- {
- int ret;
- int fd;
- struct io_uring ring;
- pid_t fork_pid;
- static char * const new_argv[] = {"1", "2", "3", NULL};
- static char * const new_env[] = {NULL};
- char *buff;
- fork_pid = fork();
- CHECK(fork_pid >= 0);
- if (fork_pid > 0) {
- int wstatus;
- CHECK(waitpid(fork_pid, &wstatus, 0) != (pid_t)-1);
- if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) == T_EXIT_FAIL) {
- fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
- return -1;
- }
- return T_EXIT_PASS;
- }
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN);
- if (ret)
- return ret;
- if (filename) {
- fd = open(filename, O_RDONLY | O_DIRECT);
- if (fd < 0 && errno == EINVAL)
- return T_EXIT_SKIP;
- } else {
- t_create_file(EXEC_FILENAME, EXEC_FILESIZE);
- fd = open(EXEC_FILENAME, O_RDONLY | O_DIRECT);
- if (fd < 0 && errno == EINVAL) {
- unlink(EXEC_FILENAME);
- return T_EXIT_SKIP;
- }
- unlink(EXEC_FILENAME);
- }
- buff = (char*)malloc(EXEC_FILESIZE);
- CHECK(posix_memalign((void **)&buff, 4096, EXEC_FILESIZE) == 0);
- CHECK(buff);
- CHECK(fd >= 0);
- io_uring_prep_read(io_uring_get_sqe(&ring), fd, buff, EXEC_FILESIZE, 0);
- io_uring_submit(&ring);
- ret = execve("/proc/self/exe", new_argv, new_env);
- /* if we get here it failed anyway */
- fprintf(stderr, "execve failed %d\n", ret);
- return T_EXIT_FAIL;
- }
- static int test_flag(void)
- {
- struct io_uring ring;
- int ret;
- int fd;
- struct io_uring_cqe *cqe;
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN |
- IORING_SETUP_TASKRUN_FLAG);
- CHECK(!ret);
- fd = eventfd(0, EFD_NONBLOCK);
- CHECK(fd >= 0);
- io_uring_prep_poll_add(io_uring_get_sqe(&ring), fd, POLLIN);
- io_uring_submit(&ring);
- CHECK(!can_read(fd)); /* poll should not have completed */
- eventfd_trigger(fd);
- CHECK(can_read(fd));
- /* should not have processed the poll cqe yet */
- CHECK(io_uring_cq_ready(&ring) == 0);
- /* flag should be set */
- CHECK(IO_URING_READ_ONCE(*ring.sq.kflags) & IORING_SQ_TASKRUN);
- /* Specifically peek, knowing we have only no cqe
- * but because the flag is set, liburing should try and get more
- */
- ret = io_uring_peek_cqe(&ring, &cqe);
- CHECK(ret == 0 && cqe);
- CHECK(!(IO_URING_READ_ONCE(*ring.sq.kflags) & IORING_SQ_TASKRUN));
- close(fd);
- io_uring_queue_exit(&ring);
- return 0;
- }
- static int test_ring_shutdown(void)
- {
- struct io_uring ring;
- int ret;
- int fd[2];
- char buff = '\0';
- char send = 'X';
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN |
- IORING_SETUP_TASKRUN_FLAG);
- CHECK(!ret);
- ret = t_create_socket_pair(fd, true);
- CHECK(!ret);
- io_uring_prep_recv(io_uring_get_sqe(&ring), fd[0], &buff, 1, 0);
- io_uring_submit(&ring);
- ret = write(fd[1], &send, 1);
- CHECK(ret == 1);
- /* should not have processed the poll cqe yet */
- CHECK(io_uring_cq_ready(&ring) == 0);
- io_uring_queue_exit(&ring);
- /* task work should have been processed by now */
- CHECK(buff = 'X');
- return 0;
- }
- static int test_drain(void)
- {
- struct io_uring ring;
- int ret, i, fd[2];
- struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
- struct iovec iovecs[128];
- char buff[ARRAY_SIZE(iovecs)];
- ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
- IORING_SETUP_DEFER_TASKRUN |
- IORING_SETUP_TASKRUN_FLAG);
- CHECK(!ret);
- for (i = 0; i < ARRAY_SIZE(iovecs); i++) {
- iovecs[i].iov_base = &buff[i];
- iovecs[i].iov_len = 1;
- }
- ret = t_create_socket_pair(fd, true);
- CHECK(!ret);
- sqe = io_uring_get_sqe(&ring);
- io_uring_prep_writev(sqe, fd[1], &iovecs[0], ARRAY_SIZE(iovecs), 0);
- sqe->flags |= IOSQE_IO_DRAIN;
- io_uring_submit(&ring);
- for (i = 0; i < ARRAY_SIZE(iovecs); i++)
- iovecs[i].iov_base = NULL;
- CHECK(io_uring_wait_cqe(&ring, &cqe) == 0);
- CHECK(cqe->res == 128);
- close(fd[0]);
- close(fd[1]);
- io_uring_queue_exit(&ring);
- return 0;
- }
- int main(int argc, char *argv[])
- {
- int ret;
- const char *filename = NULL;
- if (argc > 2)
- return T_EXIT_SKIP;
- if (argc == 2) {
- /* This test exposes interesting behaviour with a null-blk
- * device configured like:
- * $ modprobe null-blk completion_nsec=100000000 irqmode=2
- * and then run with $ defer-taskrun.t /dev/nullb0
- */
- filename = argv[1];
- }
- if (!t_probe_defer_taskrun())
- return T_EXIT_SKIP;
- ret = test_thread_shutdown();
- if (ret) {
- fprintf(stderr, "test_thread_shutdown failed\n");
- return T_EXIT_FAIL;
- }
- ret = test_exec(filename);
- if (ret == T_EXIT_FAIL) {
- fprintf(stderr, "test_exec failed\n");
- return T_EXIT_FAIL;
- }
- ret = test_eventfd();
- if (ret) {
- fprintf(stderr, "eventfd failed\n");
- return T_EXIT_FAIL;
- }
- ret = test_flag();
- if (ret) {
- fprintf(stderr, "flag failed\n");
- return T_EXIT_FAIL;
- }
- ret = test_ring_shutdown();
- if (ret) {
- fprintf(stderr, "test_ring_shutdown failed\n");
- return T_EXIT_FAIL;
- }
- ret = test_drain();
- if (ret) {
- fprintf(stderr, "test_drain failed\n");
- return T_EXIT_FAIL;
- }
- return T_EXIT_PASS;
- }
|