blob: eb6705490da38bcaa6d5e8412730bbf5180571e5 [file] [log] [blame]
/* SPDX-License-Identifier: MIT */
/*
* Description: test io_uring_register_sync_cancel()
*
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "liburing.h"
#include "helpers.h"
static int no_sync_cancel, no_sync_cancel_op;
static int test_sync_cancel_timeout(struct io_uring *ring, int async, int by_op)
{
struct io_uring_sync_cancel_reg reg = { };
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int ret, fds[2], to_prep;
char buf[32];
if (pipe(fds) < 0) {
perror("pipe");
return 1;
}
to_prep = 1;
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
sqe->user_data = 0x89;
if (async)
sqe->flags |= IOSQE_ASYNC;
ret = io_uring_submit(ring);
if (ret != to_prep) {
fprintf(stderr, "submit=%d\n", ret);
return 1;
}
usleep(10000);
reg.flags = IORING_ASYNC_CANCEL_OP;
reg.opcode = IORING_OP_READ;
reg.timeout.tv_nsec = 1;
ret = io_uring_register_sync_cancel(ring, &reg);
/* earlier kernels had sync cancel, but not per-op */
if (ret == -EINVAL) {
no_sync_cancel_op = 1;
return 0;
}
if (async) {
/* we expect -ETIME here, but can race and get 0 */
if (ret != -ETIME && ret != 0) {
fprintf(stderr, "sync_cancel=%d\n", ret);
return 1;
}
} else {
if (ret < 0) {
fprintf(stderr, "sync_cancel=%d\n", ret);
return 1;
}
}
/*
* we could _almost_ use peek_cqe() here, but there is still
* a small gap where io-wq is done with the request and on
* its way to posting a completion, but hasn't done it just
* yet. the request is canceled and won't be doing any IO
* to buffers etc, but the cqe may not have quite arrived yet.
*/
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stderr, "peek=%d\n", ret);
return 1;
}
if (cqe->res >= 0) {
fprintf(stderr, "cqe->res=%d\n", cqe->res);
return 1;
}
io_uring_cqe_seen(ring, cqe);
return 0;
}
static int test_sync_cancel(struct io_uring *ring, int async, int nr_all,
int use_fd, int by_op)
{
struct io_uring_sync_cancel_reg reg = { };
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int ret, fds[2], to_prep, i;
char buf[32];
if (pipe(fds) < 0) {
perror("pipe");
return 1;
}
to_prep = 1;
if (nr_all)
to_prep = 4;
for (i = 0; i < to_prep; i++) {
sqe = io_uring_get_sqe(ring);
io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
sqe->user_data = 0x89;
if (async)
sqe->flags |= IOSQE_ASYNC;
}
ret = io_uring_submit(ring);
if (ret != to_prep) {
fprintf(stderr, "submit=%d\n", ret);
return 1;
}
usleep(10000);
if (!use_fd)
reg.addr = 0x89;
else
reg.fd = fds[0];
reg.timeout.tv_sec = 200;
if (nr_all)
reg.flags |= IORING_ASYNC_CANCEL_ALL;
if (use_fd)
reg.flags |= IORING_ASYNC_CANCEL_FD;
ret = io_uring_register_sync_cancel(ring, &reg);
if (ret < 0) {
if (ret == -EINVAL && !no_sync_cancel) {
no_sync_cancel = 1;
return 0;
}
fprintf(stderr, "sync_cancel=%d\n", ret);
return 1;
}
for (i = 0; i < to_prep; i++) {
/*
* we could _almost_ use peek_cqe() here, but there is still
* a small gap where io-wq is done with the request and on
* its way to posting a completion, but hasn't done it just
* yet. the request is canceled and won't be doing any IO
* to buffers etc, but the cqe may not have quite arrived yet.
*/
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stderr, "peek=%d\n", ret);
return 1;
}
if (cqe->res >= 0) {
fprintf(stderr, "cqe->res=%d\n", cqe->res);
return 1;
}
io_uring_cqe_seen(ring, cqe);
}
return 0;
}
int main(int argc, char *argv[])
{
struct io_uring ring;
int ret;
if (argc > 1)
return T_EXIT_SKIP;
ret = t_create_ring(7, &ring, 0);
if (ret == T_SETUP_SKIP)
return T_EXIT_SKIP;
else if (ret != T_SETUP_OK)
return ret;
ret = test_sync_cancel(&ring, 0, 0, 0, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 0 0 failed\n");
return T_EXIT_FAIL;
}
if (no_sync_cancel)
return T_EXIT_SKIP;
ret = test_sync_cancel(&ring, 0, 0, 0, 1);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 0 1 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 1, 0, 0, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 1 0 0 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 1, 0, 0, 1);
if (ret) {
fprintf(stderr, "test_sync_cancel 1 0 0 1 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 0, 1, 0, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 1 0 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 0, 1, 0, 1);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 1 0 1 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 1, 1, 0, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 1 1 0 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 0, 0, 1, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 0 1 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 1, 0, 1, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 1 0 1 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 0, 1, 1, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 0 1 1 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel(&ring, 1, 1, 1, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel 1 1 1 0 failed\n");
return T_EXIT_FAIL;
}
ret = test_sync_cancel_timeout(&ring, 0, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel_timeout 0 0\n");
return T_EXIT_FAIL;
}
if (no_sync_cancel_op)
return T_EXIT_PASS;
ret = test_sync_cancel_timeout(&ring, 0, 1);
if (ret) {
fprintf(stderr, "test_sync_cancel_timeout 0 1\n");
return T_EXIT_FAIL;
}
/* must be last, leaves request */
ret = test_sync_cancel_timeout(&ring, 1, 0);
if (ret) {
fprintf(stderr, "test_sync_cancel_timeout 1\n");
return T_EXIT_FAIL;
}
return T_EXIT_PASS;
}