Add a test specifically for IORING_SETUP_DEFER_TASKRUN Signed-off-by: Dylan Yudaken <dylany@xxxxxx> --- test/Makefile | 1 + test/defer-taskrun.c | 217 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 218 insertions(+) create mode 100644 test/defer-taskrun.c diff --git a/test/Makefile b/test/Makefile index 418c11c95875..78a499a357d7 100644 --- a/test/Makefile +++ b/test/Makefile @@ -62,6 +62,7 @@ test_srcs := \ d4ae271dfaae.c \ d77a67ed5f27.c \ defer.c \ + defer-taskrun.c \ double-poll-crash.c \ drop-submit.c \ eeed8b54e0df.c \ diff --git a/test/defer-taskrun.c b/test/defer-taskrun.c new file mode 100644 index 000000000000..5ba044a0955d --- /dev/null +++ b/test/defer-taskrun.c @@ -0,0 +1,217 @@ +// SPDX-License-Identifier: MIT +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <error.h> +#include <sys/eventfd.h> +#include <signal.h> +#include <poll.h> +#include <assert.h> +#include <pthread.h> + +#include "liburing.h" +#include "test.h" +#include "helpers.h" + +static bool can_read(int fd) +{ + int ret; + struct pollfd p = { + .fd = fd, + .events = POLLIN, + }; + + ret = poll(&p, 1, 0); + + return ret == 1; +} + +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) if (!(x)) { \ + fprintf(stderr, "%s:%d %s failed\n", __FILE__, __LINE__, #x); \ + return -1; } + +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_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); + CHECK(can_read(fda)); + + /* 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]; +}; + +void *thread(void *t) +{ + struct thread_data *td = t; + + 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_DEFER_TASKRUN); + if (ret) + return ret; + + td.efd = eventfd(0, 0); + CHECK(td.efd >= 0); + + CHECK(pthread_create(&t1, NULL, thread, &td) == 0); + CHECK(pthread_join(t1, NULL) == 0); + + CHECK(write(td.efd, &val, sizeof(val)) == sizeof(val)); + CHECK(!io_uring_wait_cqe(&td.ring, &cqe)); + CHECK(cqe->res == -ECANCELED); + io_uring_cqe_seen(&td.ring, cqe); + + close(td.efd); + io_uring_queue_exit(&td.ring); + return 0; +} + +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_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; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + 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_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; + } + + return T_EXIT_PASS; +} -- 2.30.2