Test for a recent locking problem during poll cancellation with offset timeouts queued. Signed-off-by: Pavel Begunkov <asml.silence@xxxxxxxxx> --- test/poll-cancel.c | 101 +++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 97 insertions(+), 4 deletions(-) diff --git a/test/poll-cancel.c b/test/poll-cancel.c index a74e915..408159d 100644 --- a/test/poll-cancel.c +++ b/test/poll-cancel.c @@ -26,7 +26,7 @@ static void sig_alrm(int sig) exit(1); } -int main(int argc, char *argv[]) +static int test_poll_cancel(void) { struct io_uring ring; int pipe1[2]; @@ -36,9 +36,6 @@ int main(int argc, char *argv[]) struct sigaction act; int ret; - if (argc > 1) - return 0; - if (pipe(pipe1) != 0) { perror("pipe"); return 1; @@ -130,6 +127,102 @@ int main(int argc, char *argv[]) return 1; } + close(pipe1[0]); + close(pipe1[1]); io_uring_cqe_seen(&ring, cqe); + io_uring_queue_exit(&ring); + return 0; +} + + +static int __test_poll_cancel_with_timeouts(void) +{ + struct __kernel_timespec ts = { .tv_sec = 10, }; + struct io_uring ring, ring2; + struct io_uring_sqe *sqe; + int ret, off_nr = 1000; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + ret = io_uring_queue_init(1, &ring2, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + /* test timeout-offset triggering path during cancellation */ + sqe = io_uring_get_sqe(&ring); + io_uring_prep_timeout(sqe, &ts, off_nr, 0); + + /* poll ring2 to trigger cancellation on exit() */ + sqe = io_uring_get_sqe(&ring); + io_uring_prep_poll_add(sqe, ring2.ring_fd, POLLIN); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_link_timeout(sqe, &ts, 0); + + ret = io_uring_submit(&ring); + if (ret != 3) { + fprintf(stderr, "sqe submit failed\n"); + return 1; + } + + /* just drop all rings/etc. intact, exit() will clean them up */ + return 0; +} + +static int test_poll_cancel_with_timeouts(void) +{ + int ret; + pid_t p; + + p = fork(); + if (p == -1) { + fprintf(stderr, "fork() failed\n"); + return 1; + } + + if (p == 0) { + ret = __test_poll_cancel_with_timeouts(); + exit(ret); + } else { + int wstatus; + + if (waitpid(p, &wstatus, 0) == (pid_t)-1) { + perror("waitpid()"); + return 1; + } + if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) { + fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus)); + return 1; + } + } + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return 0; + + ret = test_poll_cancel(); + if (ret) { + fprintf(stderr, "test_poll_cancel failed\n"); + return -1; + } + + ret = test_poll_cancel_with_timeouts(); + if (ret) { + fprintf(stderr, "test_poll_cancel_with_timeouts failed\n"); + return -1; + } + return 0; } -- 2.34.0