summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2022-05-14 09:45:21 -0600
committerJens Axboe <axboe@kernel.dk>2022-05-14 09:45:21 -0600
commit6d94339b5050c31be8cc2734b7ef17cc41ec5fb6 (patch)
treebae72c7be5cd106af1cb3f2260627eed789990e9
parent15af89d0d906791639ac7950f4f12ed9f30341d3 (diff)
parent6977ce1f49d38c496a3781bd2f1d39c6f23b8639 (diff)
downloadliburing-6d94339b5050c31be8cc2734b7ef17cc41ec5fb6.tar.gz
liburing-6d94339b5050c31be8cc2734b7ef17cc41ec5fb6.tar.bz2
Merge branch 'cancel-fd-all'
* cancel-fd-all: test/poll-cancel-all: add async read test case test/poll-cancel-all: test IORING_ASYNC_CANCEL_ANY io_uring.h: add IORING_ASYNC_CANCEL_ANY test/poll-cancel-all: abort test if cancel flags not supported test/poll-cancel-all: add test case canceling a subset of pending test/poll-cancel-all: test issuing CANCEL_ALL | CANCEL_FD liburing.h: add io_uring_prep_cancel_fd() io_uring: add new cancel flags
-rw-r--r--src/include/liburing.h7
-rw-r--r--src/include/liburing/io_uring.h12
-rw-r--r--test/Makefile1
-rw-r--r--test/poll-cancel-all.c472
4 files changed, 492 insertions, 0 deletions
diff --git a/src/include/liburing.h b/src/include/liburing.h
index c220e90..1ec33cd 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -511,6 +511,13 @@ static inline void io_uring_prep_cancel(struct io_uring_sqe *sqe,
sqe->cancel_flags = (__u32) flags;
}
+static inline void io_uring_prep_cancel_fd(struct io_uring_sqe *sqe, int fd,
+ unsigned int flags)
+{
+ io_uring_prep_rw(IORING_OP_ASYNC_CANCEL, sqe, fd, NULL, 0, 0);
+ sqe->cancel_flags = (__u32) flags | IORING_ASYNC_CANCEL_FD;
+}
+
static inline void io_uring_prep_link_timeout(struct io_uring_sqe *sqe,
struct __kernel_timespec *ts,
unsigned flags)
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index 2541573..c9b3593 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -192,6 +192,18 @@ enum {
#define IORING_POLL_UPDATE_USER_DATA (1U << 2)
/*
+ * ASYNC_CANCEL flags.
+ *
+ * IORING_ASYNC_CANCEL_ALL Cancel all requests that match the given key
+ * IORING_ASYNC_CANCEL_FD Key off 'fd' for cancelation rather than the
+ * request 'user_data'
+ * IORING_ASYNC_CANCEL_ANY Match any request
+ */
+#define IORING_ASYNC_CANCEL_ALL (1U << 0)
+#define IORING_ASYNC_CANCEL_FD (1U << 1)
+#define IORING_ASYNC_CANCEL_ANY (1U << 2)
+
+/*
* IO completion data structure (Completion Queue Entry)
*/
struct io_uring_cqe {
diff --git a/test/Makefile b/test/Makefile
index 923f984..60b204b 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -109,6 +109,7 @@ test_srcs := \
pipe-reuse.c \
poll.c \
poll-cancel.c \
+ poll-cancel-all.c \
poll-cancel-ton.c \
poll-link.c \
poll-many.c \
diff --git a/test/poll-cancel-all.c b/test/poll-cancel-all.c
new file mode 100644
index 0000000..35116f5
--- /dev/null
+++ b/test/poll-cancel-all.c
@@ -0,0 +1,472 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test IORING_ASYNC_CANCEL_{ALL,FD}
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+
+#include "liburing.h"
+
+static int no_cancel_flags;
+
+static int test1(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 9; i++) {
+ if (no_cancel_flags)
+ break;
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ return 1;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res == -EINVAL) {
+ no_cancel_flags = 1;
+ break;
+ }
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ case 1 ... 8:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test2(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, fd2[2];
+
+ if (pipe(fd2) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (!(i & 1))
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ else
+ io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
+ sqe->user_data = i & 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 4) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 0:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ usleep(1000);
+
+ /*
+ * Should not have any pending CQEs now
+ */
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (!ret) {
+ fprintf(stderr, "Unexpected extra cancel cqe\n");
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd2[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 4) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fd2[0]);
+ close(fd2[1]);
+ return 0;
+err:
+ close(fd2[0]);
+ close(fd2[1]);
+ return 1;
+}
+
+static int test3(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, fd2[2];
+
+ if (pipe(fd2) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (!(i & 1)) {
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ sqe->flags |= IOSQE_ASYNC;
+ } else
+ io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
+ sqe->user_data = i & 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ usleep(10000);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
+ sqe->fd = 0;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 9; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 0:
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fd2[0]);
+ close(fd2[1]);
+ return 0;
+err:
+ close(fd2[0]);
+ close(fd2[1]);
+ return 1;
+}
+
+static int test4(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char buffer[32];
+ int ret, i;
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_read(sqe, fd[0], &buffer, sizeof(buffer), 0);
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ usleep(10000);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
+ sqe->fd = 0;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 9; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 1 ... 8:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, fd[2];
+
+ if (argc > 1)
+ return 0;
+
+ if (pipe(fd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test1(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test1 failed\n");
+ return ret;
+ }
+ if (no_cancel_flags)
+ return 0;
+
+ ret = test2(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test2 failed\n");
+ return ret;
+ }
+
+ ret = test3(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test3 failed\n");
+ return ret;
+ }
+
+ ret = test4(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test4 failed\n");
+ return ret;
+ }
+
+ return 0;
+}