diff options
author | thegeorg <thegeorg@yandex-team.com> | 2024-06-09 11:55:21 +0300 |
---|---|---|
committer | thegeorg <thegeorg@yandex-team.com> | 2024-06-09 12:07:55 +0300 |
commit | afd4899380eea1c70e2a68714b5da1c9919ccdbd (patch) | |
tree | cd5120708784139bc6a0f8881da1ed8389a065b3 /contrib/libs/liburing/test | |
parent | a83bd2dd3c21e38c6c0807ec5e679497ab567f24 (diff) | |
download | ydb-afd4899380eea1c70e2a68714b5da1c9919ccdbd.tar.gz |
Update contrib/libs/liburing to 2.6
3b51a9fb14de805208d11f1c077c78bb5d487e0f
Diffstat (limited to 'contrib/libs/liburing/test')
69 files changed, 5360 insertions, 894 deletions
diff --git a/contrib/libs/liburing/test/232c93d07b74.c b/contrib/libs/liburing/test/232c93d07b74.c index a89e7aebbf..27ed403eec 100644 --- a/contrib/libs/liburing/test/232c93d07b74.c +++ b/contrib/libs/liburing/test/232c93d07b74.c @@ -65,8 +65,7 @@ static void *rcv(void *arg) int res; if (p->tcp) { - int val = 1; - + int ret, val = 1; s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP); res = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); @@ -78,7 +77,8 @@ static void *rcv(void *arg) addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(t_bind_ephemeral_port(s0, &addr) == 0); + ret = t_bind_ephemeral_port(s0, &addr); + assert(!ret); p->bind_port = addr.sin_port; } else { s0 = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0); diff --git a/contrib/libs/liburing/test/a4c0b3decb33.c b/contrib/libs/liburing/test/a4c0b3decb33.c index 89a750a471..0e77ba8ee7 100644 --- a/contrib/libs/liburing/test/a4c0b3decb33.c +++ b/contrib/libs/liburing/test/a4c0b3decb33.c @@ -110,7 +110,7 @@ static void execute_one(void); static void loop(void) { int iter; - for (iter = 0; iter < 5000; iter++) { + for (iter = 0; iter < 50; iter++) { int pid = fork(); if (pid < 0) exit(1); diff --git a/contrib/libs/liburing/test/accept-reuse.c b/contrib/libs/liburing/test/accept-reuse.c index 47c1b1361f..f1a42b792d 100644 --- a/contrib/libs/liburing/test/accept-reuse.c +++ b/contrib/libs/liburing/test/accept-reuse.c @@ -46,7 +46,7 @@ int main(int argc, char **argv) return T_EXIT_SKIP; memset(¶ms, 0, sizeof(params)); - ret = io_uring_queue_init_params(4, &io_uring, ¶ms); + ret = t_io_uring_init_sqarray(4, &io_uring, ¶ms); if (ret) { fprintf(stderr, "io_uring_init_failed: %d\n", ret); return T_EXIT_FAIL; diff --git a/contrib/libs/liburing/test/accept.c b/contrib/libs/liburing/test/accept.c index df23eab53f..2208a54708 100644 --- a/contrib/libs/liburing/test/accept.c +++ b/contrib/libs/liburing/test/accept.c @@ -196,7 +196,8 @@ static int start_accept_listen(struct sockaddr_in *addr, int port_off, addr->sin_family = AF_INET; addr->sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(!t_bind_ephemeral_port(fd, addr)); + ret = t_bind_ephemeral_port(fd, addr); + assert(!ret); ret = listen(fd, 128); assert(ret != -1); @@ -311,6 +312,9 @@ static int test_loop(struct io_uring *ring, multishot ? "Multishot" : "", i, s_fd[i]); goto err; + } else if (s_fd[i] == 195 && args.overflow) { + fprintf(stderr, "Broken overflow handling\n"); + goto err; } if (multishot && fixed) { @@ -555,6 +559,9 @@ static int test_accept_cancel(unsigned usecs, unsigned int nr, bool multishot) fprintf(stderr, "unexpected 0 user data\n"); goto err; } else if (cqe->user_data <= nr) { + /* no multishot */ + if (cqe->res == -EINVAL) + return T_EXIT_SKIP; if (cqe->res != -EINTR && cqe->res != -ECANCELED) { fprintf(stderr, "Cancelled accept got %d\n", cqe->res); goto err; @@ -679,7 +686,12 @@ static int test_accept_fixed(void) ret = io_uring_queue_init(32, &m_io_uring, 0); assert(ret >= 0); ret = io_uring_register_files(&m_io_uring, &fd, 1); - assert(ret == 0); + if (ret) { + /* kernel doesn't support sparse registered files, skip */ + if (ret == -EBADF || ret == -EINVAL) + return T_EXIT_SKIP; + return T_EXIT_FAIL; + } ret = test(&m_io_uring, args); io_uring_queue_exit(&m_io_uring); return ret; @@ -701,7 +713,12 @@ static int test_multishot_fixed_accept(void) ret = io_uring_queue_init(MAX_FDS + 10, &m_io_uring, 0); assert(ret >= 0); ret = io_uring_register_files(&m_io_uring, fd, MAX_FDS); - assert(ret == 0); + if (ret) { + /* kernel doesn't support sparse registered files, skip */ + if (ret == -EBADF || ret == -EINVAL) + return T_EXIT_SKIP; + return T_EXIT_FAIL; + } ret = test(&m_io_uring, args); io_uring_queue_exit(&m_io_uring); return ret; diff --git a/contrib/libs/liburing/test/buf-ring-nommap.c b/contrib/libs/liburing/test/buf-ring-nommap.c new file mode 100644 index 0000000000..7624c67fa0 --- /dev/null +++ b/contrib/libs/liburing/test/buf-ring-nommap.c @@ -0,0 +1,122 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test IOU_PBUF_RING_MMAP with a ring setup with a ring + * setup without mmap'ing sq/cq arrays + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> + +#include "liburing.h" +#include "helpers.h" + +static int bgid = 5; +static int bid = 89; + +int main(int argc, char *argv[]) +{ + struct io_uring_buf_ring *br; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + size_t ring_size; + int ret, ring_mask, fds[2]; + struct io_uring_buf_reg reg = { + .ring_entries = 1, + .bgid = bgid, + .flags = IOU_PBUF_RING_MMAP, + }; + struct io_uring_params p = { }; + void *ring_mem; + char buf[32]; + off_t off; + + if (argc > 1) + return T_EXIT_SKIP; + + if (posix_memalign(&ring_mem, 16384, 16384)) + return T_EXIT_FAIL; + + p.flags = IORING_SETUP_NO_MMAP; + ret = io_uring_queue_init_mem(1, &ring, &p, ring_mem, 16384); + if (ret < 0) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "queue init failed %d\n", ret); + return T_EXIT_FAIL; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ring_size = sizeof(struct io_uring_buf); + ring_mask = io_uring_buf_ring_mask(1); + + ret = io_uring_register_buf_ring(&ring, ®, 0); + if (ret) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "reg buf ring: %d\n", ret); + return T_EXIT_FAIL; + } + + off = IORING_OFF_PBUF_RING | + (unsigned long long) bgid << IORING_OFF_PBUF_SHIFT; + br = mmap(NULL, ring_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, ring.ring_fd, off); + if (br == MAP_FAILED) { + if (errno == ENOMEM) + return T_EXIT_SKIP; + perror("mmap"); + return T_EXIT_FAIL; + } + + io_uring_buf_ring_add(br, buf, sizeof(buf), bid, ring_mask, 0); + io_uring_buf_ring_advance(br, 1); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], NULL, 0, 0); + sqe->flags |= IOSQE_BUFFER_SELECT; + sqe->buf_group = bgid; + + io_uring_submit(&ring); + + ret = write(fds[1], "Hello", 5); + if (ret < 0) { + perror("write"); + return T_EXIT_FAIL; + } else if (ret != 5) { + fprintf(stderr, "short write %d\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_BUFFER)) { + fprintf(stderr, "buffer not selected in cqe\n"); + return T_EXIT_FAIL; + } + if ((cqe->flags >> IORING_CQE_BUFFER_SHIFT) != bid) { + fprintf(stderr, "wrong buffer id returned\n"); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(&ring, cqe); + + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/sqpoll-cancel-hang.t/ya.make b/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make index 2ce4882281..1d44c5d29c 100644 --- a/contrib/libs/liburing/test/sqpoll-cancel-hang.t/ya.make +++ b/contrib/libs/liburing/test/buf-ring-nommap.t/ya.make @@ -26,8 +26,8 @@ CFLAGS( SRCDIR(contrib/libs/liburing/test) SRCS( + buf-ring-nommap.c helpers.c - sqpoll-cancel-hang.c ) END() diff --git a/contrib/libs/liburing/test/buf-ring-put.c b/contrib/libs/liburing/test/buf-ring-put.c new file mode 100644 index 0000000000..6508f87023 --- /dev/null +++ b/contrib/libs/liburing/test/buf-ring-put.c @@ -0,0 +1,84 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test persistence of mmap'ed provided ring buffers. Use a range + * of buffer group IDs that puts us into both the lower end array + * and higher end xarry. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <fcntl.h> +#include <string.h> +#include <sys/mman.h> + +#include "liburing.h" +#include "helpers.h" + +#define BGID_START 60 +#define BGID_NR 10 +#define ENTRIES 512 + +int main(int argc, char *argv[]) +{ + struct io_uring_buf_ring *br[BGID_NR]; + struct io_uring ring; + size_t ring_size; + int ret, i, j; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = io_uring_queue_init(1, &ring, 0); + if (ret) { + fprintf(stderr, "queue init failed %d\n", ret); + return T_EXIT_FAIL; + } + + ring_size = ENTRIES * sizeof(struct io_uring_buf); + + for (i = 0; i < BGID_NR; i++) { + int bgid = BGID_START + i; + struct io_uring_buf_reg reg = { + .ring_entries = ENTRIES, + .bgid = bgid, + .flags = IOU_PBUF_RING_MMAP, + }; + off_t off; + + ret = io_uring_register_buf_ring(&ring, ®, 0); + if (ret) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "reg buf ring: %d\n", ret); + return T_EXIT_FAIL; + } + + off = IORING_OFF_PBUF_RING | + (unsigned long long) bgid << IORING_OFF_PBUF_SHIFT; + br[i] = mmap(NULL, ring_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, ring.ring_fd, off); + if (br[i] == MAP_FAILED) { + perror("mmap"); + return T_EXIT_FAIL; + } + } + + for (i = 0; i < BGID_NR; i++) { + ret = io_uring_unregister_buf_ring(&ring, BGID_START + i); + if (ret) { + fprintf(stderr, "reg buf ring: %d\n", ret); + return T_EXIT_FAIL; + } + } + + for (j = 0; j < 1000; j++) { + for (i = 0; i < BGID_NR; i++) + memset(br[i], 0x5a, ring_size); + usleep(1000); + } + + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/buf-ring-put.t/ya.make b/contrib/libs/liburing/test/buf-ring-put.t/ya.make new file mode 100644 index 0000000000..12318ed718 --- /dev/null +++ b/contrib/libs/liburing/test/buf-ring-put.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + buf-ring-put.c + helpers.c +) + +END() diff --git a/contrib/libs/liburing/test/buf-ring.c b/contrib/libs/liburing/test/buf-ring.c index 08f03f99ec..682a97ec28 100644 --- a/contrib/libs/liburing/test/buf-ring.c +++ b/contrib/libs/liburing/test/buf-ring.c @@ -293,7 +293,7 @@ static int test_one_read(int fd, int bgid, struct io_uring *ring) return cqe->flags >> 16; } -static int test_running(int bgid, int entries, int loops) +static int test_running(int bgid, int entries, int loops, int use_mmap) { int ring_mask = io_uring_buf_ring_mask(entries); struct io_uring_buf_ring *br; @@ -308,11 +308,37 @@ static int test_running(int bgid, int entries, int loops) else if (ret != T_SETUP_OK) return 1; - br = io_uring_setup_buf_ring(&ring, entries, bgid, 0, &ret); - if (!br) { - /* by now should have checked if this is supported or not */ - fprintf(stderr, "Buffer ring register failed %d\n", ret); - return 1; + if (!use_mmap) { + br = io_uring_setup_buf_ring(&ring, entries, bgid, 0, &ret); + if (!br) { + /* by now should have checked if this is supported or not */ + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + } else { + struct io_uring_buf_reg reg = { + .ring_entries = entries, + .bgid = bgid, + .flags = IOU_PBUF_RING_MMAP, + }; + size_t ring_size; + off_t off; + + ret = io_uring_register_buf_ring(&ring, ®, 0); + if (ret) { + fprintf(stderr, "mmap ring register failed %d\n", ret); + return 1; + } + + off = IORING_OFF_PBUF_RING | + (unsigned long long) bgid << IORING_OFF_PBUF_SHIFT; + ring_size = sizeof(struct io_uring_buf) * entries; + br = mmap(NULL, ring_size, PROT_READ | PROT_WRITE, + MAP_SHARED | MAP_POPULATE, ring.ring_fd, off); + if (br == MAP_FAILED) { + perror("mmap"); + return 1; + } } buffers = malloc(sizeof(bool) * entries); @@ -424,12 +450,21 @@ int main(int argc, char *argv[]) } for (i = 0; !no_buf_ring && entries[i] != -1; i++) { - ret = test_running(2, entries[i], 3); + ret = test_running(2, entries[i], 3, 0); if (ret) { fprintf(stderr, "test_running(%d) failed\n", entries[i]); return T_EXIT_FAIL; } } + for (i = 0; !no_buf_ring && entries[i] != -1; i++) { + ret = test_running(2, entries[i], 3, 1); + if (ret) { + fprintf(stderr, "test_running(%d) mmap failed\n", entries[i]); + return T_EXIT_FAIL; + } + } + + return T_EXIT_PASS; } diff --git a/contrib/libs/liburing/test/connect.c b/contrib/libs/liburing/test/connect.c index f1b7d941f4..cabf3599eb 100644 --- a/contrib/libs/liburing/test/connect.c +++ b/contrib/libs/liburing/test/connect.c @@ -134,7 +134,7 @@ static int configure_connect(int fd, struct sockaddr_in* addr) return ret; } -static int connect_socket(struct io_uring *ring, int fd, int *code) +static int connect_socket(struct io_uring *ring, int fd, int *code, int async) { struct sockaddr_in addr; int ret, res; @@ -151,6 +151,8 @@ static int connect_socket(struct io_uring *ring, int fd, int *code) } io_uring_prep_connect(sqe, fd, (struct sockaddr*)&addr, sizeof(addr)); + if (async) + sqe->flags |= IOSQE_ASYNC; sqe->user_data = 1; ret = submit_and_wait(ring, &res); @@ -187,7 +189,7 @@ static int test_connect_with_no_peer(struct io_uring *ring) if (connect_fd == -1) return -1; - ret = connect_socket(ring, connect_fd, &code); + ret = connect_socket(ring, connect_fd, &code, 0); if (ret == -1) goto err; @@ -210,7 +212,7 @@ err: return -1; } -static int test_connect(struct io_uring *ring) +static int test_connect(struct io_uring *ring, int async) { int accept_fd; int connect_fd; @@ -228,7 +230,7 @@ static int test_connect(struct io_uring *ring) if (connect_fd == -1) goto err1; - ret = connect_socket(ring, connect_fd, &code); + ret = connect_socket(ring, connect_fd, &code, async); if (ret == -1) goto err2; @@ -297,7 +299,7 @@ static int test_connect_timeout(struct io_uring *ring) } // We first connect with one client socket in order to fill the accept queue. - ret = connect_socket(ring, connect_fd[0], &code); + ret = connect_socket(ring, connect_fd[0], &code, 0); if (ret == -1 || code != 0) { fprintf(stderr, "unable to connect\n"); goto err; @@ -364,15 +366,12 @@ err: return -1; } -int main(int argc, char *argv[]) +static int test(int flags) { struct io_uring ring; int ret; - if (argc > 1) - return T_EXIT_SKIP; - - ret = io_uring_queue_init(8, &ring, 0); + ret = io_uring_queue_init(8, &ring, flags); if (ret) { fprintf(stderr, "io_uring_queue_setup() = %d\n", ret); return T_EXIT_FAIL; @@ -391,7 +390,13 @@ int main(int argc, char *argv[]) if (no_connect) return T_EXIT_SKIP; - ret = test_connect(&ring); + ret = test_connect(&ring, 0); + if (ret == -1) { + fprintf(stderr, "test_connect(): failed\n"); + return T_EXIT_FAIL; + } + + ret = test_connect(&ring, 1); if (ret == -1) { fprintf(stderr, "test_connect(): failed\n"); return T_EXIT_FAIL; @@ -406,3 +411,33 @@ int main(int argc, char *argv[]) io_uring_queue_exit(&ring); return T_EXIT_PASS; } + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0); + if (ret == -1) { + fprintf(stderr, "test 0 failed\n"); + return T_EXIT_FAIL; + } + if (no_connect) + return T_EXIT_SKIP; + + ret = test(IORING_SETUP_SQPOLL); + if (ret == -1) { + fprintf(stderr, "test SQPOLL failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN); + if (ret == -1) { + fprintf(stderr, "test DEFER failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/coredump.c b/contrib/libs/liburing/test/coredump.c new file mode 100644 index 0000000000..44e2817897 --- /dev/null +++ b/contrib/libs/liburing/test/coredump.c @@ -0,0 +1,60 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: trigger segfault. A recent 6.4-rc kernel introduced a bug + * via vhost where segfaults for applications using io_uring + * would hang in D state forever upon trying to generate the + * core file. Perform a trivial test where a child process + * generates a NULL pointer dereference and ensure that we don't + * hang. + * + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/wait.h> + +#include "liburing.h" +#include "helpers.h" + +static void test(void) +{ + struct io_uring_sqe *sqe; + struct io_uring ring; + int *ptr = NULL; + int fds[2]; + char r1; + + if (pipe(fds) < 0) { + perror("pipe"); + exit(0); + } + + io_uring_queue_init(8, &ring, 0); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], &r1, sizeof(r1), 0); + sqe->flags = IOSQE_ASYNC; + sqe->user_data = 1; + + io_uring_submit(&ring); + *ptr = 0; + exit(0); +} + +int main(int argc, char *argv[]) +{ + pid_t pid; + int wstat; + + pid = fork(); + if (pid < 0) { + perror("fork"); + return T_EXIT_SKIP; + } else if (!pid) { + test(); + } + + wait(&wstat); + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/pollfree.t/ya.make b/contrib/libs/liburing/test/coredump.t/ya.make index 10e4570b18..c57a92c1b6 100644 --- a/contrib/libs/liburing/test/pollfree.t/ya.make +++ b/contrib/libs/liburing/test/coredump.t/ya.make @@ -26,8 +26,8 @@ CFLAGS( SRCDIR(contrib/libs/liburing/test) SRCS( + coredump.c helpers.c - pollfree.c ) END() diff --git a/contrib/libs/liburing/test/defer-tw-timeout.c b/contrib/libs/liburing/test/defer-tw-timeout.c new file mode 100644 index 0000000000..ea06e19243 --- /dev/null +++ b/contrib/libs/liburing/test/defer-tw-timeout.c @@ -0,0 +1,169 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test waiting for more events than what will be posted with + * a timeout with DEFER_TASKRUN. All kernels should time out, + * but a non-buggy kernel will end up with one CQE available + * for reaping. Buggy kernels will not have processed the + * task_work and will have 0 events. + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <pthread.h> + +#include "liburing.h" +#include "helpers.h" + +struct d { + int fd; +}; + +static void *thread_fn(void *data) +{ + struct d *d = data; + int ret; + + usleep(100000); + ret = write(d->fd, "Hello", 5); + if (ret < 0) + perror("write"); + return NULL; +} + +static int test_poll(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + struct __kernel_timespec ts; + int ret, fds[2], i; + pthread_t thread; + char buf[32]; + struct d d; + void *tret; + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + d.fd = fds[1]; + + sqe = io_uring_get_sqe(ring); + io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0); + + pthread_create(&thread, NULL, thread_fn, &d); + + ts.tv_sec = 1; + ts.tv_nsec = 0; + + ret = io_uring_submit_and_wait_timeout(ring, &cqe, 2, &ts, NULL); + if (ret != 1) { + fprintf(stderr, "unexpected wait ret %d\n", ret); + return T_EXIT_FAIL; + } + + for (i = 0; i < 2; i++) { + ret = io_uring_peek_cqe(ring, &cqe); + if (ret) + break; + io_uring_cqe_seen(ring, cqe); + } + + if (i != 1) { + fprintf(stderr, "Got %d request, expected 1\n", i); + return T_EXIT_FAIL; + } + + pthread_join(thread, &tret); + return T_EXIT_PASS; +} + +static int test_file(struct io_uring *ring, char *__fname) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + struct __kernel_timespec ts; + char filename[64], *fname; + int fd, ret, i; + void *buf; + + if (!__fname) { + fname = filename; + sprintf(fname, ".defer-tw-timeout.%d", getpid()); + t_create_file(fname, 128*1024); + } else { + fname = __fname; + } + + fd = open(fname, O_RDONLY | O_DIRECT); + if (fd < 0) { + perror("open"); + if (!__fname) + unlink(fname); + return T_EXIT_FAIL; + } + + if (!__fname) + unlink(fname); + + if (posix_memalign(&buf, 4096, 4096)) { + close(fd); + return T_EXIT_FAIL; + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_read(sqe, fd, buf, 4096, 0); + + ts.tv_sec = 1; + ts.tv_nsec = 0; + + ret = io_uring_submit_and_wait_timeout(ring, &cqe, 2, &ts, NULL); + if (ret != 1) { + fprintf(stderr, "unexpected wait ret %d\n", ret); + close(fd); + return T_EXIT_FAIL; + } + + for (i = 0; i < 2; i++) { + ret = io_uring_peek_cqe(ring, &cqe); + if (ret) + break; + io_uring_cqe_seen(ring, cqe); + } + + if (i != 1) { + fprintf(stderr, "Got %d request, expected 1\n", i); + close(fd); + return T_EXIT_FAIL; + } + + close(fd); + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + char *fname = NULL; + int ret; + + ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN); + if (ret == -EINVAL) + return T_EXIT_SKIP; + + if (argc > 1) + fname = argv[1]; + + ret = test_file(&ring, fname); + if (ret != T_EXIT_PASS) + return ret; + + ret = test_poll(&ring); + if (ret != T_EXIT_PASS) + return ret; + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make b/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make new file mode 100644 index 0000000000..f742bd6e16 --- /dev/null +++ b/contrib/libs/liburing/test/defer-tw-timeout.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + defer-tw-timeout.c + helpers.c +) + +END() diff --git a/contrib/libs/liburing/test/eventfd-reg.c b/contrib/libs/liburing/test/eventfd-reg.c index a1655267ab..b1efb17383 100644 --- a/contrib/libs/liburing/test/eventfd-reg.c +++ b/contrib/libs/liburing/test/eventfd-reg.c @@ -44,7 +44,7 @@ int main(int argc, char *argv[]) return T_EXIT_FAIL; } - /* Check that registrering again will get -EBUSY */ + /* Check that registering again will get -EBUSY */ ret = io_uring_register_eventfd(&ring, evfd[1]); if (ret != -EBUSY) { fprintf(stderr, "unexpected 2nd register: %d\n", ret); diff --git a/contrib/libs/liburing/test/fd-install.c b/contrib/libs/liburing/test/fd-install.c new file mode 100644 index 0000000000..222eb1de03 --- /dev/null +++ b/contrib/libs/liburing/test/fd-install.c @@ -0,0 +1,501 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test installing a direct descriptor into the regular + * file table + * + */ +#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_fd_install; + +/* test that O_CLOEXEC is accepted, and others are not */ +static int test_flags(struct io_uring *ring, int async) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, fds[2], fd; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_register_files(ring, &fds[0], 1); + if (ret) { + fprintf(stderr, "failed register files %d\n", ret); + return T_EXIT_FAIL; + } + + /* check that setting an invalid flag fails */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, 1U << 17); + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != -EINVAL) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + + /* check that IORING_FIXED_FD_NO_CLOEXEC is accepted */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, IORING_FIXED_FD_NO_CLOEXEC); + if (async) + sqe->flags |= IOSQE_ASYNC; + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res < 0) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + fd = cqe->res; + io_uring_cqe_seen(ring, cqe); + + close(fds[0]); + close(fds[1]); + close(fd); + io_uring_unregister_files(ring); + + return T_EXIT_PASS; +} + +static int test_linked(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, fds[2], fd, i; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_register_files(ring, &fds[0], 1); + if (ret) { + fprintf(stderr, "failed register files %d\n", ret); + return T_EXIT_FAIL; + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + sqe->user_data = 1; + + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, 0); + sqe->user_data = 2; + + ret = io_uring_submit(ring); + if (ret != 2) { + fprintf(stderr, "submit: %d\n", ret); + return T_EXIT_FAIL; + } + + fd = -1; + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res < 0) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (cqe->user_data == 2) + fd = cqe->res; + io_uring_cqe_seen(ring, cqe); + } + + close(fds[0]); + close(fds[1]); + if (fd != -1) + close(fd); + io_uring_unregister_files(ring); + return T_EXIT_PASS; +} + +/* test not setting IOSQE_FIXED_FILE */ +static int test_not_fixed(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, fds[2]; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_register_files(ring, &fds[0], 1); + if (ret) { + fprintf(stderr, "failed register files %d\n", ret); + return T_EXIT_FAIL; + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, 0); + sqe->flags &= ~IOSQE_FIXED_FILE; + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != -EBADF) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(ring, cqe); + + close(fds[0]); + close(fds[1]); + io_uring_unregister_files(ring); + + return T_EXIT_PASS; +} + +/* test invalid direct descriptor indexes */ +static int test_bad_fd(struct io_uring *ring, int some_fd) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret; + + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, some_fd, 0); + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != -EBADF) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(ring, cqe); + return T_EXIT_PASS; +} + +/* test basic functionality of shifting a direct descriptor to a normal file */ +static int test_working(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, fds[2]; + char buf[32]; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + /* register read side */ + ret = io_uring_register_files(ring, &fds[0], 1); + if (ret) { + fprintf(stderr, "failed register files %d\n", ret); + return T_EXIT_FAIL; + } + + /* close normal descriptor */ + close(fds[0]); + + /* normal read should fail */ + ret = read(fds[0], buf, 1); + if (ret != -1) { + fprintf(stderr, "unexpected read ret %d\n", ret); + return T_EXIT_FAIL; + } + if (errno != EBADF) { + fprintf(stderr, "unexpected read failure %d\n", errno); + return T_EXIT_FAIL; + } + + /* verify we can read the data */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0); + sqe->flags |= IOSQE_FIXED_FILE; + io_uring_submit(ring); + + /* put some data in the pipe */ + ret = write(fds[1], "Hello", 5); + if (ret < 0) { + perror("write"); + return T_EXIT_FAIL; + } else if (ret != 5) { + fprintf(stderr, "short write %d\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != 5) { + fprintf(stderr, "weird pipe read ret %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + + /* fixed pipe read worked, now re-install as a regular fd */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, 0); + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res == -EINVAL) { + no_fd_install = 1; + return T_EXIT_SKIP; + } + if (cqe->res < 0) { + fprintf(stderr, "failed install fd: %d\n", cqe->res); + return T_EXIT_FAIL; + } + /* stash new pipe read side fd in old spot */ + fds[0] = cqe->res; + io_uring_cqe_seen(ring, cqe); + + ret = write(fds[1], "Hello", 5); + if (ret < 0) { + perror("write"); + return T_EXIT_FAIL; + } else if (ret != 5) { + fprintf(stderr, "short write %d\n", ret); + return T_EXIT_FAIL; + } + + /* normal pipe read should now work with new fd */ + ret = read(fds[0], buf, sizeof(buf)); + if (ret != 5) { + fprintf(stderr, "unexpected read ret %d\n", ret); + return T_EXIT_FAIL; + } + + /* close fixed file */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_close_direct(sqe, 0); + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res) { + fprintf(stderr, "close fixed fd %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + + ret = write(fds[1], "Hello", 5); + if (ret < 0) { + perror("write"); + return T_EXIT_FAIL; + } else if (ret != 5) { + fprintf(stderr, "short write %d\n", ret); + return T_EXIT_FAIL; + } + + /* normal pipe read should still work with new fd */ + ret = read(fds[0], buf, sizeof(buf)); + if (ret != 5) { + fprintf(stderr, "unexpected read ret %d\n", ret); + return T_EXIT_FAIL; + } + + /* fixed fd pipe read should now fail */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0); + sqe->flags = IOSQE_FIXED_FILE; + io_uring_submit(ring); + + /* put some data in the pipe */ + ret = write(fds[1], "Hello", 5); + if (ret < 0) { + perror("write"); + return T_EXIT_FAIL; + } else if (ret != 5) { + fprintf(stderr, "short write %d\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != -EBADF) { + fprintf(stderr, "weird pipe read ret %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + + close(fds[0]); + close(fds[1]); + io_uring_unregister_files(ring); + return T_EXIT_PASS; +} + +static int test_creds(struct io_uring *ring, int async) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int cred_id, ret, fds[2]; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_register_files(ring, &fds[0], 1); + if (ret) { + fprintf(stderr, "failed register files %d\n", ret); + return T_EXIT_FAIL; + } + + cred_id = io_uring_register_personality(ring); + if (cred_id < 0) { + fprintf(stderr, "Failed registering creds: %d\n", cred_id); + return T_EXIT_FAIL; + } + + /* check that asking for creds fails */ + sqe = io_uring_get_sqe(ring); + io_uring_prep_fixed_fd_install(sqe, 0, 0); + if (async) + sqe->flags |= IOSQE_ASYNC; + sqe->personality = cred_id; + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res > 0) { + fprintf(stderr, "install succeeded with creds\n"); + return T_EXIT_FAIL; + } + if (cqe->res != -EPERM) { + fprintf(stderr, "unexpected cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + + close(fds[0]); + close(fds[1]); + io_uring_unregister_files(ring); + io_uring_unregister_personality(ring, cred_id); + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = io_uring_queue_init(4, &ring, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + + ret = test_working(&ring); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_working failed\n"); + return ret; + } + if (no_fd_install) + return T_EXIT_SKIP; + + ret = test_bad_fd(&ring, 0); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_bad_fd 0 failed\n"); + return ret; + } + + ret = test_bad_fd(&ring, 500); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_bad_fd 500 failed\n"); + return ret; + } + + ret = test_not_fixed(&ring); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_not_fixed failed\n"); + return ret; + } + + ret = test_flags(&ring, 0); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_flags 0 failed\n"); + return ret; + } + + ret = test_flags(&ring, 1); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_flags 1 failed\n"); + return ret; + } + + ret = test_creds(&ring, 0); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_creds 0 failed\n"); + return ret; + } + + ret = test_creds(&ring, 1); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_creds 1 failed\n"); + return ret; + } + + ret = test_linked(&ring); + if (ret != T_EXIT_PASS) { + if (ret == T_EXIT_FAIL) + fprintf(stderr, "test_linked failed\n"); + return ret; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/fd-install.t/ya.make b/contrib/libs/liburing/test/fd-install.t/ya.make new file mode 100644 index 0000000000..0f41ccec34 --- /dev/null +++ b/contrib/libs/liburing/test/fd-install.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + fd-install.c + helpers.c +) + +END() diff --git a/contrib/libs/liburing/test/file-register.c b/contrib/libs/liburing/test/file-register.c index ee30b3e4f8..ce51022632 100644 --- a/contrib/libs/liburing/test/file-register.c +++ b/contrib/libs/liburing/test/file-register.c @@ -306,7 +306,7 @@ static int test_sparse(struct io_uring *ring) files = open_files(100, 100, 0); ret = io_uring_register_files(ring, files, 200); if (ret) { - if (ret == -EBADF) { + if (ret == -EBADF || ret == -EINVAL) { fprintf(stdout, "Sparse files not supported, skipping\n"); no_update = 1; goto done; @@ -353,10 +353,14 @@ err: static int test_basic(struct io_uring *ring, int fail) { int *files; - int ret; + int ret, i; int nr_files = fail ? 10 : 100; - files = open_files(nr_files, 0, 0); + files = open_files(nr_files, fail ? 90 : 0, 0); + if (fail) { + for (i = nr_files; i < nr_files + 90; i++) + files[i] = -2; + } ret = io_uring_register_files(ring, files, 100); if (ret) { if (fail) { diff --git a/contrib/libs/liburing/test/file-verify.c b/contrib/libs/liburing/test/file-verify.c index fd192ec272..0014236f5c 100644 --- a/contrib/libs/liburing/test/file-verify.c +++ b/contrib/libs/liburing/test/file-verify.c @@ -29,7 +29,7 @@ #define MAX_VECS 16 /* - * Can be anything, let's just do something for a bit of parallellism + * Can be anything, let's just do something for a bit of parallelism */ #define READ_BATCH 16 diff --git a/contrib/libs/liburing/test/fixed-buf-merge.c b/contrib/libs/liburing/test/fixed-buf-merge.c new file mode 100644 index 0000000000..e75948fa79 --- /dev/null +++ b/contrib/libs/liburing/test/fixed-buf-merge.c @@ -0,0 +1,98 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Test fixed buffer merging/skipping + * + * Taken from: https://github.com/axboe/liburing/issues/994 + * + */ +#include <stdio.h> +#include <string.h> +#include <fcntl.h> +#include <stdlib.h> +#include <unistd.h> + +#include "liburing.h" +#include "helpers.h" + +int main(int argc, char *argv[]) +{ + int ret, i, fd, initial_offset = 4096, num_requests = 3; + struct io_uring ring; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct iovec iov; + char *buffer, *to_free; + unsigned head; + char filename[64]; + + ret = io_uring_queue_init(4, &ring, 0); + if (ret) { + fprintf(stderr, "queue_init: %d\n", ret); + return T_EXIT_FAIL; + } + + sprintf(filename, ".fixed-buf-%d", getpid()); + t_create_file(filename, 4 * 4096); + + fd = open(filename, O_RDONLY | O_DIRECT, 0644); + if (fd < 0) { + perror("open"); + goto err_unlink; + } + + to_free = buffer = aligned_alloc(4096, 128 * 4096); + if (!buffer) { + perror("aligned_alloc"); + goto err_unlink; + } + + /* Register buffer */ + iov.iov_base = buffer; + iov.iov_len = 128 * 4096; + + ret = io_uring_register_buffers(&ring, &iov, 1); + if (ret) { + fprintf(stderr, "buf register: %d\n", ret); + goto err_unlink; + } + + /* Prepare read requests */ + buffer += initial_offset; + for (i = 0; i < num_requests; i++) { + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read_fixed(sqe, fd, buffer, 4096, 4096 * i, 0); + buffer += 4096; + } + + /* Submit requests and reap completions */ + ret = io_uring_submit_and_wait(&ring, num_requests); + if (ret != num_requests) { + fprintf(stderr, "Submit and wait: %d\n", ret); + goto err_unlink; + } + + i = 0; + io_uring_for_each_cqe(&ring, head, cqe) { + if (cqe->res != 4096) { + fprintf(stderr, "cqe: %d\n", cqe->res); + goto err_unlink; + } + i++; + } + + if (i != num_requests) { + fprintf(stderr, "Got %d completions\n", i); + goto err_unlink; + } + + io_uring_cq_advance(&ring, i); + io_uring_queue_exit(&ring); + close(fd); + free(to_free); + unlink(filename); + return T_EXIT_PASS; +err_unlink: + unlink(filename); + return T_EXIT_FAIL; +} diff --git a/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make b/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make new file mode 100644 index 0000000000..c988deecdb --- /dev/null +++ b/contrib/libs/liburing/test/fixed-buf-merge.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + fixed-buf-merge.c + helpers.c +) + +END() diff --git a/contrib/libs/liburing/test/futex.c b/contrib/libs/liburing/test/futex.c new file mode 100644 index 0000000000..7a7f0c235c --- /dev/null +++ b/contrib/libs/liburing/test/futex.c @@ -0,0 +1,572 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: exercise futex wait/wake/waitv + * + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <pthread.h> +#include <errno.h> +#include <linux/futex.h> + +#include "liburing.h" +#include "helpers.h" + +#define LOOPS 500 +#define NFUTEX 8 + +#ifndef FUTEX2_SIZE_U8 +#define FUTEX2_SIZE_U8 0x00 +#define FUTEX2_SIZE_U16 0x01 +#define FUTEX2_SIZE_U32 0x02 +#define FUTEX2_SIZE_U64 0x03 +#define FUTEX2_NUMA 0x04 + /* 0x08 */ + /* 0x10 */ + /* 0x20 */ + /* 0x40 */ +#define FUTEX2_PRIVATE FUTEX_PRIVATE_FLAG + +#define FUTEX2_SIZE_MASK 0x03 +#endif + +static int no_futex; + +static void *fwake(void *data) +{ + unsigned int *futex = data; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + int ret; + + ret = io_uring_queue_init(1, &ring, 0); + if (ret) { + fprintf(stderr, "queue init: %d\n", ret); + return NULL; + } + + *futex = 1; + sqe = io_uring_get_sqe(&ring); + io_uring_prep_futex_wake(sqe, futex, 1, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + sqe->user_data = 3; + + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait: %d\n", ret); + return NULL; + } + io_uring_cqe_seen(&ring, cqe); + io_uring_queue_exit(&ring); + return NULL; +} + +static int __test(struct io_uring *ring, int vectored, int async, + int async_cancel) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct futex_waitv fw[NFUTEX]; + unsigned int *futex; + pthread_t threads[NFUTEX]; + void *tret; + int ret, i, nfutex; + + nfutex = NFUTEX; + if (!vectored) + nfutex = 1; + + futex = calloc(nfutex, sizeof(*futex)); + for (i = 0; i < nfutex; i++) { + fw[i].val = 0; + fw[i].uaddr = (unsigned long) &futex[i]; + fw[i].flags = FUTEX2_SIZE_U32; + fw[i].__reserved = 0; + } + + sqe = io_uring_get_sqe(ring); + if (vectored) + io_uring_prep_futex_waitv(sqe, fw, nfutex, 0); + else + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + if (async) + sqe->flags |= IOSQE_ASYNC; + sqe->user_data = 1; + + io_uring_submit(ring); + + for (i = 0; i < nfutex; i++) + pthread_create(&threads[i], NULL, fwake, &futex[i]); + + sqe = io_uring_get_sqe(ring); + io_uring_prep_cancel64(sqe, 1, 0); + if (async_cancel) + sqe->flags |= IOSQE_ASYNC; + sqe->user_data = 2; + + io_uring_submit(ring); + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "parent wait %d\n", ret); + return 1; + } + + if (cqe->res == -EINVAL || cqe->res == -EOPNOTSUPP) { + no_futex = 1; + return 0; + } + io_uring_cqe_seen(ring, cqe); + } + + ret = io_uring_peek_cqe(ring, &cqe); + if (!ret) { + fprintf(stderr, "peek found cqe!\n"); + return 1; + } + + for (i = 0; i < nfutex; i++) + pthread_join(threads[i], &tret); + + return 0; +} + +static int test(int flags, int vectored) +{ + struct io_uring ring; + int ret, i; + + ret = io_uring_queue_init(8, &ring, flags); + if (ret) + return ret; + + for (i = 0; i < LOOPS; i++) { + int async_cancel = (!i % 2); + int async_wait = !(i % 3); + ret = __test(&ring, vectored, async_wait, async_cancel); + if (ret) { + fprintf(stderr, "flags=%x, failed=%d\n", flags, i); + break; + } + if (no_futex) + break; + } + + io_uring_queue_exit(&ring); + return ret; +} + +static int test_order(int vectored, int async) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct futex_waitv fw; + struct io_uring ring; + unsigned int *futex; + int ret, i; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) + return ret; + + futex = malloc(sizeof(*futex)); + *futex = 0; + + fw.val = 0; + fw.uaddr = (unsigned long) futex; + fw.flags = FUTEX2_SIZE_U32; + fw.__reserved = 0; + + /* + * Submit two futex waits + */ + sqe = io_uring_get_sqe(&ring); + if (!vectored) + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + else + io_uring_prep_futex_waitv(sqe, &fw, 1, 0); + sqe->user_data = 1; + + sqe = io_uring_get_sqe(&ring); + if (!vectored) + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + else + io_uring_prep_futex_waitv(sqe, &fw, 1, 0); + sqe->user_data = 2; + + io_uring_submit(&ring); + + /* + * Now submit wake for just one futex + */ + *futex = 1; + sqe = io_uring_get_sqe(&ring); + io_uring_prep_futex_wake(sqe, futex, 1, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + sqe->user_data = 100; + if (async) + sqe->flags |= IOSQE_ASYNC; + + io_uring_submit(&ring); + + /* + * We expect to find completions for the first futex wait, and + * the futex wake. We should not see the last futex wait. + */ + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait %d\n", ret); + return 1; + } + if (cqe->user_data == 1 || cqe->user_data == 100) { + io_uring_cqe_seen(&ring, cqe); + continue; + } + fprintf(stderr, "unexpected cqe %lu, res %d\n", (unsigned long) cqe->user_data, cqe->res); + return 1; + } + + ret = io_uring_peek_cqe(&ring, &cqe); + if (ret != -EAGAIN) { + fprintf(stderr, "Unexpected cqe available: %d\n", cqe->res); + return 1; + } + + io_uring_queue_exit(&ring); + return 0; +} + +static int test_multi_wake(int vectored) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct futex_waitv fw; + struct io_uring ring; + unsigned int *futex; + int ret, i; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) + return ret; + + futex = malloc(sizeof(*futex)); + *futex = 0; + + fw.val = 0; + fw.uaddr = (unsigned long) futex; + fw.flags = FUTEX2_SIZE_U32; + fw.__reserved = 0; + + /* + * Submit two futex waits + */ + sqe = io_uring_get_sqe(&ring); + if (!vectored) + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + else + io_uring_prep_futex_waitv(sqe, &fw, 1, 0); + sqe->user_data = 1; + + sqe = io_uring_get_sqe(&ring); + if (!vectored) + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + else + io_uring_prep_futex_waitv(sqe, &fw, 1, 0); + sqe->user_data = 2; + + io_uring_submit(&ring); + + /* + * Now submit wake for both futexes + */ + *futex = 1; + sqe = io_uring_get_sqe(&ring); + io_uring_prep_futex_wake(sqe, futex, 2, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + sqe->user_data = 100; + + io_uring_submit(&ring); + + /* + * We expect to find completions for the both futex waits, and + * the futex wake. + */ + for (i = 0; i < 3; i++) { + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe error %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + } + + ret = io_uring_peek_cqe(&ring, &cqe); + if (!ret) { + fprintf(stderr, "peek found cqe!\n"); + return 1; + } + + io_uring_queue_exit(&ring); + return 0; +} + +/* + * Test that waking 0 futexes returns 0 + */ +static int test_wake_zero(void) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + unsigned int *futex; + int ret; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) + return ret; + + futex = malloc(sizeof(*futex)); + *futex = 0; + + sqe = io_uring_get_sqe(&ring); + sqe->user_data = 1; + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + + io_uring_submit(&ring); + + sqe = io_uring_get_sqe(&ring); + sqe->user_data = 2; + io_uring_prep_futex_wake(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, + FUTEX2_SIZE_U32, 0); + + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + + /* + * Should get zero res and it should be the wake + */ + if (cqe->res || cqe->user_data != 2) { + fprintf(stderr, "cqe res %d, data %ld\n", cqe->res, (long) cqe->user_data); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + + /* + * Should not have the wait complete + */ + ret = io_uring_peek_cqe(&ring, &cqe); + if (!ret) { + fprintf(stderr, "peek found cqe!\n"); + return 1; + } + + io_uring_queue_exit(&ring); + return 0; +} + +/* + * Test invalid wait/wake/waitv flags + */ +static int test_invalid(void) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct futex_waitv fw; + struct io_uring ring; + unsigned int *futex; + int ret; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) + return ret; + + futex = malloc(sizeof(*futex)); + *futex = 0; + + sqe = io_uring_get_sqe(&ring); + sqe->user_data = 1; + io_uring_prep_futex_wait(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, 0x1000, + 0); + + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + + /* + * Should get zero res and it should be the wake + */ + if (cqe->res != -EINVAL) { + fprintf(stderr, "wait cqe res %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + + sqe = io_uring_get_sqe(&ring); + sqe->user_data = 1; + io_uring_prep_futex_wake(sqe, futex, 0, FUTEX_BITSET_MATCH_ANY, 0x1000, + 0); + + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + + /* + * Should get zero res and it should be the wake + */ + if (cqe->res != -EINVAL) { + fprintf(stderr, "wake cqe res %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + + fw.val = 0; + fw.uaddr = (unsigned long) futex; + fw.flags = FUTEX2_SIZE_U32 | 0x1000; + fw.__reserved = 0; + + sqe = io_uring_get_sqe(&ring); + sqe->user_data = 1; + io_uring_prep_futex_waitv(sqe, &fw, 1, 0); + + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + + /* + * Should get zero res and it should be the wake + */ + if (cqe->res != -EINVAL) { + fprintf(stderr, "waitv cqe res %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + + io_uring_queue_exit(&ring); + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0, 0); + if (ret) { + fprintf(stderr, "test 0 0 failed\n"); + return T_EXIT_FAIL; + } + if (no_futex) + return T_EXIT_SKIP; + + ret = test(0, 1); + if (ret) { + fprintf(stderr, "test 0 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_wake_zero(); + if (ret) { + fprintf(stderr, "wake 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_invalid(); + if (ret) { + fprintf(stderr, "test invalid failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_SQPOLL, 0); + if (ret) { + fprintf(stderr, "test sqpoll 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_SQPOLL, 1); + if (ret) { + fprintf(stderr, "test sqpoll 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, 0); + if (ret) { + fprintf(stderr, "test single coop 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN, 1); + if (ret) { + fprintf(stderr, "test single coop 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_COOP_TASKRUN, 0); + if (ret) { + fprintf(stderr, "test taskrun 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(IORING_SETUP_COOP_TASKRUN, 1); + if (ret) { + fprintf(stderr, "test taskrun 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_order(0, 0); + if (ret) { + fprintf(stderr, "test_order 0 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_order(1, 0); + if (ret) { + fprintf(stderr, "test_order 1 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_order(0, 1); + if (ret) { + fprintf(stderr, "test_order 0 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_order(1, 1); + if (ret) { + fprintf(stderr, "test_order 1 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_multi_wake(0); + if (ret) { + fprintf(stderr, "multi_wake 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_multi_wake(1); + if (ret) { + fprintf(stderr, "multi_wake 1 failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/futex.t/ya.make b/contrib/libs/liburing/test/futex.t/ya.make new file mode 100644 index 0000000000..1f35661353 --- /dev/null +++ b/contrib/libs/liburing/test/futex.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + futex.c + helpers.c +) + +END() diff --git a/contrib/libs/liburing/test/hardlink.c b/contrib/libs/liburing/test/hardlink.c index 1bd7a1c882..6ac5f39f98 100644 --- a/contrib/libs/liburing/test/hardlink.c +++ b/contrib/libs/liburing/test/hardlink.c @@ -13,36 +13,34 @@ #include "liburing.h" #include "helpers.h" - -static int do_linkat(struct io_uring *ring, const char *oldname, const char *newname) +static int do_linkat(struct io_uring *ring, int olddirfd, const char *oldname, + const char *newname, int flags) { - int ret; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; + int ret; sqe = io_uring_get_sqe(ring); if (!sqe) { fprintf(stderr, "sqe get failed\n"); - goto err; + return 1; } - io_uring_prep_linkat(sqe, AT_FDCWD, oldname, AT_FDCWD, newname, 0); + io_uring_prep_linkat(sqe, olddirfd, oldname, AT_FDCWD, newname, flags); ret = io_uring_submit(ring); if (ret != 1) { fprintf(stderr, "submit failed: %d\n", ret); - goto err; + return 1; } ret = io_uring_wait_cqes(ring, &cqe, 1, 0, 0); if (ret) { fprintf(stderr, "wait_cqe failed: %d\n", ret); - goto err; + return 1; } ret = cqe->res; io_uring_cqe_seen(ring, cqe); return ret; -err: - return 1; } static int files_linked_ok(const char* fn1, const char *fn2) @@ -71,9 +69,11 @@ static int files_linked_ok(const char* fn1, const char *fn2) int main(int argc, char *argv[]) { static const char target[] = "io_uring-linkat-test-target"; + static const char emptyname[] = "io_uring-linkat-test-empty"; static const char linkname[] = "io_uring-linkat-test-link"; - int ret; + static const char symlinkname[] = "io_uring-linkat-test-symlink"; struct io_uring ring; + int ret, fd, exit_status = T_EXIT_FAIL; if (argc > 1) return T_EXIT_SKIP; @@ -84,58 +84,88 @@ int main(int argc, char *argv[]) return ret; } - ret = open(target, O_CREAT | O_RDWR | O_EXCL, 0600); + ret = fd = open(target, O_CREAT | O_RDWR | O_EXCL, 0600); if (ret < 0) { perror("open"); - goto err; + goto out; + } + if (write(fd, "linktest", 8) != 8) { + close(fd); + goto out; } - if (write(ret, "linktest", 8) != 8) { - close(ret); - goto err1; + if(geteuid()) { + fprintf(stdout, "not root, skipping AT_EMPTY_PATH test\n"); + } else { + ret = do_linkat(&ring, fd, "", emptyname, AT_EMPTY_PATH); + if (ret < 0) { + if (ret == -EBADF || ret == -EINVAL) { + fprintf(stdout, "linkat not supported, skipping\n"); + exit_status = T_EXIT_SKIP; + goto out; + } + fprintf(stderr, "linkat: %s\n", strerror(-ret)); + goto out; + } else if (ret) { + goto out; + } + if (!files_linked_ok(emptyname, target)) + goto out; + unlinkat(AT_FDCWD, emptyname, 0); } - close(ret); + close(fd); - ret = do_linkat(&ring, target, linkname); + ret = symlink(target, symlinkname); + if (ret < 0) { + perror("open"); + goto out; + } + + ret = do_linkat(&ring, AT_FDCWD, target, linkname, 0); if (ret < 0) { if (ret == -EBADF || ret == -EINVAL) { fprintf(stdout, "linkat not supported, skipping\n"); - goto skip; + exit_status = T_EXIT_SKIP; + goto out; } fprintf(stderr, "linkat: %s\n", strerror(-ret)); - goto err1; + goto out; } else if (ret) { - goto err1; + goto out; } if (!files_linked_ok(linkname, target)) - goto err2; + goto out; + + unlinkat(AT_FDCWD, linkname, 0); - ret = do_linkat(&ring, target, linkname); + ret = do_linkat(&ring, AT_FDCWD, symlinkname, linkname, AT_SYMLINK_FOLLOW); + if (ret < 0) { + fprintf(stderr, "linkat: %s\n", strerror(-ret)); + goto out; + } else if (ret) { + goto out; + } + + if (!files_linked_ok(symlinkname, target)) + goto out; + + ret = do_linkat(&ring, AT_FDCWD, target, linkname, 0); if (ret != -EEXIST) { fprintf(stderr, "test_linkat linkname already exists failed: %d\n", ret); - goto err2; + goto out; } - ret = do_linkat(&ring, target, "surely/this/does/not/exist"); + ret = do_linkat(&ring, AT_FDCWD, target, "surely/this/does/not/exist", 0); if (ret != -ENOENT) { fprintf(stderr, "test_linkat no parent failed: %d\n", ret); - goto err2; + goto out; } - - unlinkat(AT_FDCWD, linkname, 0); - unlinkat(AT_FDCWD, target, 0); - io_uring_queue_exit(&ring); - return T_EXIT_PASS; -skip: - unlinkat(AT_FDCWD, linkname, 0); - unlinkat(AT_FDCWD, target, 0); - io_uring_queue_exit(&ring); - return T_EXIT_SKIP; -err2: + exit_status = T_EXIT_PASS; +out: + unlinkat(AT_FDCWD, symlinkname, 0); unlinkat(AT_FDCWD, linkname, 0); -err1: + unlinkat(AT_FDCWD, emptyname, 0); unlinkat(AT_FDCWD, target, 0); -err: io_uring_queue_exit(&ring); - return T_EXIT_FAIL; + return exit_status; } diff --git a/contrib/libs/liburing/test/helpers.c b/contrib/libs/liburing/test/helpers.c index 7133b00b91..0d897271d8 100644 --- a/contrib/libs/liburing/test/helpers.c +++ b/contrib/libs/liburing/test/helpers.c @@ -37,13 +37,15 @@ void *t_malloc(size_t size) int t_bind_ephemeral_port(int fd, struct sockaddr_in *addr) { socklen_t addrlen; + int ret; addr->sin_port = 0; if (bind(fd, (struct sockaddr *)addr, sizeof(*addr))) return -errno; addrlen = sizeof(*addr); - assert(!getsockname(fd, (struct sockaddr *)addr, &addrlen)); + ret = getsockname(fd, (struct sockaddr *)addr, &addrlen); + assert(!ret); assert(addr->sin_port != 0); return 0; } @@ -285,22 +287,18 @@ unsigned __io_uring_flush_sq(struct io_uring *ring) * Ensure kernel sees the SQE updates before the tail update. */ if (!(ring->flags & IORING_SETUP_SQPOLL)) - IO_URING_WRITE_ONCE(*sq->ktail, tail); + *sq->ktail = tail; else io_uring_smp_store_release(sq->ktail, tail); } /* - * This _may_ look problematic, as we're not supposed to be reading - * SQ->head without acquire semantics. When we're in SQPOLL mode, the - * kernel submitter could be updating this right now. For non-SQPOLL, - * task itself does it, and there's no potential race. But even for - * SQPOLL, the load is going to be potentially out-of-date the very - * instant it's done, regardless or whether or not it's done - * atomically. Worst case, we're going to be over-estimating what - * we can submit. The point is, we need to be able to deal with this - * situation regardless of any perceived atomicity. - */ - return tail - *sq->khead; + * This load needs to be atomic, since sq->khead is written concurrently + * by the kernel, but it doesn't need to be load_acquire, since the + * kernel doesn't store to the submission queue; it advances khead just + * to indicate that it's finished reading the submission queue entries + * so they're available for us to write to. + */ + return tail - IO_URING_READ_ONCE(*sq->khead); } /* diff --git a/contrib/libs/liburing/test/helpers.h b/contrib/libs/liburing/test/helpers.h index 530732422c..9f62a5f63f 100644 --- a/contrib/libs/liburing/test/helpers.h +++ b/contrib/libs/liburing/test/helpers.h @@ -10,6 +10,7 @@ extern "C" { #endif #include "liburing.h" +#include "../src/setup.h" #include <arpa/inet.h> enum t_setup_ret { @@ -87,6 +88,15 @@ bool t_probe_defer_taskrun(void); unsigned __io_uring_flush_sq(struct io_uring *ring); +static inline int t_io_uring_init_sqarray(unsigned entries, struct io_uring *ring, + struct io_uring_params *p) +{ + int ret; + + ret = __io_uring_queue_init_params(entries, ring, p, NULL, 0); + return ret >= 0 ? 0 : ret; +} + #define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0])) void t_error(int status, int errnum, const char *format, ...); diff --git a/contrib/libs/liburing/test/io_uring_enter.c b/contrib/libs/liburing/test/io_uring_enter.c index 7f87f0cf55..7c73d0a403 100644 --- a/contrib/libs/liburing/test/io_uring_enter.c +++ b/contrib/libs/liburing/test/io_uring_enter.c @@ -184,13 +184,16 @@ int main(int argc, char **argv) unsigned ktail, mask, index; unsigned sq_entries; unsigned completed, dropped; + struct io_uring_params p; if (argc > 1) return T_EXIT_SKIP; - ret = io_uring_queue_init(IORING_MAX_ENTRIES, &ring, 0); + memset(&p, 0, sizeof(p)); + ret = t_io_uring_init_sqarray(IORING_MAX_ENTRIES, &ring, &p); if (ret == -ENOMEM) - ret = io_uring_queue_init(IORING_MAX_ENTRIES_FALLBACK, &ring, 0); + ret = t_io_uring_init_sqarray(IORING_MAX_ENTRIES_FALLBACK, + &ring, &p); if (ret < 0) { perror("io_uring_queue_init"); exit(T_EXIT_FAIL); diff --git a/contrib/libs/liburing/test/io_uring_register.c b/contrib/libs/liburing/test/io_uring_register.c index 1201ffb2dc..c315750adc 100644 --- a/contrib/libs/liburing/test/io_uring_register.c +++ b/contrib/libs/liburing/test/io_uring_register.c @@ -33,7 +33,7 @@ static rlim_t mlock_limit; static int devnull; static int expect_fail(int fd, unsigned int opcode, void *arg, - unsigned int nr_args, int error) + unsigned int nr_args, int error, int error2) { int ret; @@ -56,8 +56,8 @@ static int expect_fail(int fd, unsigned int opcode, void *arg, return 1; } - if (ret != error) { - fprintf(stderr, "expected %d, got %d\n", error, ret); + if (ret != error && (error2 && ret != error2)) { + fprintf(stderr, "expected %d/%d, got %d\n", error, error2, ret); return 1; } return 0; @@ -196,8 +196,7 @@ static int test_max_fds(int uring_fd) status = 0; ret = io_uring_register(uring_fd, IORING_UNREGISTER_FILES, 0, 0); if (ret < 0) { - ret = errno; - errno = ret; + errno = -ret; perror("io_uring_register UNREGISTER_FILES"); exit(1); } @@ -231,22 +230,20 @@ static int test_memlock_exceeded(int fd) while (iov.iov_len) { ret = io_uring_register(fd, IORING_REGISTER_BUFFERS, &iov, 1); - if (ret < 0) { - if (errno == ENOMEM) { - iov.iov_len /= 2; - continue; - } - if (errno == EFAULT) { - free(buf); - return 0; - } - fprintf(stderr, "expected success or EFAULT, got %d\n", errno); + if (ret == -ENOMEM) { + iov.iov_len /= 2; + continue; + } else if (ret == -EFAULT) { + free(buf); + return 0; + } else if (ret) { + fprintf(stderr, "expected success or EFAULT, got %d\n", ret); free(buf); return 1; } ret = io_uring_register(fd, IORING_UNREGISTER_BUFFERS, NULL, 0); if (ret != 0) { - fprintf(stderr, "error: unregister failed with %d\n", errno); + fprintf(stderr, "error: unregister failed with %d\n", ret); free(buf); return 1; } @@ -278,15 +275,15 @@ static int test_iovec_nr(int fd) iovs[i].iov_len = pagesize; } - status |= expect_fail(fd, IORING_REGISTER_BUFFERS, iovs, nr, -EINVAL); + status |= expect_fail(fd, IORING_REGISTER_BUFFERS, iovs, nr, -EINVAL, 0); /* reduce to UIO_MAXIOV */ nr = UIO_MAXIOV; ret = io_uring_register(fd, IORING_REGISTER_BUFFERS, iovs, nr); - if (ret && (errno == ENOMEM || errno == EPERM) && geteuid()) { + if ((ret == -ENOMEM || ret == -EPERM) && geteuid()) { fprintf(stderr, "can't register large iovec for regular users, skip\n"); } else if (ret != 0) { - fprintf(stderr, "expected success, got %d\n", errno); + fprintf(stderr, "expected success, got %d\n", ret); status = 1; } else { io_uring_register(fd, IORING_UNREGISTER_BUFFERS, 0, 0); @@ -309,12 +306,12 @@ static int test_iovec_size(int fd) /* NULL pointer for base */ iov.iov_base = 0; iov.iov_len = 4096; - status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT); + status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT, 0); /* valid base, 0 length */ iov.iov_base = &buf; iov.iov_len = 0; - status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT); + status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT, 0); /* valid base, length exceeds size */ /* this requires an unampped page directly after buf */ @@ -325,7 +322,7 @@ static int test_iovec_size(int fd) assert(ret == 0); iov.iov_base = buf; iov.iov_len = 2 * pagesize; - status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT); + status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT, 0); munmap(buf, pagesize); /* huge page */ @@ -373,7 +370,7 @@ static int test_iovec_size(int fd) status = 1; iov.iov_base = buf; iov.iov_len = 2*1024*1024; - status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EOPNOTSUPP); + status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT, -EOPNOTSUPP); munmap(buf, 2*1024*1024); /* bump up against the soft limit and make sure we get EFAULT @@ -443,7 +440,7 @@ static int test_poll_ringfd(void) * fail, because the kernel does not allow registering of the * ring_fd. */ - status |= expect_fail(fd, IORING_REGISTER_FILES, &fd, 1, -EBADF); + status |= expect_fail(fd, IORING_REGISTER_FILES, &fd, 1, -EBADF, 0); /* tear down queue */ io_uring_queue_exit(&ring); @@ -476,14 +473,14 @@ int main(int argc, char **argv) } /* invalid fd */ - status |= expect_fail(-1, 0, NULL, 0, -EBADF); + status |= expect_fail(-1, 0, NULL, 0, -EBADF, 0); /* valid fd that is not an io_uring fd */ - status |= expect_fail(devnull, 0, NULL, 0, -EOPNOTSUPP); + status |= expect_fail(devnull, 0, NULL, 0, -EOPNOTSUPP, 0); /* invalid opcode */ memset(&p, 0, sizeof(p)); fd = new_io_uring(1, &p); - ret = expect_fail(fd, ~0U, NULL, 0, -EINVAL); + ret = expect_fail(fd, ~0U, NULL, 0, -EINVAL, 0); if (ret) { /* if this succeeds, tear down the io_uring instance * and start clean for the next test. */ diff --git a/contrib/libs/liburing/test/io_uring_setup.c b/contrib/libs/liburing/test/io_uring_setup.c index e0c6bcb519..84f4573700 100644 --- a/contrib/libs/liburing/test/io_uring_setup.c +++ b/contrib/libs/liburing/test/io_uring_setup.c @@ -18,7 +18,7 @@ #include "liburing.h" #include "helpers.h" -#include "../syscall.h" +#include "../src/syscall.h" /* bogus: setup returns a valid fd on success... expect can't predict the fd we'll get, so this really only takes 1 parameter: error */ diff --git a/contrib/libs/liburing/test/msg-ring-fd.c b/contrib/libs/liburing/test/msg-ring-fd.c new file mode 100644 index 0000000000..84a7d24f5c --- /dev/null +++ b/contrib/libs/liburing/test/msg-ring-fd.c @@ -0,0 +1,319 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test fd passing with MSG_RING + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <pthread.h> + +#include "liburing.h" +#include "helpers.h" + +static int no_msg; +static int no_sparse; + +struct data { + pthread_t thread; + pthread_barrier_t barrier; + int ring_flags; + int ring_fd; + char buf[32]; +}; + +static void *thread_fn(void *__data) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct data *d = __data; + struct io_uring ring; + int ret, fd = -1; + + io_uring_queue_init(8, &ring, d->ring_flags); + ret = io_uring_register_files(&ring, &fd, 1); + if (ret) { + if (ret != -EINVAL && ret != -EBADF) + fprintf(stderr, "thread file register: %d\n", ret); + no_sparse = 1; + pthread_barrier_wait(&d->barrier); + return NULL; + } + + d->ring_fd = ring.ring_fd; + pthread_barrier_wait(&d->barrier); + + /* wait for MSG */ + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe dst: %d\n", ret); + return NULL; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe error dst: %d\n", cqe->res); + return NULL; + } + + fd = cqe->res; + io_uring_cqe_seen(&ring, cqe); + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fd, d->buf, sizeof(d->buf), 0); + sqe->flags |= IOSQE_FIXED_FILE; + io_uring_submit(&ring); + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe dst: %d\n", ret); + return NULL; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe error dst: %d\n", cqe->res); + return NULL; + } + + io_uring_queue_exit(&ring); + return NULL; +} + +static int test_remote(struct io_uring *src, int ring_flags) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int fds[2], fd, ret; + struct data d; + char buf[32]; + void *tret; + int i; + + pthread_barrier_init(&d.barrier, NULL, 2); + d.ring_flags = ring_flags; + pthread_create(&d.thread, NULL, thread_fn, &d); + pthread_barrier_wait(&d.barrier); + memset(d.buf, 0, sizeof(d.buf)); + + if (no_sparse) + return 0; + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + + fd = fds[0]; + ret = io_uring_register_files(src, &fd, 1); + if (ret) { + fprintf(stderr, "register files failed: %d\n", ret); + return 1; + } + + for (i = 0; i < ARRAY_SIZE(buf); i++) + buf[i] = rand(); + + sqe = io_uring_get_sqe(src); + io_uring_prep_write(sqe, fds[1], buf, sizeof(buf), 0); + sqe->user_data = 1; + + sqe = io_uring_get_sqe(src); + io_uring_prep_msg_ring_fd(sqe, d.ring_fd, 0, 0, 0, 0); + sqe->user_data = 2; + + io_uring_submit(src); + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(src, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe: %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe res %d\n", cqe->res); + return 1; + } + if (cqe->user_data == 1 && cqe->res != sizeof(buf)) { + fprintf(stderr, "short write %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(src, cqe); + } + + pthread_join(d.thread, &tret); + + if (memcmp(buf, d.buf, sizeof(buf))) { + fprintf(stderr, "buffers differ\n"); + return 1; + } + + close(fds[0]); + close(fds[1]); + io_uring_unregister_files(src); + return 0; +} + +static int test_local(struct io_uring *src, struct io_uring *dst) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int fds[2], fd, ret; + char buf[32], dst_buf[32]; + int i; + + fd = -1; + ret = io_uring_register_files(dst, &fd, 1); + if (ret) { + if (ret == -EBADF || ret == -EINVAL) + return 0; + fprintf(stderr, "register files failed: %d\n", ret); + return 1; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + + fd = fds[0]; + ret = io_uring_register_files(src, &fd, 1); + if (ret) { + fprintf(stderr, "register files failed: %d\n", ret); + return 1; + } + + memset(dst_buf, 0, sizeof(dst_buf)); + for (i = 0; i < ARRAY_SIZE(buf); i++) + buf[i] = rand(); + + sqe = io_uring_get_sqe(src); + io_uring_prep_write(sqe, fds[1], buf, sizeof(buf), 0); + sqe->user_data = 1; + + sqe = io_uring_get_sqe(src); + io_uring_prep_msg_ring_fd(sqe, dst->ring_fd, 0, 0, 10, 0); + sqe->user_data = 2; + + io_uring_submit(src); + + fd = -1; + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(src, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe: %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe res %d\n", cqe->res); + return 1; + } + if (cqe->user_data == 1 && cqe->res != sizeof(buf)) { + fprintf(stderr, "short write %d\n", cqe->res); + return 1; + } + io_uring_cqe_seen(src, cqe); + } + + ret = io_uring_wait_cqe(dst, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe dst: %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe error dst: %d\n", cqe->res); + return 1; + } + + fd = cqe->res; + io_uring_cqe_seen(dst, cqe); + sqe = io_uring_get_sqe(dst); + io_uring_prep_read(sqe, fd, dst_buf, sizeof(dst_buf), 0); + sqe->flags |= IOSQE_FIXED_FILE; + sqe->user_data = 3; + io_uring_submit(dst); + + ret = io_uring_wait_cqe(dst, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe dst: %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe error dst: %d\n", cqe->res); + return 1; + } + if (cqe->res != sizeof(dst_buf)) { + fprintf(stderr, "short read %d\n", cqe->res); + return 1; + } + if (memcmp(buf, dst_buf, sizeof(buf))) { + fprintf(stderr, "buffers differ\n"); + return 1; + } + + close(fds[0]); + close(fds[1]); + io_uring_unregister_files(src); + io_uring_unregister_files(dst); + return 0; +} + +static int test(int ring_flags) +{ + struct io_uring ring, ring2; + int ret; + + ret = io_uring_queue_init(8, &ring, ring_flags); + if (ret) { + if (ret == -EINVAL) + return 0; + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + ret = io_uring_queue_init(8, &ring2, ring_flags); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + + ret = test_local(&ring, &ring2); + if (ret) { + fprintf(stderr, "test local failed\n"); + return T_EXIT_FAIL; + } + if (no_msg) + return T_EXIT_SKIP; + + ret = test_remote(&ring, ring_flags); + if (ret) { + fprintf(stderr, "test_remote failed\n"); + return T_EXIT_FAIL; + } + + io_uring_queue_exit(&ring); + io_uring_queue_exit(&ring2); + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0); + if (ret != T_EXIT_PASS) { + fprintf(stderr, "ring flags 0 failed\n"); + return ret; + } + if (no_msg) + return T_EXIT_SKIP; + + ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN); + if (ret != T_EXIT_PASS) { + fprintf(stderr, "ring flags defer failed\n"); + return ret; + } + + return ret; +} diff --git a/contrib/libs/liburing/test/msg-ring-fd.t/ya.make b/contrib/libs/liburing/test/msg-ring-fd.t/ya.make new file mode 100644 index 0000000000..55b1db64bb --- /dev/null +++ b/contrib/libs/liburing/test/msg-ring-fd.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + msg-ring-fd.c +) + +END() diff --git a/contrib/libs/liburing/test/msg-ring-flags.c b/contrib/libs/liburing/test/msg-ring-flags.c index d533b8f72d..c1e8abb8aa 100644 --- a/contrib/libs/liburing/test/msg-ring-flags.c +++ b/contrib/libs/liburing/test/msg-ring-flags.c @@ -118,7 +118,7 @@ static void *thread_fn(void *data) return NULL; } -int main(int argc, char *argv[]) +static int test(int ring_flags) { struct io_uring ring, ring2; pthread_t thread; @@ -126,16 +126,13 @@ int main(int argc, char *argv[]) void *ret2; int ret, i; - if (argc > 1) - return T_EXIT_SKIP; - - ret = io_uring_queue_init(2, &ring, 0); + ret = io_uring_queue_init(2, &ring, ring_flags); if (ret) { fprintf(stderr, "io_uring_queue_init failed for ring1: %d\n", ret); return T_EXIT_FAIL; } - ret = io_uring_queue_init(2, &ring2, 0); + ret = io_uring_queue_init(2, &ring2, ring_flags); if (ret) { fprintf(stderr, "io_uring_queue_init failed for ring2: %d\n", ret); return T_EXIT_FAIL; @@ -191,3 +188,26 @@ int main(int argc, char *argv[]) return T_EXIT_PASS; } + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test ring_flags 0 failed\n"); + return ret; + } else if (ret == T_EXIT_SKIP) + return ret; + + ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test ring_flags defer failed\n"); + return ret; + } + + return ret; +} diff --git a/contrib/libs/liburing/test/msg-ring.c b/contrib/libs/liburing/test/msg-ring.c index 221fb7895a..ef9e3b1f85 100644 --- a/contrib/libs/liburing/test/msg-ring.c +++ b/contrib/libs/liburing/test/msg-ring.c @@ -73,12 +73,20 @@ err: return 1; } -static void *wait_cqe_fn(void *data) +struct data { + struct io_uring *ring; + pthread_barrier_t barrier; +}; + +static void *wait_cqe_fn(void *__data) { - struct io_uring *ring = data; + struct data *d = __data; + struct io_uring *ring = d->ring; struct io_uring_cqe *cqe; int ret; + pthread_barrier_wait(&d->barrier); + ret = io_uring_wait_cqe(ring, &cqe); if (ret) { fprintf(stderr, "wait cqe %d\n", ret); @@ -107,9 +115,12 @@ static int test_remote(struct io_uring *ring, struct io_uring *target) void *tret; struct io_uring_cqe *cqe; struct io_uring_sqe *sqe; + struct data d; int ret; - pthread_create(&thread, NULL, wait_cqe_fn, target); + d.ring = target; + pthread_barrier_init(&d.barrier, NULL, 2); + pthread_create(&thread, NULL, wait_cqe_fn, &d); sqe = io_uring_get_sqe(ring); if (!sqe) { @@ -126,6 +137,8 @@ static int test_remote(struct io_uring *ring, struct io_uring *target) goto err; } + pthread_barrier_wait(&d.barrier); + ret = io_uring_wait_cqe(ring, &cqe); if (ret < 0) { fprintf(stderr, "wait completion %d\n", ret); @@ -315,25 +328,22 @@ static int test_disabled_ring(struct io_uring *ring, int flags) return 0; } -int main(int argc, char *argv[]) +static int test(int ring_flags) { struct io_uring ring, ring2, pring; int ret, i; - if (argc > 1) - return T_EXIT_SKIP; - - ret = io_uring_queue_init(8, &ring, 0); + ret = io_uring_queue_init(8, &ring, ring_flags); if (ret) { fprintf(stderr, "ring setup failed: %d\n", ret); return T_EXIT_FAIL; } - ret = io_uring_queue_init(8, &ring2, 0); + ret = io_uring_queue_init(8, &ring2, ring_flags); if (ret) { fprintf(stderr, "ring setup failed: %d\n", ret); return T_EXIT_FAIL; } - ret = io_uring_queue_init(8, &pring, IORING_SETUP_IOPOLL); + ret = io_uring_queue_init(8, &pring, ring_flags | IORING_SETUP_IOPOLL); if (ret) { fprintf(stderr, "ring setup failed: %d\n", ret); return T_EXIT_FAIL; @@ -419,3 +429,25 @@ int main(int argc, char *argv[]) io_uring_queue_exit(&ring2); return T_EXIT_PASS; } + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0); + if (ret != T_EXIT_PASS) { + fprintf(stderr, "ring flags 0 failed\n"); + return ret; + } + + ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN); + if (ret != T_EXIT_PASS) { + fprintf(stderr, "ring flags defer failed\n"); + return ret; + } + + return ret; +} diff --git a/contrib/libs/liburing/test/multicqes_drain.c b/contrib/libs/liburing/test/multicqes_drain.c index fb83441219..43880c0b18 100644 --- a/contrib/libs/liburing/test/multicqes_drain.c +++ b/contrib/libs/liburing/test/multicqes_drain.c @@ -234,7 +234,7 @@ static int test_generic_drain(struct io_uring *ring) } sleep(1); - // TODO: randomize event triggerring order + // TODO: randomize event triggering order for (i = 0; i < max_entry; i++) { if (si[i].op != multi && si[i].op != single) continue; diff --git a/contrib/libs/liburing/test/no-mmap-inval.c b/contrib/libs/liburing/test/no-mmap-inval.c new file mode 100644 index 0000000000..e340311ac3 --- /dev/null +++ b/contrib/libs/liburing/test/no-mmap-inval.c @@ -0,0 +1,43 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test that using SETUP_NO_MMAP with an invalid SQ ring + * address fails. + * + */ +#include <stdlib.h> +#include <sys/types.h> +#include <stdio.h> +#include <unistd.h> + +#include "liburing.h" +#include "helpers.h" + +int main(int argc, char *argv[]) +{ + struct io_uring_params p = { + .sq_entries = 2, + .cq_entries = 4, + .flags = IORING_SETUP_NO_MMAP, + }; + struct io_uring ring; + void *addr; + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + t_posix_memalign(&addr, sysconf(_SC_PAGESIZE), 8192); + p.cq_off.user_addr = (unsigned long long) (uintptr_t) addr; + + ret = io_uring_queue_init_params(2, &ring, &p); + if (ret == -EINVAL) { + /* kernel doesn't support SETUP_NO_MMAP */ + return T_EXIT_SKIP; + } else if (ret && ret != -EFAULT) { + fprintf(stderr, "Got %d, wanted -EFAULT\n", ret); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/no-mmap-inval.t/ya.make b/contrib/libs/liburing/test/no-mmap-inval.t/ya.make new file mode 100644 index 0000000000..2dd5080da5 --- /dev/null +++ b/contrib/libs/liburing/test/no-mmap-inval.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + no-mmap-inval.c +) + +END() diff --git a/contrib/libs/liburing/test/nolibc.c b/contrib/libs/liburing/test/nolibc.c index fd07fa13cd..e6ae6057bc 100644 --- a/contrib/libs/liburing/test/nolibc.c +++ b/contrib/libs/liburing/test/nolibc.c @@ -7,11 +7,13 @@ * 1) x86 * 2) x86-64 * 3) aarch64 + * 4) riscv64 * */ #include "helpers.h" -#if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) +#if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) && (!defined(__riscv) && __riscv_xlen != 64) + /* * This arch doesn't support nolibc. @@ -21,7 +23,7 @@ int main(void) return T_EXIT_SKIP; } -#else /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) */ +#else /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) && (!defined(__riscv) && __riscv_xlen != 64) */ #ifndef CONFIG_NOLIBC #define CONFIG_NOLIBC @@ -58,4 +60,4 @@ int main(int argc, char *argv[]) return T_EXIT_PASS; } -#endif /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) */ +#endif /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) && (!defined(__riscv) && __riscv_xlen != 64) */ diff --git a/contrib/libs/liburing/test/openat2.c b/contrib/libs/liburing/test/openat2.c index a3ad70e143..5206b73f3a 100644 --- a/contrib/libs/liburing/test/openat2.c +++ b/contrib/libs/liburing/test/openat2.c @@ -1,7 +1,7 @@ #include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* - * Description: run various openat(2) tests + * Description: run various openat2(2) tests * */ #include <errno.h> @@ -73,6 +73,8 @@ static int test_open_fixed(const char *path, int dfd) } ret = io_uring_register_files(&ring, &fd, 1); if (ret) { + if (ret == -EINVAL || ret == -EBADF) + return 0; fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret); return -1; } @@ -142,6 +144,8 @@ static int test_open_fixed_fail(const char *path, int dfd) ret = io_uring_register_files(&ring, &fd, 1); if (ret) { + if (ret == -EINVAL || ret == -EBADF) + return 0; fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret); return -1; } diff --git a/contrib/libs/liburing/test/poll-cancel-all.c b/contrib/libs/liburing/test/poll-cancel-all.c index 83c48abeea..dd830b44c7 100644 --- a/contrib/libs/liburing/test/poll-cancel-all.c +++ b/contrib/libs/liburing/test/poll-cancel-all.c @@ -15,11 +15,22 @@ static int no_cancel_flags; -static int test1(struct io_uring *ring, int *fd) +static int test1(struct io_uring *ring, int *fd, int fixed) { struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; - int ret, i; + int ret, i, __fd = fd[0]; + + if (fixed) + __fd = 0; + + if (fixed) { + ret = io_uring_register_files(ring, fd, 1); + if (ret) { + fprintf(stderr, "failed file register %d\n", ret); + return 1; + } + } for (i = 0; i < 8; i++) { sqe = io_uring_get_sqe(ring); @@ -28,8 +39,10 @@ static int test1(struct io_uring *ring, int *fd) return 1; } - io_uring_prep_poll_add(sqe, fd[0], POLLIN); + io_uring_prep_poll_add(sqe, __fd, POLLIN); sqe->user_data = i + 1; + if (fixed) + sqe->flags |= IOSQE_FIXED_FILE; } ret = io_uring_submit(ring); @@ -52,7 +65,9 @@ static int test1(struct io_uring *ring, int *fd) */ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL); sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD; - sqe->fd = fd[0]; + if (fixed) + sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD_FIXED; + sqe->fd = __fd; sqe->user_data = 100; ret = io_uring_submit(ring); @@ -94,6 +109,9 @@ static int test1(struct io_uring *ring, int *fd) io_uring_cqe_seen(ring, cqe); } + if (fixed) + io_uring_unregister_files(ring); + return 0; } @@ -443,7 +461,7 @@ int main(int argc, char *argv[]) return 1; } - ret = test1(&ring, fd); + ret = test1(&ring, fd, 0); if (ret) { fprintf(stderr, "test1 failed\n"); return ret; @@ -451,6 +469,12 @@ int main(int argc, char *argv[]) if (no_cancel_flags) return 0; + ret = test1(&ring, fd, 1); + if (ret) { + fprintf(stderr, "test1 fixed failed\n"); + return ret; + } + ret = test2(&ring, fd); if (ret) { fprintf(stderr, "test2 failed\n"); diff --git a/contrib/libs/liburing/test/pollfree.c b/contrib/libs/liburing/test/pollfree.c deleted file mode 100644 index 20a8bec60b..0000000000 --- a/contrib/libs/liburing/test/pollfree.c +++ /dev/null @@ -1,427 +0,0 @@ -#include "../config-host.h" -/* SPDX-License-Identifier: MIT */ -// https://syzkaller.appspot.com/bug?id=5f5a44abb4cba056fe24255c4fcb7e7bbe13de7a -// autogenerated by syzkaller (https://github.com/google/syzkaller) - -#include <dirent.h> -#include <endian.h> -#include <errno.h> -#include <fcntl.h> -#include <pthread.h> -#include <signal.h> -#include <stdarg.h> -#include <stdbool.h> -#include <stdint.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/prctl.h> -#include <sys/stat.h> -#include <sys/syscall.h> -#include <sys/types.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> - -#include <linux/futex.h> - -#ifdef __NR_futex - -static void sleep_ms(uint64_t ms) -{ - usleep(ms * 1000); -} - -static uint64_t current_time_ms(void) -{ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - exit(1); - return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; -} - -static void thread_start(void* (*fn)(void*), void* arg) -{ - pthread_t th; - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 128 << 10); - int i = 0; - for (; i < 100; i++) { - if (pthread_create(&th, &attr, fn, arg) == 0) { - pthread_attr_destroy(&attr); - return; - } - if (errno == EAGAIN) { - usleep(50); - continue; - } - break; - } - exit(1); -} - -typedef struct { - int state; -} event_t; - -static void event_init(event_t* ev) -{ - ev->state = 0; -} - -static void event_reset(event_t* ev) -{ - ev->state = 0; -} - -static void event_set(event_t* ev) -{ - if (ev->state) - exit(1); - __atomic_store_n(&ev->state, 1, __ATOMIC_RELEASE); - syscall(__NR_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1000000); -} - -static void event_wait(event_t* ev) -{ - while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) - syscall(__NR_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, 0); -} - -static int event_isset(event_t* ev) -{ - return __atomic_load_n(&ev->state, __ATOMIC_ACQUIRE); -} - -static int event_timedwait(event_t* ev, uint64_t timeout) -{ - uint64_t start = current_time_ms(); - uint64_t now = start; - for (;;) { - uint64_t remain = timeout - (now - start); - struct timespec ts; - ts.tv_sec = remain / 1000; - ts.tv_nsec = (remain % 1000) * 1000 * 1000; - syscall(__NR_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts); - if (__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE)) - return 1; - now = current_time_ms(); - if (now - start > timeout) - return 0; - } -} - -#define SIZEOF_IO_URING_SQE 64 -#define SIZEOF_IO_URING_CQE 16 -#define SQ_HEAD_OFFSET 0 -#define SQ_TAIL_OFFSET 64 -#define SQ_RING_MASK_OFFSET 256 -#define SQ_RING_ENTRIES_OFFSET 264 -#define SQ_FLAGS_OFFSET 276 -#define SQ_DROPPED_OFFSET 272 -#define CQ_HEAD_OFFSET 128 -#define CQ_TAIL_OFFSET 192 -#define CQ_RING_MASK_OFFSET 260 -#define CQ_RING_ENTRIES_OFFSET 268 -#define CQ_RING_OVERFLOW_OFFSET 284 -#define CQ_FLAGS_OFFSET 280 -#define CQ_CQES_OFFSET 320 - -struct io_sqring_offsets { - uint32_t head; - uint32_t tail; - uint32_t ring_mask; - uint32_t ring_entries; - uint32_t flags; - uint32_t dropped; - uint32_t array; - uint32_t resv1; - uint64_t resv2; -}; - -struct io_cqring_offsets { - uint32_t head; - uint32_t tail; - uint32_t ring_mask; - uint32_t ring_entries; - uint32_t overflow; - uint32_t cqes; - uint64_t resv[2]; -}; - -struct io_uring_params { - uint32_t sq_entries; - uint32_t cq_entries; - uint32_t flags; - uint32_t sq_thread_cpu; - uint32_t sq_thread_idle; - uint32_t features; - uint32_t resv[4]; - struct io_sqring_offsets sq_off; - struct io_cqring_offsets cq_off; -}; - -#define IORING_OFF_SQ_RING 0 -#define IORING_OFF_SQES 0x10000000ULL - -#define sys_io_uring_setup 425 -static long syz_io_uring_setup(volatile long a0, volatile long a1, - volatile long a2, volatile long a3, - volatile long a4, volatile long a5) -{ - uint32_t entries = (uint32_t)a0; - struct io_uring_params* setup_params = (struct io_uring_params*)a1; - void* vma1 = (void*)a2; - void* vma2 = (void*)a3; - void** ring_ptr_out = (void**)a4; - void** sqes_ptr_out = (void**)a5; - uint32_t fd_io_uring = syscall(sys_io_uring_setup, entries, setup_params); - uint32_t sq_ring_sz = - setup_params->sq_off.array + setup_params->sq_entries * sizeof(uint32_t); - uint32_t cq_ring_sz = setup_params->cq_off.cqes + - setup_params->cq_entries * SIZEOF_IO_URING_CQE; - uint32_t ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz; - *ring_ptr_out = mmap(vma1, ring_sz, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, - IORING_OFF_SQ_RING); - uint32_t sqes_sz = setup_params->sq_entries * SIZEOF_IO_URING_SQE; - *sqes_ptr_out = - mmap(vma2, sqes_sz, PROT_READ | PROT_WRITE, - MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQES); - return fd_io_uring; -} - -static long syz_io_uring_submit(volatile long a0, volatile long a1, - volatile long a2, volatile long a3) -{ - char* ring_ptr = (char*)a0; - char* sqes_ptr = (char*)a1; - char* sqe = (char*)a2; - uint32_t sqes_index = (uint32_t)a3; - uint32_t sq_ring_entries = *(uint32_t*)(ring_ptr + SQ_RING_ENTRIES_OFFSET); - uint32_t cq_ring_entries = *(uint32_t*)(ring_ptr + CQ_RING_ENTRIES_OFFSET); - uint32_t sq_array_off = - (CQ_CQES_OFFSET + cq_ring_entries * SIZEOF_IO_URING_CQE + 63) & ~63; - if (sq_ring_entries) - sqes_index %= sq_ring_entries; - char* sqe_dest = sqes_ptr + sqes_index * SIZEOF_IO_URING_SQE; - memcpy(sqe_dest, sqe, SIZEOF_IO_URING_SQE); - uint32_t sq_ring_mask = *(uint32_t*)(ring_ptr + SQ_RING_MASK_OFFSET); - uint32_t* sq_tail_ptr = (uint32_t*)(ring_ptr + SQ_TAIL_OFFSET); - uint32_t sq_tail = *sq_tail_ptr & sq_ring_mask; - uint32_t sq_tail_next = *sq_tail_ptr + 1; - uint32_t* sq_array = (uint32_t*)(ring_ptr + sq_array_off); - *(sq_array + sq_tail) = sqes_index; - __atomic_store_n(sq_tail_ptr, sq_tail_next, __ATOMIC_RELEASE); - return 0; -} - -static void kill_and_wait(int pid, int* status) -{ - kill(-pid, SIGKILL); - kill(pid, SIGKILL); - for (int i = 0; i < 100; i++) { - if (waitpid(-1, status, WNOHANG | __WALL) == pid) - return; - usleep(1000); - } - DIR* dir = opendir("/sys/fs/fuse/connections"); - if (dir) { - for (;;) { - struct dirent* ent = readdir(dir); - if (!ent) - break; - if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) - continue; - char abort[300]; - snprintf(abort, sizeof(abort), "/sys/fs/fuse/connections/%s/abort", - ent->d_name); - int fd = open(abort, O_WRONLY); - if (fd == -1) { - continue; - } - if (write(fd, abort, 1) < 0) { - } - close(fd); - } - closedir(dir); - } else { - } - while (waitpid(-1, status, __WALL) != pid) { - } -} - -static void setup_test(void) -{ - prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); - setpgrp(); -} - -struct thread_t { - int created, call; - event_t ready, done; -}; - -static struct thread_t threads[16]; -static void execute_call(int call); -static int running; - -static void* thr(void* arg) -{ - struct thread_t* th = (struct thread_t*)arg; - for (;;) { - event_wait(&th->ready); - event_reset(&th->ready); - execute_call(th->call); - __atomic_fetch_sub(&running, 1, __ATOMIC_RELAXED); - event_set(&th->done); - } - return 0; -} - -static void execute_one(void) -{ - int i, call, thread; - for (call = 0; call < 4; call++) { - for (thread = 0; thread < (int)(sizeof(threads) / sizeof(threads[0])); - thread++) { - struct thread_t* th = &threads[thread]; - if (!th->created) { - th->created = 1; - event_init(&th->ready); - event_init(&th->done); - event_set(&th->done); - thread_start(thr, th); - } - if (!event_isset(&th->done)) - continue; - event_reset(&th->done); - th->call = call; - __atomic_fetch_add(&running, 1, __ATOMIC_RELAXED); - event_set(&th->ready); - event_timedwait(&th->done, 50); - break; - } - } - for (i = 0; i < 100 && __atomic_load_n(&running, __ATOMIC_RELAXED); i++) - sleep_ms(1); -} - -static void execute_one(void); - -#define WAIT_FLAGS __WALL - -static void loop(void) -{ - int iter = 0; - for (; iter < 5000; iter++) { - int pid = fork(); - if (pid < 0) - exit(1); - if (pid == 0) { - setup_test(); - execute_one(); - exit(0); - } - int status = 0; - uint64_t start = current_time_ms(); - for (;;) { - if (waitpid(-1, &status, WNOHANG | WAIT_FLAGS) == pid) - break; - sleep_ms(1); - if (current_time_ms() - start < 5000) - continue; - kill_and_wait(pid, &status); - break; - } - } -} - -#ifndef __NR_io_uring_enter -#define __NR_io_uring_enter 426 -#endif - -static uint64_t r[4] = {0xffffffffffffffff, 0xffffffffffffffff, 0x0, 0x0}; - -void execute_call(int call) -{ - intptr_t res = 0; - switch (call) { - case 0: - *(uint64_t*)0x200000c0 = 0; - res = syscall(__NR_signalfd4, -1, 0x200000c0ul, 8ul, 0ul); - if (res != -1) - r[0] = res; - break; - case 1: - *(uint32_t*)0x20000a84 = 0; - *(uint32_t*)0x20000a88 = 0; - *(uint32_t*)0x20000a8c = 0; - *(uint32_t*)0x20000a90 = 0; - *(uint32_t*)0x20000a98 = -1; - memset((void*)0x20000a9c, 0, 12); - res = -1; - res = syz_io_uring_setup(0x87, 0x20000a80, 0x206d6000, 0x206d7000, - 0x20000000, 0x20000040); - if (res != -1) { - r[1] = res; - r[2] = *(uint64_t*)0x20000000; - r[3] = *(uint64_t*)0x20000040; - } - break; - case 2: - *(uint8_t*)0x20002240 = 6; - *(uint8_t*)0x20002241 = 0; - *(uint16_t*)0x20002242 = 0; - *(uint32_t*)0x20002244 = r[0]; - *(uint64_t*)0x20002248 = 0; - *(uint64_t*)0x20002250 = 0; - *(uint32_t*)0x20002258 = 0; - *(uint16_t*)0x2000225c = 0; - *(uint16_t*)0x2000225e = 0; - *(uint64_t*)0x20002260 = 0; - *(uint16_t*)0x20002268 = 0; - *(uint16_t*)0x2000226a = 0; - memset((void*)0x2000226c, 0, 20); - syz_io_uring_submit(r[2], r[3], 0x20002240, 0); - break; - case 3: - syscall(__NR_io_uring_enter, r[1], 0x1523a, 0, 0ul, 0ul, 0xaul); - break; - } -} - -int main(int argc, char *argv[]) -{ - void *ret; - -#if !defined(__i386) && !defined(__x86_64__) - return 0; -#endif - - if (argc > 1) - return 0; - - ret = mmap((void *)0x1ffff000ul, 0x1000ul, 0ul, MAP_ANON|MAP_PRIVATE, -1, 0ul); - if (ret == MAP_FAILED) - return 0; - ret = mmap((void *)0x20000000ul, 0x1000000ul, 7ul, MAP_ANON|MAP_PRIVATE, -1, 0ul); - if (ret == MAP_FAILED) - return 0; - ret = mmap((void *)0x21000000ul, 0x1000ul, 0ul, MAP_ANON|MAP_PRIVATE, -1, 0ul); - if (ret == MAP_FAILED) - return 0; - loop(); - return 0; -} - -#else /* __NR_futex */ - -int main(int argc, char *argv[]) -{ - return 0; -} - -#endif /* __NR_futex */ diff --git a/contrib/libs/liburing/test/read-mshot-empty.c b/contrib/libs/liburing/test/read-mshot-empty.c new file mode 100644 index 0000000000..2fc5a63184 --- /dev/null +++ b/contrib/libs/liburing/test/read-mshot-empty.c @@ -0,0 +1,154 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test that multishot read correctly keeps reading until all + * data has been emptied. the original implementation failed + * to do so, if the available buffer size was less than what + * was available, hence requiring multiple reads to empty the + * file buffer. + */ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <assert.h> +#include <pthread.h> +#include <sys/time.h> + +#include "liburing.h" +#include "helpers.h" + +#define BGID 17 +#define NR_BUFS 4 +#define BR_MASK (NR_BUFS - 1) +#define BUF_SIZE 32 + +static int do_write(int fd, void *buf, int buf_size) +{ + int ret; + + ret = write(fd, buf, buf_size); + if (ret < 0) { + perror("write"); + return 0; + } else if (ret != buf_size) { + fprintf(stderr, "bad write size %d\n", ret); + return 0; + } + + return 1; +} + +static void *thread_fn(void *data) +{ + char w1[BUF_SIZE], w2[BUF_SIZE]; + int *fds = data; + + memset(w1, 0x11, BUF_SIZE); + memset(w2, 0x22, BUF_SIZE); + + if (!do_write(fds[1], w1, BUF_SIZE)) + return NULL; + if (!do_write(fds[1], w2, BUF_SIZE)) + return NULL; + + usleep(100000); + + if (!do_write(fds[1], w1, BUF_SIZE)) + return NULL; + if (!do_write(fds[1], w2, BUF_SIZE)) + return NULL; + + return NULL; +} + +int main(int argc, char *argv[]) +{ + struct io_uring_buf_ring *br; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + pthread_t thread; + int i, ret, fds[2]; + void *buf, *tret; + + if (argc > 1) + return T_EXIT_SKIP; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "queue_init: %d\n", ret); + return T_EXIT_FAIL; + } + + br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret); + if (!br) { + if (ret == -EINVAL) + return T_EXIT_SKIP; + fprintf(stderr, "failed buffer ring %d\n", ret); + return T_EXIT_FAIL; + } + + buf = malloc(NR_BUFS * BUF_SIZE); + for (i = 0; i < NR_BUFS; i++) { + void *this_buf = buf + i * BUF_SIZE; + + io_uring_buf_ring_add(br, this_buf, BUF_SIZE, i, BR_MASK, i); + } + io_uring_buf_ring_advance(br, NR_BUFS); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BGID); + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "bad submit %d\n", ret); + return T_EXIT_FAIL; + } + + /* + * read multishot not available would be ready as a cqe when + * submission returns, check and skip if not. + */ + ret = io_uring_peek_cqe(&ring, &cqe); + if (!ret) { + if (cqe->res == -EINVAL || cqe->res == -EBADF) + return T_EXIT_SKIP; + } + + pthread_create(&thread, NULL, thread_fn, fds); + + for (i = 0; i < 4; i++) { + int buf_index; + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait %d\n", ret); + break; + } + + if (cqe->res != BUF_SIZE) { + fprintf(stderr, "size %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_BUFFER)) { + fprintf(stderr, "buffer not set\n"); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_MORE)) { + fprintf(stderr, "more not set\n"); + return T_EXIT_FAIL; + } + buf_index = cqe->flags >> 16; + assert(buf_index >= 0 && buf_index <= NR_BUFS); + io_uring_cqe_seen(&ring, cqe); + } + + pthread_join(thread, &tret); + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/read-mshot-empty.t/ya.make b/contrib/libs/liburing/test/read-mshot-empty.t/ya.make new file mode 100644 index 0000000000..b72fbc2cb8 --- /dev/null +++ b/contrib/libs/liburing/test/read-mshot-empty.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + read-mshot-empty.c +) + +END() diff --git a/contrib/libs/liburing/test/read-mshot.c b/contrib/libs/liburing/test/read-mshot.c new file mode 100644 index 0000000000..681a306ad4 --- /dev/null +++ b/contrib/libs/liburing/test/read-mshot.c @@ -0,0 +1,405 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test multishot read (IORING_OP_READ_MULTISHOT) on pipes, + * using ring provided buffers + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#include "liburing.h" +#include "helpers.h" + +#define BUF_SIZE 32 +#define BUF_SIZE_FIRST 17 +#define NR_BUFS 64 +#define BUF_BGID 1 + +#define BR_MASK (NR_BUFS - 1) + +#define NR_OVERFLOW (NR_BUFS / 4) + +static int no_buf_ring, no_read_mshot; + +static int test_clamp(void) +{ + struct io_uring_buf_ring *br; + struct io_uring_params p = { }; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + int ret, fds[2], i; + char tmp[32]; + char *buf; + void *ptr; + + ret = io_uring_queue_init_params(4, &ring, &p); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + + if (posix_memalign((void **) &buf, 4096, NR_BUFS * BUF_SIZE)) + return 1; + + br = io_uring_setup_buf_ring(&ring, NR_BUFS, BUF_BGID, 0, &ret); + if (!br) { + if (ret == -EINVAL) { + no_buf_ring = 1; + return 0; + } + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + + ptr = buf; + io_uring_buf_ring_add(br, buf, 16, 1, BR_MASK, 0); + buf += 16; + io_uring_buf_ring_add(br, buf, 32, 2, BR_MASK, 1); + buf += 32; + io_uring_buf_ring_add(br, buf, 32, 3, BR_MASK, 2); + buf += 32; + io_uring_buf_ring_add(br, buf, 32, 4, BR_MASK, 3); + buf += 32; + io_uring_buf_ring_advance(br, 4); + + memset(tmp, 0xaa, sizeof(tmp)); + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BUF_BGID); + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "submit: %d\n", ret); + return 1; + } + + /* prevent pipe buffer merging */ + usleep(1000); + ret = write(fds[1], tmp, 16); + + usleep(1000); + ret = write(fds[1], tmp, sizeof(tmp)); + + /* prevent pipe buffer merging */ + usleep(1000); + ret = write(fds[1], tmp, 16); + + usleep(1000); + ret = write(fds[1], tmp, sizeof(tmp)); + + /* + * We should see a 16 byte completion, then a 32 byte, then a 16 byte, + * and finally a 32 byte again. + */ + for (i = 0; i < 4; i++) { + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe failed %d\n", ret); + return 1; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return 1; + } + if (!(cqe->flags & IORING_CQE_F_MORE)) { + fprintf(stderr, "no more cqes\n"); + return 1; + } + if (i == 0 || i == 2) { + if (cqe->res != 16) { + fprintf(stderr, "%d cqe got %d\n", i, cqe->res); + return 1; + } + } else if (i == 1 || i == 3) { + if (cqe->res != 32) { + fprintf(stderr, "%d cqe got %d\n", i, cqe->res); + return 1; + } + } + io_uring_cqe_seen(&ring, cqe); + } + + io_uring_queue_exit(&ring); + free(ptr); + return 0; +} + +static int test(int first_good, int async, int overflow) +{ + struct io_uring_buf_ring *br; + struct io_uring_params p = { }; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + int ret, fds[2], i; + char tmp[32]; + void *ptr[NR_BUFS]; + + p.flags = IORING_SETUP_CQSIZE; + if (!overflow) + p.cq_entries = NR_BUFS + 1; + else + p.cq_entries = NR_OVERFLOW; + ret = io_uring_queue_init_params(1, &ring, &p); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + + br = io_uring_setup_buf_ring(&ring, NR_BUFS, BUF_BGID, 0, &ret); + if (!br) { + if (ret == -EINVAL) { + no_buf_ring = 1; + return 0; + } + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + + for (i = 0; i < NR_BUFS; i++) { + unsigned size = i <= 1 ? BUF_SIZE_FIRST : BUF_SIZE; + ptr[i] = malloc(size); + if (!ptr[i]) + return 1; + io_uring_buf_ring_add(br, ptr[i], size, i + 1, BR_MASK, i); + } + io_uring_buf_ring_advance(br, NR_BUFS); + + if (first_good) { + sprintf(tmp, "this is buffer %d\n", 0); + ret = write(fds[1], tmp, strlen(tmp)); + } + + sqe = io_uring_get_sqe(&ring); + /* len == 0 means just use the defined provided buffer length */ + io_uring_prep_read_multishot(sqe, fds[0], 0, 0, BUF_BGID); + if (async) + sqe->flags |= IOSQE_ASYNC; + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "submit: %d\n", ret); + return 1; + } + + /* write NR_BUFS + 1, or if first_good is set, NR_BUFS */ + for (i = 0; i < NR_BUFS + !first_good; i++) { + /* prevent pipe buffer merging */ + usleep(1000); + sprintf(tmp, "this is buffer %d\n", i + 1); + ret = write(fds[1], tmp, strlen(tmp)); + if (ret != strlen(tmp)) { + fprintf(stderr, "write ret %d\n", ret); + return 1; + } + } + + for (i = 0; i < NR_BUFS + 1; i++) { + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe failed %d\n", ret); + return 1; + } + if (cqe->res < 0) { + /* expected failure as we try to read one too many */ + if (cqe->res == -ENOBUFS && i == NR_BUFS) + break; + if (!i && cqe->res == -EINVAL) { + no_read_mshot = 1; + break; + } + fprintf(stderr, "%d: cqe res %d\n", i, cqe->res); + return 1; + } else if (i > 9 && cqe->res <= 17) { + fprintf(stderr, "truncated message %d %d\n", i, cqe->res); + return 1; + } + + if (!(cqe->flags & IORING_CQE_F_BUFFER)) { + fprintf(stderr, "no buffer selected\n"); + return 1; + } + if (!(cqe->flags & IORING_CQE_F_MORE)) { + /* we expect this on overflow */ + if (overflow && i >= NR_OVERFLOW) + break; + fprintf(stderr, "no more cqes\n"); + return 1; + } + /* should've overflown! */ + if (overflow && i > NR_OVERFLOW) { + fprintf(stderr, "Expected overflow!\n"); + return 1; + } + io_uring_cqe_seen(&ring, cqe); + } + + io_uring_queue_exit(&ring); + for (i = 0; i < NR_BUFS; i++) + free(ptr[i]); + return 0; +} + +static int test_invalid(int async) +{ + struct io_uring_buf_ring *br; + struct io_uring_params p = { }; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + char fname[32] = ".mshot.%d.XXXXXX"; + int ret, fd; + char *buf; + + p.flags = IORING_SETUP_CQSIZE; + p.cq_entries = NR_BUFS; + ret = io_uring_queue_init_params(1, &ring, &p); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + fd = mkstemp(fname); + if (fd < 0) { + perror("mkstemp"); + return 1; + } + unlink(fname); + + if (posix_memalign((void **) &buf, 4096, BUF_SIZE)) + return 1; + + br = io_uring_setup_buf_ring(&ring, 1, BUF_BGID, 0, &ret); + if (!br) { + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + + io_uring_buf_ring_add(br, buf, BUF_SIZE, 1, BR_MASK, 0); + io_uring_buf_ring_advance(br, 1); + + sqe = io_uring_get_sqe(&ring); + /* len == 0 means just use the defined provided buffer length */ + io_uring_prep_read_multishot(sqe, fd, 0, 0, BUF_BGID); + if (async) + sqe->flags |= IOSQE_ASYNC; + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "submit: %d\n", ret); + return 1; + } + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe failed %d\n", ret); + return 1; + } + if (cqe->flags & IORING_CQE_F_MORE) { + fprintf(stderr, "MORE flag set unexpected %d\n", cqe->flags); + return 1; + } + if (cqe->res != -EBADFD) { + fprintf(stderr, "Got cqe res %d, wanted -EBADFD\n", cqe->res); + return 1; + } + + io_uring_cqe_seen(&ring, cqe); + io_uring_queue_exit(&ring); + free(buf); + return 0; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(0, 0, 0); + if (ret) { + fprintf(stderr, "test 0 0 0 failed\n"); + return T_EXIT_FAIL; + } + if (no_buf_ring || no_read_mshot) + return T_EXIT_SKIP; + + ret = test(0, 1, 0); + if (ret) { + fprintf(stderr, "test 0 1 0, failed\n"); + return T_EXIT_FAIL; + } + + ret = test(1, 0, 0); + if (ret) { + fprintf(stderr, "test 1 0 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(0, 0, 1); + if (ret) { + fprintf(stderr, "test 0 0 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(0, 1, 1); + if (ret) { + fprintf(stderr, "test 0 1 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(1, 0, 1); + if (ret) { + fprintf(stderr, "test 1 0 1, failed\n"); + return T_EXIT_FAIL; + } + + ret = test(1, 0, 1); + if (ret) { + fprintf(stderr, "test 1 0 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test(1, 1, 1); + if (ret) { + fprintf(stderr, "test 1 1 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_invalid(0); + if (ret) { + fprintf(stderr, "test_invalid 0 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_invalid(1); + if (ret) { + fprintf(stderr, "test_invalid 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_clamp(); + if (ret) { + fprintf(stderr, "test_clamp failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/read-mshot.t/ya.make b/contrib/libs/liburing/test/read-mshot.t/ya.make new file mode 100644 index 0000000000..ba64db60ba --- /dev/null +++ b/contrib/libs/liburing/test/read-mshot.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + read-mshot.c +) + +END() diff --git a/contrib/libs/liburing/test/recv-multishot.c b/contrib/libs/liburing/test/recv-multishot.c index c80fa18547..de07809f0d 100644 --- a/contrib/libs/liburing/test/recv-multishot.c +++ b/contrib/libs/liburing/test/recv-multishot.c @@ -58,7 +58,7 @@ static int test(struct args *args) int const N = 8; int const N_BUFFS = N * 64; int const N_CQE_OVERFLOW = 4; - int const min_cqes = 2; + int const min_cqes = args->early_error ? 2 : 8; int const NAME_LEN = sizeof(struct sockaddr_storage); int const CONTROL_LEN = CMSG_ALIGN(sizeof(struct sockaddr_storage)) + sizeof(struct cmsghdr); @@ -238,7 +238,11 @@ static int test(struct args *args) usleep(1000); if ((args->stream && !early_error) || recv_cqes < min_cqes) { - ret = io_uring_wait_cqes(&ring, &cqe, 1, &timeout, NULL); + unsigned int to_wait = 1; + + if (recv_cqes < min_cqes) + to_wait = min_cqes - recv_cqes; + ret = io_uring_wait_cqes(&ring, &cqe, to_wait, &timeout, NULL); if (ret && ret != -ETIME) { fprintf(stderr, "wait final failed: %d\n", ret); ret = -1; @@ -272,7 +276,7 @@ static int test(struct args *args) */ bool const early_last = args->early_error == ERROR_EARLY_OVERFLOW && !args->wait_each && - i == N_CQE_OVERFLOW && + i >= N_CQE_OVERFLOW && !(cqe->flags & IORING_CQE_F_MORE); bool const should_be_last = diff --git a/contrib/libs/liburing/test/reg-fd-only.c b/contrib/libs/liburing/test/reg-fd-only.c new file mode 100644 index 0000000000..1ff6ea250e --- /dev/null +++ b/contrib/libs/liburing/test/reg-fd-only.c @@ -0,0 +1,132 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Test io_uring_setup with IORING_SETUP_REGISTERED_FD_ONLY + * + */ +#include <stdio.h> + +#include "helpers.h" + +#define NORMAL_PAGE_ENTRIES 8 +#define HUGE_PAGE_ENTRIES 512 + +static int no_mmap; + +static int test_nops(struct io_uring *ring, int sq_size, int nr_nops) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int i, ret; + + do { + int todo = nr_nops; + + if (todo > sq_size) + todo = sq_size; + + for (i = 0; i < todo; i++) { + sqe = io_uring_get_sqe(ring); + io_uring_prep_nop(sqe); + } + + ret = io_uring_submit(ring); + if (ret != todo) { + fprintf(stderr, "short submit %d\n", ret); + return T_EXIT_FAIL; + } + + for (i = 0; i < todo; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait err %d\n", ret); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + } + nr_nops -= todo; + } while (nr_nops); + + return T_EXIT_PASS; +} + +static int test(int nentries) +{ + struct io_uring ring; + unsigned values[2]; + int ret; + + ret = io_uring_queue_init(nentries, &ring, + IORING_SETUP_REGISTERED_FD_ONLY | IORING_SETUP_NO_MMAP); + if (ret == -EINVAL) { + no_mmap = 1; + return T_EXIT_SKIP; + } else if (ret == -ENOMEM) { + fprintf(stdout, "Enable huge pages to test big rings\n"); + return T_EXIT_SKIP; + } else if (ret) { + fprintf(stderr, "ring setup failed\n"); + return T_EXIT_FAIL; + } + + ret = io_uring_register_ring_fd(&ring); + if (ret != -EEXIST) { + fprintf(stderr, "registering already-registered ring fd should fail\n"); + goto err; + } + + ret = io_uring_close_ring_fd(&ring); + if (ret != -EBADF) { + fprintf(stderr, "closing already-closed ring fd should fail\n"); + goto err; + } + + /* Test a simple io_uring_register operation expected to work. + * io_uring_register_iowq_max_workers is arbitrary. + */ + values[0] = values[1] = 0; + ret = io_uring_register_iowq_max_workers(&ring, values); + if (ret || (values[0] == 0 && values[1] == 0)) { + fprintf(stderr, "io_uring_register operation failed after closing ring fd\n"); + goto err; + } + + ret = test_nops(&ring, nentries, nentries * 4); + if (ret) + goto err; + + io_uring_queue_exit(&ring); + return T_EXIT_PASS; + +err: + io_uring_queue_exit(&ring); + return T_EXIT_FAIL; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + /* test single normal page */ + ret = test(NORMAL_PAGE_ENTRIES); + if (ret == T_EXIT_SKIP || no_mmap) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "test 8 failed\n"); + return T_EXIT_FAIL; + } + + /* test with entries requiring a huge page */ + ret = test(HUGE_PAGE_ENTRIES); + if (ret == T_EXIT_SKIP) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "test 512 failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/reg-fd-only.t/ya.make b/contrib/libs/liburing/test/reg-fd-only.t/ya.make new file mode 100644 index 0000000000..f2975eddd3 --- /dev/null +++ b/contrib/libs/liburing/test/reg-fd-only.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + reg-fd-only.c +) + +END() diff --git a/contrib/libs/liburing/test/ring-leak.c b/contrib/libs/liburing/test/ring-leak.c index e052802314..6b8237fdc4 100644 --- a/contrib/libs/liburing/test/ring-leak.c +++ b/contrib/libs/liburing/test/ring-leak.c @@ -24,6 +24,7 @@ #include <linux/fs.h> #include "liburing.h" +#include "helpers.h" #include "../src/syscall.h" static int __io_uring_register_files(int ring_fd, int fd1, int fd2) @@ -49,7 +50,7 @@ static int get_ring_fd(void) return fd; } -static void send_fd(int socket, int fd) +static int send_fd(int socket, int fd) { char buf[CMSG_SPACE(sizeof(fd))]; struct cmsghdr *cmsg; @@ -70,8 +71,14 @@ static void send_fd(int socket, int fd) msg.msg_controllen = CMSG_SPACE(sizeof(fd)); - if (sendmsg(socket, &msg, 0) < 0) + if (sendmsg(socket, &msg, 0) < 0) { + if (errno == EINVAL) + return T_EXIT_SKIP; perror("sendmsg"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; } static int test_iowq_request_cancel(void) @@ -167,7 +174,9 @@ static int test_scm_cycles(bool update) perror("pipe"); return -1; } - send_fd(sp[0], ring.ring_fd); + ret = send_fd(sp[0], ring.ring_fd); + if (ret != T_EXIT_PASS) + return ret; /* register an empty set for updates */ if (update) { @@ -237,6 +246,8 @@ int main(int argc, char *argv[]) bool update = !!(i & 1); ret = test_scm_cycles(update); + if (ret == T_EXIT_SKIP) + return T_EXIT_SKIP; if (ret) { fprintf(stderr, "test_scm_cycles() failed %i\n", update); @@ -260,8 +271,11 @@ int main(int argc, char *argv[]) } pid = fork(); - if (pid) - send_fd(sp[0], ring_fd); + if (pid) { + ret = send_fd(sp[0], ring_fd); + if (ret != T_EXIT_PASS) + return ret; + } close(ring_fd); close(sp[0]); diff --git a/contrib/libs/liburing/test/ringbuf-status.c b/contrib/libs/liburing/test/ringbuf-status.c new file mode 100644 index 0000000000..d4a56c7473 --- /dev/null +++ b/contrib/libs/liburing/test/ringbuf-status.c @@ -0,0 +1,243 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test reading provided ring buf head + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> + +#include "liburing.h" +#include "helpers.h" + +#define BUF_SIZE 32 +#define NR_BUFS 8 +#define FSIZE (BUF_SIZE * NR_BUFS) + +#define BR_MASK (NR_BUFS - 1) +#define BGID 1 + +static int no_buf_ring; +static int no_buf_ring_status; + +static int test_max(void) +{ + struct io_uring_buf_ring *br; + struct io_uring ring; + int nr_bufs = 32768; + int ret, i; + char *buf; + + ret = io_uring_queue_init(1, &ring, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + if (posix_memalign((void **) &buf, 4096, FSIZE)) + return 1; + + br = io_uring_setup_buf_ring(&ring, nr_bufs, BGID, 0, &ret); + if (!br) { + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + + ret = io_uring_buf_ring_available(&ring, br, BGID); + if (ret) { + fprintf(stderr, "Bad available count %d\n", ret); + return 1; + } + + for (i = 0; i < nr_bufs / 2; i++) + io_uring_buf_ring_add(br, buf, BUF_SIZE, i + 1, nr_bufs - 1, i); + io_uring_buf_ring_advance(br, nr_bufs / 2); + + ret = io_uring_buf_ring_available(&ring, br, BGID); + if (ret != nr_bufs / 2) { + fprintf(stderr, "Bad half full available count %d\n", ret); + return 1; + } + + for (i = 0; i < nr_bufs / 2; i++) + io_uring_buf_ring_add(br, buf, BUF_SIZE, i + 1, nr_bufs - 1, i); + io_uring_buf_ring_advance(br, nr_bufs / 2); + + ret = io_uring_buf_ring_available(&ring, br, BGID); + if (ret != nr_bufs) { + fprintf(stderr, "Bad half full available count %d\n", ret); + return 1; + } + + free(buf); + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} + +static int test(int invalid) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct io_uring ring; + struct io_uring_buf_ring *br; + int ret, i, fds[2]; + uint16_t head; + char *buf; + void *ptr; + char output[16]; + + memset(output, 0x55, sizeof(output)); + + ret = io_uring_queue_init(NR_BUFS, &ring, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return 1; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + if (posix_memalign((void **) &buf, 4096, FSIZE)) + return 1; + + br = io_uring_setup_buf_ring(&ring, NR_BUFS, BGID, 0, &ret); + if (!br) { + if (ret == -EINVAL) { + no_buf_ring = 1; + return 0; + } + fprintf(stderr, "Buffer ring register failed %d\n", ret); + return 1; + } + + ptr = buf; + for (i = 0; i < NR_BUFS; i++) { + io_uring_buf_ring_add(br, ptr, BUF_SIZE, i + 1, BR_MASK, i); + ptr += BUF_SIZE; + } + io_uring_buf_ring_advance(br, NR_BUFS); + + /* head should be zero at this point */ + head = 1; + if (!invalid) + ret = io_uring_buf_ring_head(&ring, BGID, &head); + else + ret = io_uring_buf_ring_head(&ring, BGID + 10, &head); + if (ret) { + if (ret == -EINVAL) { + no_buf_ring_status = 1; + return T_EXIT_SKIP; + } + if (invalid && ret == -ENOENT) + return T_EXIT_PASS; + fprintf(stderr, "buf_ring_head: %d\n", ret); + return T_EXIT_FAIL; + } + if (invalid) { + fprintf(stderr, "lookup of bad group id succeeded\n"); + return T_EXIT_FAIL; + } + if (head != 0) { + fprintf(stderr, "bad head %d\n", head); + return T_EXIT_FAIL; + } + + ret = io_uring_buf_ring_available(&ring, br, BGID); + if (ret != NR_BUFS) { + fprintf(stderr, "ring available %d\n", ret); + return T_EXIT_FAIL; + } + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], NULL, BUF_SIZE, i * BUF_SIZE); + sqe->buf_group = BGID; + sqe->flags |= IOSQE_BUFFER_SELECT; + sqe->user_data = 1; + + ret = io_uring_submit(&ring); + if (ret != 1) { + fprintf(stderr, "submit: %d\n", ret); + return T_EXIT_FAIL; + } + + /* head should still be zero at this point, no buffers consumed */ + head = 1; + ret = io_uring_buf_ring_head(&ring, BGID, &head); + if (head != 0) { + fprintf(stderr, "bad head after submit %d\n", head); + return T_EXIT_FAIL; + } + + ret = write(fds[1], output, sizeof(output)); + if (ret != sizeof(output)) { + fprintf(stderr, "pipe buffer write %d\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_wait_cqe(&ring, &cqe); + if (ret) { + fprintf(stderr, "wait cqe failed %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->res != sizeof(output)) { + fprintf(stderr, "cqe res %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (!(cqe->flags & IORING_CQE_F_BUFFER)) { + fprintf(stderr, "no buffer selected\n"); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(&ring, cqe); + + /* head should now be one, we consumed a buffer */ + ret = io_uring_buf_ring_head(&ring, BGID, &head); + if (head != 1) { + fprintf(stderr, "bad head after cqe %d\n", head); + return T_EXIT_FAIL; + } + + ret = io_uring_buf_ring_available(&ring, br, BGID); + if (ret != NR_BUFS - 1) { + fprintf(stderr, "ring available %d\n", ret); + return T_EXIT_FAIL; + } + + close(fds[0]); + close(fds[1]); + free(buf); + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + int ret; + + ret = test(0); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test 0 failed\n"); + return T_EXIT_FAIL; + } + if (no_buf_ring || no_buf_ring_status) + return T_EXIT_SKIP; + + ret = test(1); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test 1 failed\n"); + return T_EXIT_FAIL; + } + + ret = test_max(); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_max failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/ringbuf-status.t/ya.make b/contrib/libs/liburing/test/ringbuf-status.t/ya.make new file mode 100644 index 0000000000..9eaa5a50b9 --- /dev/null +++ b/contrib/libs/liburing/test/ringbuf-status.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + ringbuf-status.c +) + +END() diff --git a/contrib/libs/liburing/test/send-zerocopy.c b/contrib/libs/liburing/test/send-zerocopy.c index 27300f9209..dab707d1e5 100644 --- a/contrib/libs/liburing/test/send-zerocopy.c +++ b/contrib/libs/liburing/test/send-zerocopy.c @@ -69,8 +69,37 @@ enum { static size_t page_sz; static char *tx_buffer, *rx_buffer; static struct iovec buffers_iov[__BUF_NR]; + +static bool has_sendzc; static bool has_sendmsg; +static int probe_zc_support(void) +{ + struct io_uring ring; + struct io_uring_probe *p; + int ret; + + has_sendzc = has_sendmsg = false; + + ret = io_uring_queue_init(1, &ring, 0); + if (ret) + return -1; + + p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op)); + if (!p) + return -1; + + ret = io_uring_register_probe(&ring, p, 256); + if (ret) + return -1; + + has_sendzc = p->ops_len > IORING_OP_SEND_ZC; + has_sendmsg = p->ops_len > IORING_OP_SENDMSG_ZC; + io_uring_queue_exit(&ring); + free(p); + return 0; +} + static bool check_cq_empty(struct io_uring *ring) { struct io_uring_cqe *cqe = NULL; @@ -99,10 +128,7 @@ static int test_basic_send(struct io_uring *ring, int sock_tx, int sock_rx) ret = io_uring_wait_cqe(ring, &cqe); assert(!ret && cqe->user_data == 1); - if (cqe->res == -EINVAL) { - assert(!(cqe->flags & IORING_CQE_F_MORE)); - return T_EXIT_SKIP; - } else if (cqe->res != payload_size) { + if (cqe->res != payload_size) { fprintf(stderr, "send failed %i\n", cqe->res); return T_EXIT_FAIL; } @@ -123,17 +149,60 @@ static int test_basic_send(struct io_uring *ring, int sock_tx, int sock_rx) return T_EXIT_PASS; } +static int test_send_faults_check(struct io_uring *ring, int expected) +{ + struct io_uring_cqe *cqe; + int ret, nr_cqes = 0; + bool more = true; + + while (more) { + nr_cqes++; + ret = io_uring_wait_cqe(ring, &cqe); + assert(!ret); + assert(cqe->user_data == 1); + + if (nr_cqes == 1 && (cqe->flags & IORING_CQE_F_NOTIF)) { + fprintf(stderr, "test_send_faults_check notif came first\n"); + return -1; + } + + if (!(cqe->flags & IORING_CQE_F_NOTIF)) { + if (cqe->res != expected) { + fprintf(stderr, "invalid cqe res %i vs expected %i, " + "user_data %i\n", + cqe->res, expected, (int)cqe->user_data); + return -1; + } + } else { + if (cqe->res != 0 || cqe->flags != IORING_CQE_F_NOTIF) { + fprintf(stderr, "invalid notif cqe %i %i\n", + cqe->res, cqe->flags); + return -1; + } + } + + more = cqe->flags & IORING_CQE_F_MORE; + io_uring_cqe_seen(ring, cqe); + } + + if (nr_cqes > 2) { + fprintf(stderr, "test_send_faults_check() too many CQEs %i\n", + nr_cqes); + return -1; + } + assert(check_cq_empty(ring)); + return 0; +} + static int test_send_faults(int sock_tx, int sock_rx) { struct io_uring_sqe *sqe; - struct io_uring_cqe *cqe; int msg_flags = 0; unsigned zc_flags = 0; - int payload_size = 100; - int ret, i, nr_cqes, nr_reqs = 3; + int ret, payload_size = 100; struct io_uring ring; - ret = io_uring_queue_init(32, &ring, IORING_SETUP_SUBMIT_ALL); + ret = io_uring_queue_init(32, &ring, 0); if (ret) { fprintf(stderr, "queue init failed: %d\n", ret); return -1; @@ -144,6 +213,14 @@ static int test_send_faults(int sock_tx, int sock_rx) io_uring_prep_send_zc(sqe, sock_tx, (void *)1UL, payload_size, msg_flags, zc_flags); sqe->user_data = 1; + ret = io_uring_submit(&ring); + assert(ret == 1); + + ret = test_send_faults_check(&ring, -EFAULT); + if (ret) { + fprintf(stderr, "test_send_faults with invalid buf failed\n"); + return -1; + } /* invalid address */ sqe = io_uring_get_sqe(&ring); @@ -151,44 +228,30 @@ static int test_send_faults(int sock_tx, int sock_rx) msg_flags, zc_flags); io_uring_prep_send_set_addr(sqe, (const struct sockaddr *)1UL, sizeof(struct sockaddr_in6)); - sqe->user_data = 2; + sqe->user_data = 1; + ret = io_uring_submit(&ring); + assert(ret == 1); + + ret = test_send_faults_check(&ring, -EFAULT); + if (ret) { + fprintf(stderr, "test_send_faults with invalid addr failed\n"); + return -1; + } /* invalid send/recv flags */ sqe = io_uring_get_sqe(&ring); io_uring_prep_send_zc(sqe, sock_tx, tx_buffer, payload_size, msg_flags, ~0U); - sqe->user_data = 3; - + sqe->user_data = 1; ret = io_uring_submit(&ring); - assert(ret == nr_reqs); - - nr_cqes = nr_reqs; - for (i = 0; i < nr_cqes; i++) { - ret = io_uring_wait_cqe(&ring, &cqe); - assert(!ret); - assert(cqe->user_data <= nr_reqs); - - if (!(cqe->flags & IORING_CQE_F_NOTIF)) { - int expected = (cqe->user_data == 3) ? -EINVAL : -EFAULT; + assert(ret == 1); - if (cqe->res != expected) { - fprintf(stderr, "invalid cqe res %i vs expected %i, " - "user_data %i\n", - cqe->res, expected, (int)cqe->user_data); - return -1; - } - if (cqe->flags & IORING_CQE_F_MORE) - nr_cqes++; - } else { - if (cqe->res != 0 || cqe->flags != IORING_CQE_F_NOTIF) { - fprintf(stderr, "invalid notif cqe %i %i\n", - cqe->res, cqe->flags); - return -1; - } - } - io_uring_cqe_seen(&ring, cqe); + ret = test_send_faults_check(&ring, -EINVAL); + if (ret) { + fprintf(stderr, "test_send_faults with invalid flags failed\n"); + return -1; } - assert(check_cq_empty(&ring)); + return T_EXIT_PASS; } @@ -279,6 +342,10 @@ static int create_socketpair_ip(struct sockaddr_storage *addr, #ifdef SO_ZEROCOPY int val = 1; + /* + * NOTE: apps must not set SO_ZEROCOPY when using io_uring zc. + * It's only here to test interactions with MSG_ZEROCOPY. + */ if (setsockopt(*sock_client, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) { perror("setsockopt zc"); return 1; @@ -660,22 +727,6 @@ static int test_async_addr(struct io_uring *ring) return 0; } -static bool io_check_zc_sendmsg(struct io_uring *ring) -{ - struct io_uring_probe *p; - int ret; - - p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op)); - if (!p) { - fprintf(stderr, "probe allocation failed\n"); - return false; - } - ret = io_uring_register_probe(ring, p, 256); - if (ret) - return false; - return p->ops_len > IORING_OP_SENDMSG_ZC; -} - /* see also send_recv.c:test_invalid */ static int test_invalid_zc(int fds[2]) { @@ -719,24 +770,84 @@ static int test_invalid_zc(int fds[2]) return 0; } -int main(int argc, char *argv[]) +static int run_basic_tests(void) { struct sockaddr_storage addr; - struct io_uring ring; - int i, ret, sp[2]; + int ret, i, sp[2]; + + /* create TCP IPv6 pair */ + ret = create_socketpair_ip(&addr, &sp[0], &sp[1], true, true, false, true); + if (ret) { + fprintf(stderr, "sock prep failed %d\n", ret); + return -1; + } + + for (i = 0; i < 2; i++) { + struct io_uring ring; + unsigned ring_flags = 0; + + if (i & 1) + ring_flags |= IORING_SETUP_DEFER_TASKRUN; + + ret = io_uring_queue_init(32, &ring, ring_flags); + if (ret) { + if (ret == -EINVAL) + continue; + fprintf(stderr, "queue init failed: %d\n", ret); + return -1; + } + + ret = test_basic_send(&ring, sp[0], sp[1]); + if (ret) { + fprintf(stderr, "test_basic_send() failed\n"); + return -1; + } + + ret = test_send_faults(sp[0], sp[1]); + if (ret) { + fprintf(stderr, "test_send_faults() failed\n"); + return -1; + } + + ret = test_invalid_zc(sp); + if (ret) { + fprintf(stderr, "test_invalid_zc() failed\n"); + return -1; + } + + ret = test_async_addr(&ring); + if (ret) { + fprintf(stderr, "test_async_addr() failed\n"); + return T_EXIT_FAIL; + } + + io_uring_queue_exit(&ring); + } + + close(sp[0]); + close(sp[1]); + return 0; +} + +int main(int argc, char *argv[]) +{ size_t len; + int ret, i; if (argc > 1) return T_EXIT_SKIP; - page_sz = sysconf(_SC_PAGESIZE); - - /* create TCP IPv6 pair */ - ret = create_socketpair_ip(&addr, &sp[0], &sp[1], true, true, false, true); + ret = probe_zc_support(); if (ret) { - fprintf(stderr, "sock prep failed %d\n", ret); + printf("probe failed\n"); return T_EXIT_FAIL; } + if (!has_sendzc) { + printf("no IORING_OP_SEND_ZC support, skip\n"); + return T_EXIT_SKIP; + } + + page_sz = sysconf(_SC_PAGESIZE); len = LARGE_BUF_SIZE; tx_buffer = aligned_alloc(page_sz, len); @@ -787,69 +898,62 @@ int main(int argc, char *argv[]) } } - ret = io_uring_queue_init(32, &ring, 0); - if (ret) { - fprintf(stderr, "queue init failed: %d\n", ret); - return T_EXIT_FAIL; - } - - ret = test_basic_send(&ring, sp[0], sp[1]); - if (ret == T_EXIT_SKIP) - return ret; - if (ret) { - fprintf(stderr, "test_basic_send() failed\n"); + ret = run_basic_tests(); + if (ret) return T_EXIT_FAIL; - } - has_sendmsg = io_check_zc_sendmsg(&ring); + for (i = 0; i < 2; i++) { + struct io_uring ring; + unsigned ring_flags = 0; - ret = test_send_faults(sp[0], sp[1]); - if (ret) { - fprintf(stderr, "test_send_faults() failed\n"); - return T_EXIT_FAIL; - } + if (i & 1) + ring_flags |= IORING_SETUP_SINGLE_ISSUER | + IORING_SETUP_DEFER_TASKRUN; - ret = test_invalid_zc(sp); - if (ret) { - fprintf(stderr, "test_invalid_zc() failed\n"); - return T_EXIT_FAIL; - } + ret = io_uring_queue_init(32, &ring, ring_flags); + if (ret) { + if (ret == -EINVAL) + continue; + fprintf(stderr, "queue init failed: %d\n", ret); + return -1; + } - close(sp[0]); - close(sp[1]); + ret = t_register_buffers(&ring, buffers_iov, ARRAY_SIZE(buffers_iov)); + if (ret == T_SETUP_SKIP) { + fprintf(stderr, "can't register bufs, skip\n"); + goto out; + } else if (ret != T_SETUP_OK) { + fprintf(stderr, "buffer registration failed %i\n", ret); + return T_EXIT_FAIL; + } - ret = test_async_addr(&ring); - if (ret) { - fprintf(stderr, "test_async_addr() failed\n"); - return T_EXIT_FAIL; - } + if (buffers_iov[BUF_T_HUGETLB].iov_base) { + buffers_iov[BUF_T_HUGETLB].iov_base += 13; + buffers_iov[BUF_T_HUGETLB].iov_len -= 26; + } + if (buffers_iov[BUF_T_LARGE].iov_base) { + buffers_iov[BUF_T_LARGE].iov_base += 13; + buffers_iov[BUF_T_LARGE].iov_len -= 26; + } - ret = t_register_buffers(&ring, buffers_iov, ARRAY_SIZE(buffers_iov)); - if (ret == T_SETUP_SKIP) { - fprintf(stderr, "can't register bufs, skip\n"); - goto out; - } else if (ret != T_SETUP_OK) { - fprintf(stderr, "buffer registration failed %i\n", ret); - return T_EXIT_FAIL; - } + ret = test_inet_send(&ring); + if (ret) { + fprintf(stderr, "test_inet_send() failed (defer_taskrun %i)\n", + ring_flags & IORING_SETUP_DEFER_TASKRUN); + return T_EXIT_FAIL; + } - if (buffers_iov[BUF_T_HUGETLB].iov_base) { - buffers_iov[BUF_T_HUGETLB].iov_base += 13; - buffers_iov[BUF_T_HUGETLB].iov_len -= 26; - } - if (buffers_iov[BUF_T_LARGE].iov_base) { - buffers_iov[BUF_T_LARGE].iov_base += 13; - buffers_iov[BUF_T_LARGE].iov_len -= 26; + if (buffers_iov[BUF_T_HUGETLB].iov_base) { + buffers_iov[BUF_T_HUGETLB].iov_base -= 13; + buffers_iov[BUF_T_HUGETLB].iov_len += 26; + } + if (buffers_iov[BUF_T_LARGE].iov_base) { + buffers_iov[BUF_T_LARGE].iov_base -= 13; + buffers_iov[BUF_T_LARGE].iov_len += 26; + } +out: + io_uring_queue_exit(&ring); } - ret = test_inet_send(&ring); - if (ret) { - fprintf(stderr, "test_inet_send() failed\n"); - return T_EXIT_FAIL; - } -out: - io_uring_queue_exit(&ring); - close(sp[0]); - close(sp[1]); return T_EXIT_PASS; } diff --git a/contrib/libs/liburing/test/send_recv.c b/contrib/libs/liburing/test/send_recv.c index 05b2ffa902..5bf929ba16 100644 --- a/contrib/libs/liburing/test/send_recv.c +++ b/contrib/libs/liburing/test/send_recv.c @@ -269,9 +269,12 @@ static int test_invalid(void) struct io_uring_cqe *cqe; struct io_uring_sqe *sqe; - ret = t_create_ring(8, &ring, 0); - if (ret) + ret = t_create_ring(8, &ring, IORING_SETUP_SUBMIT_ALL); + if (ret) { + if (ret == -EINVAL) + return 0; return ret; + } ret = t_create_socket_pair(fds, true); if (ret) diff --git a/contrib/libs/liburing/test/shutdown.c b/contrib/libs/liburing/test/shutdown.c index e2c59c7666..d7f2f603b0 100644 --- a/contrib/libs/liburing/test/shutdown.c +++ b/contrib/libs/liburing/test/shutdown.c @@ -48,7 +48,8 @@ int main(int argc, char *argv[]) addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(!t_bind_ephemeral_port(recv_s0, &addr)); + ret = t_bind_ephemeral_port(recv_s0, &addr); + assert(!ret); ret = listen(recv_s0, 128); assert(ret != -1); diff --git a/contrib/libs/liburing/test/socket-getsetsock-cmd.c b/contrib/libs/liburing/test/socket-getsetsock-cmd.c new file mode 100644 index 0000000000..275ac66537 --- /dev/null +++ b/contrib/libs/liburing/test/socket-getsetsock-cmd.c @@ -0,0 +1,333 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: Check that {g,s}etsockopt CMD operations on sockets are + * consistent. + * + * The tests basically do the same socket operation using regular system calls + * and io_uring commands, and then compare the results. + */ + +#include <stdio.h> +#include <assert.h> +#include <string.h> +#include <unistd.h> +#include <linux/tcp.h> + +#include "liburing.h" +#include "helpers.h" + +#define USERDATA 0xff42ff +#define MSG "foobarbaz" + +static int no_sock_opt; + +struct fds { + int tx; + int rx; +}; + +static struct fds create_sockets(void) +{ + struct fds retval; + int fd[2]; + + t_create_socket_pair(fd, true); + + retval.tx = fd[0]; + retval.rx = fd[1]; + + return retval; +} + +static struct io_uring create_ring(void) +{ + struct io_uring ring; + int ring_flags = 0; + int err; + + err = io_uring_queue_init(32, &ring, ring_flags); + assert(err == 0); + + return ring; +} + +static int submit_cmd_sqe(struct io_uring *ring, int32_t fd, + int op, int level, int optname, + void *optval, int optlen) +{ + struct io_uring_sqe *sqe; + int err; + + assert(fd > 0); + + sqe = io_uring_get_sqe(ring); + assert(sqe != NULL); + + io_uring_prep_cmd_sock(sqe, op, fd, level, optname, optval, optlen); + sqe->user_data = USERDATA; + + /* Submitting SQE */ + err = io_uring_submit_and_wait(ring, 1); + if (err != 1) + fprintf(stderr, "Failure: io_uring_submit_and_wait returned %d\n", err); + + return err; +} + +static int receive_cqe(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + int err; + + err = io_uring_wait_cqe(ring, &cqe); + assert(err == 0); + assert(cqe->user_data == USERDATA); + io_uring_cqe_seen(ring, cqe); + + /* Return the result of the operation */ + return cqe->res; +} + +/* + * Run getsock operation using SO_RCVBUF using io_uring cmd operation and + * getsockopt(2) and compare the results. + */ +static int run_get_rcvbuf(struct io_uring *ring, struct fds *sockfds) +{ + int sval, uval, ulen, err; + unsigned int slen; + + /* System call values */ + slen = sizeof(sval); + /* io_uring values */ + ulen = sizeof(uval); + + /* get through io_uring cmd */ + err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT, + SOL_SOCKET, SO_RCVBUF, &uval, ulen); + assert(err == 1); + + /* Wait for the CQE */ + err = receive_cqe(ring); + if (err == -EOPNOTSUPP) + return T_EXIT_SKIP; + if (err < 0) { + fprintf(stderr, "Error received. %d\n", err); + return T_EXIT_FAIL; + } + /* The output of CQE->res contains the length */ + ulen = err; + + /* Executes the same operation using system call */ + err = getsockopt(sockfds->rx, SOL_SOCKET, SO_RCVBUF, &sval, &slen); + assert(err == 0); + + /* Make sure that io_uring operation returns the same value as the systemcall */ + assert(ulen == slen); + assert(uval == sval); + + return T_EXIT_PASS; +} + +/* + * Run getsock operation using SO_PEERNAME using io_uring cmd operation + * and getsockopt(2) and compare the results. + */ +static int run_get_peername(struct io_uring *ring, struct fds *sockfds) +{ + struct sockaddr sval, uval = {}; + socklen_t slen = sizeof(sval); + socklen_t ulen = sizeof(uval); + int err; + + /* Get values from the systemcall */ + err = getsockopt(sockfds->tx, SOL_SOCKET, SO_PEERNAME, &sval, &slen); + assert(err == 0); + + /* Getting SO_PEERNAME */ + err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_GETSOCKOPT, + SOL_SOCKET, SO_PEERNAME, &uval, ulen); + assert(err == 1); + + /* Wait for the CQE */ + err = receive_cqe(ring); + if (err == -EOPNOTSUPP || err == -EINVAL) { + no_sock_opt = 1; + return T_EXIT_SKIP; + } + + if (err < 0) { + fprintf(stderr, "%s: Error in the CQE: %d\n", __func__, err); + return T_EXIT_FAIL; + } + + /* The length comes from cqe->res, which is returned from receive_cqe() */ + ulen = err; + + /* Make sure that io_uring operation returns the same values as the systemcall */ + assert(sval.sa_family == uval.sa_family); + assert(slen == ulen); + + return T_EXIT_PASS; +} + +/* + * Run getsockopt tests. Basically comparing io_uring output and systemcall results + */ +static int run_getsockopt_test(struct io_uring *ring, struct fds *sockfds) +{ + int err; + + fprintf(stderr, "Testing getsockopt SO_PEERNAME\n"); + err = run_get_peername(ring, sockfds); + if (err) + return err; + + fprintf(stderr, "Testing getsockopt SO_RCVBUF\n"); + return run_get_rcvbuf(ring, sockfds); +} + +/* + * Given a `val` value, set it in SO_REUSEPORT using io_uring cmd, and read using + * getsockopt(2), and make sure they match. + */ +static int run_setsockopt_reuseport(struct io_uring *ring, struct fds *sockfds, int val) +{ + unsigned int slen, ulen; + int sval, uval = val; + int err; + + slen = sizeof(sval); + ulen = sizeof(uval); + + /* Setting SO_REUSEPORT */ + err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT, + SOL_SOCKET, SO_REUSEPORT, &uval, ulen); + assert(err == 1); + + err = receive_cqe(ring); + if (err == -EOPNOTSUPP) + return T_EXIT_SKIP; + + /* Get values from the systemcall */ + err = getsockopt(sockfds->rx, SOL_SOCKET, SO_REUSEPORT, &sval, &slen); + assert(err == 0); + + /* Make sure the set using io_uring cmd matches what systemcall returns */ + assert(uval == sval); + assert(ulen == slen); + + return T_EXIT_PASS; +} + +/* + * Given a `val` value, set the TCP_USER_TIMEOUT using io_uring and read using + * getsockopt(2). Make sure they match + */ +static int run_setsockopt_usertimeout(struct io_uring *ring, struct fds *sockfds, int val) +{ + int optname = TCP_USER_TIMEOUT; + int level = IPPROTO_TCP; + unsigned int slen, ulen; + int sval, uval, err; + + slen = sizeof(uval); + ulen = sizeof(uval); + + uval = val; + + /* Setting timeout */ + err = submit_cmd_sqe(ring, sockfds->rx, SOCKET_URING_OP_SETSOCKOPT, + level, optname, &uval, ulen); + assert(err == 1); + + err = receive_cqe(ring); + if (err == -EOPNOTSUPP) + return T_EXIT_SKIP; + if (err < 0) { + fprintf(stderr, "%s: Got an error: %d\n", __func__, err); + return T_EXIT_FAIL; + } + + /* Get the value from the systemcall, to make sure it was set */ + err = getsockopt(sockfds->rx, level, optname, &sval, &slen); + assert(err == 0); + assert(uval == sval); + + return T_EXIT_PASS; +} + +/* Test setsockopt() for SOL_SOCKET */ +static int run_setsockopt_test(struct io_uring *ring, struct fds *sockfds) +{ + int err, i; + + fprintf(stderr, "Testing setsockopt SOL_SOCKET/SO_REUSEPORT\n"); + for (i = 0; i <= 1; i++) { + err = run_setsockopt_reuseport(ring, sockfds, i); + if (err) + return err; + } + + fprintf(stderr, "Testing setsockopt IPPROTO_TCP/TCP_FASTOPEN\n"); + for (i = 1; i <= 10; i++) { + err = run_setsockopt_usertimeout(ring, sockfds, i); + if (err) + return err; + } + + return err; +} + +/* Send data through the sockets */ +static void send_data(struct fds *s) +{ + int written_bytes; + /* Send data sing the sockstruct->send */ + written_bytes = write(s->tx, MSG, strlen(MSG)); + assert(written_bytes == strlen(MSG)); +} + +int main(int argc, char *argv[]) +{ + struct fds sockfds; + struct io_uring ring; + int err; + + if (argc > 1) + return T_EXIT_SKIP; + + /* Simply io_uring ring creation */ + ring = create_ring(); + + /* Create sockets */ + sockfds = create_sockets(); + + send_data(&sockfds); + + err = run_getsockopt_test(&ring, &sockfds); + if (err) { + if (err == T_EXIT_SKIP) { + fprintf(stderr, "Skipping tests.\n"); + return T_EXIT_SKIP; + } + fprintf(stderr, "Failed to run test: %d\n", err); + return err; + } + if (no_sock_opt) + return T_EXIT_SKIP; + + err = run_setsockopt_test(&ring, &sockfds); + if (err) { + if (err == T_EXIT_SKIP) { + fprintf(stderr, "Skipping tests.\n"); + return T_EXIT_SKIP; + } + fprintf(stderr, "Failed to run test: %d\n", err); + return err; + } + + io_uring_queue_exit(&ring); + return err; +} diff --git a/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make b/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make new file mode 100644 index 0000000000..3d0c645c78 --- /dev/null +++ b/contrib/libs/liburing/test/socket-getsetsock-cmd.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + socket-getsetsock-cmd.c +) + +END() diff --git a/contrib/libs/liburing/test/socket-io-cmd.c b/contrib/libs/liburing/test/socket-io-cmd.c new file mode 100644 index 0000000000..c4dd3bb8be --- /dev/null +++ b/contrib/libs/liburing/test/socket-io-cmd.c @@ -0,0 +1,238 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Check that CMD operations on sockets are consistent. + */ +#include <stdio.h> +#include <stdlib.h> +#include <stdint.h> +#include <assert.h> +#include <string.h> +#include <unistd.h> +#include <linux/sockios.h> +#include <sys/ioctl.h> + +#include "liburing.h" +#include "helpers.h" + +#define USERDATA 0x1234 +#define MSG "foobarbaz" + +static int no_io_cmd; + +struct fds { + int tx; + int rx; +}; + +/* Create 2 sockets (tx, rx) given the socket type */ +static struct fds create_sockets(bool stream) +{ + struct fds retval; + int fd[2]; + + t_create_socket_pair(fd, stream); + + retval.tx = fd[0]; + retval.rx = fd[1]; + + return retval; +} + +static int create_sqe_and_submit(struct io_uring *ring, int32_t fd, int op) +{ + struct io_uring_sqe *sqe; + int ret; + + assert(fd > 0); + sqe = io_uring_get_sqe(ring); + assert(sqe != NULL); + + io_uring_prep_cmd_sock(sqe, op, fd, 0, 0, NULL, 0); + sqe->user_data = USERDATA; + + /* Submitting SQE */ + ret = io_uring_submit_and_wait(ring, 1); + if (ret <= 0) + return ret; + + return 0; +} + +static int receive_cqe(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + int err; + + err = io_uring_wait_cqe(ring, &cqe); + assert(err == 0); + assert(cqe->user_data == USERDATA); + err = cqe->res; + io_uring_cqe_seen(ring, cqe); + + /* Return the result of the operation */ + return err; +} + +static ssize_t send_data(struct fds *s, char *str) +{ + size_t written_bytes; + + written_bytes = write(s->tx, str, strlen(str)); + assert(written_bytes == strlen(MSG)); + + return written_bytes; +} + +static int run_test(bool stream) +{ + struct fds sockfds; + ssize_t bytes_in, bytes_out; + struct io_uring ring; + size_t written_bytes; + int error; + + /* Create three sockets */ + sockfds = create_sockets(stream); + assert(sockfds.tx > 0); + assert(sockfds.rx > 0); + /* Send data sing the sockfds->send */ + written_bytes = send_data(&sockfds, MSG); + + /* Simply io_uring ring creation */ + error = t_create_ring(1, &ring, 0); + if (error == T_SETUP_SKIP) + return error; + else if (error != T_SETUP_OK) + return T_EXIT_FAIL; + + error = create_sqe_and_submit(&ring, sockfds.rx, + SOCKET_URING_OP_SIOCINQ); + if (error) + return T_EXIT_FAIL; + bytes_in = receive_cqe(&ring); + if (bytes_in < 0) { + if (bytes_in == -EINVAL || bytes_in == -EOPNOTSUPP) { + no_io_cmd = 1; + return T_EXIT_SKIP; + } + fprintf(stderr, "Bad return value %ld\n", (long) bytes_in); + return T_EXIT_FAIL; + } + + error = create_sqe_and_submit(&ring, sockfds.tx, + SOCKET_URING_OP_SIOCOUTQ); + if (error) + return T_EXIT_FAIL; + + bytes_out = receive_cqe(&ring); + if (bytes_in == -ENOTSUP || bytes_out == -ENOTSUP) { + fprintf(stderr, "Skipping tests. -ENOTSUP returned\n"); + return T_EXIT_SKIP; + } + + /* + * Assert the number of written bytes are either in the socket buffer + * or on the receive side + */ + if (bytes_in + bytes_out != written_bytes) { + fprintf(stderr, "values does not match: %zu+%zu != %zu\n", + bytes_in, bytes_out, written_bytes); + return T_EXIT_FAIL; + } + + io_uring_queue_exit(&ring); + + return T_EXIT_PASS; +} + +/* + * Make sure that siocoutq and siocinq returns the same value + * using ioctl(2) and uring commands for raw sockets + */ +static int run_test_raw(void) +{ + int ioctl_siocoutq, ioctl_siocinq; + int uring_siocoutq, uring_siocinq; + struct io_uring ring; + int retry = 0, sock, error; + + sock = socket(PF_INET, SOCK_RAW, IPPROTO_TCP); + if (sock == -1) { + /* You need root to create raw socket */ + perror("Not able to create a raw socket"); + return T_EXIT_SKIP; + } + + /* Get the same operation using uring cmd */ + error = t_create_ring(1, &ring, 0); + if (error == T_SETUP_SKIP) + return error; + else if (error != T_SETUP_OK) + return T_EXIT_FAIL; + +again: + /* Simple SIOCOUTQ using ioctl */ + error = ioctl(sock, SIOCOUTQ, &ioctl_siocoutq); + if (error < 0) { + fprintf(stderr, "Failed to run ioctl(SIOCOUTQ): %d\n", error); + return T_EXIT_FAIL; + } + + error = ioctl(sock, SIOCINQ, &ioctl_siocinq); + if (error < 0) { + fprintf(stderr, "Failed to run ioctl(SIOCINQ): %d\n", error); + return T_EXIT_FAIL; + } + + create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCOUTQ); + uring_siocoutq = receive_cqe(&ring); + + create_sqe_and_submit(&ring, sock, SOCKET_URING_OP_SIOCINQ); + uring_siocinq = receive_cqe(&ring); + + /* Compare that both values (ioctl and uring CMD) should be similar */ + if (uring_siocoutq != ioctl_siocoutq) { + if (!retry) { + retry = 1; + goto again; + } + fprintf(stderr, "values does not match: %d != %d\n", + uring_siocoutq, ioctl_siocoutq); + return T_EXIT_FAIL; + } + if (uring_siocinq != ioctl_siocinq) { + if (!retry) { + retry = 1; + goto again; + } + fprintf(stderr, "values does not match: %d != %d\n", + uring_siocinq, ioctl_siocinq); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + int err; + + if (argc > 1) + return 0; + + /* Test SOCK_STREAM */ + err = run_test(true); + if (err) + return err; + if (no_io_cmd) + return T_EXIT_SKIP; + + /* Test SOCK_DGRAM */ + err = run_test(false); + if (err) + return err; + + /* Test raw sockets */ + return run_test_raw(); +} diff --git a/contrib/libs/liburing/test/socket-io-cmd.t/ya.make b/contrib/libs/liburing/test/socket-io-cmd.t/ya.make new file mode 100644 index 0000000000..b03d5f0ea0 --- /dev/null +++ b/contrib/libs/liburing/test/socket-io-cmd.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + socket-io-cmd.c +) + +END() diff --git a/contrib/libs/liburing/test/socket-rw-eagain.c b/contrib/libs/liburing/test/socket-rw-eagain.c index e762da531f..9fe9517e56 100644 --- a/contrib/libs/liburing/test/socket-rw-eagain.c +++ b/contrib/libs/liburing/test/socket-rw-eagain.c @@ -43,7 +43,8 @@ int main(int argc, char *argv[]) addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(!t_bind_ephemeral_port(recv_s0, &addr)); + ret = t_bind_ephemeral_port(recv_s0, &addr); + assert(!ret); ret = listen(recv_s0, 128); assert(ret != -1); diff --git a/contrib/libs/liburing/test/socket-rw-offset.c b/contrib/libs/liburing/test/socket-rw-offset.c index 26173876ef..21681bfaab 100644 --- a/contrib/libs/liburing/test/socket-rw-offset.c +++ b/contrib/libs/liburing/test/socket-rw-offset.c @@ -45,7 +45,8 @@ int main(int argc, char *argv[]) addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(!t_bind_ephemeral_port(recv_s0, &addr)); + ret = t_bind_ephemeral_port(recv_s0, &addr); + assert(!ret); ret = listen(recv_s0, 128); assert(ret != -1); diff --git a/contrib/libs/liburing/test/socket-rw.c b/contrib/libs/liburing/test/socket-rw.c index 5546714e65..b50e0427b3 100644 --- a/contrib/libs/liburing/test/socket-rw.c +++ b/contrib/libs/liburing/test/socket-rw.c @@ -45,7 +45,8 @@ int main(int argc, char *argv[]) addr.sin_family = AF_INET; addr.sin_addr.s_addr = inet_addr("127.0.0.1"); - assert(!t_bind_ephemeral_port(recv_s0, &addr)); + ret = t_bind_ephemeral_port(recv_s0, &addr); + assert(!ret); ret = listen(recv_s0, 128); assert(ret != -1); diff --git a/contrib/libs/liburing/test/sqpoll-cancel-hang.c b/contrib/libs/liburing/test/sqpoll-cancel-hang.c deleted file mode 100644 index 302a662bcd..0000000000 --- a/contrib/libs/liburing/test/sqpoll-cancel-hang.c +++ /dev/null @@ -1,169 +0,0 @@ -#include "../config-host.h" -/* SPDX-License-Identifier: MIT */ -#include <fcntl.h> -#include <signal.h> -#include <stdint.h> -#include <stdlib.h> -#include <string.h> -#include <sys/mman.h> -#include <sys/wait.h> -#include <time.h> -#include <unistd.h> -#include "liburing.h" -#include "helpers.h" -#include "../src/syscall.h" - -/* - * This syzbot test is known broken on some archs, just allow the ones that - * are regularly tested. - */ -#if defined(__i386__) || defined(__x86_64__) || defined(__arm__) || \ - defined(__aarch64__) - -static uint64_t current_time_ms(void) -{ - struct timespec ts; - if (clock_gettime(CLOCK_MONOTONIC, &ts)) - exit(1); - return (uint64_t)ts.tv_sec * 1000 + (uint64_t)ts.tv_nsec / 1000000; -} - -#define SIZEOF_IO_URING_SQE 64 -#define SIZEOF_IO_URING_CQE 16 -#define SQ_TAIL_OFFSET 64 -#define SQ_RING_MASK_OFFSET 256 -#define SQ_RING_ENTRIES_OFFSET 264 -#define CQ_RING_ENTRIES_OFFSET 268 -#define CQ_CQES_OFFSET 320 - -#define IORING_OFF_SQES 0x10000000ULL - -static void kill_and_wait(int pid, int* status) -{ - kill(-pid, SIGKILL); - kill(pid, SIGKILL); - while (waitpid(-1, status, __WALL) != pid) { - } -} - -#define WAIT_FLAGS __WALL - -static uint64_t r[3] = {0xffffffffffffffff, 0x0, 0x0}; - -static long syz_io_uring_setup(volatile long a0, volatile long a1, -volatile long a2, volatile long a3, volatile long a4, volatile long -a5) -{ - uint32_t entries = (uint32_t)a0; - struct io_uring_params* setup_params = (struct io_uring_params*)a1; - void* vma1 = (void*)a2; - void* vma2 = (void*)a3; - void** ring_ptr_out = (void**)a4; - void** sqes_ptr_out = (void**)a5; - uint32_t fd_io_uring = __sys_io_uring_setup(entries, setup_params); - uint32_t sq_ring_sz = setup_params->sq_off.array + -setup_params->sq_entries * sizeof(uint32_t); - uint32_t cq_ring_sz = setup_params->cq_off.cqes + -setup_params->cq_entries * SIZEOF_IO_URING_CQE; - uint32_t ring_sz = sq_ring_sz > cq_ring_sz ? sq_ring_sz : cq_ring_sz; - *ring_ptr_out = mmap(vma1, ring_sz, PROT_READ | PROT_WRITE, -MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, -IORING_OFF_SQ_RING); - uint32_t sqes_sz = setup_params->sq_entries * SIZEOF_IO_URING_SQE; - *sqes_ptr_out = mmap(vma2, sqes_sz, PROT_READ | PROT_WRITE, -MAP_SHARED | MAP_POPULATE | MAP_FIXED, fd_io_uring, IORING_OFF_SQES); - return fd_io_uring; -} - -static long syz_io_uring_submit(volatile long a0, volatile long a1, -volatile long a2, volatile long a3) -{ - char* ring_ptr = (char*)a0; - char* sqes_ptr = (char*)a1; - char* sqe = (char*)a2; - uint32_t sqes_index = (uint32_t)a3; - uint32_t sq_ring_entries = *(uint32_t*)(ring_ptr + SQ_RING_ENTRIES_OFFSET); - uint32_t cq_ring_entries = *(uint32_t*)(ring_ptr + CQ_RING_ENTRIES_OFFSET); - uint32_t sq_array_off = (CQ_CQES_OFFSET + cq_ring_entries * -SIZEOF_IO_URING_CQE + 63) & ~63; - if (sq_ring_entries) - sqes_index %= sq_ring_entries; - char* sqe_dest = sqes_ptr + sqes_index * SIZEOF_IO_URING_SQE; - memcpy(sqe_dest, sqe, SIZEOF_IO_URING_SQE); - uint32_t sq_ring_mask = *(uint32_t*)(ring_ptr + SQ_RING_MASK_OFFSET); - uint32_t* sq_tail_ptr = (uint32_t*)(ring_ptr + SQ_TAIL_OFFSET); - uint32_t sq_tail = *sq_tail_ptr & sq_ring_mask; - uint32_t sq_tail_next = *sq_tail_ptr + 1; - uint32_t* sq_array = (uint32_t*)(ring_ptr + sq_array_off); - *(sq_array + sq_tail) = sqes_index; - __atomic_store_n(sq_tail_ptr, sq_tail_next, __ATOMIC_RELEASE); - return 0; -} - - -static void trigger_bug(void) -{ - intptr_t res = 0; - *(uint32_t*)0x20000204 = 0; - *(uint32_t*)0x20000208 = 2; - *(uint32_t*)0x2000020c = 0; - *(uint32_t*)0x20000210 = 0; - *(uint32_t*)0x20000218 = -1; - memset((void*)0x2000021c, 0, 12); - res = -1; - res = syz_io_uring_setup(0x7987, 0x20000200, 0x20400000, 0x20ffd000, 0x200000c0, 0x200001c0); - if (res != -1) { - r[0] = res; - r[1] = *(uint64_t*)0x200000c0; - r[2] = *(uint64_t*)0x200001c0; - } - *(uint8_t*)0x20000180 = 0xb; - *(uint8_t*)0x20000181 = 1; - *(uint16_t*)0x20000182 = 0; - *(uint32_t*)0x20000184 = 0; - *(uint64_t*)0x20000188 = 4; - *(uint64_t*)0x20000190 = 0x20000140; - *(uint64_t*)0x20000140 = 0x77359400; - *(uint64_t*)0x20000148 = 0; - *(uint32_t*)0x20000198 = 1; - *(uint32_t*)0x2000019c = 0; - *(uint64_t*)0x200001a0 = 0; - *(uint16_t*)0x200001a8 = 0; - *(uint16_t*)0x200001aa = 0; - memset((void*)0x200001ac, 0, 20); - syz_io_uring_submit(r[1], r[2], 0x20000180, 1); - *(uint32_t*)0x20000544 = 0; - *(uint32_t*)0x20000548 = 0x36; - *(uint32_t*)0x2000054c = 0; - *(uint32_t*)0x20000550 = 0; - *(uint32_t*)0x20000558 = r[0]; - memset((void*)0x2000055c, 0, 12); - -} -int main(void) -{ - mmap((void *)0x20000000ul, 0x1000000ul, 7ul, MAP_ANON|MAP_PRIVATE, -1, 0ul); - int pid = fork(); - if (pid < 0) - exit(1); - if (pid == 0) { - trigger_bug(); - exit(0); - } - int status = 0; - uint64_t start = current_time_ms(); - for (;;) { - if (current_time_ms() - start < 1000) { - continue; - } - kill_and_wait(pid, &status); - break; - } - return 0; -} -#else -int main(void) -{ - return T_EXIT_SKIP; -} -#endif diff --git a/contrib/libs/liburing/test/truncate.c b/contrib/libs/liburing/test/truncate.c new file mode 100644 index 0000000000..395d09e70d --- /dev/null +++ b/contrib/libs/liburing/test/truncate.c @@ -0,0 +1,187 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: run various truncate tests + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/stat.h> +#include <sys/ioctl.h> + +#include "liburing.h" +#include "helpers.h" + +#define TWO_GIG_SIZE ((loff_t)2 * 1024 * 1024 * 1024) +#define ONE_GIG_SIZE ((loff_t)1024 * 1024 * 1024) +#define HALF_GIG_SIZE ((loff_t)512 * 1024 * 1024) + +static int test_truncate(struct io_uring *ring, int fd) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret = -1; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + fprintf(stderr, "get sqe failed\n"); + return T_EXIT_FAIL; + } + + memset(sqe, 0, sizeof(*sqe)); + + io_uring_prep_rw(IORING_OP_FTRUNCATE, sqe, fd, "fail", 0, 4); + + ret = io_uring_submit(ring); + if (ret <= 0) { + fprintf(stderr, "sqe submit failed: %d\n", ret); + return T_EXIT_FAIL; + } + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + fprintf(stderr, "wait completion %d\n", ret); + return T_EXIT_FAIL; + } + ret = cqe->res; + io_uring_cqe_seen(ring, cqe); + if (ret == -EINVAL) + return T_EXIT_PASS; + + fprintf(stderr, "unexpected truncate res %d\n", ret); + return T_EXIT_FAIL; +} + +static int test_ftruncate(struct io_uring *ring, int fd, loff_t len) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + fprintf(stderr, "get sqe failed\n"); + goto err; + } + + memset(sqe, 0, sizeof(*sqe)); + + io_uring_prep_ftruncate(sqe, fd, len); + + ret = io_uring_submit(ring); + if (ret <= 0) { + fprintf(stderr, "sqe submit failed: %d\n", ret); + goto err; + } + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + fprintf(stderr, "wait completion %d\n", ret); + goto err; + } + ret = cqe->res; + io_uring_cqe_seen(ring, cqe); + return ret; +err: + return 1; +} + +static int get_file_size(int fd, loff_t *size) +{ + struct stat st; + + if (fstat(fd, &st) < 0) { + perror("fstat"); + return -1; + } + if (S_ISREG(st.st_mode)) { + *size = st.st_size; + return 0; + } else if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) { + perror("ioctl"); + return -1; + } + + *size = bytes; + return 0; + } + + return -1; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + char path[32] = ".truncate.XXXXXX"; + int ret; + int fd; + int i; + loff_t size; + loff_t test_sizes[3]; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = io_uring_queue_init(1, &ring, 0); + if (ret) { + fprintf(stderr, "ring setup failed: %d\n", ret); + return T_EXIT_FAIL; + } + + fd = mkostemp(path, O_WRONLY | O_CREAT | O_TRUNC); + if (fd < 0) { + perror("mkostemp"); + return T_EXIT_FAIL; + } + + test_sizes[0] = TWO_GIG_SIZE; + test_sizes[1] = ONE_GIG_SIZE; + test_sizes[2] = HALF_GIG_SIZE; + + for (i = 0; i < 3; i++) { + ret = test_ftruncate(&ring, fd, test_sizes[i]); + if (ret < 0) { + if (ret == -EBADF || ret == -EINVAL) { + if (i == 0) { + fprintf(stdout, "Ftruncate not supported, skipping\n"); + ret = T_EXIT_SKIP; + goto out; + } + goto err; + } + fprintf(stderr, "ftruncate: %s\n", strerror(-ret)); + goto err; + } else if (ret) { + fprintf(stderr, "unexpected cqe->res %d\n", ret); + goto err; + } + if (get_file_size(fd, &size)) + goto err; + if (size != test_sizes[i]) { + fprintf(stderr, "fail %d size=%llu, %llu\n", i, + (unsigned long long) size, + (unsigned long long) test_sizes[i]); + goto err; + } + } + + ret = test_truncate(&ring, fd); + if (ret != T_EXIT_PASS) + goto err; + +out: + unlink(path); + close(fd); + return T_EXIT_PASS; +err: + unlink(path); + close(fd); + return T_EXIT_FAIL; +} diff --git a/contrib/libs/liburing/test/truncate.t/ya.make b/contrib/libs/liburing/test/truncate.t/ya.make new file mode 100644 index 0000000000..e360ea5773 --- /dev/null +++ b/contrib/libs/liburing/test/truncate.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + truncate.c +) + +END() diff --git a/contrib/libs/liburing/test/version.c b/contrib/libs/liburing/test/version.c index b7fbd8420b..9ffe7a6c49 100644 --- a/contrib/libs/liburing/test/version.c +++ b/contrib/libs/liburing/test/version.c @@ -9,7 +9,7 @@ int main(int argc, char *argv[]) { - if (!IO_URING_CHECK_VERSION(io_uring_major_version(), io_uring_minor_version())) + if (IO_URING_CHECK_VERSION(io_uring_major_version(), io_uring_minor_version())) return T_EXIT_FAIL; if (io_uring_major_version() != IO_URING_VERSION_MAJOR) @@ -18,7 +18,7 @@ int main(int argc, char *argv[]) if (io_uring_minor_version() != IO_URING_VERSION_MINOR) return T_EXIT_FAIL; -#if !IO_URING_CHECK_VERSION(IO_URING_VERSION_MAJOR, IO_URING_VERSION_MINOR) +#if IO_URING_CHECK_VERSION(IO_URING_VERSION_MAJOR, IO_URING_VERSION_MINOR) return T_EXIT_FAIL; #endif diff --git a/contrib/libs/liburing/test/waitid.c b/contrib/libs/liburing/test/waitid.c new file mode 100644 index 0000000000..f7ebe55ac7 --- /dev/null +++ b/contrib/libs/liburing/test/waitid.c @@ -0,0 +1,374 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test waitid functionality + */ +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> + +#include "liburing.h" +#include "helpers.h" + +static bool no_waitid; + +static void child(long usleep_time) +{ + if (usleep_time) + usleep(usleep_time); + exit(0); +} + +/* + * Test linked timeout with child not exiting in time + */ +static int test_noexit(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct __kernel_timespec ts; + siginfo_t si; + pid_t pid; + int ret, i; + + pid = fork(); + if (!pid) { + child(200000); + exit(0); + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0); + sqe->flags |= IOSQE_IO_LINK; + sqe->user_data = 1; + + ts.tv_sec = 0; + ts.tv_nsec = 100 * 1000 * 1000ULL; + sqe = io_uring_get_sqe(ring); + io_uring_prep_link_timeout(sqe, &ts, 0); + sqe->user_data = 2; + + io_uring_submit(ring); + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->user_data == 2 && cqe->res != 1) { + fprintf(stderr, "timeout res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (cqe->user_data == 1 && cqe->res != -ECANCELED) { + fprintf(stderr, "waitid res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + } + + return T_EXIT_PASS; +} + +/* + * Test one child exiting, but not the one we were looking for + */ +static int test_double(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + siginfo_t si; + pid_t p1, p2; + int ret; + + /* p1 will exit shortly */ + p1 = fork(); + if (!p1) { + child(100000); + exit(0); + } + + /* p2 will linger */ + p2 = fork(); + if (!p2) { + child(200000); + exit(0); + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_PID, p2, &si, WEXITED, 0); + + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + + if (cqe->res < 0) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (si.si_pid != p2) { + fprintf(stderr, "expected pid %d, got %d\n", p2, si.si_pid); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(ring, cqe); + return T_EXIT_PASS; +} + +/* + * Test reaping of an already exited task + */ +static int test_ready(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + siginfo_t si; + pid_t pid; + int ret; + + pid = fork(); + if (!pid) { + child(0); + exit(0); + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0); + + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + + if (cqe->res < 0) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (si.si_pid != pid) { + fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(ring, cqe); + return T_EXIT_PASS; +} + +/* + * Test cancelation of pending waitid + */ +static int test_cancel(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, i; + pid_t pid; + + pid = fork(); + if (!pid) { + child(20000); + exit(0); + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_PID, pid, NULL, WEXITED, 0); + sqe->user_data = 1; + + io_uring_submit(ring); + + sqe = io_uring_get_sqe(ring); + io_uring_prep_cancel64(sqe, 1, 0); + sqe->user_data = 2; + + io_uring_submit(ring); + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->user_data == 1 && cqe->res != -ECANCELED) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (cqe->user_data == 2 && cqe->res != 1) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + } + + return T_EXIT_PASS; +} + +/* + * Test cancelation of pending waitid, with expected races that either + * waitid trigger or cancelation will win. + */ +static int test_cancel_race(struct io_uring *ring, int async) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + int ret, i; + pid_t pid; + + for (i = 0; i < 10; i++) { + pid = fork(); + if (!pid) { + child(getpid() & 1); + exit(0); + } + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_ALL, -1, NULL, WEXITED, 0); + if (async) + sqe->flags |= IOSQE_ASYNC; + sqe->user_data = 1; + + io_uring_submit(ring); + + sqe = io_uring_get_sqe(ring); + io_uring_prep_cancel64(sqe, 1, 0); + sqe->user_data = 2; + + usleep(1); + + io_uring_submit(ring); + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + if (cqe->user_data == 1 && !(cqe->res == -ECANCELED || + cqe->res == 0)) { + fprintf(stderr, "cqe1 res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (cqe->user_data == 2 && + !(cqe->res == 1 || cqe->res == 0 || cqe->res == -ENOENT || + cqe->res == -EALREADY)) { + fprintf(stderr, "cqe2 res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + io_uring_cqe_seen(ring, cqe); + } + + return T_EXIT_PASS; +} + +/* + * Test basic reap of child exit + */ +static int test(struct io_uring *ring) +{ + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + siginfo_t si; + pid_t pid; + int ret; + + pid = fork(); + if (!pid) { + child(100); + exit(0); + } + + sqe = io_uring_get_sqe(ring); + io_uring_prep_waitid(sqe, P_PID, pid, &si, WEXITED, 0); + + io_uring_submit(ring); + + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "cqe wait: %d\n", ret); + return T_EXIT_FAIL; + } + + /* no waitid support */ + if (cqe->res == -EINVAL) { + no_waitid = true; + return T_EXIT_SKIP; + } + if (cqe->res < 0) { + fprintf(stderr, "cqe res: %d\n", cqe->res); + return T_EXIT_FAIL; + } + if (si.si_pid != pid) { + fprintf(stderr, "expected pid %d, got %d\n", pid, si.si_pid); + return T_EXIT_FAIL; + } + + io_uring_cqe_seen(ring, cqe); + return T_EXIT_PASS; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int ret, i; + + if (argc > 1) + return T_EXIT_SKIP; + + io_uring_queue_init(8, &ring, 0); + + ret = test(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test failed\n"); + return T_EXIT_FAIL; + } + if (no_waitid) + return T_EXIT_SKIP; + + ret = test_noexit(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_noexit failed\n"); + return T_EXIT_FAIL; + } + + ret = test_noexit(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_noexit failed\n"); + return T_EXIT_FAIL; + } + + ret = test_double(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_double failed\n"); + return T_EXIT_FAIL; + } + + ret = test_ready(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_ready failed\n"); + return T_EXIT_FAIL; + } + + ret = test_cancel(&ring); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_cancel failed\n"); + return T_EXIT_FAIL; + } + + for (i = 0; i < 1000; i++) { + ret = test_cancel_race(&ring, i & 1); + if (ret == T_EXIT_FAIL) { + fprintf(stderr, "test_cancel_race failed\n"); + return T_EXIT_FAIL; + } + } + + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/waitid.t/ya.make b/contrib/libs/liburing/test/waitid.t/ya.make new file mode 100644 index 0000000000..f7f2ef1773 --- /dev/null +++ b/contrib/libs/liburing/test/waitid.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + waitid.c +) + +END() diff --git a/contrib/libs/liburing/test/wq-aff.c b/contrib/libs/liburing/test/wq-aff.c new file mode 100644 index 0000000000..0548d6a698 --- /dev/null +++ b/contrib/libs/liburing/test/wq-aff.c @@ -0,0 +1,147 @@ +#include "../config-host.h" +/* SPDX-License-Identifier: MIT */ +/* + * Description: test that io-wq affinity is correctly set for SQPOLL + */ +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <stdlib.h> +#include <string.h> + +#include "liburing.h" +#include "helpers.h" + +#define IOWQ_CPU 0 +#define SQPOLL_CPU 1 + +static int verify_comm(pid_t pid, const char *name, int cpu) +{ + char comm[64], buf[64]; + cpu_set_t set; + int fd, ret; + + sprintf(comm, "/proc/%d/comm", pid); + fd = open(comm, O_RDONLY); + if (fd < 0) { + perror("open"); + return T_EXIT_SKIP; + } + + ret = read(fd, buf, sizeof(buf)); + if (ret < 0) { + close(fd); + return T_EXIT_SKIP; + } + + if (strncmp(buf, name, strlen(name) - 1)) { + close(fd); + return T_EXIT_SKIP; + } + + close(fd); + + ret = sched_getaffinity(pid, sizeof(set), &set); + if (ret < 0) { + perror("sched_getaffinity"); + return T_EXIT_SKIP; + } + + if (CPU_COUNT(&set) != 1) { + fprintf(stderr, "More than one CPU set in mask\n"); + return T_EXIT_FAIL; + } + if (!CPU_ISSET(cpu, &set)) { + fprintf(stderr, "Wrong CPU set in mask\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} + +static int verify_affinity(pid_t pid, int sqpoll) +{ + pid_t wq_pid, sqpoll_pid = -1; + char name[64]; + int ret; + + wq_pid = pid + 2; + if (sqpoll) + sqpoll_pid = pid + 1; + + /* verify we had the pids right */ + sprintf(name, "iou-wrk-%d", pid); + ret = verify_comm(wq_pid, name, IOWQ_CPU); + if (ret != T_EXIT_PASS) + return ret; + + if (sqpoll_pid != -1) { + sprintf(name, "iou-sqp-%d", pid); + ret = verify_comm(sqpoll_pid, name, SQPOLL_CPU); + if (ret != T_EXIT_PASS) + return ret; + } + + return T_EXIT_PASS; +} + +static int test(int sqpoll) +{ + struct io_uring_params p = { }; + struct io_uring ring; + struct io_uring_sqe *sqe; + char buf[64]; + int fds[2], ret; + cpu_set_t set; + + if (sqpoll) { + p.flags = IORING_SETUP_SQPOLL | IORING_SETUP_SQ_AFF; + p.sq_thread_cpu = SQPOLL_CPU; + } + + io_uring_queue_init_params(8, &ring, &p); + + CPU_ZERO(&set); + CPU_SET(IOWQ_CPU, &set); + + ret = io_uring_register_iowq_aff(&ring, sizeof(set), &set); + if (ret) { + fprintf(stderr, "register aff: %d\n", ret); + return T_EXIT_FAIL; + } + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0); + sqe->flags |= IOSQE_ASYNC; + + io_uring_submit(&ring); + + usleep(10000); + + ret = verify_affinity(getpid(), sqpoll); + io_uring_queue_exit(&ring); + return ret; +} + +int main(int argc, char *argv[]) +{ + int ret; + + if (argc > 1) + return T_EXIT_SKIP; + + ret = test(1); + if (ret == T_EXIT_SKIP) { + return T_EXIT_SKIP; + } else if (ret != T_EXIT_PASS) { + fprintf(stderr, "test sqpoll failed\n"); + return T_EXIT_FAIL; + } + + return T_EXIT_PASS; +} diff --git a/contrib/libs/liburing/test/wq-aff.t/ya.make b/contrib/libs/liburing/test/wq-aff.t/ya.make new file mode 100644 index 0000000000..78b9a860e2 --- /dev/null +++ b/contrib/libs/liburing/test/wq-aff.t/ya.make @@ -0,0 +1,33 @@ +# Generated by devtools/yamaker. + +PROGRAM() + +WITHOUT_LICENSE_TEXTS() + +LICENSE(MIT) + +PEERDIR( + contrib/libs/liburing +) + +ADDINCL( + contrib/libs/liburing/src/include +) + +NO_COMPILER_WARNINGS() + +NO_RUNTIME() + +CFLAGS( + -DLIBURING_BUILD_TEST + -D__SANE_USERSPACE_TYPES__ +) + +SRCDIR(contrib/libs/liburing/test) + +SRCS( + helpers.c + wq-aff.c +) + +END() diff --git a/contrib/libs/liburing/test/xattr.c b/contrib/libs/liburing/test/xattr.c index 0c5870d33d..fe2c9ee9ad 100644 --- a/contrib/libs/liburing/test/xattr.c +++ b/contrib/libs/liburing/test/xattr.c @@ -295,7 +295,6 @@ Exit: /* Test driver for failure cases of fsetxattr and fgetxattr. */ static int test_failure_fxattr(void) { - int rc = 0; struct io_uring ring; char value[XATTR_SIZE]; @@ -314,31 +313,36 @@ static int test_failure_fxattr(void) } /* Test writing attributes. */ - assert(io_uring_fsetxattr(&ring, -1, KEY1, VALUE1, strlen(VALUE1), 0) < 0); - assert(io_uring_fsetxattr(&ring, fd, NULL, VALUE1, strlen(VALUE1), 0) < 0); - assert(io_uring_fsetxattr(&ring, fd, KEY1, NULL, strlen(VALUE1), 0) < 0); - assert(io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, 0, 0) == 0); - assert(io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, -1, 0) < 0); + if (io_uring_fsetxattr(&ring, -1, KEY1, VALUE1, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_fsetxattr(&ring, fd, NULL, VALUE1, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_fsetxattr(&ring, fd, KEY1, NULL, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, 0, 0) != 0) + return 1; + if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, -1, 0) >= 0) + return 1; /* Test reading attributes. */ - assert(io_uring_fgetxattr(&ring, -1, KEY1, value, XATTR_SIZE) < 0); - assert(io_uring_fgetxattr(&ring, fd, NULL, value, XATTR_SIZE) < 0); - assert(io_uring_fgetxattr(&ring, fd, KEY1, value, 0) == 0); + if (io_uring_fgetxattr(&ring, -1, KEY1, value, XATTR_SIZE) >= 0) + return 1; + if (io_uring_fgetxattr(&ring, fd, NULL, value, XATTR_SIZE) >= 0) + return 1; + if (io_uring_fgetxattr(&ring, fd, KEY1, value, 0) != 0) + return 1; /* Cleanup. */ close(fd); unlink(FILENAME); - io_uring_queue_exit(&ring); - - return rc; + return 0; } /* Test driver for failure cases for setxattr and getxattr. */ static int test_failure_xattr(void) { - int rc = 0; struct io_uring ring; char value[XATTR_SIZE]; @@ -353,24 +357,33 @@ static int test_failure_xattr(void) t_create_file(FILENAME, 0); /* Test writing attributes. */ - assert(io_uring_setxattr(&ring, "complete garbage", KEY1, VALUE1, strlen(VALUE1), 0) < 0); - assert(io_uring_setxattr(&ring, NULL, KEY1, VALUE1, strlen(VALUE1), 0) < 0); - assert(io_uring_setxattr(&ring, FILENAME, NULL, VALUE1, strlen(VALUE1), 0) < 0); - assert(io_uring_setxattr(&ring, FILENAME, KEY1, NULL, strlen(VALUE1), 0) < 0); - assert(io_uring_setxattr(&ring, FILENAME, KEY1, VALUE1, 0, 0) == 0); + if (io_uring_setxattr(&ring, "complete garbage", KEY1, VALUE1, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_setxattr(&ring, NULL, KEY1, VALUE1, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_setxattr(&ring, FILENAME, NULL, VALUE1, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_setxattr(&ring, FILENAME, KEY1, NULL, strlen(VALUE1), 0) >= 0) + return 1; + if (io_uring_setxattr(&ring, FILENAME, KEY1, VALUE1, 0, 0) != 0) + return 1; /* Test reading attributes. */ - assert(io_uring_getxattr(&ring, "complete garbage", KEY1, value, XATTR_SIZE) < 0); - assert(io_uring_getxattr(&ring, NULL, KEY1, value, XATTR_SIZE) < 0); - assert(io_uring_getxattr(&ring, FILENAME, NULL, value, XATTR_SIZE) < 0); - assert(io_uring_getxattr(&ring, FILENAME, KEY1, NULL, XATTR_SIZE) == 0); - assert(io_uring_getxattr(&ring, FILENAME, KEY1, value, 0) == 0); + if (io_uring_getxattr(&ring, "complete garbage", KEY1, value, XATTR_SIZE) >= 0) + return 1; + if (io_uring_getxattr(&ring, NULL, KEY1, value, XATTR_SIZE) >= 0) + return 1; + if (io_uring_getxattr(&ring, FILENAME, NULL, value, XATTR_SIZE) >= 0) + return 1; + if (io_uring_getxattr(&ring, FILENAME, KEY1, NULL, XATTR_SIZE) != 0) + return 1; + if (io_uring_getxattr(&ring, FILENAME, KEY1, value, 0) != 0) + return 1; /* Cleanup. */ io_uring_queue_exit(&ring); unlink(FILENAME); - - return rc; + return 0; } /* Test for invalid SQE, this will cause a segmentation fault if enabled. */ |