|
Packit |
d3489f |
/* SPDX-License-Identifier: MIT */
|
|
Packit |
d3489f |
#include <errno.h>
|
|
Packit |
d3489f |
#include <stdio.h>
|
|
Packit |
d3489f |
#include <unistd.h>
|
|
Packit |
d3489f |
#include <stdlib.h>
|
|
Packit |
d3489f |
#include <string.h>
|
|
Packit |
d3489f |
#include <fcntl.h>
|
|
Packit |
d3489f |
#include <sys/uio.h>
|
|
Packit |
d3489f |
#include <stdbool.h>
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#include "liburing.h"
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
struct test_context {
|
|
Packit |
d3489f |
struct io_uring *ring;
|
|
Packit |
d3489f |
struct io_uring_sqe **sqes;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqes;
|
|
Packit |
d3489f |
int nr;
|
|
Packit |
d3489f |
};
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static void free_context(struct test_context *ctx)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
free(ctx->sqes);
|
|
Packit |
d3489f |
free(ctx->cqes);
|
|
Packit |
d3489f |
memset(ctx, 0, sizeof(*ctx));
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int init_context(struct test_context *ctx, struct io_uring *ring, int nr)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
int i;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
memset(ctx, 0, sizeof(*ctx));
|
|
Packit |
d3489f |
ctx->nr = nr;
|
|
Packit |
d3489f |
ctx->ring = ring;
|
|
Packit |
d3489f |
ctx->sqes = malloc(nr * sizeof(*ctx->sqes));
|
|
Packit |
d3489f |
ctx->cqes = malloc(nr * sizeof(*ctx->cqes));
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!ctx->sqes || !ctx->cqes)
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++) {
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(ring);
|
|
Packit |
d3489f |
if (!sqe)
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
io_uring_prep_nop(sqe);
|
|
Packit |
d3489f |
sqe->user_data = i;
|
|
Packit |
d3489f |
ctx->sqes[i] = sqe;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
free_context(ctx);
|
|
Packit |
d3489f |
printf("init context failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int wait_cqes(struct test_context *ctx)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int ret, i;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < ctx->nr; i++) {
|
|
Packit |
d3489f |
ret = io_uring_wait_cqe(ctx->ring, &cqe);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (ret < 0) {
|
|
Packit |
d3489f |
printf("wait_cqes: wait completion %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
|
|
Packit |
d3489f |
io_uring_cqe_seen(ctx->ring, cqe);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_cancelled_userdata(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct test_context ctx;
|
|
Packit |
d3489f |
int ret, i, nr = 100;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (init_context(&ctx, ring, nr))
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++)
|
|
Packit |
d3489f |
ctx.sqes[i]->flags |= IOSQE_IO_LINK;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret <= 0) {
|
|
Packit |
d3489f |
printf("sqe submit failed: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (wait_cqes(&ctx))
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++) {
|
|
Packit |
d3489f |
if (i != ctx.cqes[i].user_data) {
|
|
Packit |
d3489f |
printf("invalid user data\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_thread_link_cancel(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct test_context ctx;
|
|
Packit |
d3489f |
int ret, i, nr = 100;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (init_context(&ctx, ring, nr))
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++)
|
|
Packit |
d3489f |
ctx.sqes[i]->flags |= IOSQE_IO_LINK;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret <= 0) {
|
|
Packit |
d3489f |
printf("sqe submit failed: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (wait_cqes(&ctx))
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++) {
|
|
Packit |
d3489f |
bool fail = false;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (i == 0)
|
|
Packit |
d3489f |
fail = (ctx.cqes[i].res != -EINVAL);
|
|
Packit |
d3489f |
else
|
|
Packit |
d3489f |
fail = (ctx.cqes[i].res != -ECANCELED);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (fail) {
|
|
Packit |
d3489f |
printf("invalid status\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int run_drained(struct io_uring *ring, int nr)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct test_context ctx;
|
|
Packit |
d3489f |
int ret, i;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (init_context(&ctx, ring, nr))
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++)
|
|
Packit |
d3489f |
ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret <= 0) {
|
|
Packit |
d3489f |
printf("sqe submit failed: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (wait_cqes(&ctx))
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
free_context(&ctx;;
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_overflow_hung(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
int ret, nr = 10;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
while (*ring->cq.koverflow != 1000) {
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(ring);
|
|
Packit |
d3489f |
if (!sqe) {
|
|
Packit |
d3489f |
printf("get sqe failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_prep_nop(sqe);
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret <= 0) {
|
|
Packit |
d3489f |
printf("sqe submit failed: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return run_drained(ring, nr);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_dropped_hung(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
int nr = 10;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
*ring->sq.kdropped = 1000;
|
|
Packit |
d3489f |
return run_drained(ring, nr);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int main(int argc, char *argv[])
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring ring, poll_ring, sqthread_ring;
|
|
Packit |
d3489f |
struct io_uring_params p;
|
|
Packit |
d3489f |
int ret, no_sqthread = 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (argc > 1)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
memset(&p, 0, sizeof(p));
|
|
Packit |
d3489f |
ret = io_uring_queue_init_params(1000, &ring, &p);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("ring setup failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(1000, &poll_ring, IORING_SETUP_IOPOLL);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("poll_ring setup failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(1000, &sqthread_ring,
|
|
Packit |
d3489f |
IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
if (geteuid()) {
|
|
Packit |
d3489f |
no_sqthread = 1;
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
printf("poll_ring setup failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_cancelled_userdata(&poll_ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("test_cancelled_userdata failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (no_sqthread) {
|
|
Packit |
d3489f |
printf("test_thread_link_cancel: skipped, not root\n");
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
ret = test_thread_link_cancel(&sqthread_ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("test_thread_link_cancel failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!(p.features & IORING_FEAT_NODROP)) {
|
|
Packit |
d3489f |
ret = test_overflow_hung(&ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("test_overflow_hung failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_dropped_hung(&ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
printf("test_dropped_hung failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|