summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPavel Begunkov <asml.silence@gmail.com>2019-10-25 12:48:30 +0300
committerJens Axboe <axboe@kernel.dk>2019-10-25 09:04:35 -0600
commit6f6de47d6126bdc63fc45c21ba4317650165240a (patch)
tree69dc86da570fd821b64b533d389a7790474b7a45
parent6494e84d4c3485279521cf75ef8d38f899d9bd89 (diff)
downloadliburing-6f6de47d6126bdc63fc45c21ba4317650165240a.tar.gz
liburing-6f6de47d6126bdc63fc45c21ba4317650165240a.tar.bz2
test/defer: Test deferring with drain and links
1. test user_data integrity with cancelled links 2. test the whole link is cancelled by sq_thread 3. hunging io_uring based on koverflow and kdropped Be aware, that this test may leave unkillable user process, or unstopped actively polling kthread. Signed-off-by: Pavel Begunkov <asml.silence@gmail.com> Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--test/Makefile4
-rw-r--r--test/defer.c257
2 files changed, 259 insertions, 2 deletions
diff --git a/test/Makefile b/test/Makefile
index 175afda..f6b092a 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -8,7 +8,7 @@ all_targets += poll poll-cancel ring-leak fsync io_uring_setup io_uring_register
send_recvmsg a4c0b3decb33-test 500f9fbadef8-test timeout \
sq-space_left stdout cq-ready cq-peek-batch file-register \
cq-size 8a9973408177-test a0908ae19763-test 232c93d07b74-test \
- socket-rw accept timeout-overflow
+ socket-rw accept timeout-overflow defer
include ../Makefile.quiet
@@ -24,7 +24,7 @@ test_srcs := poll.c poll-cancel.c ring-leak.c fsync.c io_uring_setup.c \
500f9fbadef8-test.c timeout.c sq-space_left.c stdout.c cq-ready.c\
cq-peek-batch.c file-register.c cq-size.c 8a9973408177-test.c \
a0908ae19763-test.c 232c93d07b74-test.c socket-rw.c accept.c \
- timeout-overflow.c
+ timeout-overflow.c defer.c
test_objs := $(patsubst %.c,%.ol,$(test_srcs))
diff --git a/test/defer.c b/test/defer.c
new file mode 100644
index 0000000..db0d904
--- /dev/null
+++ b/test/defer.c
@@ -0,0 +1,257 @@
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <stdbool.h>
+
+#include "liburing.h"
+
+struct test_context {
+ struct io_uring *ring;
+ struct io_uring_sqe **sqes;
+ struct io_uring_cqe *cqes;
+ int nr;
+};
+
+static void free_context(struct test_context *ctx)
+{
+ free(ctx->sqes);
+ free(ctx->cqes);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static int init_context(struct test_context *ctx, struct io_uring *ring, int nr)
+{
+ struct io_uring_sqe *sqe;
+ int i;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->nr = nr;
+ ctx->ring = ring;
+ ctx->sqes = malloc(nr * sizeof(*ctx->sqes));
+ ctx->cqes = malloc(nr * sizeof(*ctx->cqes));
+
+ if (!ctx->sqes || !ctx->cqes)
+ goto err;
+
+ for (i = 0; i < nr; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ goto err;
+ io_uring_prep_nop(sqe);
+ sqe->user_data = i;
+ ctx->sqes[i] = sqe;
+ }
+
+ return 0;
+err:
+ free_context(ctx);
+ printf("init context failed\n");
+ return 1;
+}
+
+static int wait_cqes(struct test_context *ctx)
+{
+ int ret, i;
+ struct io_uring_cqe *cqe;
+
+ for (i = 0; i < ctx->nr; i++) {
+ ret = io_uring_wait_cqe(ctx->ring, &cqe);
+
+ if (ret < 0) {
+ printf("wait_cqes: wait completion %d\n", ret);
+ return 1;
+ }
+ memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
+ io_uring_cqe_seen(ctx->ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test_cancelled_userdata(struct io_uring *ring)
+{
+ struct test_context ctx;
+ int ret, i, nr = 100;
+
+ if (init_context(&ctx, ring, nr))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_LINK;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ for (int i = 0; i < nr; i++) {
+ if (i != ctx.cqes[i].user_data) {
+ printf("invalid user data\n");
+ goto err;
+ }
+ }
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int test_thread_link_cancel(struct io_uring *ring)
+{
+ struct test_context ctx;
+ int ret, i, nr = 100;
+
+ if (init_context(&ctx, ring, nr))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_LINK;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ for (int i = 0; i < nr; i++) {
+ bool fail = false;
+
+ if (i == 0)
+ fail = (ctx.cqes[i].res != -EINVAL);
+ else
+ fail = (ctx.cqes[i].res != -ECANCELED);
+
+ if (fail) {
+ printf("invalid status\n");
+ goto err;
+ }
+ }
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int run_drained(struct io_uring *ring, int nr)
+{
+ struct test_context ctx;
+ int ret, i;
+
+ if (init_context(&ctx, ring, nr))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int test_overflow_hung(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ int ret, nr = 10;
+
+ while (*ring->cq.koverflow != 1000) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_nop(sqe);
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ return 1;
+ }
+ }
+
+ return run_drained(ring, nr);
+}
+
+static int test_dropped_hung(struct io_uring *ring)
+{
+ int nr = 10;
+
+ *ring->sq.kdropped = 1000;
+ return run_drained(ring, nr);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring, poll_ring, sqthread_ring;
+ int ret;
+
+ ret = io_uring_queue_init(1000, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(1000, &poll_ring, IORING_SETUP_IOPOLL);
+ if (ret) {
+ printf("poll_ring setup failed\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(1000, &sqthread_ring,
+ IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
+ if (ret) {
+ printf("poll_ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_cancelled_userdata(&poll_ring);
+ if (ret) {
+ printf("test_cancelled_userdata failed\n");
+ return ret;
+ }
+
+ ret = test_thread_link_cancel(&sqthread_ring);
+ if (ret) {
+ printf("test_thread_link_cancel failed\n");
+ return ret;
+ }
+
+ ret = test_overflow_hung(&ring);
+ if (ret) {
+ printf("test_overflow_hung failed\n");
+ return ret;
+ }
+
+ ret = test_dropped_hung(&ring);
+ if (ret) {
+ printf("test_dropped_hung failed\n");
+ return ret;
+ }
+
+ return 0;
+}