aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/libs/liburing/test
diff options
context:
space:
mode:
authorilnaz <ilnaz@ydb.tech>2022-12-13 16:01:38 +0300
committerilnaz <ilnaz@ydb.tech>2022-12-13 16:01:38 +0300
commitf2bea70bea01921ec43846224d100f2c70dd5719 (patch)
treeeead917572063b63adc1c9a76284c8fbd10f25a3 /contrib/libs/liburing/test
parent1ab9ee3dfe0ab4023a3a57bf55de31dff3eac908 (diff)
downloadydb-f2bea70bea01921ec43846224d100f2c70dd5719.tar.gz
Add cross-link
Diffstat (limited to 'contrib/libs/liburing/test')
-rw-r--r--contrib/libs/liburing/test/232c93d07b74.c306
-rw-r--r--contrib/libs/liburing/test/35fa71a030ca.c330
-rw-r--r--contrib/libs/liburing/test/500f9fbadef8.c90
-rw-r--r--contrib/libs/liburing/test/7ad0e4b2f83c.c95
-rw-r--r--contrib/libs/liburing/test/8a9973408177.c108
-rw-r--r--contrib/libs/liburing/test/917257daa0fe.c55
-rw-r--r--contrib/libs/liburing/test/a0908ae19763.c60
-rw-r--r--contrib/libs/liburing/test/a4c0b3decb33.c182
-rw-r--r--contrib/libs/liburing/test/accept-link.c256
-rw-r--r--contrib/libs/liburing/test/accept-reuse.c166
-rw-r--r--contrib/libs/liburing/test/accept-test.c84
-rw-r--r--contrib/libs/liburing/test/accept.c900
-rw-r--r--contrib/libs/liburing/test/across-fork.c285
-rw-r--r--contrib/libs/liburing/test/b19062a56726.c55
-rw-r--r--contrib/libs/liburing/test/b5837bd5311d.c79
-rw-r--r--contrib/libs/liburing/test/buf-ring.c421
-rw-r--r--contrib/libs/liburing/test/ce593a6c480a.c140
-rw-r--r--contrib/libs/liburing/test/close-opath.c123
-rw-r--r--contrib/libs/liburing/test/connect.c400
-rw-r--r--contrib/libs/liburing/test/cq-full.c98
-rw-r--r--contrib/libs/liburing/test/cq-overflow.c525
-rw-r--r--contrib/libs/liburing/test/cq-peek-batch.c104
-rw-r--r--contrib/libs/liburing/test/cq-ready.c96
-rw-r--r--contrib/libs/liburing/test/cq-size.c66
-rw-r--r--contrib/libs/liburing/test/d4ae271dfaae.c97
-rw-r--r--contrib/libs/liburing/test/d77a67ed5f27.c66
-rw-r--r--contrib/libs/liburing/test/defer-taskrun.c337
-rw-r--r--contrib/libs/liburing/test/defer.c320
-rw-r--r--contrib/libs/liburing/test/double-poll-crash.c196
-rw-r--r--contrib/libs/liburing/test/drop-submit.c95
-rw-r--r--contrib/libs/liburing/test/eeed8b54e0df.c116
-rw-r--r--contrib/libs/liburing/test/empty-eownerdead.c46
-rw-r--r--contrib/libs/liburing/test/eventfd-disable.c180
-rw-r--r--contrib/libs/liburing/test/eventfd-reg.c78
-rw-r--r--contrib/libs/liburing/test/eventfd-ring.c99
-rw-r--r--contrib/libs/liburing/test/eventfd.c114
-rw-r--r--contrib/libs/liburing/test/exec-target.c7
-rw-r--r--contrib/libs/liburing/test/exit-no-cleanup.c118
-rw-r--r--contrib/libs/liburing/test/fadvise.c203
-rw-r--r--contrib/libs/liburing/test/fallocate.c257
-rw-r--r--contrib/libs/liburing/test/fc2a85cb02ef.c133
-rw-r--r--contrib/libs/liburing/test/fd-pass.c188
-rw-r--r--contrib/libs/liburing/test/file-register.c1125
-rw-r--r--contrib/libs/liburing/test/file-update.c232
-rw-r--r--contrib/libs/liburing/test/file-verify.c634
-rw-r--r--contrib/libs/liburing/test/files-exit-hang-poll.c115
-rw-r--r--contrib/libs/liburing/test/files-exit-hang-timeout.c138
-rw-r--r--contrib/libs/liburing/test/fixed-buf-iter.c116
-rw-r--r--contrib/libs/liburing/test/fixed-link.c91
-rw-r--r--contrib/libs/liburing/test/fixed-reuse.c161
-rw-r--r--contrib/libs/liburing/test/fpos.c256
-rw-r--r--contrib/libs/liburing/test/fsync.c225
-rw-r--r--contrib/libs/liburing/test/hardlink.c141
-rw-r--r--contrib/libs/liburing/test/helpers.c269
-rw-r--r--contrib/libs/liburing/test/helpers.h94
-rw-r--r--contrib/libs/liburing/test/io-cancel.c556
-rw-r--r--contrib/libs/liburing/test/io_uring_enter.c262
-rw-r--r--contrib/libs/liburing/test/io_uring_passthrough.c452
-rw-r--r--contrib/libs/liburing/test/io_uring_register.c507
-rw-r--r--contrib/libs/liburing/test/io_uring_setup.c188
-rw-r--r--contrib/libs/liburing/test/iopoll-leak.c86
-rw-r--r--contrib/libs/liburing/test/iopoll.c380
-rw-r--r--contrib/libs/liburing/test/lfs-openat-write.c122
-rw-r--r--contrib/libs/liburing/test/lfs-openat.c276
-rw-r--r--contrib/libs/liburing/test/link-timeout.c1109
-rw-r--r--contrib/libs/liburing/test/link.c498
-rw-r--r--contrib/libs/liburing/test/link_drain.c230
-rw-r--r--contrib/libs/liburing/test/madvise.c196
-rw-r--r--contrib/libs/liburing/test/mkdir.c113
-rw-r--r--contrib/libs/liburing/test/msg-ring.c261
-rw-r--r--contrib/libs/liburing/test/multicqes_drain.c427
-rw-r--r--contrib/libs/liburing/test/nolibc.c61
-rw-r--r--contrib/libs/liburing/test/nop-all-sizes.c100
-rw-r--r--contrib/libs/liburing/test/nop.c178
-rw-r--r--contrib/libs/liburing/test/nvme.h168
-rw-r--r--contrib/libs/liburing/test/open-close.c262
-rw-r--r--contrib/libs/liburing/test/open-direct-link.c189
-rw-r--r--contrib/libs/liburing/test/open-direct-pick.c181
-rw-r--r--contrib/libs/liburing/test/openat2.c309
-rw-r--r--contrib/libs/liburing/test/personality.c205
-rw-r--r--contrib/libs/liburing/test/pipe-eof.c84
-rw-r--r--contrib/libs/liburing/test/pipe-reuse.c106
-rw-r--r--contrib/libs/liburing/test/poll-cancel-all.c473
-rw-r--r--contrib/libs/liburing/test/poll-cancel-ton.c136
-rw-r--r--contrib/libs/liburing/test/poll-cancel.c229
-rw-r--r--contrib/libs/liburing/test/poll-link.c222
-rw-r--r--contrib/libs/liburing/test/poll-many.c209
-rw-r--r--contrib/libs/liburing/test/poll-mshot-overflow.c163
-rw-r--r--contrib/libs/liburing/test/poll-mshot-update.c324
-rw-r--r--contrib/libs/liburing/test/poll-ring.c49
-rw-r--r--contrib/libs/liburing/test/poll-v-poll.c354
-rw-r--r--contrib/libs/liburing/test/poll.c110
-rw-r--r--contrib/libs/liburing/test/pollfree.c427
-rw-r--r--contrib/libs/liburing/test/probe.c136
-rw-r--r--contrib/libs/liburing/test/read-before-exit.c113
-rw-r--r--contrib/libs/liburing/test/read-write.c959
-rw-r--r--contrib/libs/liburing/test/recv-msgall-stream.c399
-rw-r--r--contrib/libs/liburing/test/recv-msgall.c266
-rw-r--r--contrib/libs/liburing/test/recv-multishot.c506
-rw-r--r--contrib/libs/liburing/test/register-restrictions.c634
-rw-r--r--contrib/libs/liburing/test/rename.c133
-rw-r--r--contrib/libs/liburing/test/ring-leak.c271
-rw-r--r--contrib/libs/liburing/test/ring-leak2.c250
-rw-r--r--contrib/libs/liburing/test/ringbuf-read.c201
-rw-r--r--contrib/libs/liburing/test/rsrc_tags.c462
-rw-r--r--contrib/libs/liburing/test/rw_merge_test.c99
-rw-r--r--contrib/libs/liburing/test/self.c92
-rw-r--r--contrib/libs/liburing/test/send-zerocopy.c685
-rw-r--r--contrib/libs/liburing/test/send_recv.c334
-rw-r--r--contrib/libs/liburing/test/send_recvmsg.c456
-rw-r--r--contrib/libs/liburing/test/sendmsg_fs_cve.c201
-rw-r--r--contrib/libs/liburing/test/shared-wq.c85
-rw-r--r--contrib/libs/liburing/test/short-read.c76
-rw-r--r--contrib/libs/liburing/test/shutdown.c165
-rw-r--r--contrib/libs/liburing/test/sigfd-deadlock.c89
-rw-r--r--contrib/libs/liburing/test/single-issuer.c172
-rw-r--r--contrib/libs/liburing/test/skip-cqe.c430
-rw-r--r--contrib/libs/liburing/test/socket-rw-eagain.c149
-rw-r--r--contrib/libs/liburing/test/socket-rw-offset.c149
-rw-r--r--contrib/libs/liburing/test/socket-rw.c137
-rw-r--r--contrib/libs/liburing/test/socket.c410
-rw-r--r--contrib/libs/liburing/test/splice.c513
-rw-r--r--contrib/libs/liburing/test/sq-full-cpp.cc46
-rw-r--r--contrib/libs/liburing/test/sq-full.c46
-rw-r--r--contrib/libs/liburing/test/sq-poll-dup.c205
-rw-r--r--contrib/libs/liburing/test/sq-poll-kthread.c170
-rw-r--r--contrib/libs/liburing/test/sq-poll-share.c138
-rw-r--r--contrib/libs/liburing/test/sq-space_left.c160
-rw-r--r--contrib/libs/liburing/test/sqpoll-cancel-hang.c158
-rw-r--r--contrib/libs/liburing/test/sqpoll-disable-exit.c197
-rw-r--r--contrib/libs/liburing/test/sqpoll-exit-hang.c79
-rw-r--r--contrib/libs/liburing/test/sqpoll-sleep.c70
-rw-r--r--contrib/libs/liburing/test/stdout.c233
-rw-r--r--contrib/libs/liburing/test/submit-and-wait.c109
-rw-r--r--contrib/libs/liburing/test/submit-link-fail.c157
-rw-r--r--contrib/libs/liburing/test/submit-reuse.c238
-rw-r--r--contrib/libs/liburing/test/symlink.c117
-rw-r--r--contrib/libs/liburing/test/sync-cancel.c236
-rw-r--r--contrib/libs/liburing/test/teardowns.c59
-rw-r--r--contrib/libs/liburing/test/test.h35
-rw-r--r--contrib/libs/liburing/test/thread-exit.c144
-rw-r--r--contrib/libs/liburing/test/timeout-new.c253
-rw-r--r--contrib/libs/liburing/test/timeout-overflow.c205
-rw-r--r--contrib/libs/liburing/test/timeout.c1524
-rw-r--r--contrib/libs/liburing/test/tty-write-dpoll.c61
-rw-r--r--contrib/libs/liburing/test/unlink.c113
-rw-r--r--contrib/libs/liburing/test/wakeup-hang.c163
-rw-r--r--contrib/libs/liburing/test/xattr.c426
148 files changed, 35387 insertions, 0 deletions
diff --git a/contrib/libs/liburing/test/232c93d07b74.c b/contrib/libs/liburing/test/232c93d07b74.c
new file mode 100644
index 00000000000..ab28adab174
--- /dev/null
+++ b/contrib/libs/liburing/test/232c93d07b74.c
@@ -0,0 +1,306 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test case for socket read/write through IORING_OP_READV and
+ * IORING_OP_WRITEV, using both TCP and sockets and blocking and
+ * non-blocking IO.
+ *
+ * Heavily based on a test case from Hrvoje Zeba <zeba.hrvoje@gmail.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <pthread.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define RECV_BUFF_SIZE 2
+#define SEND_BUFF_SIZE 3
+
+struct params {
+ int tcp;
+ int non_blocking;
+ __be16 bind_port;
+};
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+int rcv_ready = 0;
+
+static void set_rcv_ready(void)
+{
+ pthread_mutex_lock(&mutex);
+
+ rcv_ready = 1;
+ pthread_cond_signal(&cond);
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static void wait_for_rcv_ready(void)
+{
+ pthread_mutex_lock(&mutex);
+
+ while (!rcv_ready)
+ pthread_cond_wait(&cond, &mutex);
+
+ pthread_mutex_unlock(&mutex);
+}
+
+static void *rcv(void *arg)
+{
+ struct params *p = arg;
+ int s0;
+ int res;
+
+ if (p->tcp) {
+ int val = 1;
+
+
+ s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ res = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(res != -1);
+ res = setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(res != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ assert(t_bind_ephemeral_port(s0, &addr) == 0);
+ p->bind_port = addr.sin_port;
+ } else {
+ s0 = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ assert(s0 != -1);
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, "\0sock", 6);
+ res = bind(s0, (struct sockaddr *) &addr, sizeof(addr));
+ assert(res != -1);
+ }
+ res = listen(s0, 128);
+ assert(res != -1);
+
+ set_rcv_ready();
+
+ int s1 = accept(s0, NULL, NULL);
+ assert(s1 != -1);
+
+ if (p->non_blocking) {
+ int flags = fcntl(s1, F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ res = fcntl(s1, F_SETFL, flags);
+ assert(res != -1);
+ }
+
+ struct io_uring m_io_uring;
+ void *ret = NULL;
+
+ res = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(res >= 0);
+
+ int bytes_read = 0;
+ int expected_byte = 0;
+ int done = 0;
+
+ while (!done && bytes_read != 33) {
+ char buff[RECV_BUFF_SIZE];
+ struct iovec iov;
+
+ iov.iov_base = buff;
+ iov.iov_len = sizeof(buff);
+
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_readv(sqe, s1, &iov, 1, 0);
+
+ res = io_uring_submit(&m_io_uring);
+ assert(res != -1);
+
+ struct io_uring_cqe *cqe;
+ unsigned head;
+ unsigned count = 0;
+
+ while (!done && count != 1) {
+ io_uring_for_each_cqe(&m_io_uring, head, cqe) {
+ if (cqe->res < 0)
+ assert(cqe->res == -EAGAIN);
+ else {
+ int i;
+
+ for (i = 0; i < cqe->res; i++) {
+ if (buff[i] != expected_byte) {
+ fprintf(stderr,
+ "Received %d, wanted %d\n",
+ buff[i], expected_byte);
+ ret++;
+ done = 1;
+ }
+ expected_byte++;
+ }
+ bytes_read += cqe->res;
+ }
+
+ count++;
+ }
+
+ assert(count <= 1);
+ io_uring_cq_advance(&m_io_uring, count);
+ }
+ }
+
+ shutdown(s1, SHUT_RDWR);
+ close(s1);
+ close(s0);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static void *snd(void *arg)
+{
+ struct params *p = arg;
+ int s0;
+ int ret;
+
+ wait_for_rcv_ready();
+
+ if (p->tcp) {
+ int val = 1;
+
+ s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ ret = setsockopt(s0, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = p->bind_port;
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+ ret = connect(s0, (struct sockaddr*) &addr, sizeof(addr));
+ assert(ret != -1);
+ } else {
+ s0 = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
+ assert(s0 != -1);
+
+ struct sockaddr_un addr;
+ memset(&addr, 0, sizeof(addr));
+
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, "\0sock", 6);
+ ret = connect(s0, (struct sockaddr*) &addr, sizeof(addr));
+ assert(ret != -1);
+ }
+
+ if (p->non_blocking) {
+ int flags = fcntl(s0, F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(s0, F_SETFL, flags);
+ assert(ret != -1);
+ }
+
+ struct io_uring m_io_uring;
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ int bytes_written = 0;
+ int done = 0;
+
+ while (!done && bytes_written != 33) {
+ char buff[SEND_BUFF_SIZE];
+ int i;
+
+ for (i = 0; i < SEND_BUFF_SIZE; i++)
+ buff[i] = i + bytes_written;
+
+ struct iovec iov;
+
+ iov.iov_base = buff;
+ iov.iov_len = sizeof(buff);
+
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_writev(sqe, s0, &iov, 1, 0);
+
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret != -1);
+
+ struct io_uring_cqe *cqe;
+ unsigned head;
+ unsigned count = 0;
+
+ while (!done && count != 1) {
+ io_uring_for_each_cqe(&m_io_uring, head, cqe) {
+ if (cqe->res < 0) {
+ if (cqe->res == -EPIPE) {
+ done = 1;
+ break;
+ }
+ assert(cqe->res == -EAGAIN);
+ } else {
+ bytes_written += cqe->res;
+ }
+
+ count++;
+ }
+
+ assert(count <= 1);
+ io_uring_cq_advance(&m_io_uring, count);
+ }
+ usleep(100000);
+ }
+
+ shutdown(s0, SHUT_RDWR);
+ close(s0);
+ io_uring_queue_exit(&m_io_uring);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct params p;
+ pthread_t t1, t2;
+ void *res1, *res2;
+ int i, exit_val = T_EXIT_PASS;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ for (i = 0; i < 4; i++) {
+ p.tcp = i & 1;
+ p.non_blocking = (i & 2) >> 1;
+
+ rcv_ready = 0;
+
+ pthread_create(&t1, NULL, rcv, &p);
+ pthread_create(&t2, NULL, snd, &p);
+ pthread_join(t1, &res1);
+ pthread_join(t2, &res2);
+ if (res1 || res2) {
+ fprintf(stderr, "Failed tcp=%d, non_blocking=%d\n", p.tcp, p.non_blocking);
+ exit_val = T_EXIT_FAIL;
+ }
+ }
+
+ return exit_val;
+}
diff --git a/contrib/libs/liburing/test/35fa71a030ca.c b/contrib/libs/liburing/test/35fa71a030ca.c
new file mode 100644
index 00000000000..cf8e3ff6056
--- /dev/null
+++ b/contrib/libs/liburing/test/35fa71a030ca.c
@@ -0,0 +1,330 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// 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/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 <sys/mman.h>
+
+#include <linux/futex.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+#if !defined(SYS_futex) && defined(SYS_futex_time64)
+# define SYS_futex SYS_futex_time64
+#endif
+
+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;
+ for (i = 0; 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(SYS_futex, &ev->state, FUTEX_WAKE | FUTEX_PRIVATE_FLAG);
+}
+
+static void event_wait(event_t* ev)
+{
+ while (!__atomic_load_n(&ev->state, __ATOMIC_ACQUIRE))
+ syscall(SYS_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(SYS_futex, &ev->state, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, 0, &ts);
+ if (__atomic_load_n(&ev->state, __ATOMIC_RELAXED))
+ return 1;
+ now = current_time_ms();
+ if (now - start > timeout)
+ return 0;
+ }
+}
+
+static bool write_file(const char* file, const char* what, ...)
+{
+ char buf[1024];
+ va_list args;
+ va_start(args, what);
+ vsnprintf(buf, sizeof(buf), what, args);
+ va_end(args);
+ buf[sizeof(buf) - 1] = 0;
+ int len = strlen(buf);
+ int fd = open(file, O_WRONLY | O_CLOEXEC);
+ if (fd == -1)
+ return false;
+ if (write(fd, buf, len) != len) {
+ int err = errno;
+ close(fd);
+ errno = err;
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static void kill_and_wait(int pid, int* status)
+{
+ kill(-pid, SIGKILL);
+ kill(pid, SIGKILL);
+ int i;
+ for (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) {
+ }
+}
+
+#define SYZ_HAVE_SETUP_TEST 1
+static void setup_test()
+{
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ setpgrp();
+ write_file("/proc/self/oom_score_adj", "1000");
+}
+
+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 < 3; 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, 45);
+ 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)
+{
+ for (;;) {
+ 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 < 5 * 1000)
+ continue;
+ kill_and_wait(pid, &status);
+ break;
+ }
+ }
+}
+
+uint64_t r[1] = {0xffffffffffffffff};
+
+void execute_call(int call)
+{
+ long res;
+ switch (call) {
+ case 0:
+ *(uint32_t*)0x20000040 = 0;
+ *(uint32_t*)0x20000044 = 0;
+ *(uint32_t*)0x20000048 = 0;
+ *(uint32_t*)0x2000004c = 0;
+ *(uint32_t*)0x20000050 = 0;
+ *(uint32_t*)0x20000054 = 0;
+ *(uint32_t*)0x20000058 = 0;
+ *(uint32_t*)0x2000005c = 0;
+ *(uint32_t*)0x20000060 = 0;
+ *(uint32_t*)0x20000064 = 0;
+ *(uint32_t*)0x20000068 = 0;
+ *(uint32_t*)0x2000006c = 0;
+ *(uint32_t*)0x20000070 = 0;
+ *(uint32_t*)0x20000074 = 0;
+ *(uint32_t*)0x20000078 = 0;
+ *(uint32_t*)0x2000007c = 0;
+ *(uint32_t*)0x20000080 = 0;
+ *(uint32_t*)0x20000084 = 0;
+ *(uint64_t*)0x20000088 = 0;
+ *(uint32_t*)0x20000090 = 0;
+ *(uint32_t*)0x20000094 = 0;
+ *(uint32_t*)0x20000098 = 0;
+ *(uint32_t*)0x2000009c = 0;
+ *(uint32_t*)0x200000a0 = 0;
+ *(uint32_t*)0x200000a4 = 0;
+ *(uint32_t*)0x200000a8 = 0;
+ *(uint32_t*)0x200000ac = 0;
+ *(uint64_t*)0x200000b0 = 0;
+ res = __sys_io_uring_setup(0x64, (struct io_uring_params *) 0x20000040UL);
+ if (res != -1)
+ r[0] = res;
+ break;
+ case 1:
+ __sys_io_uring_register((long)r[0], 0, 0, 0);
+ break;
+ case 2:
+ __sys_io_uring_register((long)r[0], 0, 0, 0);
+ break;
+ }
+}
+
+static void sig_int(int sig)
+{
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ signal(SIGINT, sig_int);
+ mmap((void *) 0x20000000, 0x1000000, 3, 0x32, -1, 0);
+ signal(SIGALRM, sig_int);
+ alarm(5);
+
+ loop();
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/500f9fbadef8.c b/contrib/libs/liburing/test/500f9fbadef8.c
new file mode 100644
index 00000000000..c9e43978200
--- /dev/null
+++ b/contrib/libs/liburing/test/500f9fbadef8.c
@@ -0,0 +1,90 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Single depth submit+wait poll hang test
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define BLOCKS 4096
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec iov;
+ char buf[32];
+ off_t offset;
+ unsigned blocks;
+ int ret, fd;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ t_posix_memalign(&iov.iov_base, 4096, 4096);
+ iov.iov_len = 4096;
+
+ ret = io_uring_queue_init(1, &ring, IORING_SETUP_IOPOLL);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkostemp(buf, O_WRONLY | O_DIRECT | O_CREAT);
+ if (fd < 0) {
+ perror("mkostemp");
+ return T_EXIT_FAIL;
+ }
+
+ offset = 0;
+ blocks = BLOCKS;
+ do {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_writev(sqe, fd, &iov, 1, offset);
+ ret = io_uring_submit_and_wait(&ring, 1);
+ if (ret < 0) {
+ fprintf(stderr, "submit_and_wait: %d\n", ret);
+ goto err;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res != 4096) {
+ if (cqe->res == -EOPNOTSUPP)
+ goto skipped;
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ offset += 4096;
+ } while (--blocks);
+
+ close(fd);
+ unlink(buf);
+ return T_EXIT_PASS;
+err:
+ close(fd);
+ unlink(buf);
+ return T_EXIT_FAIL;
+skipped:
+ fprintf(stderr, "Polling not supported in current dir, test skipped\n");
+ close(fd);
+ unlink(buf);
+ return T_EXIT_SKIP;
+}
diff --git a/contrib/libs/liburing/test/7ad0e4b2f83c.c b/contrib/libs/liburing/test/7ad0e4b2f83c.c
new file mode 100644
index 00000000000..067def169a1
--- /dev/null
+++ b/contrib/libs/liburing/test/7ad0e4b2f83c.c
@@ -0,0 +1,95 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <stdio.h>
+#include <time.h>
+#include <sys/time.h>
+#include "liburing.h"
+#include "helpers.h"
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+int main(int argc, char *argv[])
+{
+ struct __kernel_timespec ts1, ts2;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ unsigned long msec;
+ struct timeval tv;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_init=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "io_uring_submit1=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+
+ ts1.tv_sec = 5,
+ ts1.tv_nsec = 0;
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts1);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe_timeout=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ gettimeofday(&tv, NULL);
+
+ ts2.tv_sec = 1;
+ ts2.tv_nsec = 0;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts2, 0, 0);
+ sqe->user_data = 89;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "io_uring_submit2=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_wait_cqe(&ring, &cqe);
+ io_uring_cqe_seen(&ring, cqe);
+ msec = mtime_since_now(&tv);
+ if (msec >= 900 && msec <= 1100) {
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+ }
+
+ fprintf(stderr, "%s: Timeout seems wonky (got %lu)\n", __FUNCTION__,
+ msec);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/8a9973408177.c b/contrib/libs/liburing/test/8a9973408177.c
new file mode 100644
index 00000000000..ab9a0337492
--- /dev/null
+++ b/contrib/libs/liburing/test/8a9973408177.c
@@ -0,0 +1,108 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#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 register_file(struct io_uring *ring)
+{
+ char buf[32];
+ int ret, fd;
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file register %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "file register %d\n", ret);
+ return 1;
+ }
+
+ unlink(buf);
+ close(fd);
+ return 0;
+}
+
+static int test_single_fsync(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buf[32];
+ int fd, ret;
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_fsync(sqe, fd, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ unlink(buf);
+ return 0;
+err:
+ unlink(buf);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = register_file(&ring);
+ if (ret)
+ return ret;
+ ret = test_single_fsync(&ring);
+ if (ret) {
+ printf("test_single_fsync failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/917257daa0fe.c b/contrib/libs/liburing/test/917257daa0fe.c
new file mode 100644
index 00000000000..b24d140d839
--- /dev/null
+++ b/contrib/libs/liburing/test/917257daa0fe.c
@@ -0,0 +1,55 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ mmap((void *) 0x20000000, 0x1000000, 3, 0x32, -1, 0);
+
+ *(uint32_t*)0x20000000 = 0;
+ *(uint32_t*)0x20000004 = 0;
+ *(uint32_t*)0x20000008 = 6;
+ *(uint32_t*)0x2000000c = 0;
+ *(uint32_t*)0x20000010 = 0x3af;
+ *(uint32_t*)0x20000014 = 0;
+ *(uint32_t*)0x20000018 = 0;
+ *(uint32_t*)0x2000001c = 0;
+ *(uint32_t*)0x20000020 = 0;
+ *(uint32_t*)0x20000024 = 0;
+ *(uint32_t*)0x20000028 = 0;
+ *(uint32_t*)0x2000002c = 0;
+ *(uint32_t*)0x20000030 = 0;
+ *(uint32_t*)0x20000034 = 0;
+ *(uint32_t*)0x20000038 = 0;
+ *(uint32_t*)0x2000003c = 0;
+ *(uint32_t*)0x20000040 = 0;
+ *(uint32_t*)0x20000044 = 0;
+ *(uint64_t*)0x20000048 = 0;
+ *(uint32_t*)0x20000050 = 0;
+ *(uint32_t*)0x20000054 = 0;
+ *(uint32_t*)0x20000058 = 0;
+ *(uint32_t*)0x2000005c = 0;
+ *(uint32_t*)0x20000060 = 0;
+ *(uint32_t*)0x20000064 = 0;
+ *(uint32_t*)0x20000068 = 0;
+ *(uint32_t*)0x2000006c = 0;
+ *(uint64_t*)0x20000070 = 0;
+ __sys_io_uring_setup(0x7a6, (struct io_uring_params *) 0x20000000UL);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/a0908ae19763.c b/contrib/libs/liburing/test/a0908ae19763.c
new file mode 100644
index 00000000000..dbe9c5100ae
--- /dev/null
+++ b/contrib/libs/liburing/test/a0908ae19763.c
@@ -0,0 +1,60 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+uint64_t r[1] = {0xffffffffffffffff};
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ mmap((void *) 0x20000000, 0x1000000, 3, 0x32, -1, 0);
+ intptr_t res = 0;
+ *(uint32_t*)0x20000080 = 0;
+ *(uint32_t*)0x20000084 = 0;
+ *(uint32_t*)0x20000088 = 0;
+ *(uint32_t*)0x2000008c = 0;
+ *(uint32_t*)0x20000090 = 0;
+ *(uint32_t*)0x20000094 = 0;
+ *(uint32_t*)0x20000098 = 0;
+ *(uint32_t*)0x2000009c = 0;
+ *(uint32_t*)0x200000a0 = 0;
+ *(uint32_t*)0x200000a4 = 0;
+ *(uint32_t*)0x200000a8 = 0;
+ *(uint32_t*)0x200000ac = 0;
+ *(uint32_t*)0x200000b0 = 0;
+ *(uint32_t*)0x200000b4 = 0;
+ *(uint32_t*)0x200000b8 = 0;
+ *(uint32_t*)0x200000bc = 0;
+ *(uint32_t*)0x200000c0 = 0;
+ *(uint32_t*)0x200000c4 = 0;
+ *(uint64_t*)0x200000c8 = 0;
+ *(uint32_t*)0x200000d0 = 0;
+ *(uint32_t*)0x200000d4 = 0;
+ *(uint32_t*)0x200000d8 = 0;
+ *(uint32_t*)0x200000dc = 0;
+ *(uint32_t*)0x200000e0 = 0;
+ *(uint32_t*)0x200000e4 = 0;
+ *(uint32_t*)0x200000e8 = 0;
+ *(uint32_t*)0x200000ec = 0;
+ *(uint64_t*)0x200000f0 = 0;
+ res = __sys_io_uring_setup(0xa4, (struct io_uring_params *) 0x20000080);
+ if (res != -1)
+ r[0] = res;
+ *(uint32_t*)0x20000280 = -1;
+ __sys_io_uring_register(r[0], 2, (const void *) 0x20000280, 1);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/a4c0b3decb33.c b/contrib/libs/liburing/test/a4c0b3decb33.c
new file mode 100644
index 00000000000..376ecb3d879
--- /dev/null
+++ b/contrib/libs/liburing/test/a4c0b3decb33.c
@@ -0,0 +1,182 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <dirent.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/prctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/mman.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+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 bool write_file(const char* file, const char* what, ...)
+{
+ char buf[1024];
+ va_list args;
+ va_start(args, what);
+ vsnprintf(buf, sizeof(buf), what, args);
+ va_end(args);
+ buf[sizeof(buf) - 1] = 0;
+ int len = strlen(buf);
+ int fd = open(file, O_WRONLY | O_CLOEXEC);
+ if (fd == -1)
+ return false;
+ if (write(fd, buf, len) != len) {
+ int err = errno;
+ close(fd);
+ errno = err;
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static void kill_and_wait(int pid, int* status)
+{
+ kill(-pid, SIGKILL);
+ kill(pid, SIGKILL);
+ int i;
+ for (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()
+{
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ setpgrp();
+ write_file("/proc/self/oom_score_adj", "1000");
+}
+
+static void execute_one(void);
+
+#define WAIT_FLAGS __WALL
+
+static void loop(void)
+{
+ int iter;
+ for (iter = 0; 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 < 5 * 1000)
+ continue;
+ kill_and_wait(pid, &status);
+ break;
+ }
+ }
+}
+
+void execute_one(void)
+{
+ *(uint32_t*)0x20000080 = 0;
+ *(uint32_t*)0x20000084 = 0;
+ *(uint32_t*)0x20000088 = 3;
+ *(uint32_t*)0x2000008c = 3;
+ *(uint32_t*)0x20000090 = 0x175;
+ *(uint32_t*)0x20000094 = 0;
+ *(uint32_t*)0x20000098 = 0;
+ *(uint32_t*)0x2000009c = 0;
+ *(uint32_t*)0x200000a0 = 0;
+ *(uint32_t*)0x200000a4 = 0;
+ *(uint32_t*)0x200000a8 = 0;
+ *(uint32_t*)0x200000ac = 0;
+ *(uint32_t*)0x200000b0 = 0;
+ *(uint32_t*)0x200000b4 = 0;
+ *(uint32_t*)0x200000b8 = 0;
+ *(uint32_t*)0x200000bc = 0;
+ *(uint32_t*)0x200000c0 = 0;
+ *(uint32_t*)0x200000c4 = 0;
+ *(uint64_t*)0x200000c8 = 0;
+ *(uint32_t*)0x200000d0 = 0;
+ *(uint32_t*)0x200000d4 = 0;
+ *(uint32_t*)0x200000d8 = 0;
+ *(uint32_t*)0x200000dc = 0;
+ *(uint32_t*)0x200000e0 = 0;
+ *(uint32_t*)0x200000e4 = 0;
+ *(uint32_t*)0x200000e8 = 0;
+ *(uint32_t*)0x200000ec = 0;
+ *(uint64_t*)0x200000f0 = 0;
+ __sys_io_uring_setup(0x983, (struct io_uring_params *) 0x20000080);
+}
+
+static void sig_int(int sig)
+{
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ signal(SIGINT, sig_int);
+ mmap((void *) 0x20000000, 0x1000000, 3, 0x32, -1, 0);
+ loop();
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/accept-link.c b/contrib/libs/liburing/test/accept-link.c
new file mode 100644
index 00000000000..af47e0f4f03
--- /dev/null
+++ b/contrib/libs/liburing/test/accept-link.c
@@ -0,0 +1,256 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+static int recv_thread_ready = 0;
+static int recv_thread_done = 0;
+
+static void signal_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+ *var = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+static void wait_for_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+
+ while (!*var)
+ pthread_cond_wait(&cond, &mutex);
+
+ pthread_mutex_unlock(&mutex);
+}
+
+struct data {
+ unsigned expected[2];
+ unsigned just_positive[2];
+ unsigned long timeout;
+ unsigned short port;
+ unsigned int addr;
+ int stop;
+};
+
+static void *send_thread(void *arg)
+{
+ struct data *data = arg;
+ int ret;
+
+ wait_for_var(&recv_thread_ready);
+
+ if (data->stop)
+ return NULL;
+
+ int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = data->port;
+ addr.sin_addr.s_addr = data->addr;
+
+ ret = connect(s0, (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret != -1);
+
+ wait_for_var(&recv_thread_done);
+
+ close(s0);
+ return NULL;
+}
+
+void *recv_thread(void *arg)
+{
+ struct data *data = arg;
+ struct io_uring ring;
+ int i, ret;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ assert(ret == 0);
+
+ int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ int32_t val = 1;
+ ret = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ struct sockaddr_in addr;
+
+ addr.sin_family = AF_INET;
+ data->addr = inet_addr("127.0.0.1");
+ addr.sin_addr.s_addr = data->addr;
+
+ i = 0;
+ do {
+ data->port = htons(1025 + (rand() % 64510));
+ addr.sin_port = data->port;
+
+ if (bind(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1)
+ break;
+ } while (++i < 100);
+
+ if (i >= 100) {
+ printf("Can't find good port, skipped\n");
+ data->stop = 1;
+ signal_var(&recv_thread_ready);
+ goto out;
+ }
+
+ ret = listen(s0, 128);
+ assert(ret != -1);
+
+ signal_var(&recv_thread_ready);
+
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ io_uring_prep_accept(sqe, s0, NULL, NULL, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ struct __kernel_timespec ts;
+ ts.tv_sec = data->timeout / 1000000000;
+ ts.tv_nsec = data->timeout % 1000000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ assert(ret == 2);
+
+ for (i = 0; i < 2; i++) {
+ struct io_uring_cqe *cqe;
+ int idx;
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "wait cqe failed\n");
+ goto err;
+ }
+ idx = cqe->user_data - 1;
+ if (cqe->res != data->expected[idx]) {
+ if (cqe->res > 0 && data->just_positive[idx])
+ goto ok;
+ if (cqe->res == -EBADF) {
+ fprintf(stdout, "Accept not supported, skipping\n");
+ data->stop = 1;
+ goto out;
+ }
+ fprintf(stderr, "cqe %" PRIu64 " got %d, wanted %d\n",
+ (uint64_t) cqe->user_data, cqe->res,
+ data->expected[idx]);
+ goto err;
+ }
+ok:
+ if (cqe->user_data == 1 && cqe->res > 0)
+ close(cqe->res);
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ signal_var(&recv_thread_done);
+
+out:
+ close(s0);
+ return NULL;
+err:
+ close(s0);
+ return (void *) 1;
+}
+
+static int test_accept_timeout(int do_connect, unsigned long timeout)
+{
+ struct io_uring ring;
+ struct io_uring_params p = {};
+ pthread_t t1, t2;
+ struct data d;
+ void *tret;
+ int ret, fast_poll;
+
+ ret = io_uring_queue_init_params(1, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ };
+
+ fast_poll = (p.features & IORING_FEAT_FAST_POLL) != 0;
+ io_uring_queue_exit(&ring);
+
+ recv_thread_ready = 0;
+ recv_thread_done = 0;
+
+ memset(&d, 0, sizeof(d));
+ d.timeout = timeout;
+ if (!do_connect) {
+ if (fast_poll) {
+ d.expected[0] = -ECANCELED;
+ d.expected[1] = -ETIME;
+ } else {
+ d.expected[0] = -EINTR;
+ d.expected[1] = -EALREADY;
+ }
+ } else {
+ d.expected[0] = -1U;
+ d.just_positive[0] = 1;
+ d.expected[1] = -ECANCELED;
+ }
+
+ pthread_create(&t1, NULL, recv_thread, &d);
+
+ if (do_connect)
+ pthread_create(&t2, NULL, send_thread, &d);
+
+ pthread_join(t1, &tret);
+ if (tret)
+ ret++;
+
+ if (do_connect) {
+ pthread_join(t2, &tret);
+ if (tret)
+ ret++;
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ if (test_accept_timeout(0, 200000000)) {
+ fprintf(stderr, "accept timeout 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (test_accept_timeout(1, 1000000000)) {
+ fprintf(stderr, "accept and connect timeout 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/accept-reuse.c b/contrib/libs/liburing/test/accept-reuse.c
new file mode 100644
index 00000000000..fb15ade840f
--- /dev/null
+++ b/contrib/libs/liburing/test/accept-reuse.c
@@ -0,0 +1,166 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <liburing.h>
+#include <netdb.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <errno.h>
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+struct io_uring io_uring;
+
+int sys_io_uring_enter(const int fd,
+ const unsigned to_submit,
+ const unsigned min_complete,
+ const unsigned flags, sigset_t * const sig)
+{
+ return __sys_io_uring_enter(fd, to_submit, min_complete, flags, sig);
+}
+
+int submit_sqe(void)
+{
+ struct io_uring_sq *sq = &io_uring.sq;
+ const unsigned tail = *sq->ktail;
+
+ sq->array[tail & sq->ring_mask] = 0;
+ io_uring_smp_store_release(sq->ktail, tail + 1);
+
+ return sys_io_uring_enter(io_uring.ring_fd, 1, 0, 0, NULL);
+}
+
+int main(int argc, char **argv)
+{
+ struct addrinfo *addr_info_list = NULL;
+ struct addrinfo *ai, *addr_info = NULL;
+ struct io_uring_params params;
+ struct io_uring_sqe *sqe;
+ struct addrinfo hints;
+ struct sockaddr sa;
+ socklen_t sa_size = sizeof(sa);
+ int ret, listen_fd, connect_fd, val, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ memset(&params, 0, sizeof(params));
+ ret = io_uring_queue_init_params(4, &io_uring, &params);
+ if (ret) {
+ fprintf(stderr, "io_uring_init_failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (!(params.features & IORING_FEAT_SUBMIT_STABLE)) {
+ fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
+ return T_EXIT_SKIP;
+ }
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
+
+ ret = getaddrinfo(NULL, "12345", &hints, &addr_info_list);
+ if (ret < 0) {
+ perror("getaddrinfo");
+ return T_EXIT_FAIL;
+ }
+
+ for (ai = addr_info_list; ai; ai = ai->ai_next) {
+ if (ai->ai_family == AF_INET || ai->ai_family == AF_INET6) {
+ addr_info = ai;
+ break;
+ }
+ }
+ if (!addr_info) {
+ fprintf(stderr, "addrinfo not found\n");
+ return T_EXIT_FAIL;
+ }
+
+ sqe = &io_uring.sq.sqes[0];
+ listen_fd = -1;
+
+ ret = socket(addr_info->ai_family, SOCK_STREAM,
+ addr_info->ai_protocol);
+ if (ret < 0) {
+ perror("socket");
+ return T_EXIT_FAIL;
+ }
+ listen_fd = ret;
+
+ val = 1;
+ setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(int));
+ setsockopt(listen_fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(int));
+
+ ret = bind(listen_fd, addr_info->ai_addr, addr_info->ai_addrlen);
+ if (ret < 0) {
+ perror("bind");
+ return T_EXIT_FAIL;
+ }
+
+ ret = listen(listen_fd, SOMAXCONN);
+ if (ret < 0) {
+ perror("listen");
+ return T_EXIT_FAIL;
+ }
+
+ memset(&sa, 0, sizeof(sa));
+
+ io_uring_prep_accept(sqe, listen_fd, &sa, &sa_size, 0);
+ sqe->user_data = 1;
+ ret = submit_sqe();
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ connect_fd = -1;
+ ret = socket(addr_info->ai_family, SOCK_STREAM, addr_info->ai_protocol);
+ if (ret < 0) {
+ perror("socket");
+ return T_EXIT_FAIL;
+ }
+ connect_fd = ret;
+
+ io_uring_prep_connect(sqe, connect_fd, addr_info->ai_addr,
+ addr_info->ai_addrlen);
+ sqe->user_data = 2;
+ ret = submit_sqe();
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ struct io_uring_cqe *cqe = NULL;
+
+ ret = io_uring_wait_cqe(&io_uring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res < 0) {
+ fprintf(stderr, "accept failed: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ case 2:
+ if (cqe->res) {
+ fprintf(stderr, "connect failed: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ }
+ io_uring_cq_advance(&io_uring, 1);
+ }
+
+ freeaddrinfo(addr_info_list);
+ io_uring_queue_exit(&io_uring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/accept-test.c b/contrib/libs/liburing/test/accept-test.c
new file mode 100644
index 00000000000..0c76bfea401
--- /dev/null
+++ b/contrib/libs/liburing/test/accept-test.c
@@ -0,0 +1,84 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Check to see if accept handles addr and addrlen
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ struct sockaddr_un addr;
+ socklen_t addrlen = sizeof(addr);
+ int ret, fd;
+ struct __kernel_timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 1000000
+ };
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (io_uring_queue_init(4, &ring, 0) != 0) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ assert(fd != -1);
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sun_family = AF_UNIX;
+ memcpy(addr.sun_path, "\0sock2", 7);
+
+ ret = bind(fd, (struct sockaddr *)&addr, addrlen);
+ assert(ret != -1);
+ ret = listen(fd, 128);
+ assert(ret != -1);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_accept(sqe, fd, (struct sockaddr*)&addr, &addrlen, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "Got submit %d, expected 1\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (!ret) {
+ if (cqe->res == -EBADF || cqe->res == -EINVAL) {
+ fprintf(stdout, "Accept not supported, skipping\n");
+ goto skip;
+ } else if (cqe->res < 0) {
+ fprintf(stderr, "cqe error %d\n", cqe->res);
+ goto err;
+ }
+ } else if (ret != -ETIME) {
+ fprintf(stderr, "accept() failed to use addr & addrlen parameters!\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+
+skip:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/accept.c b/contrib/libs/liburing/test/accept.c
new file mode 100644
index 00000000000..cde1542a20b
--- /dev/null
+++ b/contrib/libs/liburing/test/accept.c
@@ -0,0 +1,900 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that IORING_OP_ACCEPT works, and send some data across to verify we
+ * didn't get a junk fd.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <limits.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define MAX_FDS 32
+#define NOP_USER_DATA (1LLU << 50)
+#define INITIAL_USER_DATA 1000
+
+static int no_accept;
+static int no_accept_multi;
+
+struct data {
+ char buf[128];
+ struct iovec iov;
+};
+
+struct accept_test_args {
+ int accept_should_error;
+ bool fixed;
+ bool nonblock;
+ bool queue_accept_before_connect;
+ bool multishot;
+ int extra_loops;
+ bool overflow;
+};
+
+static void close_fds(int fds[], int nr)
+{
+ int i;
+
+ for (i = 0; i < nr; i++)
+ close(fds[i]);
+}
+
+static void close_sock_fds(int s_fd[], int c_fd[], int nr, bool fixed)
+{
+ if (!fixed)
+ close_fds(s_fd, nr);
+ close_fds(c_fd, nr);
+}
+
+static void queue_send(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+ struct data *d;
+
+ d = t_malloc(sizeof(*d));
+ d->iov.iov_base = d->buf;
+ d->iov.iov_len = sizeof(d->buf);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_writev(sqe, fd, &d->iov, 1, 0);
+ sqe->user_data = 1;
+}
+
+static void queue_recv(struct io_uring *ring, int fd, bool fixed)
+{
+ struct io_uring_sqe *sqe;
+ struct data *d;
+
+ d = t_malloc(sizeof(*d));
+ d->iov.iov_base = d->buf;
+ d->iov.iov_len = sizeof(d->buf);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_readv(sqe, fd, &d->iov, 1, 0);
+ sqe->user_data = 2;
+ if (fixed)
+ sqe->flags |= IOSQE_FIXED_FILE;
+}
+
+static void queue_accept_multishot(struct io_uring *ring, int fd,
+ int idx, bool fixed)
+{
+ struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
+ int ret;
+
+ if (fixed)
+ io_uring_prep_multishot_accept_direct(sqe, fd,
+ NULL, NULL,
+ 0);
+ else
+ io_uring_prep_multishot_accept(sqe, fd, NULL, NULL, 0);
+
+ io_uring_sqe_set_data64(sqe, idx);
+ ret = io_uring_submit(ring);
+ assert(ret != -1);
+}
+
+static void queue_accept_conn(struct io_uring *ring, int fd,
+ struct accept_test_args args)
+{
+ struct io_uring_sqe *sqe;
+ int ret;
+ int fixed_idx = args.fixed ? 0 : -1;
+ int count = 1 + args.extra_loops;
+
+ if (args.multishot) {
+ queue_accept_multishot(ring, fd, INITIAL_USER_DATA, args.fixed);
+ return;
+ }
+
+ while (count--) {
+ sqe = io_uring_get_sqe(ring);
+ if (fixed_idx < 0) {
+ io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
+ } else {
+ io_uring_prep_accept_direct(sqe, fd, NULL, NULL,
+ 0, fixed_idx);
+ }
+ ret = io_uring_submit(ring);
+ assert(ret != -1);
+ }
+}
+
+static int accept_conn(struct io_uring *ring, int fixed_idx, int *multishot, int fd)
+{
+ struct io_uring_cqe *pcqe;
+ struct io_uring_cqe cqe;
+ int ret;
+
+ do {
+ ret = io_uring_wait_cqe(ring, &pcqe);
+ assert(!ret);
+ cqe = *pcqe;
+ io_uring_cqe_seen(ring, pcqe);
+ } while (cqe.user_data == NOP_USER_DATA);
+
+ if (*multishot) {
+ if (!(cqe.flags & IORING_CQE_F_MORE)) {
+ (*multishot)++;
+ queue_accept_multishot(ring, fd, *multishot, fixed_idx == 0);
+ } else {
+ if (cqe.user_data != *multishot) {
+ fprintf(stderr, "received multishot after told done!\n");
+ return -ECANCELED;
+ }
+ }
+ }
+
+ ret = cqe.res;
+
+ if (fixed_idx >= 0) {
+ if (ret > 0) {
+ if (!multishot) {
+ close(ret);
+ return -EINVAL;
+ }
+ } else if (!ret) {
+ ret = fixed_idx;
+ }
+ }
+ return ret;
+}
+
+static int start_accept_listen(struct sockaddr_in *addr, int port_off,
+ int extra_flags)
+{
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC | extra_flags,
+ IPPROTO_TCP);
+
+ int32_t val = 1;
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ struct sockaddr_in laddr;
+
+ if (!addr)
+ addr = &laddr;
+
+ addr->sin_family = AF_INET;
+ addr->sin_addr.s_addr = inet_addr("127.0.0.1");
+ assert(!t_bind_ephemeral_port(fd, addr));
+ ret = listen(fd, 128);
+ assert(ret != -1);
+
+ return fd;
+}
+
+static int set_client_fd(struct sockaddr_in *addr)
+{
+ int32_t val;
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ val = 1;
+ ret = setsockopt(fd, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ int32_t flags = fcntl(fd, F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(fd, F_SETFL, flags);
+ assert(ret != -1);
+
+ ret = connect(fd, (struct sockaddr *)addr, sizeof(*addr));
+ assert(ret == -1);
+
+ flags = fcntl(fd, F_GETFL, 0);
+ assert(flags != -1);
+
+ flags &= ~O_NONBLOCK;
+ ret = fcntl(fd, F_SETFL, flags);
+ assert(ret != -1);
+
+ return fd;
+}
+
+static void cause_overflow(struct io_uring *ring)
+{
+ int i, ret;
+
+ for (i = 0; i < ring->cq.ring_entries; i++) {
+ struct io_uring_sqe *sqe = io_uring_get_sqe(ring);
+
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data64(sqe, NOP_USER_DATA);
+ ret = io_uring_submit(ring);
+ assert(ret != -1);
+ }
+
+}
+
+static void clear_overflow(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+
+ while (!io_uring_peek_cqe(ring, &cqe)) {
+ if (cqe->user_data != NOP_USER_DATA)
+ break;
+ io_uring_cqe_seen(ring, cqe);
+ }
+}
+
+static int test_loop(struct io_uring *ring,
+ struct accept_test_args args,
+ int recv_s0,
+ struct sockaddr_in *addr)
+{
+ struct io_uring_cqe *cqe;
+ uint32_t head, count = 0;
+ int i, ret, s_fd[MAX_FDS], c_fd[MAX_FDS], done = 0;
+ bool fixed = args.fixed;
+ bool multishot = args.multishot;
+ uint32_t multishot_mask = 0;
+ int nr_fds = multishot ? MAX_FDS : 1;
+ int multishot_idx = multishot ? INITIAL_USER_DATA : 0;
+
+ if (args.overflow)
+ cause_overflow(ring);
+
+ for (i = 0; i < nr_fds; i++) {
+ c_fd[i] = set_client_fd(addr);
+ if (args.overflow && i == nr_fds / 2)
+ clear_overflow(ring);
+ }
+
+ if (!args.queue_accept_before_connect)
+ queue_accept_conn(ring, recv_s0, args);
+
+ for (i = 0; i < nr_fds; i++) {
+ s_fd[i] = accept_conn(ring, fixed ? 0 : -1, &multishot_idx, recv_s0);
+ if (s_fd[i] == -EINVAL) {
+ if (args.accept_should_error)
+ goto out;
+ fprintf(stdout,
+ "%s %s Accept not supported, skipping\n",
+ fixed ? "Fixed" : "",
+ multishot ? "Multishot" : "");
+ if (multishot)
+ no_accept_multi = 1;
+ else
+ no_accept = 1;
+ goto out;
+ } else if (s_fd[i] < 0) {
+ if (args.accept_should_error &&
+ (s_fd[i] == -EBADF || s_fd[i] == -EINVAL))
+ goto out;
+ fprintf(stderr, "%s %s Accept[%d] got %d\n",
+ fixed ? "Fixed" : "",
+ multishot ? "Multishot" : "",
+ i, s_fd[i]);
+ goto err;
+ }
+
+ if (multishot && fixed) {
+ if (s_fd[i] >= MAX_FDS) {
+ fprintf(stderr,
+ "Fixed Multishot Accept[%d] got outbound index: %d\n",
+ i, s_fd[i]);
+ goto err;
+ }
+ /*
+ * for fixed multishot accept test, the file slots
+ * allocated are [0, 32), this means we finally end up
+ * with each bit of a u32 being 1.
+ */
+ multishot_mask |= (1U << s_fd[i]);
+ }
+ }
+
+ if (multishot) {
+ if (fixed && (~multishot_mask != 0U)) {
+ fprintf(stderr, "Fixed Multishot Accept misses events\n");
+ goto err;
+ }
+ goto out;
+ }
+
+ queue_send(ring, c_fd[0]);
+ queue_recv(ring, s_fd[0], fixed);
+
+ ret = io_uring_submit_and_wait(ring, 2);
+ assert(ret != -1);
+
+ while (count < 2) {
+ io_uring_for_each_cqe(ring, head, cqe) {
+ if (cqe->res < 0) {
+ fprintf(stderr, "Got cqe res %d, user_data %i\n",
+ cqe->res, (int)cqe->user_data);
+ done = 1;
+ break;
+ }
+ assert(cqe->res == 128);
+ count++;
+ }
+
+ assert(count <= 2);
+ io_uring_cq_advance(ring, count);
+ if (done)
+ goto err;
+ }
+
+out:
+ close_sock_fds(s_fd, c_fd, nr_fds, fixed);
+ return 0;
+err:
+ close_sock_fds(s_fd, c_fd, nr_fds, fixed);
+ return 1;
+}
+
+static int test(struct io_uring *ring, struct accept_test_args args)
+{
+ struct sockaddr_in addr;
+ int ret = 0;
+ int loop;
+ int32_t recv_s0 = start_accept_listen(&addr, 0,
+ args.nonblock ? O_NONBLOCK : 0);
+ if (args.queue_accept_before_connect)
+ queue_accept_conn(ring, recv_s0, args);
+ for (loop = 0; loop < 1 + args.extra_loops; loop++) {
+ ret = test_loop(ring, args, recv_s0, &addr);
+ if (ret)
+ break;
+ }
+
+ close(recv_s0);
+ return ret;
+}
+
+static void sig_alrm(int sig)
+{
+ exit(0);
+}
+
+static int test_accept_pending_on_exit(void)
+{
+ struct io_uring m_io_uring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fd, ret;
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ fd = start_accept_listen(NULL, 0, 0);
+
+ sqe = io_uring_get_sqe(&m_io_uring);
+ io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret != -1);
+
+ signal(SIGALRM, sig_alrm);
+ alarm(1);
+ ret = io_uring_wait_cqe(&m_io_uring, &cqe);
+ assert(!ret);
+ io_uring_cqe_seen(&m_io_uring, cqe);
+
+ io_uring_queue_exit(&m_io_uring);
+ return 0;
+}
+
+struct test_accept_many_args {
+ unsigned int usecs;
+ bool nonblock;
+ bool single_sock;
+ bool close_fds;
+};
+
+/*
+ * Test issue many accepts and see if we handle cancellation on exit
+ */
+static int test_accept_many(struct test_accept_many_args args)
+{
+ struct io_uring m_io_uring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ unsigned long cur_lim;
+ struct rlimit rlim;
+ int *fds, i, ret;
+ unsigned int nr = 128;
+ int nr_socks = args.single_sock ? 1 : nr;
+
+ if (getrlimit(RLIMIT_NPROC, &rlim) < 0) {
+ perror("getrlimit");
+ return 1;
+ }
+
+ cur_lim = rlim.rlim_cur;
+ rlim.rlim_cur = nr / 4;
+
+ if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
+ perror("setrlimit");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(2 * nr, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ fds = t_calloc(nr_socks, sizeof(int));
+
+ for (i = 0; i < nr_socks; i++)
+ fds[i] = start_accept_listen(NULL, i,
+ args.nonblock ? O_NONBLOCK : 0);
+
+ for (i = 0; i < nr; i++) {
+ int sock_idx = args.single_sock ? 0 : i;
+ sqe = io_uring_get_sqe(&m_io_uring);
+ io_uring_prep_accept(sqe, fds[sock_idx], NULL, NULL, 0);
+ sqe->user_data = 1 + i;
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
+ }
+
+ if (args.usecs)
+ usleep(args.usecs);
+
+ if (args.close_fds)
+ for (i = 0; i < nr_socks; i++)
+ close(fds[i]);
+
+ for (i = 0; i < nr; i++) {
+ if (io_uring_peek_cqe(&m_io_uring, &cqe))
+ break;
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Expected cqe to be cancelled %d\n", cqe->res);
+ ret = 1;
+ goto out;
+ }
+ io_uring_cqe_seen(&m_io_uring, cqe);
+ }
+ ret = 0;
+out:
+ rlim.rlim_cur = cur_lim;
+ if (setrlimit(RLIMIT_NPROC, &rlim) < 0) {
+ perror("setrlimit");
+ return 1;
+ }
+
+ free(fds);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_accept_cancel(unsigned usecs, unsigned int nr, bool multishot)
+{
+ struct io_uring m_io_uring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fd, i, ret;
+
+ if (multishot && no_accept_multi)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ fd = start_accept_listen(NULL, 0, 0);
+
+ for (i = 1; i <= nr; i++) {
+ sqe = io_uring_get_sqe(&m_io_uring);
+ if (!multishot)
+ io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
+ else
+ io_uring_prep_multishot_accept(sqe, fd, NULL, NULL, 0);
+ sqe->user_data = i;
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
+ }
+
+ if (usecs)
+ usleep(usecs);
+
+ for (i = 1; i <= nr; i++) {
+ sqe = io_uring_get_sqe(&m_io_uring);
+ io_uring_prep_cancel64(sqe, i, 0);
+ sqe->user_data = nr + i;
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
+ }
+ for (i = 0; i < nr * 2; i++) {
+ ret = io_uring_wait_cqe(&m_io_uring, &cqe);
+ assert(!ret);
+ /*
+ * Two cases here:
+ *
+ * 1) We cancel the accept4() before it got started, we should
+ * get '0' for the cancel request and '-ECANCELED' for the
+ * accept request.
+ * 2) We cancel the accept4() after it's already running, we
+ * should get '-EALREADY' for the cancel request and
+ * '-EINTR' for the accept request.
+ */
+ if (cqe->user_data == 0) {
+ fprintf(stderr, "unexpected 0 user data\n");
+ goto err;
+ } else if (cqe->user_data <= nr) {
+ if (cqe->res != -EINTR && cqe->res != -ECANCELED) {
+ fprintf(stderr, "Cancelled accept got %d\n", cqe->res);
+ goto err;
+ }
+ } else if (cqe->user_data <= nr * 2) {
+ if (cqe->res != -EALREADY && cqe->res != 0) {
+ fprintf(stderr, "Cancel got %d\n", cqe->res);
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&m_io_uring, cqe);
+ }
+
+ io_uring_queue_exit(&m_io_uring);
+ close(fd);
+ return 0;
+err:
+ io_uring_queue_exit(&m_io_uring);
+ close(fd);
+ return 1;
+}
+
+static int test_accept(int count, bool before)
+{
+ struct io_uring m_io_uring;
+ int ret;
+ struct accept_test_args args = {
+ .queue_accept_before_connect = before,
+ .extra_loops = count - 1
+ };
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_multishot_accept(int count, bool before, bool overflow)
+{
+ struct io_uring m_io_uring;
+ int ret;
+ struct accept_test_args args = {
+ .queue_accept_before_connect = before,
+ .multishot = true,
+ .extra_loops = count - 1,
+ .overflow = overflow
+ };
+
+ if (no_accept_multi)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(MAX_FDS + 10, &m_io_uring, 0);
+ assert(ret >= 0);
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_accept_multishot_wrong_arg()
+{
+ struct io_uring m_io_uring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fd, ret;
+
+ ret = io_uring_queue_init(4, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ fd = start_accept_listen(NULL, 0, 0);
+
+ sqe = io_uring_get_sqe(&m_io_uring);
+ io_uring_prep_multishot_accept_direct(sqe, fd, NULL, NULL, 0);
+ sqe->file_index = 1;
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
+
+ ret = io_uring_wait_cqe(&m_io_uring, &cqe);
+ assert(!ret);
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "file index should be IORING_FILE_INDEX_ALLOC \
+ if its accept in multishot direct mode\n");
+ goto err;
+ }
+ io_uring_cqe_seen(&m_io_uring, cqe);
+
+ io_uring_queue_exit(&m_io_uring);
+ close(fd);
+ return 0;
+err:
+ io_uring_queue_exit(&m_io_uring);
+ close(fd);
+ return 1;
+}
+
+
+static int test_accept_nonblock(bool queue_before_connect, int count)
+{
+ struct io_uring m_io_uring;
+ int ret;
+ struct accept_test_args args = {
+ .nonblock = true,
+ .queue_accept_before_connect = queue_before_connect,
+ .extra_loops = count - 1
+ };
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_accept_fixed(void)
+{
+ struct io_uring m_io_uring;
+ int ret, fd = -1;
+ struct accept_test_args args = {
+ .fixed = true
+ };
+
+ 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);
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_multishot_fixed_accept(void)
+{
+ struct io_uring m_io_uring;
+ int ret, fd[MAX_FDS];
+ struct accept_test_args args = {
+ .fixed = true,
+ .multishot = true
+ };
+
+ if (no_accept_multi)
+ return T_EXIT_SKIP;
+
+ memset(fd, -1, sizeof(fd));
+ 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);
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+static int test_accept_sqpoll(void)
+{
+ struct io_uring m_io_uring;
+ struct io_uring_params p = { };
+ int ret;
+ struct accept_test_args args = { };
+
+ p.flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring_params(32, &m_io_uring, &p);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret < 0)
+ return ret;
+
+ args.accept_should_error = 1;
+ if (p.features & IORING_FEAT_SQPOLL_NONFIXED)
+ args.accept_should_error = 0;
+
+ ret = test(&m_io_uring, args);
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ ret = test_accept(1, false);
+ if (ret) {
+ fprintf(stderr, "test_accept failed\n");
+ return ret;
+ }
+ if (no_accept)
+ return T_EXIT_SKIP;
+
+ ret = test_accept(2, false);
+ if (ret) {
+ fprintf(stderr, "test_accept(2) failed\n");
+ return ret;
+ }
+
+ ret = test_accept(2, true);
+ if (ret) {
+ fprintf(stderr, "test_accept(2, true) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_nonblock(false, 1);
+ if (ret) {
+ fprintf(stderr, "test_accept_nonblock failed\n");
+ return ret;
+ }
+
+ ret = test_accept_nonblock(true, 1);
+ if (ret) {
+ fprintf(stderr, "test_accept_nonblock(before, 1) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_nonblock(true, 3);
+ if (ret) {
+ fprintf(stderr, "test_accept_nonblock(before,3) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_fixed();
+ if (ret) {
+ fprintf(stderr, "test_accept_fixed failed\n");
+ return ret;
+ }
+
+ ret = test_multishot_fixed_accept();
+ if (ret) {
+ fprintf(stderr, "test_multishot_fixed_accept failed\n");
+ return ret;
+ }
+
+ ret = test_accept_multishot_wrong_arg();
+ if (ret) {
+ fprintf(stderr, "test_accept_multishot_wrong_arg failed\n");
+ return ret;
+ }
+
+ ret = test_accept_sqpoll();
+ if (ret) {
+ fprintf(stderr, "test_accept_sqpoll failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(0, 1, false);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel nodelay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(10000, 1, false);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel delay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(0, 4, false);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel nodelay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(10000, 4, false);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel delay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(0, 1, true);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel multishot nodelay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(10000, 1, true);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel multishot delay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(0, 4, true);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel multishot nodelay failed\n");
+ return ret;
+ }
+
+ ret = test_accept_cancel(10000, 4, true);
+ if (ret) {
+ fprintf(stderr, "test_accept_cancel multishot delay failed\n");
+ return ret;
+ }
+
+ ret = test_multishot_accept(1, true, true);
+ if (ret) {
+ fprintf(stderr, "test_multishot_accept(1, false, true) failed\n");
+ return ret;
+ }
+
+ ret = test_multishot_accept(1, false, false);
+ if (ret) {
+ fprintf(stderr, "test_multishot_accept(1, false, false) failed\n");
+ return ret;
+ }
+
+ ret = test_multishot_accept(1, true, false);
+ if (ret) {
+ fprintf(stderr, "test_multishot_accept(1, true, false) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_many((struct test_accept_many_args) {});
+ if (ret) {
+ fprintf(stderr, "test_accept_many failed\n");
+ return ret;
+ }
+
+ ret = test_accept_many((struct test_accept_many_args) {
+ .usecs = 100000 });
+ if (ret) {
+ fprintf(stderr, "test_accept_many(sleep) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_many((struct test_accept_many_args) {
+ .nonblock = true });
+ if (ret) {
+ fprintf(stderr, "test_accept_many(nonblock) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_many((struct test_accept_many_args) {
+ .nonblock = true,
+ .single_sock = true,
+ .close_fds = true });
+ if (ret) {
+ fprintf(stderr, "test_accept_many(nonblock,close) failed\n");
+ return ret;
+ }
+
+ ret = test_accept_pending_on_exit();
+ if (ret) {
+ fprintf(stderr, "test_accept_pending_on_exit failed\n");
+ return ret;
+ }
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/across-fork.c b/contrib/libs/liburing/test/across-fork.c
new file mode 100644
index 00000000000..8d6d9a784ff
--- /dev/null
+++ b/contrib/libs/liburing/test/across-fork.c
@@ -0,0 +1,285 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test sharing a ring across a fork
+ */
+#include <fcntl.h>
+#include <pthread.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+
+struct forktestmem
+{
+ struct io_uring ring;
+ pthread_barrier_t barrier;
+ pthread_barrierattr_t barrierattr;
+};
+
+static int open_tempfile(const char *dir, const char *fname)
+{
+ int fd;
+ char buf[32];
+
+ snprintf(buf, sizeof(buf), "%s/%s",
+ dir, fname);
+ fd = open(buf, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR);
+ if (fd < 0) {
+ perror("open");
+ exit(1);
+ }
+
+ return fd;
+}
+
+static int submit_write(struct io_uring *ring, int fd, const char *str,
+ int wait)
+{
+ struct io_uring_sqe *sqe;
+ struct iovec iovec;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "could not get sqe\n");
+ return 1;
+ }
+
+ iovec.iov_base = (char *) str;
+ iovec.iov_len = strlen(str);
+ io_uring_prep_writev(sqe, fd, &iovec, 1, 0);
+ ret = io_uring_submit_and_wait(ring, wait);
+ if (ret < 0) {
+ fprintf(stderr, "submit failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int wait_cqe(struct io_uring *ring, const char *stage)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "%s wait_cqe failed %d\n", stage, ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "%s cqe failed %d\n", stage, cqe->res);
+ return 1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+static int verify_file(const char *tmpdir, const char *fname, const char* expect)
+{
+ int fd;
+ char buf[512];
+ int err = 0;
+
+ memset(buf, 0, sizeof(buf));
+
+ fd = open_tempfile(tmpdir, fname);
+ if (fd < 0)
+ return 1;
+
+ if (read(fd, buf, sizeof(buf) - 1) < 0)
+ return 1;
+
+ if (strcmp(buf, expect) != 0) {
+ fprintf(stderr, "content mismatch for %s\n"
+ "got:\n%s\n"
+ "expected:\n%s\n",
+ fname, buf, expect);
+ err = 1;
+ }
+
+ close(fd);
+ return err;
+}
+
+static void cleanup(const char *tmpdir)
+{
+ char buf[32];
+
+ /* don't check errors, called during partial runs */
+
+ snprintf(buf, sizeof(buf), "%s/%s", tmpdir, "shared");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s", tmpdir, "parent1");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s", tmpdir, "parent2");
+ unlink(buf);
+
+ snprintf(buf, sizeof(buf), "%s/%s", tmpdir, "child");
+ unlink(buf);
+
+ rmdir(tmpdir);
+}
+
+int main(int argc, char *argv[])
+{
+ struct forktestmem *shmem;
+ char tmpdir[] = "forktmpXXXXXX";
+ int shared_fd;
+ int ret;
+ pid_t p;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ shmem = mmap(0, sizeof(struct forktestmem), PROT_READ|PROT_WRITE,
+ MAP_SHARED | MAP_ANONYMOUS, 0, 0);
+ if (!shmem) {
+ fprintf(stderr, "mmap failed\n");
+ exit(T_EXIT_FAIL);
+ }
+
+ pthread_barrierattr_init(&shmem->barrierattr);
+ pthread_barrierattr_setpshared(&shmem->barrierattr, 1);
+ pthread_barrier_init(&shmem->barrier, &shmem->barrierattr, 2);
+
+ ret = io_uring_queue_init(10, &shmem->ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "queue init failed\n");
+ exit(T_EXIT_FAIL);
+ }
+
+ if (mkdtemp(tmpdir) == NULL) {
+ fprintf(stderr, "temp directory creation failed\n");
+ exit(T_EXIT_FAIL);
+ }
+
+ shared_fd = open_tempfile(tmpdir, "shared");
+
+ /*
+ * First do a write before the fork, to test whether child can
+ * reap that
+ */
+ if (submit_write(&shmem->ring, shared_fd, "before fork: write shared fd\n", 0))
+ goto errcleanup;
+
+ p = fork();
+ switch (p) {
+ case -1:
+ fprintf(stderr, "fork failed\n");
+ goto errcleanup;
+
+ default: {
+ /* parent */
+ int parent_fd1;
+ int parent_fd2;
+ int wstatus;
+
+ /* wait till fork is started up */
+ pthread_barrier_wait(&shmem->barrier);
+
+ parent_fd1 = open_tempfile(tmpdir, "parent1");
+ parent_fd2 = open_tempfile(tmpdir, "parent2");
+
+ /* do a parent write to the shared fd */
+ if (submit_write(&shmem->ring, shared_fd, "parent: write shared fd\n", 0))
+ goto errcleanup;
+
+ /* do a parent write to an fd where same numbered fd exists in child */
+ if (submit_write(&shmem->ring, parent_fd1, "parent: write parent fd 1\n", 0))
+ goto errcleanup;
+
+ /* do a parent write to an fd where no same numbered fd exists in child */
+ if (submit_write(&shmem->ring, parent_fd2, "parent: write parent fd 2\n", 0))
+ goto errcleanup;
+
+ /* wait to switch read/writ roles with child */
+ pthread_barrier_wait(&shmem->barrier);
+
+ /* now wait for child to exit, to ensure we still can read completion */
+ waitpid(p, &wstatus, 0);
+ if (WEXITSTATUS(wstatus) != 0) {
+ fprintf(stderr, "child failed\n");
+ goto errcleanup;
+ }
+
+ if (wait_cqe(&shmem->ring, "p cqe 1"))
+ goto errcleanup;
+
+ if (wait_cqe(&shmem->ring, "p cqe 2"))
+ goto errcleanup;
+
+ /* check that IO can still be submitted after child exited */
+ if (submit_write(&shmem->ring, shared_fd, "parent: write shared fd after child exit\n", 0))
+ goto errcleanup;
+
+ if (wait_cqe(&shmem->ring, "p cqe 3"))
+ goto errcleanup;
+
+ break;
+ }
+ case 0: {
+ /* child */
+ int child_fd;
+
+ /* wait till fork is started up */
+ pthread_barrier_wait(&shmem->barrier);
+
+ child_fd = open_tempfile(tmpdir, "child");
+
+ if (wait_cqe(&shmem->ring, "c cqe shared"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 1"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 2"))
+ exit(1);
+
+ if (wait_cqe(&shmem->ring, "c cqe parent 3"))
+ exit(1);
+
+ /* wait to switch read/writ roles with parent */
+ pthread_barrier_wait(&shmem->barrier);
+
+ if (submit_write(&shmem->ring, child_fd, "child: write child fd\n", 0))
+ exit(1);
+
+ /* ensure both writes have finished before child exits */
+ if (submit_write(&shmem->ring, shared_fd, "child: write shared fd\n", 2))
+ exit(1);
+
+ exit(0);
+ }
+ }
+
+ if (verify_file(tmpdir, "shared",
+ "before fork: write shared fd\n"
+ "parent: write shared fd\n"
+ "child: write shared fd\n"
+ "parent: write shared fd after child exit\n") ||
+ verify_file(tmpdir, "parent1", "parent: write parent fd 1\n") ||
+ verify_file(tmpdir, "parent2", "parent: write parent fd 2\n") ||
+ verify_file(tmpdir, "child", "child: write child fd\n"))
+ goto errcleanup;
+
+ cleanup(tmpdir);
+ exit(T_EXIT_PASS);
+
+errcleanup:
+ cleanup(tmpdir);
+ exit(T_EXIT_FAIL);
+}
diff --git a/contrib/libs/liburing/test/b19062a56726.c b/contrib/libs/liburing/test/b19062a56726.c
new file mode 100644
index 00000000000..e065e30f6ec
--- /dev/null
+++ b/contrib/libs/liburing/test/b19062a56726.c
@@ -0,0 +1,55 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <endian.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ mmap((void *) 0x20000000, 0x1000000, 3, 0x32, -1, 0);
+
+ *(uint32_t*)0x20000200 = 0;
+ *(uint32_t*)0x20000204 = 0;
+ *(uint32_t*)0x20000208 = 5;
+ *(uint32_t*)0x2000020c = 0x400;
+ *(uint32_t*)0x20000210 = 0;
+ *(uint32_t*)0x20000214 = 0;
+ *(uint32_t*)0x20000218 = 0;
+ *(uint32_t*)0x2000021c = 0;
+ *(uint32_t*)0x20000220 = 0;
+ *(uint32_t*)0x20000224 = 0;
+ *(uint32_t*)0x20000228 = 0;
+ *(uint32_t*)0x2000022c = 0;
+ *(uint32_t*)0x20000230 = 0;
+ *(uint32_t*)0x20000234 = 0;
+ *(uint32_t*)0x20000238 = 0;
+ *(uint32_t*)0x2000023c = 0;
+ *(uint32_t*)0x20000240 = 0;
+ *(uint32_t*)0x20000244 = 0;
+ *(uint64_t*)0x20000248 = 0;
+ *(uint32_t*)0x20000250 = 0;
+ *(uint32_t*)0x20000254 = 0;
+ *(uint32_t*)0x20000258 = 0;
+ *(uint32_t*)0x2000025c = 0;
+ *(uint32_t*)0x20000260 = 0;
+ *(uint32_t*)0x20000264 = 0;
+ *(uint32_t*)0x20000268 = 0;
+ *(uint32_t*)0x2000026c = 0;
+ *(uint64_t*)0x20000270 = 0;
+ __sys_io_uring_setup(0xc9f, (struct io_uring_params *) 0x20000200);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/b5837bd5311d.c b/contrib/libs/liburing/test/b5837bd5311d.c
new file mode 100644
index 00000000000..1c332078ead
--- /dev/null
+++ b/contrib/libs/liburing/test/b5837bd5311d.c
@@ -0,0 +1,79 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Check to see if wait_nr is being honored.
+ */
+#include <stdio.h>
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+ struct __kernel_timespec ts = {
+ .tv_sec = 0,
+ .tv_nsec = 10000000
+ };
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (io_uring_queue_init(4, &ring, 0) != 0) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * First, submit the timeout sqe so we can actually finish the test
+ * if everything is in working order.
+ */
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_timeout(sqe, &ts, (unsigned)-1, 0);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "Got submit %d, expected 1\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * Next, submit a nop and wait for two events. If everything is working
+ * as it should, we should be waiting for more than a millisecond and we
+ * should see two cqes. Otherwise, execution continues immediately
+ * and we see only one cqe.
+ */
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit_and_wait(&ring, 2);
+ if (ret != 1) {
+ fprintf(stderr, "Got submit %d, expected 1\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (io_uring_peek_cqe(&ring, &cqe) != 0) {
+ fprintf(stderr, "Unable to peek cqe!\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+
+ if (io_uring_peek_cqe(&ring, &cqe) != 0) {
+ fprintf(stderr, "Unable to peek cqe!\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/buf-ring.c b/contrib/libs/liburing/test/buf-ring.c
new file mode 100644
index 00000000000..569639a347e
--- /dev/null
+++ b/contrib/libs/liburing/test/buf-ring.c
@@ -0,0 +1,421 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various shared buffer ring sanity checks
+ *
+ */
+#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_buf_ring;
+
+/* test trying to register classic group when ring group exists */
+static int test_mixed_reg2(int bgid)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ void *ptr, *bufs;
+ int ret;
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ if (posix_memalign(&ptr, 4096, 4096))
+ return 1;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ /* provide classic buffers, group 1 */
+ bufs = malloc(8 * 1024);
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, bufs, 1024, 8, bgid, 0);
+ io_uring_submit(&ring);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe %d\n", ret);
+ return 1;
+ }
+ if (cqe->res != -EEXIST && cqe->res != -EINVAL) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+/* test trying to register ring group when classic group exists */
+static int test_mixed_reg(int bgid)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ void *ptr, *bufs;
+ int ret;
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ /* provide classic buffers, group 1 */
+ bufs = malloc(8 * 1024);
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, bufs, 1024, 8, bgid, 0);
+ io_uring_submit(&ring);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe %d\n", ret);
+ return 1;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ if (posix_memalign(&ptr, 4096, 4096))
+ return 1;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret != -EEXIST) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_double_reg_unreg(int bgid)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring ring;
+ void *ptr;
+ int ret;
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ if (posix_memalign(&ptr, 4096, 4096))
+ return 1;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ /* check that 2nd register with same bgid fails */
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret != -EEXIST) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_unregister_buf_ring(&ring, bgid);
+ if (ret) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_unregister_buf_ring(&ring, bgid);
+ if (ret != -EINVAL && ret != -ENOENT) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_reg_unreg(int bgid)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring ring;
+ void *ptr;
+ int ret;
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ if (posix_memalign(&ptr, 4096, 4096))
+ return 1;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret) {
+ if (ret == -EINVAL) {
+ no_buf_ring = 1;
+ return 0;
+ }
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_unregister_buf_ring(&ring, bgid);
+ if (ret) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_bad_reg(int bgid)
+{
+ struct io_uring ring;
+ int ret;
+ struct io_uring_buf_reg reg = { };
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ reg.ring_addr = 4096;
+ reg.ring_entries = 32;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (!ret)
+ fprintf(stderr, "Buffer ring register worked unexpectedly\n");
+
+ io_uring_queue_exit(&ring);
+ return !ret;
+}
+
+static int test_one_read(int fd, int bgid, struct io_uring *ring)
+{
+ int ret;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return -1;
+ }
+
+ io_uring_prep_read(sqe, fd, NULL, 1, 0);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = bgid;
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return -1;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+
+ if (ret == -ENOBUFS)
+ return ret;
+
+ if (ret != 1) {
+ fprintf(stderr, "read result %d\n", ret);
+ return -1;
+ }
+
+ return cqe->flags >> 16;
+}
+
+static int test_running(int bgid, int entries, int loops)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring ring;
+ void *ptr;
+ char buffer[8];
+ int ret;
+ int ring_size = (entries * sizeof(struct io_uring_buf) + 4095) & (~4095);
+ int ring_mask = io_uring_buf_ring_mask(entries);
+
+ int loop, idx;
+ bool *buffers;
+ struct io_uring_buf_ring *br;
+ int read_fd;
+
+ ret = t_create_ring(1, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ if (posix_memalign(&ptr, 4096, ring_size))
+ return 1;
+
+ br = (struct io_uring_buf_ring *)ptr;
+ io_uring_buf_ring_init(br);
+
+ buffers = malloc(sizeof(bool) * entries);
+ if (!buffers)
+ return 1;
+
+ read_fd = open("/dev/zero", O_RDONLY);
+ if (read_fd < 0)
+ return 1;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = entries;
+ reg.bgid = bgid;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret) {
+ /* by now should have checked if this is supported or not */
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ for (loop = 0; loop < loops; loop++) {
+ memset(buffers, 0, sizeof(bool) * entries);
+ for (idx = 0; idx < entries; idx++)
+ io_uring_buf_ring_add(br, buffer, sizeof(buffer), idx, ring_mask, idx);
+ io_uring_buf_ring_advance(br, entries);
+
+ for (idx = 0; idx < entries; idx++) {
+ memset(buffer, 1, sizeof(buffer));
+ ret = test_one_read(read_fd, bgid, &ring);
+ if (ret < 0) {
+ fprintf(stderr, "bad run %d/%d = %d\n", loop, idx, ret);
+ return ret;
+ }
+ if (buffers[ret]) {
+ fprintf(stderr, "reused buffer %d/%d = %d!\n", loop, idx, ret);
+ return 1;
+ }
+ if (buffer[0] != 0) {
+ fprintf(stderr, "unexpected read %d %d/%d = %d!\n",
+ (int)buffer[0], loop, idx, ret);
+ return 1;
+ }
+ if (buffer[1] != 1) {
+ fprintf(stderr, "unexpected spilled read %d %d/%d = %d!\n",
+ (int)buffer[1], loop, idx, ret);
+ return 1;
+ }
+ buffers[ret] = true;
+ }
+ ret = test_one_read(read_fd, bgid, &ring);
+ if (ret != -ENOBUFS) {
+ fprintf(stderr, "expected enobufs run %d = %d\n", loop, ret);
+ return 1;
+ }
+
+ }
+
+ ret = io_uring_unregister_buf_ring(&ring, bgid);
+ if (ret) {
+ fprintf(stderr, "Buffer ring register failed %d\n", ret);
+ return 1;
+ }
+
+ close(read_fd);
+ io_uring_queue_exit(&ring);
+ free(buffers);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int bgids[] = { 1, 127, -1 };
+ int entries[] = {1, 32768, 4096, -1 };
+ int ret, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ for (i = 0; bgids[i] != -1; i++) {
+ ret = test_reg_unreg(bgids[i]);
+ if (ret) {
+ fprintf(stderr, "test_reg_unreg failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_buf_ring)
+ break;
+
+ ret = test_bad_reg(bgids[i]);
+ if (ret) {
+ fprintf(stderr, "test_bad_reg failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_double_reg_unreg(bgids[i]);
+ if (ret) {
+ fprintf(stderr, "test_double_reg_unreg failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_mixed_reg(bgids[i]);
+ if (ret) {
+ fprintf(stderr, "test_mixed_reg failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_mixed_reg2(bgids[i]);
+ if (ret) {
+ fprintf(stderr, "test_mixed_reg2 failed\n");
+ return T_EXIT_FAIL;
+ }
+ }
+
+ for (i = 0; !no_buf_ring && entries[i] != -1; i++) {
+ ret = test_running(2, entries[i], 3);
+ if (ret) {
+ fprintf(stderr, "test_running(%d) failed\n", entries[i]);
+ return T_EXIT_FAIL;
+ }
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/ce593a6c480a.c b/contrib/libs/liburing/test/ce593a6c480a.c
new file mode 100644
index 00000000000..7b6fc438ae3
--- /dev/null
+++ b/contrib/libs/liburing/test/ce593a6c480a.c
@@ -0,0 +1,140 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test 5.7 regression with task_work not being run while a task is
+ * waiting on another event in the kernel.
+ */
+#include <errno.h>
+#include <poll.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <pthread.h>
+#include "liburing.h"
+#include "helpers.h"
+
+static int use_sqpoll = 0;
+
+void notify_fd(int fd)
+{
+ char buf[8] = {0, 0, 0, 0, 0, 0, 1};
+ int ret;
+
+ ret = write(fd, &buf, 8);
+ if (ret < 0)
+ perror("write");
+}
+
+void *delay_set_fd_from_thread(void *data)
+{
+ int fd = (intptr_t) data;
+
+ sleep(1);
+ notify_fd(fd);
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct io_uring ring;
+ int loop_fd, other_fd;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe = NULL;
+ int ret, use_fd;
+ char buf[8] = {0, 0, 0, 0, 0, 0, 1};
+ pthread_t tid;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ /* Create an eventfd to be registered with the loop to be
+ * notified of events being ready
+ */
+ loop_fd = eventfd(0, EFD_CLOEXEC);
+ if (loop_fd == -1) {
+ fprintf(stderr, "eventfd errno=%d\n", errno);
+ return T_EXIT_FAIL;
+ }
+
+ /* Create an eventfd that can create events */
+ use_fd = other_fd = eventfd(0, EFD_CLOEXEC);
+ if (other_fd == -1) {
+ fprintf(stderr, "eventfd errno=%d\n", errno);
+ return T_EXIT_FAIL;
+ }
+
+ if (use_sqpoll)
+ p.flags = IORING_SETUP_SQPOLL;
+
+ /* Setup the ring with a registered event fd to be notified on events */
+ ret = t_create_ring_params(8, &ring, &p);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_PASS;
+ else if (ret < 0)
+ return ret;
+
+ ret = io_uring_register_eventfd(&ring, loop_fd);
+ if (ret < 0) {
+ fprintf(stderr, "register_eventfd=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (use_sqpoll) {
+ ret = io_uring_register_files(&ring, &other_fd, 1);
+ if (ret < 0) {
+ fprintf(stderr, "register_files=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ use_fd = 0;
+ }
+
+ /* Submit a poll operation to wait on an event in other_fd */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, use_fd, POLLIN);
+ sqe->user_data = 1;
+ if (use_sqpoll)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /*
+ * CASE 3: Hangs forever in Linux 5.7.5; Works in Linux 5.6.0 When this
+ * code is uncommented, we don't se a notification on other_fd until
+ * _after_ we have started the read on loop_fd. In that case, the read() on
+ * loop_fd seems to hang forever.
+ */
+ pthread_create(&tid, NULL, delay_set_fd_from_thread,
+ (void*) (intptr_t) other_fd);
+
+ /* Wait on the event fd for an event to be ready */
+ do {
+ ret = read(loop_fd, buf, 8);
+ } while (ret < 0 && errno == EINTR);
+
+ if (ret < 0) {
+ perror("read");
+ return T_EXIT_FAIL;
+ } else if (ret != 8) {
+ fprintf(stderr, "Odd-sized eventfd read: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return ret;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/close-opath.c b/contrib/libs/liburing/test/close-opath.c
new file mode 100644
index 00000000000..85fe30d9df6
--- /dev/null
+++ b/contrib/libs/liburing/test/close-opath.c
@@ -0,0 +1,123 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+
+#define _GNU_SOURCE 1
+#define _FILE_OFFSET_BITS 64
+
+// Test program for io_uring IORING_OP_CLOSE with O_PATH file.
+// Author: Clayton Harris <bugs@claycon.org>, 2020-06-07
+
+// linux 5.6.14-300.fc32.x86_64
+// gcc 10.1.1-1.fc32
+// liburing.x86_64 0.5-1.fc32
+
+// gcc -O2 -Wall -Wextra -std=c11 -o close_opath close_opath.c -luring
+// ./close_opath testfilepath
+
+#include <errno.h>
+#include <fcntl.h>
+#include <liburing.h>
+#include <sys/stat.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef struct
+{
+ const char *const flnames;
+ const int oflags;
+} oflgs_t;
+
+static int test_io_uring_close(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "io_uring_get_sqe() failed\n");
+ return -ENOENT;
+ }
+
+ io_uring_prep_close(sqe, fd);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_submit() failed, errno %d: %s\n",
+ -ret, strerror(-ret));
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_wait_cqe() failed, errno %d: %s\n",
+ -ret, strerror(-ret));
+ return ret;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+
+ if (ret < 0 && ret != -EOPNOTSUPP && ret != -EINVAL && ret != -EBADF) {
+ fprintf(stderr, "io_uring close() failed, errno %d: %s\n",
+ -ret, strerror(-ret));
+ return ret;
+ }
+
+ return 0;
+}
+
+static int open_file(const char *path, const oflgs_t *oflgs)
+{
+ int fd;
+
+ fd = openat(AT_FDCWD, path, oflgs->oflags, 0);
+ if (fd < 0) {
+ int err = errno;
+ fprintf(stderr, "openat(%s, %s) failed, errno %d: %s\n",
+ path, oflgs->flnames, err, strerror(err));
+ return -err;
+ }
+
+ return fd;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *fname = ".";
+ struct io_uring ring;
+ int ret, i;
+ static const oflgs_t oflgs[] = {
+ { "O_RDONLY", O_RDONLY },
+ { "O_PATH", O_PATH }
+ };
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_queue_init() failed, errno %d: %s\n",
+ -ret, strerror(-ret));
+ return 0x02;
+ }
+
+#define OFLGS_SIZE (sizeof(oflgs) / sizeof(oflgs[0]))
+
+ ret = 0;
+ for (i = 0; i < OFLGS_SIZE; i++) {
+ int fd;
+
+ fd = open_file(fname, &oflgs[i]);
+ if (fd < 0) {
+ ret |= 0x02;
+ break;
+ }
+
+ /* Should always succeed */
+ if (test_io_uring_close(&ring, fd) < 0)
+ ret |= 0x04 << i;
+ }
+#undef OFLGS_SIZE
+
+ io_uring_queue_exit(&ring);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/connect.c b/contrib/libs/liburing/test/connect.c
new file mode 100644
index 00000000000..30f3ac1c3ff
--- /dev/null
+++ b/contrib/libs/liburing/test/connect.c
@@ -0,0 +1,400 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that IORING_OP_CONNECT works, with and without other side
+ * being open.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int no_connect;
+static unsigned short use_port;
+static unsigned int use_addr;
+
+static int create_socket(void)
+{
+ int fd;
+
+ fd = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (fd == -1) {
+ perror("socket()");
+ return -1;
+ }
+
+ return fd;
+}
+
+static int submit_and_wait(struct io_uring *ring, int *res)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_submit_and_wait(ring, 1);
+ if (ret != 1) {
+ fprintf(stderr, "io_using_submit: got %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_peek_cqe(): no cqe returned");
+ return 1;
+ }
+
+ *res = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+static int wait_for(struct io_uring *ring, int fd, int mask)
+{
+ struct io_uring_sqe *sqe;
+ int ret, res;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "unable to get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_poll_add(sqe, fd, mask);
+ sqe->user_data = 2;
+
+ ret = submit_and_wait(ring, &res);
+ if (ret)
+ return -1;
+
+ if (res < 0) {
+ fprintf(stderr, "poll(): failed with %d\n", res);
+ return -1;
+ }
+
+ return res;
+}
+
+static int listen_on_socket(int fd)
+{
+ struct sockaddr_in addr;
+ int ret;
+
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_port = use_port;
+ addr.sin_addr.s_addr = use_addr;
+
+ ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
+ if (ret == -1) {
+ perror("bind()");
+ return -1;
+ }
+
+ ret = listen(fd, 128);
+ if (ret == -1) {
+ perror("listen()");
+ return -1;
+ }
+
+ return 0;
+}
+
+static int configure_connect(int fd, struct sockaddr_in* addr)
+{
+ int ret, val = 1;
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ if (ret == -1) {
+ perror("setsockopt()");
+ return -1;
+ }
+
+ ret = setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ if (ret == -1) {
+ perror("setsockopt()");
+ return -1;
+ }
+
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = use_port;
+ ret = inet_aton("127.0.0.1", &addr->sin_addr);
+ return ret;
+}
+
+static int connect_socket(struct io_uring *ring, int fd, int *code)
+{
+ struct sockaddr_in addr;
+ int ret, res;
+ socklen_t code_len = sizeof(*code);
+ struct io_uring_sqe *sqe;
+
+ if (configure_connect(fd, &addr) == -1)
+ return -1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "unable to get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_connect(sqe, fd, (struct sockaddr*)&addr, sizeof(addr));
+ sqe->user_data = 1;
+
+ ret = submit_and_wait(ring, &res);
+ if (ret)
+ return -1;
+
+ if (res == -EINPROGRESS) {
+ ret = wait_for(ring, fd, POLLOUT | POLLHUP | POLLERR);
+ if (ret == -1)
+ return -1;
+
+ int ev = (ret & POLLOUT) || (ret & POLLHUP) || (ret & POLLERR);
+ if (!ev) {
+ fprintf(stderr, "poll(): returned invalid value %#x\n", ret);
+ return -1;
+ }
+
+ ret = getsockopt(fd, SOL_SOCKET, SO_ERROR, code, &code_len);
+ if (ret == -1) {
+ perror("getsockopt()");
+ return -1;
+ }
+ } else
+ *code = res;
+ return 0;
+}
+
+static int test_connect_with_no_peer(struct io_uring *ring)
+{
+ int connect_fd;
+ int ret, code;
+
+ connect_fd = create_socket();
+ if (connect_fd == -1)
+ return -1;
+
+ ret = connect_socket(ring, connect_fd, &code);
+ if (ret == -1)
+ goto err;
+
+ if (code != -ECONNREFUSED) {
+ if (code == -EINVAL || code == -EBADF || code == -EOPNOTSUPP) {
+ fprintf(stdout, "No connect support, skipping\n");
+ no_connect = 1;
+ goto out;
+ }
+ fprintf(stderr, "connect failed with %d\n", code);
+ goto err;
+ }
+
+out:
+ close(connect_fd);
+ return 0;
+
+err:
+ close(connect_fd);
+ return -1;
+}
+
+static int test_connect(struct io_uring *ring)
+{
+ int accept_fd;
+ int connect_fd;
+ int ret, code;
+
+ accept_fd = create_socket();
+ if (accept_fd == -1)
+ return -1;
+
+ ret = listen_on_socket(accept_fd);
+ if (ret == -1)
+ goto err1;
+
+ connect_fd = create_socket();
+ if (connect_fd == -1)
+ goto err1;
+
+ ret = connect_socket(ring, connect_fd, &code);
+ if (ret == -1)
+ goto err2;
+
+ if (code != 0) {
+ fprintf(stderr, "connect failed with %d\n", code);
+ goto err2;
+ }
+
+ close(connect_fd);
+ close(accept_fd);
+
+ return 0;
+
+err2:
+ close(connect_fd);
+
+err1:
+ close(accept_fd);
+ return -1;
+}
+
+static int test_connect_timeout(struct io_uring *ring)
+{
+ int connect_fd[2] = {-1, -1};
+ int accept_fd = -1;
+ int ret, code;
+ struct sockaddr_in addr;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 100000};
+
+ connect_fd[0] = create_socket();
+ if (connect_fd[0] == -1)
+ return -1;
+
+ connect_fd[1] = create_socket();
+ if (connect_fd[1] == -1)
+ goto err;
+
+ accept_fd = create_socket();
+ if (accept_fd == -1)
+ goto err;
+
+ if (configure_connect(connect_fd[0], &addr) == -1)
+ goto err;
+
+ if (configure_connect(connect_fd[1], &addr) == -1)
+ goto err;
+
+ ret = bind(accept_fd, (struct sockaddr*)&addr, sizeof(addr));
+ if (ret == -1) {
+ perror("bind()");
+ goto err;
+ }
+
+ ret = listen(accept_fd, 0); // no backlog in order to block connect_fd[1]
+ if (ret == -1) {
+ perror("listen()");
+ goto err;
+ }
+
+ // We first connect with one client socket in order to fill the accept queue.
+ ret = connect_socket(ring, connect_fd[0], &code);
+ if (ret == -1 || code != 0) {
+ fprintf(stderr, "unable to connect\n");
+ goto err;
+ }
+
+ // We do not offload completion events from listening socket on purpose.
+ // This way we create a state where the second connect request being stalled by OS.
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "unable to get sqe\n");
+ goto err;
+ }
+
+ io_uring_prep_connect(sqe, connect_fd[1], (struct sockaddr*)&addr, sizeof(addr));
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "unable to get sqe\n");
+ goto err;
+ }
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ fprintf(stderr, "submitted %d\n", ret);
+ return -1;
+ }
+
+ for (int i = 0; i < 2; i++) {
+ int expected;
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return -1;
+ }
+
+ expected = (cqe->user_data == 1) ? -ECANCELED : -ETIME;
+ if (expected != cqe->res) {
+ fprintf(stderr, "cqe %d, res %d, wanted %d\n",
+ (int)cqe->user_data, cqe->res, expected);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(connect_fd[0]);
+ close(connect_fd[1]);
+ close(accept_fd);
+ return 0;
+
+err:
+ if (connect_fd[0] != -1)
+ close(connect_fd[0]);
+ if (connect_fd[1] != -1)
+ close(connect_fd[1]);
+
+ if (accept_fd != -1)
+ close(accept_fd);
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_setup() = %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ srand(getpid());
+ use_port = (rand() % 61440) + 4096;
+ use_port = htons(use_port);
+ use_addr = inet_addr("127.0.0.1");
+
+ ret = test_connect_with_no_peer(&ring);
+ if (ret == -1) {
+ fprintf(stderr, "test_connect_with_no_peer(): failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_connect)
+ return T_EXIT_SKIP;
+
+ ret = test_connect(&ring);
+ if (ret == -1) {
+ fprintf(stderr, "test_connect(): failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_connect_timeout(&ring);
+ if (ret == -1) {
+ fprintf(stderr, "test_connect_timeout(): failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/cq-full.c b/contrib/libs/liburing/test/cq-full.c
new file mode 100644
index 00000000000..a14b22d1edf
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-full.c
@@ -0,0 +1,98 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test CQ ring overflow
+ *
+ */
+#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 queue_n_nops(struct io_uring *ring, int n)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < n) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ } else if (ret < 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_params p;
+ struct io_uring ring;
+ int i, ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ memset(&p, 0, sizeof(p));
+ ret = io_uring_queue_init_params(4, &ring, &p);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ if (queue_n_nops(&ring, 4))
+ goto err;
+ if (queue_n_nops(&ring, 4))
+ goto err;
+ if (queue_n_nops(&ring, 4))
+ goto err;
+
+ i = 0;
+ do {
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret < 0) {
+ if (ret == -EAGAIN)
+ break;
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ if (!cqe)
+ break;
+ i++;
+ } while (1);
+
+ if (i < 8 ||
+ ((*ring.cq.koverflow != 4) && !(p.features & IORING_FEAT_NODROP))) {
+ printf("CQ overflow fail: %d completions, %u overflow\n", i,
+ *ring.cq.koverflow);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/cq-overflow.c b/contrib/libs/liburing/test/cq-overflow.c
new file mode 100644
index 00000000000..f43e19c013f
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-overflow.c
@@ -0,0 +1,525 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various CQ ring overflow tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (256 * 1024)
+#define BS 4096
+#define BUFFERS (FILE_SIZE / BS)
+
+static struct iovec *vecs;
+
+#define ENTRIES 8
+
+/*
+ * io_uring has rare cases where CQEs are lost.
+ * This happens when there is no space in the CQ ring, and also there is no
+ * GFP_ATOMIC memory available. In reality this probably means that the process
+ * is about to be killed as many other things might start failing, but we still
+ * want to test that liburing and the kernel deal with this properly. The fault
+ * injection framework allows us to test this scenario. Unfortunately this
+ * requires some system wide changes and so we do not enable this by default.
+ * The tests in this file should work in both cases (where overflows are queued
+ * and where they are dropped) on recent kernels.
+ *
+ * In order to test dropped CQEs you should enable fault injection in the kernel
+ * config:
+ *
+ * CONFIG_FAULT_INJECTION=y
+ * CONFIG_FAILSLAB=y
+ * CONFIG_FAULT_INJECTION_DEBUG_FS=y
+ *
+ * and then run the test as follows:
+ * echo Y > /sys/kernel/debug/failslab/task-filter
+ * echo 100 > /sys/kernel/debug/failslab/probability
+ * echo 0 > /sys/kernel/debug/failslab/verbose
+ * echo 100000 > /sys/kernel/debug/failslab/times
+ * bash -c "echo 1 > /proc/self/make-it-fail && exec ./cq-overflow.t"
+ */
+
+static int test_io(const char *file, unsigned long usecs, unsigned *drops, int fault)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring_params p;
+ unsigned reaped, total;
+ struct io_uring ring;
+ int nodrop, i, fd, ret;
+ bool cqe_dropped = false;
+
+ fd = open(file, O_RDONLY | O_DIRECT);
+ if (fd < 0) {
+ perror("file open");
+ return 1;
+ }
+
+ memset(&p, 0, sizeof(p));
+ ret = io_uring_queue_init_params(ENTRIES, &ring, &p);
+ if (ret) {
+ close(fd);
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ nodrop = 0;
+ if (p.features & IORING_FEAT_NODROP)
+ nodrop = 1;
+
+ total = 0;
+ for (i = 0; i < BUFFERS / 2; i++) {
+ off_t offset;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ offset = BS * (rand() % BUFFERS);
+ if (fault && i == ENTRIES + 4)
+ vecs[i].iov_base = NULL;
+ io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
+
+ ret = io_uring_submit(&ring);
+ if (nodrop && ret == -EBUSY) {
+ *drops = 1;
+ total = i;
+ break;
+ } else if (ret != 1) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
+ total = i;
+ break;
+ }
+ total++;
+ }
+
+ if (*drops)
+ goto reap_it;
+
+ usleep(usecs);
+
+ for (i = total; i < BUFFERS; i++) {
+ off_t offset;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ offset = BS * (rand() % BUFFERS);
+ io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
+
+ ret = io_uring_submit(&ring);
+ if (nodrop && ret == -EBUSY) {
+ *drops = 1;
+ break;
+ } else if (ret != 1) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
+ break;
+ }
+ total++;
+ }
+
+reap_it:
+ reaped = 0;
+ do {
+ if (nodrop && !cqe_dropped) {
+ /* nodrop should never lose events unless cqe_dropped */
+ if (reaped == total)
+ break;
+ } else {
+ if (reaped + *ring.cq.koverflow == total)
+ break;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (nodrop && ret == -EBADR) {
+ cqe_dropped = true;
+ continue;
+ } else if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res != BS) {
+ if (!(fault && cqe->res == -EFAULT)) {
+ fprintf(stderr, "cqe res %d, wanted %d\n",
+ cqe->res, BS);
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ reaped++;
+ } while (1);
+
+ if (!io_uring_peek_cqe(&ring, &cqe)) {
+ fprintf(stderr, "found unexpected completion\n");
+ goto err;
+ }
+
+ if (!nodrop || cqe_dropped) {
+ *drops = *ring.cq.koverflow;
+ } else if (*ring.cq.koverflow) {
+ fprintf(stderr, "Found %u overflows\n", *ring.cq.koverflow);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+static int reap_events(struct io_uring *ring, unsigned nr_events, int do_wait)
+{
+ struct io_uring_cqe *cqe;
+ int i, ret = 0, seq = 0;
+ unsigned int start_overflow = *ring->cq.koverflow;
+ bool dropped = false;
+
+ for (i = 0; i < nr_events; i++) {
+ if (do_wait)
+ ret = io_uring_wait_cqe(ring, &cqe);
+ else
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (do_wait && ret == -EBADR) {
+ unsigned int this_drop = *ring->cq.koverflow -
+ start_overflow;
+
+ dropped = true;
+ start_overflow = *ring->cq.koverflow;
+ assert(this_drop > 0);
+ i += (this_drop - 1);
+ continue;
+ } else if (ret) {
+ if (ret != -EAGAIN)
+ fprintf(stderr, "cqe peek failed: %d\n", ret);
+ break;
+ }
+ if (!dropped && cqe->user_data != seq) {
+ fprintf(stderr, "cqe sequence out-of-order\n");
+ fprintf(stderr, "got %d, wanted %d\n", (int) cqe->user_data,
+ seq);
+ return -EINVAL;
+ }
+ seq++;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return i ? i : ret;
+}
+
+/*
+ * Submit some NOPs and watch if the overflow is correct
+ */
+static int test_overflow(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p;
+ struct io_uring_sqe *sqe;
+ unsigned pending;
+ int ret, i, j;
+
+ memset(&p, 0, sizeof(p));
+ ret = io_uring_queue_init_params(4, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_init failed %d\n", ret);
+ return 1;
+ }
+
+ /* submit 4x4 SQEs, should overflow the ring by 8 */
+ pending = 0;
+ for (i = 0; i < 4; i++) {
+ for (j = 0; j < 4; j++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->user_data = (i * 4) + j;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret == 4) {
+ pending += 4;
+ continue;
+ }
+ if (p.features & IORING_FEAT_NODROP) {
+ if (ret == -EBUSY)
+ break;
+ }
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ /* we should now have 8 completions ready */
+ ret = reap_events(&ring, pending, 0);
+ if (ret < 0)
+ goto err;
+
+ if (!(p.features & IORING_FEAT_NODROP)) {
+ if (*ring.cq.koverflow != 8) {
+ fprintf(stderr, "cq ring overflow %d, expected 8\n",
+ *ring.cq.koverflow);
+ goto err;
+ }
+ }
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+
+static void submit_one_nop(struct io_uring *ring, int ud)
+{
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ assert(sqe);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = ud;
+ ret = io_uring_submit(ring);
+ assert(ret == 1);
+}
+
+/*
+ * Create an overflow condition and ensure that SQEs are still processed
+ */
+static int test_overflow_handling(bool batch, int cqe_multiple, bool poll,
+ bool defer)
+{
+ struct io_uring ring;
+ struct io_uring_params p;
+ int ret, i, j, ud, cqe_count;
+ unsigned int count;
+ int const N = 8;
+ int const LOOPS = 128;
+ int const QUEUE_LENGTH = 1024;
+ int completions[N];
+ int queue[QUEUE_LENGTH];
+ int queued = 0;
+ int outstanding = 0;
+ bool cqe_dropped = false;
+
+ memset(&completions, 0, sizeof(int) * N);
+ memset(&p, 0, sizeof(p));
+ p.cq_entries = 2 * cqe_multiple;
+ p.flags |= IORING_SETUP_CQSIZE;
+
+ if (poll)
+ p.flags |= IORING_SETUP_IOPOLL;
+
+ if (defer)
+ p.flags |= IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ ret = io_uring_queue_init_params(2, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_init failed %d\n", ret);
+ return 1;
+ }
+
+ assert(p.cq_entries < N);
+ /* submit N SQEs, some should overflow */
+ for (i = 0; i < N; i++) {
+ submit_one_nop(&ring, i);
+ outstanding++;
+ }
+
+ for (i = 0; i < LOOPS; i++) {
+ struct io_uring_cqe *cqes[N];
+
+ if (io_uring_cq_has_overflow(&ring)) {
+ /*
+ * Flush any overflowed CQEs and process those. Actively
+ * flush these to make sure CQEs arrive in vague order
+ * of being sent.
+ */
+ ret = io_uring_get_events(&ring);
+ if (ret != 0) {
+ fprintf(stderr,
+ "io_uring_get_events returned %d\n",
+ ret);
+ goto err;
+ }
+ } else if (!cqe_dropped) {
+ for (j = 0; j < queued; j++) {
+ submit_one_nop(&ring, queue[j]);
+ outstanding++;
+ }
+ queued = 0;
+ }
+
+ /* We have lost some random cqes, stop if no remaining. */
+ if (cqe_dropped && outstanding == *ring.cq.koverflow)
+ break;
+
+ ret = io_uring_wait_cqe(&ring, &cqes[0]);
+ if (ret == -EBADR) {
+ cqe_dropped = true;
+ fprintf(stderr, "CQE dropped\n");
+ continue;
+ } else if (ret != 0) {
+ fprintf(stderr, "io_uring_wait_cqes failed %d\n", ret);
+ goto err;
+ }
+ cqe_count = 1;
+ if (batch) {
+ ret = io_uring_peek_batch_cqe(&ring, &cqes[0], 2);
+ if (ret < 0) {
+ fprintf(stderr,
+ "io_uring_peek_batch_cqe failed %d\n",
+ ret);
+ goto err;
+ }
+ cqe_count = ret;
+ }
+ for (j = 0; j < cqe_count; j++) {
+ assert(cqes[j]->user_data < N);
+ ud = cqes[j]->user_data;
+ completions[ud]++;
+ assert(queued < QUEUE_LENGTH);
+ queue[queued++] = (int)ud;
+ }
+ io_uring_cq_advance(&ring, cqe_count);
+ outstanding -= cqe_count;
+ }
+
+ /* See if there were any drops by flushing the CQ ring *and* overflow */
+ do {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_get_events(&ring);
+ if (ret < 0) {
+ if (ret == -EBADR) {
+ fprintf(stderr, "CQE dropped\n");
+ cqe_dropped = true;
+ break;
+ }
+ goto err;
+ }
+ if (outstanding && !io_uring_cq_ready(&ring))
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, NULL);
+
+ if (ret && ret != -ETIME) {
+ if (ret == -EBADR) {
+ fprintf(stderr, "CQE dropped\n");
+ cqe_dropped = true;
+ break;
+ }
+ fprintf(stderr, "wait_cqe_timeout = %d\n", ret);
+ goto err;
+ }
+ count = io_uring_cq_ready(&ring);
+ io_uring_cq_advance(&ring, count);
+ outstanding -= count;
+ } while (count);
+
+ io_uring_queue_exit(&ring);
+
+ /* Make sure that completions come back in the same order they were
+ * sent. If they come back unfairly then this will concentrate on a
+ * couple of indices.
+ */
+ for (i = 1; !cqe_dropped && i < N; i++) {
+ if (abs(completions[i] - completions[i - 1]) > 1) {
+ fprintf(stderr, "bad completion size %d %d\n",
+ completions[i], completions[i - 1]);
+ goto err;
+ }
+ }
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *fname = ".cq-overflow";
+ unsigned iters, drops;
+ unsigned long usecs;
+ int ret;
+ int i;
+ bool can_defer;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ can_defer = t_probe_defer_taskrun();
+ for (i = 0; i < 16; i++) {
+ bool batch = i & 1;
+ int mult = (i & 2) ? 1 : 2;
+ bool poll = i & 4;
+ bool defer = i & 8;
+
+ if (defer && !can_defer)
+ continue;
+
+ ret = test_overflow_handling(batch, mult, poll, defer);
+ if (ret) {
+ fprintf(stderr, "test_overflow_handling("
+ "batch=%d, mult=%d, poll=%d, defer=%d) failed\n",
+ batch, mult, poll, defer);
+ goto err;
+ }
+ }
+
+ ret = test_overflow();
+ if (ret) {
+ fprintf(stderr, "test_overflow failed\n");
+ return ret;
+ }
+
+ t_create_file(fname, FILE_SIZE);
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ iters = 0;
+ usecs = 1000;
+ do {
+ drops = 0;
+
+ if (test_io(fname, usecs, &drops, 0)) {
+ fprintf(stderr, "test_io nofault failed\n");
+ goto err;
+ }
+ if (drops)
+ break;
+ usecs = (usecs * 12) / 10;
+ iters++;
+ } while (iters < 40);
+
+ if (test_io(fname, usecs, &drops, 0)) {
+ fprintf(stderr, "test_io nofault failed\n");
+ goto err;
+ }
+
+ if (test_io(fname, usecs, &drops, 1)) {
+ fprintf(stderr, "test_io fault failed\n");
+ goto err;
+ }
+
+ unlink(fname);
+ return T_EXIT_PASS;
+err:
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/cq-peek-batch.c b/contrib/libs/liburing/test/cq-peek-batch.c
new file mode 100644
index 00000000000..1dc32a417c2
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-peek-batch.c
@@ -0,0 +1,104 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test CQ peek-batch
+ *
+ */
+#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 queue_n_nops(struct io_uring *ring, int n, int offset)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->user_data = i + offset;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < n) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ } else if (ret < 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+#define CHECK_BATCH(ring, got, cqes, count, expected) do {\
+ got = io_uring_peek_batch_cqe((ring), cqes, count);\
+ if (got != expected) {\
+ printf("Got %d CQs, expected %d\n", got, expected);\
+ goto err;\
+ }\
+} while(0)
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_cqe *cqes[8];
+ struct io_uring ring;
+ int ret, i;
+ unsigned got;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(4, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ CHECK_BATCH(&ring, got, cqes, 4, 0);
+ if (queue_n_nops(&ring, 4, 0))
+ goto err;
+
+ CHECK_BATCH(&ring, got, cqes, 4, 4);
+ for (i=0;i<4;i++) {
+ if (i != cqes[i]->user_data) {
+ printf("Got user_data %" PRIu64 ", expected %d\n",
+ (uint64_t) cqes[i]->user_data, i);
+ goto err;
+ }
+ }
+
+ if (queue_n_nops(&ring, 4, 4))
+ goto err;
+
+ io_uring_cq_advance(&ring, 4);
+ CHECK_BATCH(&ring, got, cqes, 4, 4);
+ for (i=0;i<4;i++) {
+ if (i + 4 != cqes[i]->user_data) {
+ printf("Got user_data %" PRIu64 ", expected %d\n",
+ (uint64_t) cqes[i]->user_data, i + 4);
+ goto err;
+ }
+ }
+
+ io_uring_cq_advance(&ring, 8);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/cq-ready.c b/contrib/libs/liburing/test/cq-ready.c
new file mode 100644
index 00000000000..bcb15f0f33d
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-ready.c
@@ -0,0 +1,96 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test CQ ready
+ *
+ */
+#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 queue_n_nops(struct io_uring *ring, int n)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < n; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < n) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ } else if (ret < 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+#define CHECK_READY(ring, expected) do {\
+ ready = io_uring_cq_ready((ring));\
+ if (ready != expected) {\
+ printf("Got %d CQs ready, expected %d\n", ready, expected);\
+ goto err;\
+ }\
+} while(0)
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+ unsigned ready;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(4, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ CHECK_READY(&ring, 0);
+ if (queue_n_nops(&ring, 4))
+ goto err;
+
+ CHECK_READY(&ring, 4);
+ io_uring_cq_advance(&ring, 4);
+ CHECK_READY(&ring, 0);
+ if (queue_n_nops(&ring, 4))
+ goto err;
+
+ CHECK_READY(&ring, 4);
+
+ io_uring_cq_advance(&ring, 1);
+ CHECK_READY(&ring, 3);
+
+ io_uring_cq_advance(&ring, 2);
+ CHECK_READY(&ring, 1);
+
+ io_uring_cq_advance(&ring, 1);
+ CHECK_READY(&ring, 0);
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/cq-size.c b/contrib/libs/liburing/test/cq-size.c
new file mode 100644
index 00000000000..08aa5878303
--- /dev/null
+++ b/contrib/libs/liburing/test/cq-size.c
@@ -0,0 +1,66 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test CQ ring sizing
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p;
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_CQSIZE;
+ p.cq_entries = 64;
+
+ ret = io_uring_queue_init_params(4, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL) {
+ printf("Skipped, not supported on this kernel\n");
+ goto done;
+ }
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (p.cq_entries < 64) {
+ printf("cq entries invalid (%d)\n", p.cq_entries);
+ goto err;
+ }
+ io_uring_queue_exit(&ring);
+
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_CQSIZE;
+ p.cq_entries = 0;
+
+ ret = io_uring_queue_init_params(4, &ring, &p);
+ if (ret >= 0) {
+ printf("zero sized cq ring succeeded\n");
+ io_uring_queue_exit(&ring);
+ goto err;
+ }
+
+ if (ret != -EINVAL) {
+ printf("io_uring_queue_init_params failed, but not with -EINVAL"
+ ", returned error %d (%s)\n", ret, strerror(-ret));
+ goto err;
+ }
+
+done:
+ return T_EXIT_PASS;
+err:
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/d4ae271dfaae.c b/contrib/libs/liburing/test/d4ae271dfaae.c
new file mode 100644
index 00000000000..1a7886ed6b6
--- /dev/null
+++ b/contrib/libs/liburing/test/d4ae271dfaae.c
@@ -0,0 +1,97 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test case for SQPOLL missing a 'ret' clear in case of busy.
+ *
+ * Heavily based on a test case from
+ * Xiaoguang Wang <xiaoguang.wang@linux.alibaba.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024)
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int i, fd, ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec *iovecs;
+ struct io_uring_params p;
+ char *fname;
+ void *buf;
+
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring_params(4, &ring, &p);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret < 0)
+ return T_EXIT_FAIL;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ fname = ".sqpoll.tmp";
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ fd = open(fname, O_RDONLY | O_DIRECT);
+ if (fname != argv[1])
+ unlink(fname);
+ if (fd < 0) {
+ perror("open");
+ goto out;
+ }
+
+ iovecs = t_calloc(10, sizeof(struct iovec));
+ for (i = 0; i < 10; i++) {
+ t_posix_memalign(&buf, 4096, 4096);
+ iovecs[i].iov_base = buf;
+ iovecs[i].iov_len = 4096;
+ }
+
+ ret = io_uring_register_files(&ring, &fd, 1);
+ if (ret < 0) {
+ fprintf(stderr, "register files %d\n", ret);
+ goto out;
+ }
+
+ for (i = 0; i < 10; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe)
+ break;
+
+ io_uring_prep_readv(sqe, 0, &iovecs[i], 1, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ usleep(1000);
+ }
+
+ for (i = 0; i < 10; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ break;
+ }
+ if (cqe->res != 4096) {
+ fprintf(stderr, "ret=%d, wanted 4096\n", cqe->res);
+ ret = 1;
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ close(fd);
+out:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/d77a67ed5f27.c b/contrib/libs/liburing/test/d77a67ed5f27.c
new file mode 100644
index 00000000000..6f351681614
--- /dev/null
+++ b/contrib/libs/liburing/test/d77a67ed5f27.c
@@ -0,0 +1,66 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <stdio.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+#include <stdlib.h>
+#include "liburing.h"
+#include "helpers.h"
+
+static void sig_alrm(int sig)
+{
+ fprintf(stderr, "Timed out!\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring_params p;
+ struct io_uring ring;
+ int ret, data;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ signal(SIGALRM, sig_alrm);
+
+ memset(&p, 0, sizeof(p));
+ p.sq_thread_idle = 100;
+ p.flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring_params(4, &ring, &p);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret < 0)
+ return T_EXIT_FAIL;
+
+ /* make sure sq thread is sleeping at this point */
+ usleep(150000);
+ alarm(1);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) (unsigned long) 42);
+ io_uring_submit_and_wait(&ring, 1);
+
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "cqe get failed\n");
+ return 1;
+ }
+
+ data = (unsigned long) io_uring_cqe_get_data(cqe);
+ if (data != 42) {
+ fprintf(stderr, "invalid data: %d\n", data);
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/defer-taskrun.c b/contrib/libs/liburing/test/defer-taskrun.c
new file mode 100644
index 00000000000..b07ac2f778e
--- /dev/null
+++ b/contrib/libs/liburing/test/defer-taskrun.c
@@ -0,0 +1,337 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <sys/eventfd.h>
+#include <signal.h>
+#include <poll.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+#include "test.h"
+#include "helpers.h"
+
+#define EXEC_FILENAME ".defer-taskrun"
+#define EXEC_FILESIZE (1U<<20)
+
+static bool can_read_t(int fd, int time)
+{
+ int ret;
+ struct pollfd p = {
+ .fd = fd,
+ .events = POLLIN,
+ };
+
+ ret = poll(&p, 1, time);
+
+ return ret == 1;
+}
+
+static bool can_read(int fd)
+{
+ return can_read_t(fd, 0);
+}
+
+static void eventfd_clear(int fd)
+{
+ uint64_t val;
+ int ret;
+
+ assert(can_read(fd));
+ ret = read(fd, &val, 8);
+ assert(ret == 8);
+}
+
+static void eventfd_trigger(int fd)
+{
+ uint64_t val = 1;
+ int ret;
+
+ ret = write(fd, &val, sizeof(val));
+ assert(ret == sizeof(val));
+}
+
+#define CHECK(x) if (!(x)) { \
+ fprintf(stderr, "%s:%d %s failed\n", __FILE__, __LINE__, #x); \
+ return -1; }
+
+static int test_eventfd(void)
+{
+ struct io_uring ring;
+ int ret;
+ int fda, fdb;
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN);
+ if (ret)
+ return ret;
+
+ fda = eventfd(0, EFD_NONBLOCK);
+ fdb = eventfd(0, EFD_NONBLOCK);
+
+ CHECK(fda >= 0 && fdb >= 0);
+
+ ret = io_uring_register_eventfd(&ring, fda);
+ if (ret)
+ return ret;
+
+ CHECK(!can_read(fda));
+ CHECK(!can_read(fdb));
+
+ io_uring_prep_poll_add(io_uring_get_sqe(&ring), fdb, POLLIN);
+ io_uring_submit(&ring);
+ CHECK(!can_read(fda)); /* poll should not have completed */
+
+ io_uring_prep_nop(io_uring_get_sqe(&ring));
+ io_uring_submit(&ring);
+ CHECK(can_read(fda)); /* nop should have */
+
+ CHECK(io_uring_peek_cqe(&ring, &cqe) == 0);
+ CHECK(cqe->res == 0);
+ io_uring_cqe_seen(&ring, cqe);
+ eventfd_clear(fda);
+
+ eventfd_trigger(fdb);
+ /* can take time due to rcu_call */
+ CHECK(can_read_t(fda, 1000));
+
+ /* should not have processed the cqe yet */
+ CHECK(io_uring_cq_ready(&ring) == 0);
+
+ io_uring_get_events(&ring);
+ CHECK(io_uring_cq_ready(&ring) == 1);
+
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+struct thread_data {
+ struct io_uring ring;
+ int efd;
+ char buff[8];
+};
+
+void *thread(void *t)
+{
+ struct thread_data *td = t;
+
+ io_uring_enable_rings(&td->ring);
+ io_uring_prep_read(io_uring_get_sqe(&td->ring), td->efd, td->buff, sizeof(td->buff), 0);
+ io_uring_submit(&td->ring);
+
+ return NULL;
+}
+
+static int test_thread_shutdown(void)
+{
+ pthread_t t1;
+ int ret;
+ struct thread_data td;
+ struct io_uring_cqe *cqe;
+ uint64_t val = 1;
+
+ ret = io_uring_queue_init(8, &td.ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_R_DISABLED);
+ if (ret)
+ return ret;
+
+ CHECK(io_uring_get_events(&td.ring) == -EBADFD);
+
+ td.efd = eventfd(0, 0);
+ CHECK(td.efd >= 0);
+
+ CHECK(pthread_create(&t1, NULL, thread, &td) == 0);
+ CHECK(pthread_join(t1, NULL) == 0);
+
+ CHECK(io_uring_get_events(&td.ring) == -EEXIST);
+
+ CHECK(write(td.efd, &val, sizeof(val)) == sizeof(val));
+ CHECK(io_uring_wait_cqe(&td.ring, &cqe) == -EEXIST);
+
+ close(td.efd);
+ io_uring_queue_exit(&td.ring);
+ return 0;
+}
+
+static int test_exec(const char *filename)
+{
+ int ret;
+ int fd;
+ struct io_uring ring;
+ pid_t fork_pid;
+ static char * const new_argv[] = {"1", "2", "3", NULL};
+ static char * const new_env[] = {NULL};
+ char *buff;
+
+ fork_pid = fork();
+ CHECK(fork_pid >= 0);
+ if (fork_pid > 0) {
+ int wstatus;
+
+ CHECK(waitpid(fork_pid, &wstatus, 0) != (pid_t)-1);
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus) != T_EXIT_SKIP) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ return -1;
+ }
+ return 0;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN);
+ if (ret)
+ return ret;
+
+ if (filename) {
+ fd = open(filename, O_RDONLY | O_DIRECT);
+ } else {
+ t_create_file(EXEC_FILENAME, EXEC_FILESIZE);
+ fd = open(EXEC_FILENAME, O_RDONLY | O_DIRECT);
+ unlink(EXEC_FILENAME);
+ }
+ buff = (char*)malloc(EXEC_FILESIZE);
+ CHECK(posix_memalign((void **)&buff, 4096, EXEC_FILESIZE) == 0);
+ CHECK(buff);
+
+ CHECK(fd >= 0);
+ io_uring_prep_read(io_uring_get_sqe(&ring), fd, buff, EXEC_FILESIZE, 0);
+ io_uring_submit(&ring);
+ ret = execve("/proc/self/exe", new_argv, new_env);
+ /* if we get here it failed anyway */
+ fprintf(stderr, "execve failed %d\n", ret);
+ return -1;
+}
+
+static int test_flag(void)
+{
+ struct io_uring ring;
+ int ret;
+ int fd;
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_TASKRUN_FLAG);
+ CHECK(!ret);
+
+ fd = eventfd(0, EFD_NONBLOCK);
+ CHECK(fd >= 0);
+
+ io_uring_prep_poll_add(io_uring_get_sqe(&ring), fd, POLLIN);
+ io_uring_submit(&ring);
+ CHECK(!can_read(fd)); /* poll should not have completed */
+
+ eventfd_trigger(fd);
+ CHECK(can_read(fd));
+
+ /* should not have processed the poll cqe yet */
+ CHECK(io_uring_cq_ready(&ring) == 0);
+
+ /* flag should be set */
+ CHECK(IO_URING_READ_ONCE(*ring.sq.kflags) & IORING_SQ_TASKRUN);
+
+ /* Specifically peek, knowing we have only no cqe
+ * but because the flag is set, liburing should try and get more
+ */
+ ret = io_uring_peek_cqe(&ring, &cqe);
+
+ CHECK(ret == 0 && cqe);
+ CHECK(!(IO_URING_READ_ONCE(*ring.sq.kflags) & IORING_SQ_TASKRUN));
+
+ close(fd);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_ring_shutdown(void)
+{
+ struct io_uring ring;
+ int ret;
+ int fd[2];
+ char buff = '\0';
+ char send = 'X';
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN |
+ IORING_SETUP_TASKRUN_FLAG);
+ CHECK(!ret);
+
+ ret = t_create_socket_pair(fd, true);
+ CHECK(!ret);
+
+ io_uring_prep_recv(io_uring_get_sqe(&ring), fd[0], &buff, 1, 0);
+ io_uring_submit(&ring);
+
+ ret = write(fd[1], &send, 1);
+ CHECK(ret == 1);
+
+ /* should not have processed the poll cqe yet */
+ CHECK(io_uring_cq_ready(&ring) == 0);
+ io_uring_queue_exit(&ring);
+
+ /* task work should have been processed by now */
+ CHECK(buff = 'X');
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ const char *filename = NULL;
+
+ if (argc > 2)
+ return T_EXIT_SKIP;
+ if (argc == 2) {
+ /* This test exposes interesting behaviour with a null-blk
+ * device configured like:
+ * $ modprobe null-blk completion_nsec=100000000 irqmode=2
+ * and then run with $ defer-taskrun.t /dev/nullb0
+ */
+ filename = argv[1];
+ }
+
+ if (!t_probe_defer_taskrun())
+ return T_EXIT_SKIP;
+
+ ret = test_thread_shutdown();
+ if (ret) {
+ fprintf(stderr, "test_thread_shutdown failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_exec(filename);
+ if (ret) {
+ fprintf(stderr, "test_exec failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_eventfd();
+ if (ret) {
+ fprintf(stderr, "eventfd failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_flag();
+ if (ret) {
+ fprintf(stderr, "flag failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_ring_shutdown();
+ if (ret) {
+ fprintf(stderr, "test_ring_shutdown failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/defer.c b/contrib/libs/liburing/test/defer.c
new file mode 100644
index 00000000000..4e7cd231352
--- /dev/null
+++ b/contrib/libs/liburing/test/defer.c
@@ -0,0 +1,320 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+#include <stdbool.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define RING_SIZE 128
+enum {
+ OP_NOP,
+ OP_REMOVE_BUFFERS
+};
+
+struct test_context {
+ struct io_uring *ring;
+ struct io_uring_sqe **sqes;
+ struct io_uring_cqe *cqes;
+ int nr;
+};
+
+static void free_context(struct test_context *ctx)
+{
+ free(ctx->sqes);
+ free(ctx->cqes);
+ memset(ctx, 0, sizeof(*ctx));
+}
+
+static int init_context(struct test_context *ctx, struct io_uring *ring, int nr,
+ int op)
+{
+ struct io_uring_sqe *sqe;
+ int i;
+
+ memset(ctx, 0, sizeof(*ctx));
+ ctx->nr = nr;
+ ctx->ring = ring;
+ ctx->sqes = t_malloc(nr * sizeof(*ctx->sqes));
+ ctx->cqes = t_malloc(nr * sizeof(*ctx->cqes));
+
+ if (!ctx->sqes || !ctx->cqes)
+ goto err;
+
+ for (i = 0; i < nr; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ goto err;
+ switch (op) {
+ case OP_NOP:
+ io_uring_prep_nop(sqe);
+ break;
+ case OP_REMOVE_BUFFERS:
+ io_uring_prep_remove_buffers(sqe, 10, 1);
+ break;
+ };
+ sqe->user_data = i;
+ ctx->sqes[i] = sqe;
+ }
+
+ return 0;
+err:
+ free_context(ctx);
+ printf("init context failed\n");
+ return 1;
+}
+
+static int wait_cqes(struct test_context *ctx)
+{
+ int ret, i;
+ struct io_uring_cqe *cqe;
+
+ for (i = 0; i < ctx->nr; i++) {
+ ret = io_uring_wait_cqe(ctx->ring, &cqe);
+
+ if (ret < 0) {
+ printf("wait_cqes: wait completion %d\n", ret);
+ return 1;
+ }
+ memcpy(&ctx->cqes[i], cqe, sizeof(*cqe));
+ io_uring_cqe_seen(ctx->ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test_cancelled_userdata(struct io_uring *ring)
+{
+ struct test_context ctx;
+ int ret, i, nr = 100;
+
+ if (init_context(&ctx, ring, nr, OP_NOP))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_LINK;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ for (i = 0; i < nr; i++) {
+ if (i != ctx.cqes[i].user_data) {
+ printf("invalid user data\n");
+ goto err;
+ }
+ }
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int test_thread_link_cancel(struct io_uring *ring)
+{
+ struct test_context ctx;
+ int ret, i, nr = 100;
+
+ if (init_context(&ctx, ring, nr, OP_REMOVE_BUFFERS))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_LINK;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ for (i = 0; i < nr; i++) {
+ bool fail = false;
+
+ if (i == 0)
+ fail = (ctx.cqes[i].res != -ENOENT);
+ else
+ fail = (ctx.cqes[i].res != -ECANCELED);
+
+ if (fail) {
+ printf("invalid status %d\n", ctx.cqes[i].res);
+ goto err;
+ }
+ }
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int test_drain_with_linked_timeout(struct io_uring *ring)
+{
+ const int nr = 3;
+ struct __kernel_timespec ts = { .tv_sec = 1, .tv_nsec = 0, };
+ struct test_context ctx;
+ int ret, i;
+
+ if (init_context(&ctx, ring, nr * 2, OP_NOP))
+ return 1;
+
+ for (i = 0; i < nr; i++) {
+ io_uring_prep_timeout(ctx.sqes[2 * i], &ts, 0, 0);
+ ctx.sqes[2 * i]->flags |= IOSQE_IO_LINK | IOSQE_IO_DRAIN;
+ io_uring_prep_link_timeout(ctx.sqes[2 * i + 1], &ts, 0);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int run_drained(struct io_uring *ring, int nr)
+{
+ struct test_context ctx;
+ int ret, i;
+
+ if (init_context(&ctx, ring, nr, OP_NOP))
+ return 1;
+
+ for (i = 0; i < nr; i++)
+ ctx.sqes[i]->flags |= IOSQE_IO_DRAIN;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (wait_cqes(&ctx))
+ goto err;
+
+ free_context(&ctx);
+ return 0;
+err:
+ free_context(&ctx);
+ return 1;
+}
+
+static int test_overflow_hung(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ int ret, nr = 10;
+
+ while (*ring->cq.koverflow != 1000) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_nop(sqe);
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ return 1;
+ }
+ }
+
+ return run_drained(ring, nr);
+}
+
+static int test_dropped_hung(struct io_uring *ring)
+{
+ int nr = 10;
+
+ *ring->sq.kdropped = 1000;
+ return run_drained(ring, nr);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring, poll_ring, sqthread_ring;
+ struct io_uring_params p;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ memset(&p, 0, sizeof(p));
+ ret = io_uring_queue_init_params(RING_SIZE, &ring, &p);
+ if (ret) {
+ printf("ring setup failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_queue_init(RING_SIZE, &poll_ring, IORING_SETUP_IOPOLL);
+ if (ret) {
+ printf("poll_ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+
+ ret = test_cancelled_userdata(&poll_ring);
+ if (ret) {
+ printf("test_cancelled_userdata failed\n");
+ return ret;
+ }
+
+ if (!(p.features & IORING_FEAT_NODROP)) {
+ ret = test_overflow_hung(&ring);
+ if (ret) {
+ printf("test_overflow_hung failed\n");
+ return ret;
+ }
+ }
+
+ ret = test_dropped_hung(&ring);
+ if (ret) {
+ printf("test_dropped_hung failed\n");
+ return ret;
+ }
+
+ ret = test_drain_with_linked_timeout(&ring);
+ if (ret) {
+ printf("test_drain_with_linked_timeout failed\n");
+ return ret;
+ }
+
+ ret = t_create_ring(RING_SIZE, &sqthread_ring,
+ IORING_SETUP_SQPOLL | IORING_SETUP_IOPOLL);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret < 0)
+ return T_EXIT_FAIL;
+
+ ret = test_thread_link_cancel(&sqthread_ring);
+ if (ret) {
+ printf("test_thread_link_cancel failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/double-poll-crash.c b/contrib/libs/liburing/test/double-poll-crash.c
new file mode 100644
index 00000000000..c24ad866618
--- /dev/null
+++ b/contrib/libs/liburing/test/double-poll-crash.c
@@ -0,0 +1,196 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// https://syzkaller.appspot.com/bug?id=5c9918d20f771265ad0ffae3c8f3859d24850692
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <endian.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+#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
+
+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);
+ if (*ring_ptr_out == MAP_FAILED)
+ exit(0);
+ 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);
+ if (*sqes_ptr_out == MAP_FAILED)
+ exit(0);
+ 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 long syz_open_dev(volatile long a0, volatile long a1, volatile long a2)
+{
+ if (a0 == 0xc || a0 == 0xb) {
+ char buf[128];
+ sprintf(buf, "/dev/%s/%d:%d", a0 == 0xc ? "char" : "block", (uint8_t)a1,
+ (uint8_t)a2);
+ return open(buf, O_RDWR, 0);
+ } else {
+ char buf[1024];
+ char* hash;
+ strncpy(buf, (char*)a0, sizeof(buf) - 1);
+ buf[sizeof(buf) - 1] = 0;
+ while ((hash = strchr(buf, '#'))) {
+ *hash = '0' + (char)(a1 % 10);
+ a1 /= 10;
+ }
+ return open(buf, a2, 0);
+ }
+}
+
+uint64_t r[4] = {0xffffffffffffffff, 0x0, 0x0, 0xffffffffffffffff};
+
+int main(int argc, char *argv[])
+{
+ void *mmap_ret;
+#if !defined(__i386) && !defined(__x86_64__)
+ return T_EXIT_SKIP;
+#endif
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ mmap_ret = mmap((void *)0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
+ if (mmap_ret == MAP_FAILED)
+ return T_EXIT_SKIP;
+ mmap_ret = mmap((void *)0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
+ if (mmap_ret == MAP_FAILED)
+ return T_EXIT_SKIP;
+ intptr_t res = 0;
+ *(uint32_t*)0x20000484 = 0;
+ *(uint32_t*)0x20000488 = 0;
+ *(uint32_t*)0x2000048c = 0;
+ *(uint32_t*)0x20000490 = 0;
+ *(uint32_t*)0x20000498 = -1;
+ *(uint32_t*)0x2000049c = 0;
+ *(uint32_t*)0x200004a0 = 0;
+ *(uint32_t*)0x200004a4 = 0;
+ res = -1;
+ res = syz_io_uring_setup(0x6ad4, 0x20000480, 0x20ee7000, 0x20ffb000,
+ 0x20000180, 0x20000040);
+ if (res != -1) {
+ r[0] = res;
+ r[1] = *(uint64_t*)0x20000180;
+ r[2] = *(uint64_t*)0x20000040;
+ }
+ res = -1;
+ res = syz_open_dev(0xc, 4, 0x15);
+ if (res != -1)
+ r[3] = res;
+ *(uint8_t*)0x20000000 = 6;
+ *(uint8_t*)0x20000001 = 0;
+ *(uint16_t*)0x20000002 = 0;
+ *(uint32_t*)0x20000004 = r[3];
+ *(uint64_t*)0x20000008 = 0;
+ *(uint64_t*)0x20000010 = 0;
+ *(uint32_t*)0x20000018 = 0;
+ *(uint16_t*)0x2000001c = 0;
+ *(uint16_t*)0x2000001e = 0;
+ *(uint64_t*)0x20000020 = 0;
+ *(uint16_t*)0x20000028 = 0;
+ *(uint16_t*)0x2000002a = 0;
+ *(uint8_t*)0x2000002c = 0;
+ *(uint8_t*)0x2000002d = 0;
+ *(uint8_t*)0x2000002e = 0;
+ *(uint8_t*)0x2000002f = 0;
+ *(uint8_t*)0x20000030 = 0;
+ *(uint8_t*)0x20000031 = 0;
+ *(uint8_t*)0x20000032 = 0;
+ *(uint8_t*)0x20000033 = 0;
+ *(uint8_t*)0x20000034 = 0;
+ *(uint8_t*)0x20000035 = 0;
+ *(uint8_t*)0x20000036 = 0;
+ *(uint8_t*)0x20000037 = 0;
+ *(uint8_t*)0x20000038 = 0;
+ *(uint8_t*)0x20000039 = 0;
+ *(uint8_t*)0x2000003a = 0;
+ *(uint8_t*)0x2000003b = 0;
+ *(uint8_t*)0x2000003c = 0;
+ *(uint8_t*)0x2000003d = 0;
+ *(uint8_t*)0x2000003e = 0;
+ *(uint8_t*)0x2000003f = 0;
+ syz_io_uring_submit(r[1], r[2], 0x20000000, 0);
+ __sys_io_uring_enter(r[0], 0x20450c, 0, 0ul, 0ul);
+ *(uint32_t*)0x20000080 = 0x7ff;
+ *(uint32_t*)0x20000084 = 0x8b7;
+ *(uint32_t*)0x20000088 = 3;
+ *(uint32_t*)0x2000008c = 0x101;
+ *(uint8_t*)0x20000090 = 9;
+ memcpy((void*)0x20000091, "\xaf\x09\x01\xbc\xf9\xc6\xe4\x92\x86\x51\x7d\x7f"
+ "\xbd\x43\x7d\x16\x69\x3e\x05",
+ 19);
+ ioctl(r[3], 0x5404, 0x20000080ul);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/drop-submit.c b/contrib/libs/liburing/test/drop-submit.c
new file mode 100644
index 00000000000..daa7d7683ad
--- /dev/null
+++ b/contrib/libs/liburing/test/drop-submit.c
@@ -0,0 +1,95 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test IORING_SETUP_SUBMIT_ALL
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int test(struct io_uring *ring, int expect_drops)
+{
+ struct io_uring_sqe *sqe;
+ char buf[32];
+ int ret, i;
+
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ }
+
+ /* prep two invalid reads, these will fail */
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_read(sqe, 128, buf, sizeof(buf), 0);
+ sqe->ioprio = (short) -1;
+ }
+
+
+ ret = io_uring_submit(ring);
+ if (expect_drops) {
+ if (ret != 5) {
+ fprintf(stderr, "drops submit failed: %d\n", ret);
+ goto err;
+ }
+ } else {
+ if (ret != 6) {
+ fprintf(stderr, "no drops submit failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SUBMIT_ALL);
+ if (ret)
+ return 0;
+
+ ret = test(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "test no drops failed\n");
+ return ret;
+ }
+
+ io_uring_queue_exit(&ring);
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "test drops failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/eeed8b54e0df.c b/contrib/libs/liburing/test/eeed8b54e0df.c
new file mode 100644
index 00000000000..c4118a2d51d
--- /dev/null
+++ b/contrib/libs/liburing/test/eeed8b54e0df.c
@@ -0,0 +1,116 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: -EAGAIN handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define BLOCK 4096
+
+#ifndef RWF_NOWAIT
+#define RWF_NOWAIT 8
+#endif
+
+static int get_file_fd(void)
+{
+ ssize_t ret;
+ char *buf;
+ int fd;
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0644);
+ unlink("testfile");
+ if (fd < 0) {
+ perror("open file");
+ return -1;
+ }
+
+ buf = t_malloc(BLOCK);
+ memset(buf, 0, BLOCK);
+ ret = write(fd, buf, BLOCK);
+ if (ret != BLOCK) {
+ if (ret < 0)
+ perror("write");
+ else
+ printf("Short write\n");
+ goto err;
+ }
+ fsync(fd);
+
+ if (posix_fadvise(fd, 0, 4096, POSIX_FADV_DONTNEED)) {
+ perror("fadvise");
+err:
+ close(fd);
+ free(buf);
+ return -1;
+ }
+
+ free(buf);
+ return fd;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec iov;
+ int ret, fd;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ iov.iov_base = t_malloc(4096);
+ iov.iov_len = 4096;
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ fd = get_file_fd();
+ if (fd < 0)
+ return T_EXIT_FAIL;
+
+ io_uring_prep_readv(sqe, fd, &iov, 1, 0);
+ sqe->rw_flags = RWF_NOWAIT;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ printf("Got submit %d, expected 1\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret) {
+ printf("Ring peek got %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->res != -EAGAIN && cqe->res != 4096) {
+ printf("cqe error: %d\n", cqe->res);
+ goto err;
+ }
+
+ close(fd);
+ return T_EXIT_PASS;
+err:
+ close(fd);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/empty-eownerdead.c b/contrib/libs/liburing/test/empty-eownerdead.c
new file mode 100644
index 00000000000..b7722ab1529
--- /dev/null
+++ b/contrib/libs/liburing/test/empty-eownerdead.c
@@ -0,0 +1,46 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test if entering with nothing to submit/wait for SQPOLL returns an error.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <string.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ p.flags = IORING_SETUP_SQPOLL;
+ p.sq_thread_idle = 100;
+
+ ret = t_create_ring_params(1, &ring, &p);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret < 0)
+ goto err;
+
+ ret = __sys_io_uring_enter(ring.ring_fd, 0, 0, 0, NULL);
+ if (ret < 0) {
+ int __e = errno;
+
+ if (__e == EOWNERDEAD)
+ fprintf(stderr, "sqe submit unexpected failure due old kernel bug: %s\n", strerror(__e));
+ else
+ fprintf(stderr, "sqe submit unexpected failure: %s\n", strerror(__e));
+ goto err;
+ }
+
+ return T_EXIT_PASS;
+err:
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/eventfd-disable.c b/contrib/libs/liburing/test/eventfd-disable.c
new file mode 100644
index 00000000000..9ed81d3d14c
--- /dev/null
+++ b/contrib/libs/liburing/test/eventfd-disable.c
@@ -0,0 +1,180 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test disable/enable notifications through eventfd
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int test(bool defer)
+{
+ struct io_uring_params p = {};
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+ int ret, evfd, i;
+
+ if (defer)
+ p.flags |= IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ ret = io_uring_queue_init_params(64, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ evfd = eventfd(0, EFD_CLOEXEC);
+ if (evfd < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring, evfd);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (!io_uring_cq_eventfd_enabled(&ring)) {
+ fprintf(stderr, "eventfd disabled\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_cq_eventfd_toggle(&ring, false);
+ if (ret) {
+ fprintf(stdout, "Skipping, CQ flags not available!\n");
+ return T_EXIT_SKIP;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, evfd, &vec, 1, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 63; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 2;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != 63) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 63; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* eventfd */
+ fprintf(stderr, "eventfd unexpected: %d\n", (int)ptr);
+ return T_EXIT_FAIL;
+ case 2:
+ if (cqe->res) {
+ fprintf(stderr, "nop: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ ret = io_uring_cq_eventfd_toggle(&ring, true);
+ if (ret) {
+ fprintf(stderr, "io_uring_cq_eventfd_toggle: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* eventfd */
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "read res: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+
+ if (ptr != 1) {
+ fprintf(stderr, "eventfd: %d\n", (int)ptr);
+ return T_EXIT_FAIL;
+ }
+ break;
+ case 2:
+ if (cqe->res) {
+ fprintf(stderr, "nop: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ close(evfd);
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test(false);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(false) failed\n", argv[0]);
+ return ret;
+ }
+
+ if (t_probe_defer_taskrun()) {
+ ret = test(true);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(true) failed\n", argv[0]);
+ return ret;
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/eventfd-reg.c b/contrib/libs/liburing/test/eventfd-reg.c
new file mode 100644
index 00000000000..a1655267abe
--- /dev/null
+++ b/contrib/libs/liburing/test/eventfd-reg.c
@@ -0,0 +1,78 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test eventfd registration+unregistration
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct io_uring ring;
+ int ret, evfd[2], i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ evfd[0] = eventfd(0, EFD_CLOEXEC);
+ evfd[1] = eventfd(0, EFD_CLOEXEC);
+ if (evfd[0] < 0 || evfd[1] < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring, evfd[0]);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* Check that registrering again will get -EBUSY */
+ ret = io_uring_register_eventfd(&ring, evfd[1]);
+ if (ret != -EBUSY) {
+ fprintf(stderr, "unexpected 2nd register: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ close(evfd[1]);
+
+ ret = io_uring_unregister_eventfd(&ring);
+ if (ret) {
+ fprintf(stderr, "unexpected unregister: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* loop 100 registers/unregister */
+ for (i = 0; i < 100; i++) {
+ ret = io_uring_register_eventfd(&ring, evfd[0]);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_unregister_eventfd(&ring);
+ if (ret) {
+ fprintf(stderr, "unexpected unregister: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ }
+
+ close(evfd[0]);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/eventfd-ring.c b/contrib/libs/liburing/test/eventfd-ring.c
new file mode 100644
index 00000000000..d4bed865268
--- /dev/null
+++ b/contrib/libs/liburing/test/eventfd-ring.c
@@ -0,0 +1,99 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various nop tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct io_uring ring1, ring2;
+ struct io_uring_sqe *sqe;
+ int ret, evfd1, evfd2;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init_params(8, &ring1, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (!(p.features & IORING_FEAT_CUR_PERSONALITY)) {
+ fprintf(stdout, "Skipping\n");
+ return T_EXIT_SKIP;
+ }
+ ret = io_uring_queue_init(8, &ring2, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ evfd1 = eventfd(0, EFD_CLOEXEC);
+ if (evfd1 < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ evfd2 = eventfd(0, EFD_CLOEXEC);
+ if (evfd2 < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring1, evfd1);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring2, evfd2);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring1);
+ io_uring_prep_poll_add(sqe, evfd2, POLLIN);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring2);
+ io_uring_prep_poll_add(sqe, evfd1, POLLIN);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring1);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_submit(&ring2);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring1);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring1);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/eventfd.c b/contrib/libs/liburing/test/eventfd.c
new file mode 100644
index 00000000000..317de29fb48
--- /dev/null
+++ b/contrib/libs/liburing/test/eventfd.c
@@ -0,0 +1,114 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various nop tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+ int ret, evfd, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (!(p.features & IORING_FEAT_CUR_PERSONALITY)) {
+ fprintf(stdout, "Skipping\n");
+ return T_EXIT_SKIP;
+ }
+
+ evfd = eventfd(0, EFD_CLOEXEC);
+ if (evfd < 0) {
+ perror("eventfd");
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_eventfd(&ring, evfd);
+ if (ret) {
+ fprintf(stderr, "failed to register evfd: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, evfd, POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, evfd, &vec, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ /* POLLIN */
+ if (cqe->res != 1) {
+ fprintf(stderr, "poll: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ case 2:
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "read: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ case 3:
+ if (cqe->res) {
+ fprintf(stderr, "nop: %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/exec-target.c b/contrib/libs/liburing/test/exec-target.c
new file mode 100644
index 00000000000..21ef58ef8a6
--- /dev/null
+++ b/contrib/libs/liburing/test/exec-target.c
@@ -0,0 +1,7 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+
+int main(int argc, char *argv[])
+{
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/exit-no-cleanup.c b/contrib/libs/liburing/test/exit-no-cleanup.c
new file mode 100644
index 00000000000..2fe3dc7ddd0
--- /dev/null
+++ b/contrib/libs/liburing/test/exit-no-cleanup.c
@@ -0,0 +1,118 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test case testing exit without cleanup and io-wq work pending or queued.
+ *
+ * From Florian Fischer <florian.fl.fischer@fau.de>
+ * Link: https://lore.kernel.org/io-uring/20211202165606.mqryio4yzubl7ms5@pasture/
+ *
+ */
+#include <assert.h>
+#include <err.h>
+#include <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/sysinfo.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define IORING_ENTRIES 8
+
+static pthread_t *threads;
+static pthread_barrier_t init_barrier;
+static int sleep_fd, notify_fd;
+static sem_t sem;
+
+void *thread_func(void *arg)
+{
+ struct io_uring ring;
+ int res;
+
+ res = io_uring_queue_init(IORING_ENTRIES, &ring, 0);
+ if (res)
+ err(EXIT_FAILURE, "io_uring_queue_init failed");
+
+ pthread_barrier_wait(&init_barrier);
+
+ for(;;) {
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ uint64_t buf;
+ int res;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe);
+
+ io_uring_prep_read(sqe, sleep_fd, &buf, sizeof(buf), 0);
+
+ res = io_uring_submit_and_wait(&ring, 1);
+ if (res < 0)
+ err(EXIT_FAILURE, "io_uring_submit_and_wait failed");
+
+ res = io_uring_peek_cqe(&ring, &cqe);
+ assert(!res);
+ if (cqe->res < 0) {
+ errno = -cqe->res;
+ err(EXIT_FAILURE, "read failed");
+ }
+ assert(cqe->res == sizeof(buf));
+
+ sem_post(&sem);
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ int res, fds[2], i, cpus;
+ const uint64_t n = 0x42;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ cpus = get_nprocs();
+ res = pthread_barrier_init(&init_barrier, NULL, cpus);
+ if (res)
+ err(EXIT_FAILURE, "pthread_barrier_init failed");
+
+ res = sem_init(&sem, 0, 0);
+ if (res)
+ err(EXIT_FAILURE, "sem_init failed");
+
+ threads = t_malloc(sizeof(pthread_t) * cpus);
+
+ res = pipe(fds);
+ if (res)
+ err(EXIT_FAILURE, "pipe failed");
+
+ sleep_fd = fds[0];
+ notify_fd = fds[1];
+
+ for (i = 0; i < cpus; i++) {
+ errno = pthread_create(&threads[i], NULL, thread_func, NULL);
+ if (errno)
+ err(EXIT_FAILURE, "pthread_create failed");
+ }
+
+ // Write #cpus notifications
+ for (i = 0; i < cpus; i++) {
+ res = write(notify_fd, &n, sizeof(n));
+ if (res < 0)
+ err(EXIT_FAILURE, "write failed");
+ assert(res == sizeof(n));
+ }
+
+ // Await that all notifications were received
+ for (i = 0; i < cpus; i++)
+ sem_wait(&sem);
+
+ // Exit without resource cleanup
+ exit(EXIT_SUCCESS);
+}
diff --git a/contrib/libs/liburing/test/fadvise.c b/contrib/libs/liburing/test/fadvise.c
new file mode 100644
index 00000000000..86670b80f4f
--- /dev/null
+++ b/contrib/libs/liburing/test/fadvise.c
@@ -0,0 +1,203 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: basic fadvise test
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024)
+#define LOOPS 100
+#define MIN_LOOPS 10
+
+static unsigned long long utime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000000;
+ return sec + usec;
+}
+
+static unsigned long long utime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return utime_since(tv, &end);
+}
+
+static int do_fadvise(struct io_uring *ring, int fd, off_t offset, off_t len,
+ int advice)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+
+ io_uring_prep_fadvise(sqe, fd, offset, len, advice);
+ sqe->user_data = advice;
+ ret = io_uring_submit_and_wait(ring, 1);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ if (ret == -EINVAL || ret == -EBADF) {
+ fprintf(stdout, "Fadvise not supported, skipping\n");
+ unlink(".fadvise.tmp");
+ exit(T_EXIT_SKIP);
+ } else if (ret) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static long do_read(int fd, char *buf)
+{
+ struct timeval tv;
+ int ret;
+ long t;
+
+ ret = lseek(fd, 0, SEEK_SET);
+ if (ret) {
+ perror("lseek");
+ return -1;
+ }
+
+ gettimeofday(&tv, NULL);
+ ret = read(fd, buf, FILE_SIZE);
+ t = utime_since_now(&tv);
+ if (ret < 0) {
+ perror("read");
+ return -1;
+ } else if (ret != FILE_SIZE) {
+ fprintf(stderr, "short read1: %d\n", ret);
+ return -1;
+ }
+
+ return t;
+}
+
+static int test_fadvise(struct io_uring *ring, const char *filename)
+{
+ unsigned long cached_read, uncached_read, cached_read2;
+ int fd, ret;
+ char *buf;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ buf = t_malloc(FILE_SIZE);
+
+ cached_read = do_read(fd, buf);
+ if (cached_read == -1)
+ return 1;
+
+ ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
+ if (ret)
+ return 1;
+
+ uncached_read = do_read(fd, buf);
+ if (uncached_read == -1)
+ return 1;
+
+ ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
+ if (ret)
+ return 1;
+
+ ret = do_fadvise(ring, fd, 0, FILE_SIZE, POSIX_FADV_WILLNEED);
+ if (ret)
+ return 1;
+
+ fsync(fd);
+
+ cached_read2 = do_read(fd, buf);
+ if (cached_read2 == -1)
+ return 1;
+
+ if (cached_read < uncached_read &&
+ cached_read2 < uncached_read)
+ return 0;
+
+ return 2;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, i, good, bad;
+ char *fname;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ fname = ".fadvise.tmp";
+ t_create_file(fname, FILE_SIZE);
+ }
+ if (io_uring_queue_init(8, &ring, 0)) {
+ fprintf(stderr, "ring creation failed\n");
+ goto err;
+ }
+
+ good = bad = 0;
+ for (i = 0; i < LOOPS; i++) {
+ ret = test_fadvise(&ring, fname);
+ if (ret == 1) {
+ fprintf(stderr, "read_fadvise failed\n");
+ goto err;
+ } else if (!ret)
+ good++;
+ else if (ret == 2)
+ bad++;
+ if (i >= MIN_LOOPS && !bad)
+ break;
+ }
+
+ /* too hard to reliably test, just ignore */
+ if (0 && bad > good) {
+ fprintf(stderr, "Suspicious timings\n");
+ goto err;
+ }
+
+ if (fname != argv[1])
+ unlink(fname);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ if (fname != argv[1])
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/fallocate.c b/contrib/libs/liburing/test/fallocate.c
new file mode 100644
index 00000000000..a546922f961
--- /dev/null
+++ b/contrib/libs/liburing/test/fallocate.c
@@ -0,0 +1,257 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring fallocate
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/resource.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int no_fallocate;
+
+static int test_fallocate_rlimit(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct rlimit rlim;
+ char buf[32];
+ int fd, ret;
+
+ if (getrlimit(RLIMIT_FSIZE, &rlim) < 0) {
+ perror("getrlimit");
+ return 1;
+ }
+ rlim.rlim_cur = 64 * 1024;
+ rlim.rlim_max = 64 * 1024;
+ if (setrlimit(RLIMIT_FSIZE, &rlim) < 0) {
+ perror("setrlimit");
+ return 1;
+ }
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ unlink(buf);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_fallocate(sqe, fd, 0, 0, 128*1024);
+
+ 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;
+ }
+
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "Fallocate not supported, skipping\n");
+ no_fallocate = 1;
+ goto skip;
+ } else if (cqe->res != -EFBIG) {
+ fprintf(stderr, "Expected -EFBIG: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+skip:
+ return T_EXIT_SKIP;
+err:
+ return 1;
+}
+
+static int test_fallocate(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct stat st;
+ char buf[32];
+ int fd, ret;
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ unlink(buf);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_fallocate(sqe, fd, 0, 0, 128*1024);
+
+ 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;
+ }
+
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "Fallocate not supported, skipping\n");
+ no_fallocate = 1;
+ goto skip;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ if (fstat(fd, &st) < 0) {
+ perror("stat");
+ goto err;
+ }
+
+ if (st.st_size != 128*1024) {
+ fprintf(stderr, "Size mismatch: %llu\n",
+ (unsigned long long) st.st_size);
+ goto err;
+ }
+
+ return 0;
+skip:
+ return T_EXIT_SKIP;
+err:
+ return 1;
+}
+
+static int test_fallocate_fsync(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct stat st;
+ char buf[32];
+ int fd, ret, i;
+
+ if (no_fallocate)
+ return 0;
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ unlink(buf);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_fallocate(sqe, fd, 0, 0, 128*1024);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_fsync(sqe, fd, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "cqe->res=%d,data=%" PRIu64 "\n", cqe->res,
+ (uint64_t) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (fstat(fd, &st) < 0) {
+ perror("stat");
+ goto err;
+ }
+
+ if (st.st_size != 128*1024) {
+ fprintf(stderr, "Size mismatch: %llu\n",
+ (unsigned long long) st.st_size);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_fallocate(&ring);
+ if (ret) {
+ if (ret != T_EXIT_SKIP) {
+ fprintf(stderr, "test_fallocate failed\n");
+ }
+ return ret;
+ }
+
+ ret = test_fallocate_fsync(&ring);
+ if (ret) {
+ fprintf(stderr, "test_fallocate_fsync failed\n");
+ return ret;
+ }
+
+ ret = test_fallocate_rlimit(&ring);
+ if (ret) {
+ if (ret != T_EXIT_SKIP) {
+ fprintf(stderr, "test_fallocate_rlimit failed\n");
+ }
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/fc2a85cb02ef.c b/contrib/libs/liburing/test/fc2a85cb02ef.c
new file mode 100644
index 00000000000..3c613748457
--- /dev/null
+++ b/contrib/libs/liburing/test/fc2a85cb02ef.c
@@ -0,0 +1,133 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// https://syzkaller.appspot.com/bug?id=1f2ecd7a23dba87e5ca3505ec44514a462cfe8c0
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+#include "../src/syscall.h"
+
+static bool write_file(const char* file, const char* what, ...)
+{
+ char buf[1024];
+ va_list args;
+ va_start(args, what);
+ vsnprintf(buf, sizeof(buf), what, args);
+ va_end(args);
+ buf[sizeof(buf) - 1] = 0;
+ int len = strlen(buf);
+ int fd = open(file, O_WRONLY | O_CLOEXEC);
+ if (fd == -1)
+ return false;
+ if (write(fd, buf, len) != len) {
+ int err = errno;
+ close(fd);
+ errno = err;
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+static int inject_fault(int nth)
+{
+ int fd;
+ fd = open("/proc/thread-self/fail-nth", O_RDWR);
+ if (fd == -1)
+ exit(1);
+ char buf[16];
+ sprintf(buf, "%d", nth + 1);
+ if (write(fd, buf, strlen(buf)) != (ssize_t)strlen(buf))
+ exit(1);
+ return fd;
+}
+
+static int setup_fault()
+{
+ static struct {
+ const char* file;
+ const char* val;
+ bool fatal;
+ } files[] = {
+ {"/sys/kernel/debug/failslab/ignore-gfp-wait", "N", true},
+ {"/sys/kernel/debug/failslab/verbose", "0", false},
+ {"/sys/kernel/debug/fail_futex/ignore-private", "N", false},
+ {"/sys/kernel/debug/fail_page_alloc/verbose", "0", false},
+ {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-highmem", "N", false},
+ {"/sys/kernel/debug/fail_page_alloc/ignore-gfp-wait", "N", false},
+ {"/sys/kernel/debug/fail_page_alloc/min-order", "0", false},
+ };
+ unsigned i;
+ for (i = 0; i < sizeof(files) / sizeof(files[0]); i++) {
+ if (!write_file(files[i].file, files[i].val)) {
+ if (files[i].fatal)
+ return 1;
+ }
+ }
+ return 0;
+}
+
+uint64_t r[2] = {0xffffffffffffffff, 0xffffffffffffffff};
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return T_EXIT_SKIP;
+ mmap((void *) 0x20000000ul, 0x1000000ul, 3ul, 0x32ul, -1, 0);
+ if (setup_fault()) {
+ printf("Test needs failslab/fail_futex/fail_page_alloc enabled, skipped\n");
+ return T_EXIT_SKIP;
+ }
+ intptr_t res = 0;
+ *(uint32_t*)0x20000000 = 0;
+ *(uint32_t*)0x20000004 = 0;
+ *(uint32_t*)0x20000008 = 0;
+ *(uint32_t*)0x2000000c = 0;
+ *(uint32_t*)0x20000010 = 0;
+ *(uint32_t*)0x20000014 = 0;
+ *(uint32_t*)0x20000018 = 0;
+ *(uint32_t*)0x2000001c = 0;
+ *(uint32_t*)0x20000020 = 0;
+ *(uint32_t*)0x20000024 = 0;
+ *(uint32_t*)0x20000028 = 0;
+ *(uint32_t*)0x2000002c = 0;
+ *(uint32_t*)0x20000030 = 0;
+ *(uint32_t*)0x20000034 = 0;
+ *(uint32_t*)0x20000038 = 0;
+ *(uint32_t*)0x2000003c = 0;
+ *(uint32_t*)0x20000040 = 0;
+ *(uint32_t*)0x20000044 = 0;
+ *(uint64_t*)0x20000048 = 0;
+ *(uint32_t*)0x20000050 = 0;
+ *(uint32_t*)0x20000054 = 0;
+ *(uint32_t*)0x20000058 = 0;
+ *(uint32_t*)0x2000005c = 0;
+ *(uint32_t*)0x20000060 = 0;
+ *(uint32_t*)0x20000064 = 0;
+ *(uint32_t*)0x20000068 = 0;
+ *(uint32_t*)0x2000006c = 0;
+ *(uint64_t*)0x20000070 = 0;
+ res = __sys_io_uring_setup(0x6a6, (struct io_uring_params *) 0x20000000ul);
+ if (res != -1)
+ r[0] = res;
+ res = socket(0x11ul, 2ul, 0x300ul);
+ if (res != -1)
+ r[1] = res;
+ *(uint32_t*)0x20000080 = r[1];
+ inject_fault(1);
+ __sys_io_uring_register(r[0], 2ul, (const void *) 0x20000080ul, 1ul);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/fd-pass.c b/contrib/libs/liburing/test/fd-pass.c
new file mode 100644
index 00000000000..9549d418e5f
--- /dev/null
+++ b/contrib/libs/liburing/test/fd-pass.c
@@ -0,0 +1,188 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various fixed file fd passing tests
+ *
+ */
+#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 FSIZE 128
+#define PAT 0x9a
+
+static int no_fd_pass;
+
+static int verify_fixed_read(struct io_uring *ring, int fixed_fd, int fail)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ unsigned char buf[FSIZE];
+ int i;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fixed_fd, buf, FSIZE, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ io_uring_submit(ring);
+
+ io_uring_wait_cqe(ring, &cqe);
+ if (cqe->res != FSIZE) {
+ if (fail && cqe->res == -EBADF)
+ return 0;
+ fprintf(stderr, "Read: %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ for (i = 0; i < FSIZE; i++) {
+ if (buf[i] != PAT) {
+ fprintf(stderr, "got %x, wanted %x\n", buf[i], PAT);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int test(const char *filename)
+{
+ struct io_uring sring, dring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_queue_init(8, &sring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_queue_init(8, &dring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = io_uring_register_files_sparse(&sring, 8);
+ if (ret) {
+ if (ret == -EINVAL)
+ return T_EXIT_SKIP;
+ fprintf(stderr, "register files failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_register_files_sparse(&dring, 8);
+ if (ret) {
+ fprintf(stderr, "register files failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* open direct descriptor */
+ sqe = io_uring_get_sqe(&sring);
+ io_uring_prep_openat_direct(sqe, AT_FDCWD, filename, 0, 0644, 0);
+ io_uring_submit(&sring);
+ ret = io_uring_wait_cqe(&sring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&sring, cqe);
+
+ /* verify data is sane for source ring */
+ if (verify_fixed_read(&sring, 0, 0))
+ return T_EXIT_FAIL;
+
+ /* send direct descriptor to destination ring */
+ sqe = io_uring_get_sqe(&sring);
+ io_uring_prep_msg_ring(sqe, dring.ring_fd, 0, 0x89, 0);
+ sqe->addr = 1;
+ sqe->addr3 = 0;
+ sqe->file_index = 1;
+ io_uring_submit(&sring);
+
+ ret = io_uring_wait_cqe(&sring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res) {
+ if (cqe->res == -EINVAL && !no_fd_pass) {
+ no_fd_pass = 1;
+ return T_EXIT_SKIP;
+ }
+ fprintf(stderr, "msg_ring failed %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&sring, cqe);
+
+ /* get posted completion for the passing */
+ ret = io_uring_wait_cqe(&dring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 0x89) {
+ fprintf(stderr, "bad user_data %ld\n", (long) cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&dring, cqe);
+
+ /* now verify we can read the sane data from the destination ring */
+ if (verify_fixed_read(&dring, 0, 0))
+ return T_EXIT_FAIL;
+
+ /* close descriptor in source ring */
+ sqe = io_uring_get_sqe(&sring);
+ io_uring_prep_close_direct(sqe, 0);
+ io_uring_submit(&sring);
+
+ ret = io_uring_wait_cqe(&sring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe failed %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res) {
+ fprintf(stderr, "direct close failed %d\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&sring, cqe);
+
+ /* check that source ring fails after close */
+ if (verify_fixed_read(&sring, 0, 1))
+ return T_EXIT_FAIL;
+
+ /* check we can still read from destination ring */
+ if (verify_fixed_read(&dring, 0, 0))
+ return T_EXIT_FAIL;
+
+ return T_EXIT_PASS;
+}
+
+int main(int argc, char *argv[])
+{
+ char fname[80];
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ sprintf(fname, ".fd-pass.%d", getpid());
+ t_create_file_pattern(fname, FSIZE, PAT);
+
+ ret = test(fname);
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "test failed\n");
+ ret = T_EXIT_FAIL;
+ }
+
+ unlink(fname);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/file-register.c b/contrib/libs/liburing/test/file-register.c
new file mode 100644
index 00000000000..707cd5422fa
--- /dev/null
+++ b/contrib/libs/liburing/test/file-register.c
@@ -0,0 +1,1125 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various file registration tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <sys/resource.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int no_update = 0;
+
+static void close_files(int *files, int nr_files, int add)
+{
+ char fname[32];
+ int i;
+
+ for (i = 0; i < nr_files; i++) {
+ if (files)
+ close(files[i]);
+ if (!add)
+ sprintf(fname, ".reg.%d", i);
+ else
+ sprintf(fname, ".add.%d", i + add);
+ unlink(fname);
+ }
+ if (files)
+ free(files);
+}
+
+static int *open_files(int nr_files, int extra, int add)
+{
+ char fname[32];
+ int *files;
+ int i;
+
+ files = t_calloc(nr_files + extra, sizeof(int));
+
+ for (i = 0; i < nr_files; i++) {
+ if (!add)
+ sprintf(fname, ".reg.%d", i);
+ else
+ sprintf(fname, ".add.%d", i + add);
+ files[i] = open(fname, O_RDWR | O_CREAT, 0644);
+ if (files[i] < 0) {
+ perror("open");
+ free(files);
+ files = NULL;
+ break;
+ }
+ }
+ if (extra) {
+ for (i = nr_files; i < nr_files + extra; i++)
+ files[i] = -1;
+ }
+
+ return files;
+}
+
+static int test_shrink(struct io_uring *ring)
+{
+ int ret, off, fd;
+ int *files;
+
+ files = open_files(50, 0, 0);
+ ret = io_uring_register_files(ring, files, 50);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ off = 0;
+ do {
+ fd = -1;
+ ret = io_uring_register_files_update(ring, off, &fd, 1);
+ if (ret != 1) {
+ if (off == 50 && ret == -EINVAL)
+ break;
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ break;
+ }
+ off++;
+ } while (1);
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 50, 0);
+ return 0;
+err:
+ close_files(files, 50, 0);
+ return 1;
+}
+
+
+static int test_grow(struct io_uring *ring)
+{
+ int ret, off;
+ int *files, *fds = NULL;
+
+ files = open_files(50, 250, 0);
+ ret = io_uring_register_files(ring, files, 300);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ off = 50;
+ do {
+ fds = open_files(1, 0, off);
+ ret = io_uring_register_files_update(ring, off, fds, 1);
+ if (ret != 1) {
+ if (off == 300 && ret == -EINVAL)
+ break;
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ break;
+ }
+ if (off >= 300) {
+ fprintf(stderr, "%s: Succeeded beyond end-of-list?\n", __FUNCTION__);
+ goto err;
+ }
+ off++;
+ } while (1);
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 100, 0);
+ close_files(NULL, 251, 50);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ close_files(NULL, 251, 50);
+ return 1;
+}
+
+static int test_replace_all(struct io_uring *ring)
+{
+ int *files, *fds = NULL;
+ int ret, i;
+
+ files = open_files(100, 0, 0);
+ ret = io_uring_register_files(ring, files, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ fds = t_malloc(100 * sizeof(int));
+ for (i = 0; i < 100; i++)
+ fds[i] = -1;
+
+ ret = io_uring_register_files_update(ring, 0, fds, 100);
+ if (ret != 100) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 100, 0);
+ if (fds)
+ free(fds);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ if (fds)
+ free(fds);
+ return 1;
+}
+
+static int test_replace(struct io_uring *ring)
+{
+ int *files, *fds = NULL;
+ int ret;
+
+ files = open_files(100, 0, 0);
+ ret = io_uring_register_files(ring, files, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ fds = open_files(10, 0, 1);
+ ret = io_uring_register_files_update(ring, 90, fds, 10);
+ if (ret != 10) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 100, 0);
+ if (fds)
+ close_files(fds, 10, 1);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ if (fds)
+ close_files(fds, 10, 1);
+ return 1;
+}
+
+static int test_removals(struct io_uring *ring)
+{
+ int *files, *fds = NULL;
+ int ret, i;
+
+ files = open_files(100, 0, 0);
+ ret = io_uring_register_files(ring, files, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ fds = t_calloc(10, sizeof(int));
+ for (i = 0; i < 10; i++)
+ fds[i] = -1;
+
+ ret = io_uring_register_files_update(ring, 50, fds, 10);
+ if (ret != 10) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 100, 0);
+ if (fds)
+ free(fds);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ if (fds)
+ free(fds);
+ return 1;
+}
+
+static int test_additions(struct io_uring *ring)
+{
+ int *files, *fds = NULL;
+ int ret;
+
+ files = open_files(100, 100, 0);
+ ret = io_uring_register_files(ring, files, 200);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ fds = open_files(2, 0, 1);
+ ret = io_uring_register_files_update(ring, 100, fds, 2);
+ if (ret != 2) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ close_files(files, 100, 0);
+ if (fds)
+ close_files(fds, 2, 1);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ if (fds)
+ close_files(fds, 2, 1);
+ return 1;
+}
+
+static int test_sparse(struct io_uring *ring)
+{
+ int *files;
+ int ret;
+
+ files = open_files(100, 100, 0);
+ ret = io_uring_register_files(ring, files, 200);
+ if (ret) {
+ if (ret == -EBADF) {
+ fprintf(stdout, "Sparse files not supported, skipping\n");
+ no_update = 1;
+ goto done;
+ }
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+done:
+ close_files(files, 100, 0);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ return 1;
+}
+
+static int test_basic_many(struct io_uring *ring)
+{
+ int *files;
+ int ret;
+
+ files = open_files(768, 0, 0);
+ ret = io_uring_register_files(ring, files, 768);
+ if (ret) {
+ fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ close_files(files, 768, 0);
+ return 0;
+err:
+ close_files(files, 768, 0);
+ return 1;
+}
+
+static int test_basic(struct io_uring *ring, int fail)
+{
+ int *files;
+ int ret;
+ int nr_files = fail ? 10 : 100;
+
+ files = open_files(nr_files, 0, 0);
+ ret = io_uring_register_files(ring, files, 100);
+ if (ret) {
+ if (fail) {
+ if (ret == -EBADF || ret == -EFAULT)
+ return 0;
+ }
+ fprintf(stderr, "%s: register %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ if (fail) {
+ fprintf(stderr, "Registration succeeded, but expected fail\n");
+ goto err;
+ }
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ close_files(files, nr_files, 0);
+ return 0;
+err:
+ close_files(files, nr_files, 0);
+ return 1;
+}
+
+/*
+ * Register 0 files, but reserve space for 10. Then add one file.
+ */
+static int test_zero(struct io_uring *ring)
+{
+ int *files, *fds = NULL;
+ int ret;
+
+ files = open_files(0, 10, 0);
+ ret = io_uring_register_files(ring, files, 10);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ fds = open_files(1, 0, 1);
+ ret = io_uring_register_files_update(ring, 0, fds, 1);
+ if (ret != 1) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (fds)
+ close_files(fds, 1, 1);
+ free(files);
+ return 0;
+err:
+ if (fds)
+ close_files(fds, 1, 1);
+ free(files);
+ return 1;
+}
+
+static int test_fixed_read_write(struct io_uring *ring, int index)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec iov[2];
+ int ret;
+
+ iov[0].iov_base = t_malloc(4096);
+ iov[0].iov_len = 4096;
+ memset(iov[0].iov_base, 0x5a, 4096);
+
+ iov[1].iov_base = t_malloc(4096);
+ iov[1].iov_len = 4096;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_writev(sqe, index, &iov[0], 1, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ if (cqe->res != 4096) {
+ fprintf(stderr, "%s: write cqe->res=%d\n", __FUNCTION__, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_readv(sqe, index, &iov[1], 1, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ if (cqe->res != 4096) {
+ fprintf(stderr, "%s: read cqe->res=%d\n", __FUNCTION__, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ if (memcmp(iov[1].iov_base, iov[0].iov_base, 4096)) {
+ fprintf(stderr, "%s: data mismatch\n", __FUNCTION__);
+ return 1;
+ }
+
+ free(iov[0].iov_base);
+ free(iov[1].iov_base);
+ return 0;
+}
+
+static void adjust_nfiles(int want_files)
+{
+ struct rlimit rlim;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0)
+ return;
+ if (rlim.rlim_cur >= want_files)
+ return;
+ rlim.rlim_cur = want_files;
+ setrlimit(RLIMIT_NOFILE, &rlim);
+}
+
+/*
+ * Register 8K of sparse files, update one at a random spot, then do some
+ * file IO to verify it works.
+ */
+static int test_huge(struct io_uring *ring)
+{
+ int *files;
+ int ret;
+
+ adjust_nfiles(16384);
+
+ files = open_files(0, 8192, 0);
+ ret = io_uring_register_files(ring, files, 8192);
+ if (ret) {
+ /* huge sets not supported */
+ if (ret == -EMFILE) {
+ fprintf(stdout, "%s: No huge file set support, skipping\n", __FUNCTION__);
+ goto out;
+ }
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ files[7193] = open(".reg.7193", O_RDWR | O_CREAT, 0644);
+ if (files[7193] < 0) {
+ fprintf(stderr, "%s: open=%d\n", __FUNCTION__, errno);
+ goto err;
+ }
+
+ ret = io_uring_register_files_update(ring, 7193, &files[7193], 1);
+ if (ret != 1) {
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (test_fixed_read_write(ring, 7193))
+ goto err;
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (files[7193] != -1) {
+ close(files[7193]);
+ unlink(".reg.7193");
+ }
+out:
+ free(files);
+ return 0;
+err:
+ if (files[7193] != -1) {
+ close(files[7193]);
+ unlink(".reg.7193");
+ }
+ free(files);
+ return 1;
+}
+
+static int test_skip(struct io_uring *ring)
+{
+ int *files;
+ int ret;
+
+ files = open_files(100, 0, 0);
+ ret = io_uring_register_files(ring, files, 100);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ files[90] = IORING_REGISTER_FILES_SKIP;
+ ret = io_uring_register_files_update(ring, 90, &files[90], 1);
+ if (ret != 1) {
+ if (ret == -EBADF) {
+ fprintf(stdout, "Skipping files not supported\n");
+ goto done;
+ }
+ fprintf(stderr, "%s: update ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ /* verify can still use file index 90 */
+ if (test_fixed_read_write(ring, 90))
+ goto err;
+
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "%s: unregister ret=%d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+done:
+ close_files(files, 100, 0);
+ return 0;
+err:
+ close_files(files, 100, 0);
+ return 1;
+}
+
+static int test_sparse_updates(void)
+{
+ struct io_uring ring;
+ int ret, i, *fds, newfd;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return ret;
+ }
+
+ fds = t_malloc(256 * sizeof(int));
+ for (i = 0; i < 256; i++)
+ fds[i] = -1;
+
+ ret = io_uring_register_files(&ring, fds, 256);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+
+ newfd = 1;
+ for (i = 0; i < 256; i++) {
+ ret = io_uring_register_files_update(&ring, i, &newfd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "file_update: %d\n", ret);
+ return ret;
+ }
+ }
+ io_uring_unregister_files(&ring);
+
+ for (i = 0; i < 256; i++)
+ fds[i] = 1;
+
+ ret = io_uring_register_files(&ring, fds, 256);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+
+ newfd = -1;
+ for (i = 0; i < 256; i++) {
+ ret = io_uring_register_files_update(&ring, i, &newfd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "file_update: %d\n", ret);
+ return ret;
+ }
+ }
+ io_uring_unregister_files(&ring);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_fixed_removal_ordering(void)
+{
+ char buffer[128];
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret, fd, i, fds[2];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+ ret = io_uring_register_files(&ring, fds, 2);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+ /* ring should have fds referenced, can close them */
+ close(fds[0]);
+ close(fds[1]);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ return 1;
+ }
+ /* outwait file recycling delay */
+ ts.tv_sec = 3;
+ ts.tv_nsec = 0;
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return -1;
+ }
+ io_uring_prep_write(sqe, 1, buffer, sizeof(buffer), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ /* remove unused pipe end */
+ fd = -1;
+ ret = io_uring_register_files_update(&ring, 0, &fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "update off=0 failed\n");
+ return -1;
+ }
+
+ /* remove used pipe end */
+ fd = -1;
+ ret = io_uring_register_files_update(&ring, 1, &fd, 1);
+ if (ret != 1) {
+ fprintf(stderr, "update off=1 failed\n");
+ return -1;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+/* mix files requiring SCM-accounting and not in a single register */
+static int test_mixed_af_unix(void)
+{
+ struct io_uring ring;
+ int i, ret, fds[2];
+ int reg_fds[32];
+ int sp[2];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
+ perror("Failed to create Unix-domain socket pair\n");
+ return 1;
+ }
+
+ for (i = 0; i < 16; i++) {
+ reg_fds[i * 2] = fds[0];
+ reg_fds[i * 2 + 1] = sp[0];
+ }
+
+ ret = io_uring_register_files(&ring, reg_fds, 32);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ close(sp[0]);
+ close(sp[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_partial_register_fail(void)
+{
+ char buffer[128];
+ struct io_uring ring;
+ int ret, fds[2];
+ int reg_fds[5];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+
+ /*
+ * Expect register to fail as it doesn't support io_uring fds, shouldn't
+ * leave any fds referenced afterwards.
+ */
+ reg_fds[0] = fds[0];
+ reg_fds[1] = fds[1];
+ reg_fds[2] = -1;
+ reg_fds[3] = ring.ring_fd;
+ reg_fds[4] = -1;
+ ret = io_uring_register_files(&ring, reg_fds, 5);
+ if (!ret) {
+ fprintf(stderr, "file_register unexpectedly succeeded\n");
+ return 1;
+ }
+
+ /* ring should have fds referenced, can close them */
+ close(fds[1]);
+
+ /* confirm that fds[1] is actually close and to ref'ed by io_uring */
+ ret = read(fds[0], buffer, 10);
+ if (ret < 0)
+ perror("read");
+ close(fds[0]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int file_update_alloc(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_files_update(sqe, fd, 1, IORING_FILE_INDEX_ALLOC);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_wait_cqe=%d\n", __FUNCTION__, ret);
+ return -1;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static int test_out_of_range_file_ranges(struct io_uring *ring)
+{
+ int ret;
+
+ ret = io_uring_register_file_alloc_range(ring, 8, 3);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "overlapping range %i\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_file_alloc_range(ring, 10, 1);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "out of range index %i\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_file_alloc_range(ring, 7, ~1U);
+ if (ret != -EOVERFLOW) {
+ fprintf(stderr, "overflow %i\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_overallocating_file_range(struct io_uring *ring, int fds[2])
+{
+ int roff = 7, rlen = 2;
+ int ret, i, fd;
+
+ ret = io_uring_register_file_alloc_range(ring, roff, rlen);
+ if (ret) {
+ fprintf(stderr, "io_uring_register_file_alloc_range %i\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < rlen; i++) {
+ fd = fds[0];
+ ret = file_update_alloc(ring, &fd);
+ if (ret != 1) {
+ fprintf(stderr, "file_update_alloc\n");
+ return 1;
+ }
+
+ if (fd < roff || fd >= roff + rlen) {
+ fprintf(stderr, "invalid off result %i\n", fd);
+ return 1;
+ }
+ }
+
+ fd = fds[0];
+ ret = file_update_alloc(ring, &fd);
+ if (ret != -ENFILE) {
+ fprintf(stderr, "overallocated %i, off %i\n", ret, fd);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_zero_range_alloc(struct io_uring *ring, int fds[2])
+{
+ int ret, fd;
+
+ ret = io_uring_register_file_alloc_range(ring, 7, 0);
+ if (ret) {
+ fprintf(stderr, "io_uring_register_file_alloc_range failed %i\n", ret);
+ return 1;
+ }
+
+ fd = fds[0];
+ ret = file_update_alloc(ring, &fd);
+ if (ret != -ENFILE) {
+ fprintf(stderr, "zero alloc %i\n", ret);
+ return 1;
+ }
+ return 0;
+}
+
+static int test_file_alloc_ranges(void)
+{
+ struct io_uring ring;
+ int ret, pipe_fds[2];
+
+ if (pipe(pipe_fds)) {
+ fprintf(stderr, "pipes\n");
+ return 1;
+ }
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, 10);
+ if (ret == -EINVAL) {
+not_supported:
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ printf("file alloc ranges are not supported, skip\n");
+ return 0;
+ } else if (ret) {
+ fprintf(stderr, "io_uring_register_files_sparse %i\n", ret);
+ return ret;
+ }
+
+ ret = io_uring_register_file_alloc_range(&ring, 0, 1);
+ if (ret) {
+ if (ret == -EINVAL)
+ goto not_supported;
+ fprintf(stderr, "io_uring_register_file_alloc_range %i\n", ret);
+ return 1;
+ }
+
+ ret = test_overallocating_file_range(&ring, pipe_fds);
+ if (ret) {
+ fprintf(stderr, "test_overallocating_file_range() failed\n");
+ return 1;
+ }
+
+ ret = test_out_of_range_file_ranges(&ring);
+ if (ret) {
+ fprintf(stderr, "test_out_of_range_file_ranges() failed\n");
+ return 1;
+ }
+
+ ret = test_zero_range_alloc(&ring, pipe_fds);
+ if (ret) {
+ fprintf(stderr, "test_zero_range_alloc() failed\n");
+ return 1;
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_basic(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "test_basic failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_basic(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "test_basic failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_basic_many(&ring);
+ if (ret) {
+ fprintf(stderr, "test_basic_many failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sparse(&ring);
+ if (ret) {
+ fprintf(stderr, "test_sparse failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (no_update)
+ return T_EXIT_SKIP;
+
+ ret = test_additions(&ring);
+ if (ret) {
+ fprintf(stderr, "test_additions failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_removals(&ring);
+ if (ret) {
+ fprintf(stderr, "test_removals failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_replace(&ring);
+ if (ret) {
+ fprintf(stderr, "test_replace failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_replace_all(&ring);
+ if (ret) {
+ fprintf(stderr, "test_replace_all failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_grow(&ring);
+ if (ret) {
+ fprintf(stderr, "test_grow failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_shrink(&ring);
+ if (ret) {
+ fprintf(stderr, "test_shrink failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_zero(&ring);
+ if (ret) {
+ fprintf(stderr, "test_zero failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_huge(&ring);
+ if (ret) {
+ fprintf(stderr, "test_huge failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_skip(&ring);
+ if (ret) {
+ fprintf(stderr, "test_skip failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sparse_updates();
+ if (ret) {
+ fprintf(stderr, "test_sparse_updates failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_fixed_removal_ordering();
+ if (ret) {
+ fprintf(stderr, "test_fixed_removal_ordering failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_mixed_af_unix();
+ if (ret) {
+ fprintf(stderr, "test_mixed_af_unix failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_partial_register_fail();
+ if (ret) {
+ fprintf(stderr, "test_partial_register_fail failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_file_alloc_ranges();
+ if (ret) {
+ fprintf(stderr, "test_partial_register_fail failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/file-update.c b/contrib/libs/liburing/test/file-update.c
new file mode 100644
index 00000000000..0896807ddfc
--- /dev/null
+++ b/contrib/libs/liburing/test/file-update.c
@@ -0,0 +1,232 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various file registration tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static void close_files(int *files, int nr_files, int add)
+{
+ char fname[32];
+ int i;
+
+ for (i = 0; i < nr_files; i++) {
+ if (files)
+ close(files[i]);
+ if (!add)
+ sprintf(fname, ".reg.%d", i);
+ else
+ sprintf(fname, ".add.%d", i + add);
+ unlink(fname);
+ }
+ if (files)
+ free(files);
+}
+
+static int *open_files(int nr_files, int extra, int add)
+{
+ char fname[32];
+ int *files;
+ int i;
+
+ files = t_calloc(nr_files + extra, sizeof(int));
+
+ for (i = 0; i < nr_files; i++) {
+ if (!add)
+ sprintf(fname, ".reg.%d", i);
+ else
+ sprintf(fname, ".add.%d", i + add);
+ files[i] = open(fname, O_RDWR | O_CREAT, 0644);
+ if (files[i] < 0) {
+ perror("open");
+ free(files);
+ files = NULL;
+ break;
+ }
+ }
+ if (extra) {
+ for (i = nr_files; i < nr_files + extra; i++)
+ files[i] = -1;
+ }
+
+ return files;
+}
+
+static int test_update_multiring(struct io_uring *r1, struct io_uring *r2,
+ struct io_uring *r3, int do_unreg)
+{
+ int *fds, *newfds;
+
+ fds = open_files(10, 0, 0);
+ newfds = open_files(10, 0, 1);
+
+ if (io_uring_register_files(r1, fds, 10) ||
+ io_uring_register_files(r2, fds, 10) ||
+ io_uring_register_files(r3, fds, 10)) {
+ fprintf(stderr, "%s: register files failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ if (io_uring_register_files_update(r1, 0, newfds, 10) != 10 ||
+ io_uring_register_files_update(r2, 0, newfds, 10) != 10 ||
+ io_uring_register_files_update(r3, 0, newfds, 10) != 10) {
+ fprintf(stderr, "%s: update files failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ if (!do_unreg)
+ goto done;
+
+ if (io_uring_unregister_files(r1) ||
+ io_uring_unregister_files(r2) ||
+ io_uring_unregister_files(r3)) {
+ fprintf(stderr, "%s: unregister files failed\n", __FUNCTION__);
+ goto err;
+ }
+
+done:
+ close_files(fds, 10, 0);
+ close_files(newfds, 10, 1);
+ return 0;
+err:
+ close_files(fds, 10, 0);
+ close_files(newfds, 10, 1);
+ return 1;
+}
+
+static int test_sqe_update(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int *fds, i, ret;
+
+ fds = t_malloc(sizeof(int) * 10);
+ for (i = 0; i < 10; i++)
+ fds[i] = -1;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_files_update(sqe, fds, 10, 0);
+ 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: %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ free(fds);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "IORING_OP_FILES_UPDATE not supported, skipping\n");
+ return T_EXIT_SKIP;
+ }
+ return ret != 10;
+}
+
+static int test_update_no_table(void)
+{
+ int up_fd, fds[4] = {-1, 0, 1, 4};
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = t_create_ring(2, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret != T_SETUP_OK)
+ return ret;
+
+ ret = io_uring_register_files(&ring, fds, 4);
+ /* ignore other failures */
+ if (ret && ret != -EBADF) {
+ fprintf(stderr, "Failed registering file table: %d\n", ret);
+ goto fail;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ up_fd = ring.ring_fd;
+ io_uring_prep_files_update(sqe, &up_fd, 1, -1); //offset = -1
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "Failed submit: %d\n", ret);
+ goto fail;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Failed wait: %d\n", ret);
+ goto fail;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(&ring, cqe);
+ if (ret != -EMFILE && ret != -EINVAL && ret != -EOVERFLOW &&
+ ret != -ENXIO) {
+ fprintf(stderr, "Bad cqe res: %d\n", ret);
+ goto fail;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+fail:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring r1, r2, r3;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (io_uring_queue_init(8, &r1, 0) ||
+ io_uring_queue_init(8, &r2, 0) ||
+ io_uring_queue_init(8, &r3, 0)) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_update_multiring(&r1, &r2, &r3, 1);
+ if (ret) {
+ fprintf(stderr, "test_update_multiring w/unreg\n");
+ return ret;
+ }
+
+ ret = test_update_multiring(&r1, &r2, &r3, 0);
+ if (ret) {
+ fprintf(stderr, "test_update_multiring wo/unreg\n");
+ return ret;
+ }
+
+ ret = test_sqe_update(&r1);
+ if (ret) {
+ if (ret != T_EXIT_SKIP)
+ fprintf(stderr, "test_sqe_update failed\n");
+ return ret;
+ }
+
+ ret = test_update_no_table();
+ if (ret) {
+ if (ret != T_EXIT_SKIP)
+ fprintf(stderr, "test_sqe_update failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/file-verify.c b/contrib/libs/liburing/test/file-verify.c
new file mode 100644
index 00000000000..7950b739cc8
--- /dev/null
+++ b/contrib/libs/liburing/test/file-verify.c
@@ -0,0 +1,634 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various reads tests, verifying data
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <string.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <linux/fs.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FSIZE 128*1024*1024
+#define CHUNK_SIZE 131072
+#define PUNCH_SIZE 32768
+
+/*
+ * 8 because it fits within the on-stack iov, 16 because it's larger than 8
+ */
+#define MIN_VECS 8
+#define MAX_VECS 16
+
+/*
+ * Can be anything, let's just do something for a bit of parallellism
+ */
+#define READ_BATCH 16
+
+/*
+ * Each offset in the file has the offset / sizeof(int) stored for every
+ * sizeof(int) address.
+ */
+static int verify_buf(void *buf, size_t size, off_t off)
+{
+ int i, u_in_buf = size / sizeof(unsigned int);
+ unsigned int *ptr;
+
+ off /= sizeof(unsigned int);
+ ptr = buf;
+ for (i = 0; i < u_in_buf; i++) {
+ if (off != *ptr) {
+ fprintf(stderr, "Found %u, wanted %lu\n", *ptr, off);
+ return 1;
+ }
+ ptr++;
+ off++;
+ }
+
+ return 0;
+}
+
+static int test_truncate(struct io_uring *ring, const char *fname, int buffered,
+ int vectored, int provide_buf)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec vec;
+ struct stat sb;
+ off_t punch_off, off, file_size;
+ void *buf = NULL;
+ int u_in_buf, i, ret, fd, first_pass = 1;
+ unsigned int *ptr;
+
+ if (buffered)
+ fd = open(fname, O_RDWR);
+ else
+ fd = open(fname, O_DIRECT | O_RDWR);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ if (fstat(fd, &sb) < 0) {
+ perror("stat");
+ close(fd);
+ return 1;
+ }
+
+ if (S_ISREG(sb.st_mode)) {
+ file_size = sb.st_size;
+ } else if (S_ISBLK(sb.st_mode)) {
+ unsigned long long bytes;
+
+ if (ioctl(fd, BLKGETSIZE64, &bytes) < 0) {
+ perror("ioctl");
+ close(fd);
+ return 1;
+ }
+ file_size = bytes;
+ } else {
+ goto out;
+ }
+
+ if (file_size < CHUNK_SIZE)
+ goto out;
+
+ t_posix_memalign(&buf, 4096, CHUNK_SIZE);
+
+ off = file_size - (CHUNK_SIZE / 2);
+ punch_off = off + CHUNK_SIZE / 4;
+
+ u_in_buf = CHUNK_SIZE / sizeof(unsigned int);
+ ptr = buf;
+ for (i = 0; i < u_in_buf; i++) {
+ *ptr = i;
+ ptr++;
+ }
+ ret = pwrite(fd, buf, CHUNK_SIZE / 2, off);
+ if (ret < 0) {
+ perror("pwrite");
+ goto err;
+ } else if (ret != CHUNK_SIZE / 2)
+ goto out;
+
+again:
+ /*
+ * Read in last bit of file so it's known cached, then remove half of that
+ * last bit so we get a short read that needs retry
+ */
+ ret = pread(fd, buf, CHUNK_SIZE / 2, off);
+ if (ret < 0) {
+ perror("pread");
+ goto err;
+ } else if (ret != CHUNK_SIZE / 2)
+ goto out;
+
+ if (posix_fadvise(fd, punch_off, CHUNK_SIZE / 4, POSIX_FADV_DONTNEED) < 0) {
+ perror("posix_fadivse");
+ goto err;
+ }
+
+ if (provide_buf) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_provide_buffers(sqe, buf, CHUNK_SIZE, 1, 0, 0);
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "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);
+ if (ret) {
+ fprintf(stderr, "Provide buffer failed %d\n", ret);
+ goto err;
+ }
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (vectored) {
+ assert(!provide_buf);
+ vec.iov_base = buf;
+ vec.iov_len = CHUNK_SIZE;
+ io_uring_prep_readv(sqe, fd, &vec, 1, off);
+ } else {
+ if (provide_buf) {
+ io_uring_prep_read(sqe, fd, NULL, CHUNK_SIZE, off);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ } else {
+ io_uring_prep_read(sqe, fd, buf, CHUNK_SIZE, off);
+ }
+ }
+ memset(buf, 0, CHUNK_SIZE);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "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);
+ if (ret != CHUNK_SIZE / 2) {
+ fprintf(stderr, "Unexpected truncated read %d\n", ret);
+ goto err;
+ }
+
+ if (verify_buf(buf, CHUNK_SIZE / 2, 0))
+ goto err;
+
+ /*
+ * Repeat, but punch first part instead of last
+ */
+ if (first_pass) {
+ punch_off = file_size - CHUNK_SIZE / 4;
+ first_pass = 0;
+ goto again;
+ }
+
+out:
+ free(buf);
+ close(fd);
+ return 0;
+err:
+ free(buf);
+ close(fd);
+ return 1;
+}
+
+enum {
+ PUNCH_NONE,
+ PUNCH_FRONT,
+ PUNCH_MIDDLE,
+ PUNCH_END,
+};
+
+/*
+ * For each chunk in file, DONTNEED a start, end, or middle segment of it.
+ * We enter here with the file fully cached every time, either freshly
+ * written or after other reads. This forces (at least) the buffered reads
+ * to be handled incrementally, exercising that path.
+ */
+static int do_punch(int fd)
+{
+ off_t offset = 0;
+ int punch_type;
+
+ while (offset + CHUNK_SIZE <= FSIZE) {
+ off_t punch_off;
+
+ punch_type = rand() % (PUNCH_END + 1);
+ switch (punch_type) {
+ default:
+ case PUNCH_NONE:
+ punch_off = -1; /* gcc... */
+ break;
+ case PUNCH_FRONT:
+ punch_off = offset;
+ break;
+ case PUNCH_MIDDLE:
+ punch_off = offset + PUNCH_SIZE;
+ break;
+ case PUNCH_END:
+ punch_off = offset + CHUNK_SIZE - PUNCH_SIZE;
+ break;
+ }
+
+ offset += CHUNK_SIZE;
+ if (punch_type == PUNCH_NONE)
+ continue;
+ if (posix_fadvise(fd, punch_off, PUNCH_SIZE, POSIX_FADV_DONTNEED) < 0) {
+ perror("posix_fadivse");
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int provide_buffers(struct io_uring *ring, void **buf)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ /* real use case would have one buffer chopped up, but... */
+ for (i = 0; i < READ_BATCH; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_provide_buffers(sqe, buf[i], CHUNK_SIZE, 1, 0, i);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != READ_BATCH) {
+ fprintf(stderr, "Submit failed %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < READ_BATCH; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe res provide %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test(struct io_uring *ring, const char *fname, int buffered,
+ int vectored, int small_vecs, int registered, int provide)
+{
+ struct iovec vecs[READ_BATCH][MAX_VECS];
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ void *buf[READ_BATCH];
+ int ret, fd, flags;
+ int i, j, nr_vecs;
+ off_t off, voff;
+ size_t left;
+
+ if (registered) {
+ assert(!provide);
+ assert(!vectored && !small_vecs);
+ }
+ if (provide) {
+ assert(!registered);
+ assert(!vectored && !small_vecs);
+ }
+
+ flags = O_RDONLY;
+ if (!buffered)
+ flags |= O_DIRECT;
+ fd = open(fname, flags);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ if (do_punch(fd))
+ return 1;
+
+ if (vectored) {
+ if (small_vecs)
+ nr_vecs = MIN_VECS;
+ else
+ nr_vecs = MAX_VECS;
+
+ for (j = 0; j < READ_BATCH; j++) {
+ for (i = 0; i < nr_vecs; i++) {
+ void *ptr;
+
+ t_posix_memalign(&ptr, 4096, CHUNK_SIZE / nr_vecs);
+ vecs[j][i].iov_base = ptr;
+ vecs[j][i].iov_len = CHUNK_SIZE / nr_vecs;
+ }
+ }
+ } else {
+ for (j = 0; j < READ_BATCH; j++)
+ t_posix_memalign(&buf[j], 4096, CHUNK_SIZE);
+ nr_vecs = 0;
+ }
+
+ if (registered) {
+ struct iovec v[READ_BATCH];
+
+ for (i = 0; i < READ_BATCH; i++) {
+ v[i].iov_base = buf[i];
+ v[i].iov_len = CHUNK_SIZE;
+ }
+ ret = io_uring_register_buffers(ring, v, READ_BATCH);
+ if (ret) {
+ fprintf(stderr, "Error buffer reg %d\n", ret);
+ goto err;
+ }
+ }
+
+ i = 0;
+ left = FSIZE;
+ off = 0;
+ while (left) {
+ int pending = 0;
+
+ if (provide && provide_buffers(ring, buf))
+ goto err;
+
+ for (i = 0; i < READ_BATCH; i++) {
+ size_t this = left;
+
+ if (this > CHUNK_SIZE)
+ this = CHUNK_SIZE;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (vectored) {
+ io_uring_prep_readv(sqe, fd, vecs[i], nr_vecs, off);
+ } else {
+ if (registered) {
+ io_uring_prep_read_fixed(sqe, fd, buf[i], this, off, i);
+ } else if (provide) {
+ io_uring_prep_read(sqe, fd, NULL, this, off);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ } else {
+ io_uring_prep_read(sqe, fd, buf[i], this, off);
+ }
+ }
+ sqe->user_data = ((uint64_t)off << 32) | i;
+ off += this;
+ left -= this;
+ pending++;
+ if (!left)
+ break;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != pending) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < pending; i++) {
+ int index;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "bad read %d, read %d\n", cqe->res, i);
+ goto err;
+ }
+ if (cqe->res < CHUNK_SIZE) {
+ fprintf(stderr, "short read %d, read %d\n", cqe->res, i);
+ goto err;
+ }
+ if (cqe->flags & IORING_CQE_F_BUFFER)
+ index = cqe->flags >> 16;
+ else
+ index = cqe->user_data & 0xffffffff;
+ voff = cqe->user_data >> 32;
+ io_uring_cqe_seen(ring, cqe);
+ if (vectored) {
+ for (j = 0; j < nr_vecs; j++) {
+ void *buf = vecs[index][j].iov_base;
+ size_t len = vecs[index][j].iov_len;
+
+ if (verify_buf(buf, len, voff))
+ goto err;
+ voff += len;
+ }
+ } else {
+ if (verify_buf(buf[index], CHUNK_SIZE, voff))
+ goto err;
+ }
+ }
+ }
+
+ ret = 0;
+done:
+ if (registered)
+ io_uring_unregister_buffers(ring);
+ if (vectored) {
+ for (j = 0; j < READ_BATCH; j++)
+ for (i = 0; i < nr_vecs; i++)
+ free(vecs[j][i].iov_base);
+ } else {
+ for (j = 0; j < READ_BATCH; j++)
+ free(buf[j]);
+ }
+ close(fd);
+ return ret;
+err:
+ ret = 1;
+ goto done;
+}
+
+static int fill_pattern(const char *fname)
+{
+ size_t left = FSIZE;
+ unsigned int val, *ptr;
+ void *buf;
+ int fd, i;
+
+ fd = open(fname, O_WRONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ val = 0;
+ buf = t_malloc(4096);
+ while (left) {
+ int u_in_buf = 4096 / sizeof(val);
+ size_t this = left;
+
+ if (this > 4096)
+ this = 4096;
+ ptr = buf;
+ for (i = 0; i < u_in_buf; i++) {
+ *ptr = val;
+ val++;
+ ptr++;
+ }
+ if (write(fd, buf, 4096) != 4096)
+ return 1;
+ left -= 4096;
+ }
+
+ fsync(fd);
+ close(fd);
+ free(buf);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *fname;
+ char buf[32];
+ int ret;
+
+ srand(getpid());
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ sprintf(buf, ".file-verify.%d", getpid());
+ fname = buf;
+ t_create_file(fname, FSIZE);
+ }
+
+ ret = io_uring_queue_init(READ_BATCH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ goto err;
+ }
+
+ if (fill_pattern(fname))
+ goto err;
+
+ ret = test(&ring, fname, 1, 0, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered novec test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 1, 0, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered novec reg test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 1, 0, 0, 0, 1);
+ if (ret) {
+ fprintf(stderr, "Buffered novec provide test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 1, 1, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered vec test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 1, 1, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered small vec test failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, fname, 0, 0, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT novec test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 0, 0, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT novec reg test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 0, 0, 0, 0, 1);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT novec provide test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 0, 1, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT vec test failed\n");
+ goto err;
+ }
+ ret = test(&ring, fname, 0, 1, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT small vec test failed\n");
+ goto err;
+ }
+
+ ret = test_truncate(&ring, fname, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered end truncate read failed\n");
+ goto err;
+ }
+ ret = test_truncate(&ring, fname, 1, 1, 0);
+ if (ret) {
+ fprintf(stderr, "Buffered end truncate vec read failed\n");
+ goto err;
+ }
+ ret = test_truncate(&ring, fname, 1, 0, 1);
+ if (ret) {
+ fprintf(stderr, "Buffered end truncate pbuf read failed\n");
+ goto err;
+ }
+
+ ret = test_truncate(&ring, fname, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT end truncate read failed\n");
+ goto err;
+ }
+ ret = test_truncate(&ring, fname, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT end truncate vec read failed\n");
+ goto err;
+ }
+ ret = test_truncate(&ring, fname, 0, 0, 1);
+ if (ret) {
+ fprintf(stderr, "O_DIRECT end truncate pbuf read failed\n");
+ goto err;
+ }
+
+ if (buf == fname)
+ unlink(fname);
+ return T_EXIT_PASS;
+err:
+ if (buf == fname)
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/files-exit-hang-poll.c b/contrib/libs/liburing/test/files-exit-hang-poll.c
new file mode 100644
index 00000000000..2428a10283b
--- /dev/null
+++ b/contrib/libs/liburing/test/files-exit-hang-poll.c
@@ -0,0 +1,115 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Based on a test case from Josef Grieb - test that we can exit without
+ * hanging if we have the task file table pinned by a request that is linked
+ * to another request that doesn't finish.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <poll.h>
+#include "liburing.h"
+#include "helpers.h"
+
+#define BACKLOG 512
+
+static struct io_uring ring;
+
+static void add_poll(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd, POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+}
+
+static void add_accept(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_accept(sqe, fd, 0, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
+}
+
+static int setup_io_uring(void)
+{
+ int ret;
+
+ ret = io_uring_queue_init(16, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "Unable to setup io_uring: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+static void alarm_sig(int sig)
+{
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in serv_addr;
+ struct io_uring_cqe *cqe;
+ int ret, sock_listen_fd;
+ const int val = 1;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ sock_listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (sock_listen_fd < 0) {
+ perror("socket");
+ return T_EXIT_FAIL;
+ }
+
+ setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+
+ if (t_bind_ephemeral_port(sock_listen_fd, &serv_addr)) {
+ perror("bind");
+ return T_EXIT_FAIL;
+ }
+
+ if (listen(sock_listen_fd, BACKLOG) < 0) {
+ perror("Error listening on socket\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (setup_io_uring())
+ return T_EXIT_FAIL;
+
+ add_poll(&ring, sock_listen_fd);
+ add_accept(&ring, sock_listen_fd);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ signal(SIGALRM, alarm_sig);
+ alarm(1);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/files-exit-hang-timeout.c b/contrib/libs/liburing/test/files-exit-hang-timeout.c
new file mode 100644
index 00000000000..708e42cded3
--- /dev/null
+++ b/contrib/libs/liburing/test/files-exit-hang-timeout.c
@@ -0,0 +1,138 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Based on a test case from Josef Grieb - test that we can exit without
+ * hanging if we have the task file table pinned by a request that is linked
+ * to another request that doesn't finish.
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <poll.h>
+#include "liburing.h"
+#include "helpers.h"
+
+#define BACKLOG 512
+
+#define PORT 9100
+
+struct io_uring ring;
+
+struct __kernel_timespec ts = {
+ .tv_sec = 300,
+ .tv_nsec = 0,
+};
+
+static void add_timeout(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_timeout(sqe, &ts, 100, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+}
+
+static void add_accept(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_accept(sqe, fd, 0, 0, SOCK_NONBLOCK | SOCK_CLOEXEC);
+ sqe->flags |= IOSQE_IO_LINK;
+}
+
+static int setup_io_uring(void)
+{
+ int ret;
+
+ ret = io_uring_queue_init(16, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "Unable to setup io_uring: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ return 0;
+}
+
+static void alarm_sig(int sig)
+{
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_in serv_addr;
+ struct io_uring_cqe *cqe;
+ int ret, sock_listen_fd;
+ const int val = 1;
+ int i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ sock_listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ if (sock_listen_fd < 0) {
+ perror("socket");
+ return T_EXIT_FAIL;
+ }
+
+ setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+
+ for (i = 0; i < 100; i++) {
+ serv_addr.sin_port = htons(PORT + i);
+
+ ret = bind(sock_listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr));
+ if (!ret)
+ break;
+ if (errno != EADDRINUSE) {
+ fprintf(stderr, "bind: %s\n", strerror(errno));
+ return T_EXIT_FAIL;
+ }
+ if (i == 99) {
+ printf("Gave up on finding a port, skipping\n");
+ goto skip;
+ }
+ }
+
+ if (listen(sock_listen_fd, BACKLOG) < 0) {
+ perror("Error listening on socket\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (setup_io_uring())
+ return T_EXIT_FAIL;
+
+ add_timeout(&ring, sock_listen_fd);
+ add_accept(&ring, sock_listen_fd);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ signal(SIGALRM, alarm_sig);
+ alarm(1);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+skip:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+}
diff --git a/contrib/libs/liburing/test/fixed-buf-iter.c b/contrib/libs/liburing/test/fixed-buf-iter.c
new file mode 100644
index 00000000000..1cdb327166f
--- /dev/null
+++ b/contrib/libs/liburing/test/fixed-buf-iter.c
@@ -0,0 +1,116 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test fixed buffers with non-iterators.
+ *
+ * Taken from: https://github.com/axboe/liburing/issues/549
+ */
+#include <stdio.h>
+#include <string.h>
+#include <fcntl.h>
+#include <stdlib.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define BUF_SIZE 4096
+#define BUFFERS 1
+#define IN_FD "/dev/urandom"
+#define OUT_FD "/dev/zero"
+
+static int test(struct io_uring *ring)
+{
+ struct iovec iov[BUFFERS];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fd_in, fd_out, i;
+
+ fd_in = open(IN_FD, O_RDONLY, 0644);
+ if (fd_in < 0) {
+ perror("open in");
+ return 1;
+ }
+
+ fd_out = open(OUT_FD, O_RDWR, 0644);
+ if (fd_out < 0) {
+ perror("open out");
+ return 1;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ iov[i].iov_base = malloc(BUF_SIZE);
+ iov[i].iov_len = BUF_SIZE;
+ memset(iov[i].iov_base, 0, BUF_SIZE);
+ }
+
+ ret = io_uring_register_buffers(ring, iov, BUFFERS);
+ if (ret) {
+ fprintf(stderr, "Error registering buffers: %s", strerror(-ret));
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Could not get SQE.\n");
+ return 1;
+ }
+
+ io_uring_prep_read_fixed(sqe, fd_in, iov[0].iov_base, BUF_SIZE, 0, 0);
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "Error waiting for completion: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res));
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Could not get SQE.\n");
+ return 1;
+ }
+ io_uring_prep_write_fixed(sqe, fd_out, iov[0].iov_base, BUF_SIZE, 0, 0);
+ io_uring_submit(ring);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "Error waiting for completion: %s\n", strerror(-ret));
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res));
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = t_create_ring(8, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret < 0)
+ return T_EXIT_FAIL;
+
+ ret = test(&ring);
+ if (ret) {
+ fprintf(stderr, "Test failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/fixed-link.c b/contrib/libs/liburing/test/fixed-link.c
new file mode 100644
index 00000000000..3e9eb8c4f14
--- /dev/null
+++ b/contrib/libs/liburing/test/fixed-link.c
@@ -0,0 +1,91 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define IOVECS_LEN 2
+
+int main(int argc, char *argv[])
+{
+ struct iovec iovecs[IOVECS_LEN];
+ struct io_uring ring;
+ int i, fd, ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ fd = open("/dev/zero", O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "Failed to open /dev/zero\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (io_uring_queue_init(32, &ring, 0) < 0) {
+ fprintf(stderr, "Failed to init io_uring\n");
+ close(fd);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < IOVECS_LEN; ++i) {
+ iovecs[i].iov_base = t_malloc(64);
+ iovecs[i].iov_len = 64;
+ };
+
+ ret = io_uring_register_buffers(&ring, iovecs, IOVECS_LEN);
+ if (ret) {
+ fprintf(stderr, "Failed to register buffers\n");
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < IOVECS_LEN; ++i) {
+ struct io_uring_sqe *sqe = io_uring_get_sqe(&ring);
+ const char *str = "#include <errno.h>";
+
+ iovecs[i].iov_len = strlen(str);
+ io_uring_prep_read_fixed(sqe, fd, iovecs[i].iov_base, strlen(str), 0, i);
+ if (i == 0)
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_LINK);
+ io_uring_sqe_set_data(sqe, (void *)str);
+ }
+
+ ret = io_uring_submit_and_wait(&ring, IOVECS_LEN);
+ if (ret < 0) {
+ fprintf(stderr, "Failed to submit IO\n");
+ return T_EXIT_FAIL;
+ } else if (ret < 2) {
+ fprintf(stderr, "Submitted %d, wanted %d\n", ret, IOVECS_LEN);
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < IOVECS_LEN; i++) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (cqe->res != iovecs[i].iov_len) {
+ fprintf(stderr, "read: wanted %ld, got %d\n",
+ (long) iovecs[i].iov_len, cqe->res);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ close(fd);
+ io_uring_queue_exit(&ring);
+
+ for (i = 0; i < IOVECS_LEN; ++i)
+ free(iovecs[i].iov_base);
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/fixed-reuse.c b/contrib/libs/liburing/test/fixed-reuse.c
new file mode 100644
index 00000000000..5383f48af18
--- /dev/null
+++ b/contrib/libs/liburing/test/fixed-reuse.c
@@ -0,0 +1,161 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: link <open file><read from file><close file> with an existing
+ * file present in the opened slot, verifying that we get the new file
+ * rather than the old one.
+ *
+ */
+#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 MAX_FILES 8
+#define FNAME1 ".slot.reuse.1"
+#define FNAME2 ".slot.reuse.2"
+#define PAT1 0xaa
+#define PAT2 0x55
+#define BSIZE 4096
+
+static int test(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buf[BSIZE];
+ int ret, i;
+
+ /* open FNAME1 in slot 0 */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat_direct(sqe, AT_FDCWD, FNAME1, O_RDONLY, 0, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ 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;
+ }
+ if (cqe->res != 0) {
+ fprintf(stderr, "open res %d\n", ret);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ /*
+ * Now open FNAME2 in that same slot, verifying we get data from
+ * FNAME2 and not FNAME1.
+ */
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat_direct(sqe, AT_FDCWD, FNAME2, O_RDONLY, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 3;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_close_direct(sqe, 0);
+ sqe->user_data = 4;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 2:
+ if (cqe->res) {
+ fprintf(stderr, "bad open %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != sizeof(buf)) {
+ fprintf(stderr, "bad read %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 4:
+ if (cqe->res) {
+ fprintf(stderr, "bad close %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ for (i = 0; i < sizeof(buf); i++) {
+ if (buf[i] == PAT2)
+ continue;
+ fprintf(stderr, "Bad pattern %x at %d\n", buf[i], i);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ int ret, files[MAX_FILES];
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ if (!(p.features & IORING_FEAT_CQE_SKIP))
+ return T_EXIT_SKIP;
+
+ memset(files, -1, sizeof(files));
+ ret = io_uring_register_files(&ring, files, ARRAY_SIZE(files));
+ if (ret) {
+ fprintf(stderr, "Failed registering files\n");
+ return T_EXIT_FAIL;
+ }
+
+ t_create_file_pattern(FNAME1, 4096, PAT1);
+ t_create_file_pattern(FNAME2, 4096, PAT2);
+
+ ret = test(&ring);
+ if (ret) {
+ fprintf(stderr, "test failed\n");
+ goto err;
+ }
+
+ unlink(FNAME1);
+ unlink(FNAME2);
+ return T_EXIT_PASS;
+err:
+ unlink(FNAME1);
+ unlink(FNAME2);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/fpos.c b/contrib/libs/liburing/test/fpos.c
new file mode 100644
index 00000000000..239a5b29593
--- /dev/null
+++ b/contrib/libs/liburing/test/fpos.c
@@ -0,0 +1,256 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring fpos handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE 5000
+#define QUEUE_SIZE 2048
+
+static void create_file(const char *file, size_t size)
+{
+ ssize_t ret;
+ char *buf;
+ size_t idx;
+ int fd;
+
+ buf = t_malloc(size);
+ for (idx = 0; idx < size; ++idx) {
+ /* write 0 or 1 */
+ buf[idx] = (unsigned char)(idx & 0x01);
+ }
+
+ fd = open(file, O_WRONLY | O_CREAT, 0644);
+ assert(fd >= 0);
+
+ ret = write(fd, buf, size);
+ fsync(fd);
+ close(fd);
+ free(buf);
+ assert(ret == size);
+}
+
+static int test_read(struct io_uring *ring, bool async, int blocksize)
+{
+ int ret, fd, i;
+ bool done = false;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ loff_t current, expected = 0;
+ int count_ok;
+ int count_0 = 0, count_1 = 0;
+ unsigned char buff[QUEUE_SIZE * blocksize];
+ unsigned char reordered[QUEUE_SIZE * blocksize];
+
+ memset(buff, 0, QUEUE_SIZE * blocksize);
+ memset(reordered, 0, QUEUE_SIZE * blocksize);
+
+ create_file(".test_fpos_read", FILE_SIZE);
+ fd = open(".test_fpos_read", O_RDONLY);
+ unlink(".test_fpos_read");
+ assert(fd >= 0);
+
+ while (!done) {
+ for (i = 0; i < QUEUE_SIZE; ++i) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "no sqe\n");
+ return -1;
+ }
+ io_uring_prep_read(sqe, fd,
+ buff + i * blocksize,
+ blocksize, -1);
+ sqe->user_data = i;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ if (i != QUEUE_SIZE - 1)
+ sqe->flags |= IOSQE_IO_LINK;
+ }
+ ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
+ if (ret != QUEUE_SIZE) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return 1;
+ }
+ count_ok = 0;
+ for (i = 0; i < QUEUE_SIZE; ++i) {
+ int res;
+
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "peek failed: %d\n", ret);
+ return ret;
+ }
+ assert(cqe->user_data < QUEUE_SIZE);
+ memcpy(reordered + count_ok,
+ buff + cqe->user_data * blocksize, blocksize);
+ res = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (res == 0) {
+ done = true;
+ } else if (res == -ECANCELED) {
+ /* cancelled, probably ok */
+ } else if (res < 0 || res > blocksize) {
+ fprintf(stderr, "bad read: %d\n", res);
+ return -1;
+ } else {
+ expected += res;
+ count_ok += res;
+ }
+ }
+ ret = 0;
+ for (i = 0; i < count_ok; i++) {
+ if (reordered[i] == 1) {
+ count_1++;
+ } else if (reordered[i] == 0) {
+ count_0++;
+ } else {
+ fprintf(stderr, "odd read %d\n",
+ (int)reordered[i]);
+ ret = -1;
+ break;
+ }
+ }
+ if (labs(count_1 - count_0) > 1) {
+ fprintf(stderr, "inconsistent reads, got 0s:%d 1s:%d\n",
+ count_0, count_1);
+ ret = -1;
+ }
+ current = lseek(fd, 0, SEEK_CUR);
+ if (current != expected) {
+ fprintf(stderr, "f_pos incorrect, expected %ld have %ld\n",
+ (long) expected, (long) current);
+ ret = -1;
+ }
+ if (ret)
+ return ret;
+ }
+ return 0;
+}
+
+
+static int test_write(struct io_uring *ring, bool async, int blocksize)
+{
+ int ret, fd, i;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ bool fail = false;
+ loff_t current;
+ char data[blocksize+1];
+ char readbuff[QUEUE_SIZE*blocksize+1];
+
+ fd = open(".test_fpos_write", O_RDWR | O_CREAT, 0644);
+ unlink(".test_fpos_write");
+ assert(fd >= 0);
+
+ for (i = 0; i < blocksize; i++)
+ data[i] = 'A' + i;
+
+ data[blocksize] = '\0';
+
+ for (i = 0; i < QUEUE_SIZE; ++i) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "no sqe\n");
+ return -1;
+ }
+ io_uring_prep_write(sqe, fd, data + (i % blocksize), 1, -1);
+ sqe->user_data = 1;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ if (i != QUEUE_SIZE - 1)
+ sqe->flags |= IOSQE_IO_LINK;
+ }
+ ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
+ if (ret != QUEUE_SIZE) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return 1;
+ }
+ for (i = 0; i < QUEUE_SIZE; ++i) {
+ int res;
+
+ ret = io_uring_peek_cqe(ring, &cqe);
+ res = cqe->res;
+ if (ret) {
+ fprintf(stderr, "peek failed: %d\n", ret);
+ return ret;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ if (!fail && res != 1) {
+ fprintf(stderr, "bad result %d\n", res);
+ fail = true;
+ }
+ }
+ current = lseek(fd, 0, SEEK_CUR);
+ if (current != QUEUE_SIZE) {
+ fprintf(stderr, "f_pos incorrect, expected %ld have %d\n",
+ (long) current, QUEUE_SIZE);
+ fail = true;
+ }
+ current = lseek(fd, 0, SEEK_SET);
+ if (current != 0) {
+ perror("seek to start");
+ return -1;
+ }
+ ret = read(fd, readbuff, QUEUE_SIZE);
+ if (ret != QUEUE_SIZE) {
+ fprintf(stderr, "did not write enough: %d\n", ret);
+ return -1;
+ }
+ i = 0;
+ while (i < QUEUE_SIZE - blocksize) {
+ if (strncmp(readbuff + i, data, blocksize)) {
+ char bad[QUEUE_SIZE+1];
+
+ memcpy(bad, readbuff + i, blocksize);
+ bad[blocksize] = '\0';
+ fprintf(stderr, "unexpected data %s\n", bad);
+ fail = true;
+ }
+ i += blocksize;
+ }
+
+ return fail ? -1 : 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(QUEUE_SIZE, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ for (int test = 0; test < 8; test++) {
+ int async = test & 0x01;
+ int write = test & 0x02;
+ int blocksize = test & 0x04 ? 1 : 7;
+
+ ret = write
+ ? test_write(&ring, !!async, blocksize)
+ : test_read(&ring, !!async, blocksize);
+ if (ret) {
+ fprintf(stderr, "failed %s async=%d blocksize=%d\n",
+ write ? "write" : "read",
+ async, blocksize);
+ return -1;
+ }
+ }
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/fsync.c b/contrib/libs/liburing/test/fsync.c
new file mode 100644
index 00000000000..22cb705e48b
--- /dev/null
+++ b/contrib/libs/liburing/test/fsync.c
@@ -0,0 +1,225 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring fsync handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int test_single_fsync(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buf[32];
+ int fd, ret;
+
+ sprintf(buf, "./XXXXXX");
+ fd = mkstemp(buf);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_fsync(sqe, fd, 0);
+
+ 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;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ unlink(buf);
+ return 0;
+err:
+ unlink(buf);
+ return 1;
+}
+
+static int test_barrier_fsync(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec iovecs[4];
+ int i, fd, ret;
+ off_t off;
+
+ fd = open("fsync-testfile", O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ unlink("fsync-testfile");
+
+ for (i = 0; i < ARRAY_SIZE(iovecs); i++) {
+ iovecs[i].iov_base = t_malloc(4096);
+ iovecs[i].iov_len = 4096;
+ }
+
+ off = 0;
+ for (i = 0; i < 4; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_writev(sqe, fd, &iovecs[i], 1, off);
+ sqe->user_data = 0;
+ off += 4096;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_fsync(sqe, fd, IORING_FSYNC_DATASYNC);
+ sqe->user_data = 1;
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ } else if (ret < 5) {
+ fprintf(stderr, "Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ /* kernel doesn't support IOSQE_IO_DRAIN */
+ if (cqe->res == -EINVAL)
+ break;
+ if (i <= 3) {
+ if (cqe->user_data) {
+ fprintf(stderr, "Got fsync early?\n");
+ goto err;
+ }
+ } else {
+ if (!cqe->user_data) {
+ fprintf(stderr, "Got write late?\n");
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+
+ ret = 0;
+ goto out;
+err:
+ ret = 1;
+out:
+ for (i = 0; i < ARRAY_SIZE(iovecs); i++)
+ free(iovecs[i].iov_base);
+ return ret;
+}
+
+#define FILE_SIZE 1024
+
+static int test_sync_file_range(struct io_uring *ring)
+{
+ int ret, fd, save_errno;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ t_create_file(".sync_file_range", FILE_SIZE);
+
+ fd = open(".sync_file_range", O_RDWR);
+ save_errno = errno;
+ unlink(".sync_file_range");
+ errno = save_errno;
+ if (fd < 0) {
+ perror("file open");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ return 1;
+ }
+ io_uring_prep_sync_file_range(sqe, fd, 0, 0, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %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->res) {
+ fprintf(stderr, "sfr failed: %d\n", cqe->res);
+ return 1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ ret = test_single_fsync(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_fsync failed\n");
+ return ret;
+ }
+
+ ret = test_barrier_fsync(&ring);
+ if (ret) {
+ fprintf(stderr, "test_barrier_fsync failed\n");
+ return ret;
+ }
+
+ ret = test_sync_file_range(&ring);
+ if (ret) {
+ fprintf(stderr, "test_sync_file_range failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/hardlink.c b/contrib/libs/liburing/test/hardlink.c
new file mode 100644
index 00000000000..1d85fe00469
--- /dev/null
+++ b/contrib/libs/liburing/test/hardlink.c
@@ -0,0 +1,141 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring linkat handling
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+
+static int do_linkat(struct io_uring *ring, const char *oldname, const char *newname)
+{
+ int ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_linkat(sqe, AT_FDCWD, oldname, AT_FDCWD, newname, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqes(ring, &cqe, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "wait_cqe failed: %d\n", ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+err:
+ return 1;
+}
+
+int files_linked_ok(const char* fn1, const char *fn2)
+{
+ struct stat s1, s2;
+
+ if (stat(fn1, &s1)) {
+ fprintf(stderr, "stat(%s): %s\n", fn1, strerror(errno));
+ return 0;
+ }
+ if (stat(fn2, &s2)) {
+ fprintf(stderr, "stat(%s): %s\n", fn2, strerror(errno));
+ return 0;
+ }
+ if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) {
+ fprintf(stderr, "linked files have different device / inode numbers\n");
+ return 0;
+ }
+ if (s1.st_nlink != 2 || s2.st_nlink != 2) {
+ fprintf(stderr, "linked files have unexpected links count\n");
+ return 0;
+ }
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char target[] = "io_uring-linkat-test-target";
+ static const char linkname[] = "io_uring-linkat-test-link";
+ int ret;
+ struct io_uring ring;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = open(target, O_CREAT | O_RDWR | O_EXCL, 0600);
+ if (ret < 0) {
+ perror("open");
+ goto err;
+ }
+ if (write(ret, "linktest", 8) != 8) {
+ close(ret);
+ goto err1;
+ }
+ close(ret);
+
+ ret = do_linkat(&ring, target, linkname);
+ if (ret < 0) {
+ if (ret == -EBADF || ret == -EINVAL) {
+ fprintf(stdout, "linkat not supported, skipping\n");
+ goto skip;
+ }
+ fprintf(stderr, "linkat: %s\n", strerror(-ret));
+ goto err1;
+ } else if (ret) {
+ goto err1;
+ }
+
+ if (!files_linked_ok(linkname, target))
+ goto err2;
+
+ ret = do_linkat(&ring, target, linkname);
+ if (ret != -EEXIST) {
+ fprintf(stderr, "test_linkat linkname already exists failed: %d\n", ret);
+ goto err2;
+ }
+
+ ret = do_linkat(&ring, target, "surely/this/does/not/exist");
+ if (ret != -ENOENT) {
+ fprintf(stderr, "test_linkat no parent failed: %d\n", ret);
+ goto err2;
+ }
+
+ 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:
+ unlinkat(AT_FDCWD, linkname, 0);
+err1:
+ unlinkat(AT_FDCWD, target, 0);
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/helpers.c b/contrib/libs/liburing/test/helpers.c
new file mode 100644
index 00000000000..a29c7b59667
--- /dev/null
+++ b/contrib/libs/liburing/test/helpers.c
@@ -0,0 +1,269 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Helpers for tests.
+ */
+#include <stdlib.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/types.h>
+
+#include <arpa/inet.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+/*
+ * Helper for allocating memory in tests.
+ */
+void *t_malloc(size_t size)
+{
+ void *ret;
+ ret = malloc(size);
+ assert(ret);
+ return ret;
+}
+
+/*
+ * Helper for binding socket to an ephemeral port.
+ * The port number to be bound is returned in @addr->sin_port.
+ */
+int t_bind_ephemeral_port(int fd, struct sockaddr_in *addr)
+{
+ socklen_t addrlen;
+
+ addr->sin_port = 0;
+ if (bind(fd, (struct sockaddr *)addr, sizeof(*addr)))
+ return -errno;
+
+ addrlen = sizeof(*addr);
+ assert(!getsockname(fd, (struct sockaddr *)addr, &addrlen));
+ assert(addr->sin_port != 0);
+ return 0;
+}
+
+/*
+ * Helper for allocating size bytes aligned on a boundary.
+ */
+void t_posix_memalign(void **memptr, size_t alignment, size_t size)
+{
+ int ret;
+ ret = posix_memalign(memptr, alignment, size);
+ assert(!ret);
+}
+
+/*
+ * Helper for allocating space for an array of nmemb elements
+ * with size bytes for each element.
+ */
+void *t_calloc(size_t nmemb, size_t size)
+{
+ void *ret;
+ ret = calloc(nmemb, size);
+ assert(ret);
+ return ret;
+}
+
+/*
+ * Helper for creating file and write @size byte buf with 0xaa value in the file.
+ */
+static void __t_create_file(const char *file, size_t size, char pattern)
+{
+ ssize_t ret;
+ char *buf;
+ int fd;
+
+ buf = t_malloc(size);
+ memset(buf, pattern, size);
+
+ fd = open(file, O_WRONLY | O_CREAT, 0644);
+ assert(fd >= 0);
+
+ ret = write(fd, buf, size);
+ fsync(fd);
+ close(fd);
+ free(buf);
+ assert(ret == size);
+}
+
+void t_create_file(const char *file, size_t size)
+{
+ __t_create_file(file, size, 0xaa);
+}
+
+void t_create_file_pattern(const char *file, size_t size, char pattern)
+{
+ __t_create_file(file, size, pattern);
+}
+
+/*
+ * Helper for creating @buf_num number of iovec
+ * with @buf_size bytes buffer of each iovec.
+ */
+struct iovec *t_create_buffers(size_t buf_num, size_t buf_size)
+{
+ struct iovec *vecs;
+ int i;
+
+ vecs = t_malloc(buf_num * sizeof(struct iovec));
+ for (i = 0; i < buf_num; i++) {
+ t_posix_memalign(&vecs[i].iov_base, buf_size, buf_size);
+ vecs[i].iov_len = buf_size;
+ }
+ return vecs;
+}
+
+/*
+ * Helper for setting up an io_uring instance, skipping if the given user isn't
+ * allowed to.
+ */
+enum t_setup_ret t_create_ring_params(int depth, struct io_uring *ring,
+ struct io_uring_params *p)
+{
+ int ret;
+
+ ret = io_uring_queue_init_params(depth, ring, p);
+ if (!ret)
+ return T_SETUP_OK;
+ if ((p->flags & IORING_SETUP_SQPOLL) && ret == -EPERM && geteuid()) {
+ fprintf(stdout, "SQPOLL skipped for regular user\n");
+ return T_SETUP_SKIP;
+ }
+
+ fprintf(stderr, "queue_init: %s\n", strerror(-ret));
+ return ret;
+}
+
+enum t_setup_ret t_create_ring(int depth, struct io_uring *ring,
+ unsigned int flags)
+{
+ struct io_uring_params p = { };
+
+ p.flags = flags;
+ return t_create_ring_params(depth, ring, &p);
+}
+
+enum t_setup_ret t_register_buffers(struct io_uring *ring,
+ const struct iovec *iovecs,
+ unsigned nr_iovecs)
+{
+ int ret;
+
+ ret = io_uring_register_buffers(ring, iovecs, nr_iovecs);
+ if (!ret)
+ return T_SETUP_OK;
+
+ if ((ret == -EPERM || ret == -ENOMEM) && geteuid()) {
+ fprintf(stdout, "too large non-root buffer registration, skip\n");
+ return T_SETUP_SKIP;
+ }
+
+ fprintf(stderr, "buffer register failed: %s\n", strerror(-ret));
+ return ret;
+}
+
+int t_create_socket_pair(int fd[2], bool stream)
+{
+ int ret;
+ int type = stream ? SOCK_STREAM : SOCK_DGRAM;
+ int val;
+ struct sockaddr_in serv_addr;
+ struct sockaddr *paddr;
+ size_t paddrlen;
+
+ type |= SOCK_CLOEXEC;
+ fd[0] = socket(AF_INET, type, 0);
+ if (fd[0] < 0)
+ return errno;
+ fd[1] = socket(AF_INET, type, 0);
+ if (fd[1] < 0) {
+ ret = errno;
+ close(fd[0]);
+ return ret;
+ }
+
+ val = 1;
+ if (setsockopt(fd[0], SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)))
+ goto errno_cleanup;
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = 0;
+ inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
+
+ paddr = (struct sockaddr *)&serv_addr;
+ paddrlen = sizeof(serv_addr);
+
+ if (bind(fd[0], paddr, paddrlen)) {
+ fprintf(stderr, "bind failed\n");
+ goto errno_cleanup;
+ }
+
+ if (stream && listen(fd[0], 16)) {
+ fprintf(stderr, "listen failed\n");
+ goto errno_cleanup;
+ }
+
+ if (getsockname(fd[0], (struct sockaddr *)&serv_addr,
+ (socklen_t *)&paddrlen)) {
+ fprintf(stderr, "getsockname failed\n");
+ goto errno_cleanup;
+ }
+ inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
+
+ if (connect(fd[1], (struct sockaddr *)&serv_addr, paddrlen)) {
+ fprintf(stderr, "connect failed\n");
+ goto errno_cleanup;
+ }
+
+ if (!stream) {
+ /* connect the other udp side */
+ if (getsockname(fd[1], (struct sockaddr *)&serv_addr,
+ (socklen_t *)&paddrlen)) {
+ fprintf(stderr, "getsockname failed\n");
+ goto errno_cleanup;
+ }
+ inet_pton(AF_INET, "127.0.0.1", &serv_addr.sin_addr);
+
+ if (connect(fd[0], (struct sockaddr *)&serv_addr, paddrlen)) {
+ fprintf(stderr, "connect failed\n");
+ goto errno_cleanup;
+ }
+ return 0;
+ }
+
+ /* for stream case we must accept and cleanup the listen socket */
+
+ ret = accept(fd[0], NULL, NULL);
+ if (ret < 0)
+ goto errno_cleanup;
+
+ close(fd[0]);
+ fd[0] = ret;
+
+ return 0;
+
+errno_cleanup:
+ ret = errno;
+ close(fd[0]);
+ close(fd[1]);
+ return ret;
+}
+
+bool t_probe_defer_taskrun(void)
+{
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN);
+ if (ret < 0)
+ return false;
+ io_uring_queue_exit(&ring);
+ return true;
+}
diff --git a/contrib/libs/liburing/test/helpers.h b/contrib/libs/liburing/test/helpers.h
new file mode 100644
index 00000000000..4375a9e4658
--- /dev/null
+++ b/contrib/libs/liburing/test/helpers.h
@@ -0,0 +1,94 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Helpers for tests.
+ */
+#ifndef LIBURING_HELPERS_H
+#define LIBURING_HELPERS_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "liburing.h"
+#include <arpa/inet.h>
+
+enum t_setup_ret {
+ T_SETUP_OK = 0,
+ T_SETUP_SKIP,
+};
+
+enum t_test_result {
+ T_EXIT_PASS = 0,
+ T_EXIT_FAIL = 1,
+ T_EXIT_SKIP = 77,
+};
+
+/*
+ * Helper for binding socket to an ephemeral port.
+ * The port number to be bound is returned in @addr->sin_port.
+ */
+int t_bind_ephemeral_port(int fd, struct sockaddr_in *addr);
+
+
+/*
+ * Helper for allocating memory in tests.
+ */
+void *t_malloc(size_t size);
+
+
+/*
+ * Helper for allocating size bytes aligned on a boundary.
+ */
+void t_posix_memalign(void **memptr, size_t alignment, size_t size);
+
+
+/*
+ * Helper for allocating space for an array of nmemb elements
+ * with size bytes for each element.
+ */
+void *t_calloc(size_t nmemb, size_t size);
+
+
+/*
+ * Helper for creating file and write @size byte buf with 0xaa value in the file.
+ */
+void t_create_file(const char *file, size_t size);
+
+/*
+ * Helper for creating file and write @size byte buf with @pattern value in
+ * the file.
+ */
+void t_create_file_pattern(const char *file, size_t size, char pattern);
+
+/*
+ * Helper for creating @buf_num number of iovec
+ * with @buf_size bytes buffer of each iovec.
+ */
+struct iovec *t_create_buffers(size_t buf_num, size_t buf_size);
+
+/*
+ * Helper for creating connected socket pairs
+ */
+int t_create_socket_pair(int fd[2], bool stream);
+
+/*
+ * Helper for setting up a ring and checking for user privs
+ */
+enum t_setup_ret t_create_ring_params(int depth, struct io_uring *ring,
+ struct io_uring_params *p);
+enum t_setup_ret t_create_ring(int depth, struct io_uring *ring,
+ unsigned int flags);
+
+enum t_setup_ret t_register_buffers(struct io_uring *ring,
+ const struct iovec *iovecs,
+ unsigned nr_iovecs);
+
+bool t_probe_defer_taskrun(void);
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/libs/liburing/test/io-cancel.c b/contrib/libs/liburing/test/io-cancel.c
new file mode 100644
index 00000000000..59932ff9a44
--- /dev/null
+++ b/contrib/libs/liburing/test/io-cancel.c
@@ -0,0 +1,556 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Basic IO cancel test
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <poll.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024)
+#define BS 4096
+#define BUFFERS (FILE_SIZE / BS)
+
+static struct iovec *vecs;
+
+static unsigned long long utime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000000;
+ return sec + usec;
+}
+
+static unsigned long long utime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return utime_since(tv, &end);
+}
+
+static int start_io(struct io_uring *ring, int fd, int do_write)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < BUFFERS; i++) {
+ off_t offset;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ offset = BS * (rand() % BUFFERS);
+ if (do_write) {
+ io_uring_prep_writev(sqe, fd, &vecs[i], 1, offset);
+ } else {
+ io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
+ }
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int wait_io(struct io_uring *ring, unsigned nr_io, int do_partial)
+{
+ struct io_uring_cqe *cqe;
+ int i, ret;
+
+ for (i = 0; i < nr_io; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (do_partial && cqe->user_data) {
+ if (!(cqe->user_data & 1)) {
+ if (cqe->res != BS) {
+ fprintf(stderr, "IO %d wasn't cancelled but got error %d\n", (unsigned) cqe->user_data, cqe->res);
+ goto err;
+ }
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+err:
+ return 1;
+
+}
+
+static int do_io(struct io_uring *ring, int fd, int do_write)
+{
+ if (start_io(ring, fd, do_write))
+ return 1;
+ if (wait_io(ring, BUFFERS, 0))
+ return 1;
+ return 0;
+}
+
+static int start_cancel(struct io_uring *ring, int do_partial, int async_cancel)
+{
+ struct io_uring_sqe *sqe;
+ int i, ret, submitted = 0;
+
+ for (i = 0; i < BUFFERS; i++) {
+ if (do_partial && (i & 1))
+ continue;
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_cancel64(sqe, i + 1, 0);
+ if (async_cancel)
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 0;
+ submitted++;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != submitted) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, submitted);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test cancels. If 'do_partial' is set, then we only attempt to cancel half of
+ * the submitted IO. This is done to verify that cancelling one piece of IO doesn't
+ * impact others.
+ */
+static int test_io_cancel(const char *file, int do_write, int do_partial,
+ int async_cancel)
+{
+ struct io_uring ring;
+ struct timeval start_tv;
+ unsigned long usecs;
+ unsigned to_wait;
+ int fd, ret;
+
+ fd = open(file, O_RDWR | O_DIRECT);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ ret = io_uring_queue_init(4 * BUFFERS, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ goto err;
+ }
+
+ if (do_io(&ring, fd, do_write))
+ goto err;
+ gettimeofday(&start_tv, NULL);
+ if (do_io(&ring, fd, do_write))
+ goto err;
+ usecs = utime_since_now(&start_tv);
+
+ if (start_io(&ring, fd, do_write))
+ goto err;
+ /* sleep for 1/3 of the total time, to allow some to start/complete */
+ usleep(usecs / 3);
+ if (start_cancel(&ring, do_partial, async_cancel))
+ goto err;
+ to_wait = BUFFERS;
+ if (do_partial)
+ to_wait += BUFFERS / 2;
+ else
+ to_wait += BUFFERS;
+ if (wait_io(&ring, to_wait, do_partial))
+ goto err;
+
+ io_uring_queue_exit(&ring);
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+
+static int test_dont_cancel_another_ring(void)
+{
+ struct io_uring ring1, ring2;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buffer[128];
+ int ret, fds[2];
+ struct __kernel_timespec ts = { .tv_sec = 0, .tv_nsec = 100000000, };
+
+ ret = io_uring_queue_init(8, &ring1, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ ret = io_uring_queue_init(8, &ring2, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring1);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_read(sqe, fds[0], buffer, 10, 0);
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring1);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ /* make sure it doesn't cancel requests of the other ctx */
+ sqe = io_uring_get_sqe(&ring2);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_cancel64(sqe, 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring2);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(&ring2, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ if (cqe->user_data != 2 || cqe->res != -ENOENT) {
+ fprintf(stderr, "error: cqe %i: res=%i, but expected -ENOENT\n",
+ (int)cqe->user_data, (int)cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring2, cqe);
+
+ ret = io_uring_wait_cqe_timeout(&ring1, &cqe, &ts);
+ if (ret != -ETIME) {
+ fprintf(stderr, "read got cancelled or wait failed\n");
+ return 1;
+ }
+ io_uring_cqe_seen(&ring1, cqe);
+
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring1);
+ io_uring_queue_exit(&ring2);
+ return 0;
+}
+
+static int test_cancel_req_across_fork(void)
+{
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buffer[128];
+ int ret, i, fds[2];
+ pid_t p;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_read(sqe, fds[0], buffer, 10, 0);
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork() failed\n");
+ return 1;
+ }
+
+ if (p == 0) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ io_uring_prep_cancel64(sqe, 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -EINTR &&
+ cqe->res != -ECANCELED) {
+ fprintf(stderr, "%i %i\n", (int)cqe->user_data, cqe->res);
+ exit(1);
+ }
+ break;
+ case 2:
+ if (cqe->res != -EALREADY && cqe->res) {
+ fprintf(stderr, "%i %i\n", (int)cqe->user_data, cqe->res);
+ exit(1);
+ }
+ break;
+ default:
+ fprintf(stderr, "%i %i\n", (int)cqe->user_data, cqe->res);
+ exit(1);
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ exit(0);
+ } else {
+ int wstatus;
+ pid_t childpid;
+
+ do {
+ childpid = waitpid(p, &wstatus, 0);
+ } while (childpid == (pid_t)-1 && errno == EINTR);
+
+ if (childpid == (pid_t)-1) {
+ perror("waitpid()");
+ return 1;
+ }
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ return 1;
+ }
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_cancel_inflight_exit(void)
+{
+ struct __kernel_timespec ts = { .tv_sec = 1, .tv_nsec = 0, };
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+ pid_t p;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork() failed\n");
+ return 1;
+ }
+
+ if (p == 0) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "io_uring_submit() failed %s, ret %i\n", __FUNCTION__, ret);
+ exit(1);
+ }
+ exit(0);
+ } else {
+ int wstatus;
+
+ if (waitpid(p, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ return 1;
+ }
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ return 1;
+ }
+ }
+
+ for (i = 0; i < 3; ++i) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ if ((cqe->user_data == 1 && cqe->res != -ECANCELED) ||
+ (cqe->user_data == 2 && cqe->res != -ECANCELED) ||
+ (cqe->user_data == 3 && cqe->res != -ETIME)) {
+ fprintf(stderr, "%i %i\n", (int)cqe->user_data, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_sqpoll_cancel_iowq_requests(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ int ret, fds[2];
+ char buffer[16];
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SQPOLL);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+ /* pin both pipe ends via io-wq */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fds[0], buffer, 10, 0);
+ sqe->flags |= IOSQE_ASYNC | IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_write(sqe, fds[1], buffer, 10, 0);
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 2;
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ /* wait for sqpoll to kick in and submit before exit */
+ sleep(1);
+ io_uring_queue_exit(&ring);
+
+ /* close the write end, so if ring is cancelled properly read() fails*/
+ close(fds[1]);
+ ret = read(fds[0], buffer, 10);
+ close(fds[0]);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *fname = ".io-cancel-test";
+ int i, ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ if (test_dont_cancel_another_ring()) {
+ fprintf(stderr, "test_dont_cancel_another_ring() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (test_cancel_req_across_fork()) {
+ fprintf(stderr, "test_cancel_req_across_fork() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (test_cancel_inflight_exit()) {
+ fprintf(stderr, "test_cancel_inflight_exit() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ if (test_sqpoll_cancel_iowq_requests()) {
+ fprintf(stderr, "test_sqpoll_cancel_iowq_requests() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ t_create_file(fname, FILE_SIZE);
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ for (i = 0; i < 8; i++) {
+ int write = (i & 1) != 0;
+ int partial = (i & 2) != 0;
+ int async = (i & 4) != 0;
+
+ ret = test_io_cancel(fname, write, partial, async);
+ if (ret) {
+ fprintf(stderr, "test_io_cancel %d %d %d failed\n",
+ write, partial, async);
+ goto err;
+ }
+ }
+
+ unlink(fname);
+ return T_EXIT_PASS;
+err:
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/io_uring_enter.c b/contrib/libs/liburing/test/io_uring_enter.c
new file mode 100644
index 00000000000..7f87f0cf553
--- /dev/null
+++ b/contrib/libs/liburing/test/io_uring_enter.c
@@ -0,0 +1,262 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * io_uring_enter.c
+ *
+ * Description: Unit tests for the io_uring_enter system call.
+ *
+ * Copyright 2019, Red Hat, Inc.
+ * Author: Jeff Moyer <jmoyer@redhat.com>
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/sysinfo.h>
+#include <poll.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <linux/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <sys/time.h>
+
+#include "helpers.h"
+#include "liburing.h"
+#include "liburing/barrier.h"
+#include "../src/syscall.h"
+
+#define IORING_MAX_ENTRIES 4096
+#define IORING_MAX_ENTRIES_FALLBACK 128
+
+static int expect_fail(int fd, unsigned int to_submit,
+ unsigned int min_complete, unsigned int flags,
+ sigset_t *sig, int error)
+{
+ int ret;
+
+ ret = io_uring_enter(fd, to_submit, min_complete, flags, sig);
+ if (ret >= 0) {
+ fprintf(stderr, "expected %s, but call succeeded\n", strerror(-error));
+ return 1;
+ }
+
+ if (ret != error) {
+ fprintf(stderr, "expected %d, got %d\n", error, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int try_io_uring_enter(int fd, unsigned int to_submit,
+ unsigned int min_complete, unsigned int flags,
+ sigset_t *sig, int expect)
+{
+ int ret;
+
+ if (expect < 0)
+ return expect_fail(fd, to_submit, min_complete, flags, sig,
+ expect);
+
+ ret = io_uring_enter(fd, to_submit, min_complete, flags, sig);
+ if (ret != expect) {
+ fprintf(stderr, "Expected %d, got %d\n", expect, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * prep a read I/O. index is treated like a block number.
+ */
+static int setup_file(char *template, off_t len)
+{
+ int fd, ret;
+ char buf[4096];
+
+ fd = mkstemp(template);
+ if (fd < 0) {
+ perror("mkstemp");
+ exit(1);
+ }
+ ret = ftruncate(fd, len);
+ if (ret < 0) {
+ perror("ftruncate");
+ exit(1);
+ }
+
+ ret = read(fd, buf, 4096);
+ if (ret != 4096) {
+ fprintf(stderr, "read returned %d, expected 4096\n", ret);
+ exit(1);
+ }
+
+ return fd;
+}
+
+static void io_prep_read(struct io_uring_sqe *sqe, int fd, off_t offset,
+ size_t len)
+{
+ struct iovec *iov;
+
+ iov = t_malloc(sizeof(*iov));
+ assert(iov);
+
+ iov->iov_base = t_malloc(len);
+ assert(iov->iov_base);
+ iov->iov_len = len;
+
+ io_uring_prep_readv(sqe, fd, iov, 1, offset);
+ io_uring_sqe_set_data(sqe, iov); // free on completion
+}
+
+static void reap_events(struct io_uring *ring, unsigned nr)
+{
+ int ret;
+ unsigned left = nr;
+ struct io_uring_cqe *cqe;
+ struct iovec *iov;
+ struct timeval start, now, elapsed;
+
+ gettimeofday(&start, NULL);
+ while (left) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_wait_cqe returned %d\n", ret);
+ exit(1);
+ }
+ if (cqe->res != 4096)
+ fprintf(stderr, "cqe->res: %d, expected 4096\n", cqe->res);
+ iov = io_uring_cqe_get_data(cqe);
+ free(iov->iov_base);
+ free(iov);
+ left--;
+ io_uring_cqe_seen(ring, cqe);
+
+ gettimeofday(&now, NULL);
+ timersub(&now, &start, &elapsed);
+ if (elapsed.tv_sec > 10) {
+ fprintf(stderr, "Timed out waiting for I/Os to complete.\n");
+ fprintf(stderr, "%u expected, %u completed\n", nr, left);
+ break;
+ }
+ }
+}
+
+static void submit_io(struct io_uring *ring, unsigned nr)
+{
+ int fd, ret;
+ off_t file_len;
+ unsigned i;
+ static char template[32] = "/tmp/io_uring_enter-test.XXXXXX";
+ struct io_uring_sqe *sqe;
+
+ file_len = nr * 4096;
+ fd = setup_file(template, file_len);
+ for (i = 0; i < nr; i++) {
+ /* allocate an sqe */
+ sqe = io_uring_get_sqe(ring);
+ /* fill it in */
+ io_prep_read(sqe, fd, i * 4096, 4096);
+ }
+
+ /* submit the I/Os */
+ ret = io_uring_submit(ring);
+ unlink(template);
+ if (ret < 0) {
+ perror("io_uring_enter");
+ exit(1);
+ }
+}
+
+int main(int argc, char **argv)
+{
+ int ret;
+ unsigned int status = 0;
+ struct io_uring ring;
+ struct io_uring_sq *sq = &ring.sq;
+ unsigned ktail, mask, index;
+ unsigned sq_entries;
+ unsigned completed, dropped;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(IORING_MAX_ENTRIES, &ring, 0);
+ if (ret == -ENOMEM)
+ ret = io_uring_queue_init(IORING_MAX_ENTRIES_FALLBACK, &ring, 0);
+ if (ret < 0) {
+ perror("io_uring_queue_init");
+ exit(T_EXIT_FAIL);
+ }
+ mask = sq->ring_mask;
+
+ /* invalid flags */
+ status |= try_io_uring_enter(ring.ring_fd, 1, 0, ~0U, NULL, -EINVAL);
+
+ /* invalid fd, EBADF */
+ status |= try_io_uring_enter(-1, 0, 0, 0, NULL, -EBADF);
+
+ /* valid, non-ring fd, EOPNOTSUPP */
+ status |= try_io_uring_enter(0, 0, 0, 0, NULL, -EOPNOTSUPP);
+
+ /* to_submit: 0, flags: 0; should get back 0. */
+ status |= try_io_uring_enter(ring.ring_fd, 0, 0, 0, NULL, 0);
+
+ /* fill the sq ring */
+ sq_entries = ring.sq.ring_entries;
+ submit_io(&ring, sq_entries);
+ ret = io_uring_enter(ring.ring_fd, 0, sq_entries,
+ IORING_ENTER_GETEVENTS, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_enter: %s\n", strerror(-ret));
+ status = 1;
+ } else {
+ /*
+ * This is a non-IOPOLL ring, which means that io_uring_enter
+ * should not return until min_complete events are available
+ * in the completion queue.
+ */
+ completed = *ring.cq.ktail - *ring.cq.khead;
+ if (completed != sq_entries) {
+ fprintf(stderr, "Submitted %u I/Os, but only got %u completions\n",
+ sq_entries, completed);
+ status = 1;
+ }
+ reap_events(&ring, sq_entries);
+ }
+
+ /*
+ * Add an invalid index to the submission queue. This should
+ * result in the dropped counter increasing.
+ */
+ index = sq->ring_entries + 1; // invalid index
+ dropped = *sq->kdropped;
+ ktail = *sq->ktail;
+ sq->array[ktail & mask] = index;
+ ++ktail;
+ /*
+ * Ensure that the kernel sees the SQE update before it sees the tail
+ * update.
+ */
+ io_uring_smp_store_release(sq->ktail, ktail);
+
+ ret = io_uring_enter(ring.ring_fd, 1, 0, 0, NULL);
+ /* now check to see if our sqe was dropped */
+ if (*sq->kdropped == dropped) {
+ fprintf(stderr, "dropped counter did not increase\n");
+ status = 1;
+ }
+
+ if (!status)
+ return T_EXIT_PASS;
+
+ fprintf(stderr, "FAIL\n");
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/io_uring_passthrough.c b/contrib/libs/liburing/test/io_uring_passthrough.c
new file mode 100644
index 00000000000..156347456e0
--- /dev/null
+++ b/contrib/libs/liburing/test/io_uring_passthrough.c
@@ -0,0 +1,452 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: basic read/write tests for io_uring passthrough commands
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "helpers.h"
+#include "liburing.h"
+#include "../src/syscall.h"
+#include "nvme.h"
+
+#define FILE_SIZE (256 * 1024)
+#define BS 8192
+#define BUFFERS (FILE_SIZE / BS)
+
+static struct iovec *vecs;
+
+/*
+ * Each offset in the file has the ((test_case / 2) * FILE_SIZE)
+ * + (offset / sizeof(int)) stored for every
+ * sizeof(int) address.
+ */
+static int verify_buf(int tc, void *buf, off_t off)
+{
+ int i, u_in_buf = BS / sizeof(unsigned int);
+ unsigned int *ptr;
+
+ off /= sizeof(unsigned int);
+ off += (tc / 2) * FILE_SIZE;
+ ptr = buf;
+ for (i = 0; i < u_in_buf; i++) {
+ if (off != *ptr) {
+ fprintf(stderr, "Found %u, wanted %lu\n", *ptr, off);
+ return 1;
+ }
+ ptr++;
+ off++;
+ }
+
+ return 0;
+}
+
+static int fill_pattern(int tc)
+{
+ unsigned int val, *ptr;
+ int i, j;
+ int u_in_buf = BS / sizeof(val);
+
+ val = (tc / 2) * FILE_SIZE;
+ for (i = 0; i < BUFFERS; i++) {
+ ptr = vecs[i].iov_base;
+ for (j = 0; j < u_in_buf; j++) {
+ *ptr = val;
+ val++;
+ ptr++;
+ }
+ }
+
+ return 0;
+}
+
+static int __test_io(const char *file, struct io_uring *ring, int tc, int read,
+ int sqthread, int fixed, int nonvec)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct nvme_uring_cmd *cmd;
+ int open_flags;
+ int do_fixed;
+ int i, ret, fd = -1;
+ off_t offset;
+ __u64 slba;
+ __u32 nlb;
+
+ if (read)
+ open_flags = O_RDONLY;
+ else
+ open_flags = O_WRONLY;
+
+ if (fixed) {
+ ret = t_register_buffers(ring, vecs, BUFFERS);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "buffer reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ if (sqthread) {
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ if (!read)
+ fill_pattern(tc);
+
+ offset = 0;
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ if (read) {
+ int use_fd = fd;
+
+ do_fixed = fixed;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ sqe->cmd_op = NVME_URING_CMD_IO;
+ } else if (nonvec) {
+ io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ sqe->cmd_op = NVME_URING_CMD_IO;
+ } else {
+ io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
+ offset);
+ sqe->cmd_op = NVME_URING_CMD_IO_VEC;
+ }
+ } else {
+ int use_fd = fd;
+
+ do_fixed = fixed;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ sqe->cmd_op = NVME_URING_CMD_IO;
+ } else if (nonvec) {
+ io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ sqe->cmd_op = NVME_URING_CMD_IO;
+ } else {
+ io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
+ offset);
+ sqe->cmd_op = NVME_URING_CMD_IO_VEC;
+ }
+ }
+ sqe->opcode = IORING_OP_URING_CMD;
+ sqe->user_data = ((uint64_t)offset << 32) | i;
+ if (sqthread)
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ cmd = (struct nvme_uring_cmd *)sqe->cmd;
+ memset(cmd, 0, sizeof(struct nvme_uring_cmd));
+
+ cmd->opcode = read ? nvme_cmd_read : nvme_cmd_write;
+
+ slba = offset >> lba_shift;
+ nlb = (BS >> lba_shift) - 1;
+
+ /* cdw10 and cdw11 represent starting lba */
+ cmd->cdw10 = slba & 0xffffffff;
+ cmd->cdw11 = slba >> 32;
+ /* cdw12 represent number of lba's for read/write */
+ cmd->cdw12 = nlb;
+ if (do_fixed || nonvec) {
+ cmd->addr = (__u64)(uintptr_t)vecs[i].iov_base;
+ cmd->data_len = vecs[i].iov_len;
+ } else {
+ cmd->addr = (__u64)(uintptr_t)&vecs[i];
+ cmd->data_len = 1;
+ }
+ cmd->nsid = nsid;
+
+ offset += BS;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res != 0) {
+ fprintf(stderr, "cqe res %d, wanted 0\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ if (read) {
+ int index = cqe->user_data & 0xffffffff;
+ void *buf = vecs[index].iov_base;
+ off_t voff = cqe->user_data >> 32;
+
+ if (verify_buf(tc, buf, voff))
+ goto err;
+ }
+ }
+
+ if (fixed) {
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "buffer unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ if (sqthread) {
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "file unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+
+static int test_io(const char *file, int tc, int read, int sqthread,
+ int fixed, int nonvec)
+{
+ struct io_uring ring;
+ int ret, ring_flags = 0;
+
+ ring_flags |= IORING_SETUP_SQE128;
+ ring_flags |= IORING_SETUP_CQE32;
+
+ if (sqthread)
+ ring_flags |= IORING_SETUP_SQPOLL;
+
+ ret = t_create_ring(64, &ring, ring_flags);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = __test_io(file, &ring, tc, read, sqthread, fixed, nonvec);
+ io_uring_queue_exit(&ring);
+
+ return ret;
+}
+
+extern unsigned __io_uring_flush_sq(struct io_uring *ring);
+
+/*
+ * Send a passthrough command that nvme will fail during submission.
+ * This comes handy for testing error handling.
+ */
+static int test_invalid_passthru_submit(const char *file)
+{
+ struct io_uring ring;
+ int fd, ret, ring_flags, open_flags;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct nvme_uring_cmd *cmd;
+
+ ring_flags = IORING_SETUP_IOPOLL | IORING_SETUP_SQE128;
+ ring_flags |= IORING_SETUP_CQE32;
+
+ ret = t_create_ring(1, &ring, ring_flags);
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ open_flags = O_RDONLY;
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, vecs[0].iov_base, vecs[0].iov_len, 0);
+ sqe->cmd_op = NVME_URING_CMD_IO;
+ sqe->opcode = IORING_OP_URING_CMD;
+ sqe->user_data = 1;
+ cmd = (struct nvme_uring_cmd *)sqe->cmd;
+ memset(cmd, 0, sizeof(struct nvme_uring_cmd));
+ cmd->opcode = nvme_cmd_read;
+ cmd->addr = (__u64)(uintptr_t)&vecs[0].iov_base;
+ cmd->data_len = vecs[0].iov_len;
+ /* populate wrong nsid to force failure */
+ cmd->nsid = nsid + 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
+ goto err;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res == 0) {
+ fprintf(stderr, "cqe res %d, wanted failure\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ close(fd);
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+/*
+ * if we are polling io_uring_submit needs to always enter the
+ * kernel to fetch events
+ */
+static int test_io_uring_submit_enters(const char *file)
+{
+ struct io_uring ring;
+ int fd, i, ret, ring_flags, open_flags;
+ unsigned head;
+ struct io_uring_cqe *cqe;
+
+ ring_flags = IORING_SETUP_IOPOLL;
+ ring_flags |= IORING_SETUP_SQE128;
+ ring_flags |= IORING_SETUP_CQE32;
+
+ ret = io_uring_queue_init(64, &ring, ring_flags);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ open_flags = O_WRONLY;
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ struct io_uring_sqe *sqe;
+ off_t offset = BS * (rand() % BUFFERS);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, fd, &vecs[i], 1, offset);
+ sqe->user_data = 1;
+ }
+
+ /* submit manually to avoid adding IORING_ENTER_GETEVENTS */
+ ret = __sys_io_uring_enter(ring.ring_fd, __io_uring_flush_sq(&ring), 0,
+ 0, NULL);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 500; i++) {
+ ret = io_uring_submit(&ring);
+ if (ret != 0) {
+ fprintf(stderr, "still had %d sqes to submit\n", ret);
+ goto err;
+ }
+
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ if (cqe->res == -EOPNOTSUPP)
+ fprintf(stdout, "Device doesn't support polled IO\n");
+ goto ok;
+ }
+ usleep(10000);
+ }
+err:
+ ret = 1;
+ if (fd != -1)
+ close(fd);
+
+ok:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret;
+ char *fname;
+
+ if (argc < 2)
+ return T_EXIT_SKIP;
+
+ fname = argv[1];
+ ret = nvme_get_info(fname);
+
+ if (ret)
+ return T_EXIT_SKIP;
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ for (i = 0; i < 16; i++) {
+ int read = (i & 1) != 0;
+ int sqthread = (i & 2) != 0;
+ int fixed = (i & 4) != 0;
+ int nonvec = (i & 8) != 0;
+
+ ret = test_io(fname, i, read, sqthread, fixed, nonvec);
+ if (ret) {
+ fprintf(stderr, "test_io failed %d/%d/%d/%d\n",
+ read, sqthread, fixed, nonvec);
+ goto err;
+ }
+ }
+
+ ret = test_io_uring_submit_enters(fname);
+ if (ret) {
+ fprintf(stderr, "test_io_uring_submit_enters failed\n");
+ goto err;
+ }
+
+ ret = test_invalid_passthru_submit(fname);
+ if (ret) {
+ fprintf(stderr, "test_invalid_passthru_submit failed\n");
+ goto err;
+ }
+
+ return T_EXIT_PASS;
+err:
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/io_uring_register.c b/contrib/libs/liburing/test/io_uring_register.c
new file mode 100644
index 00000000000..b484ef4c4fb
--- /dev/null
+++ b/contrib/libs/liburing/test/io_uring_register.c
@@ -0,0 +1,507 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * io_uring_register.c
+ *
+ * Description: Unit tests for the io_uring_register system call.
+ *
+ * Copyright 2019, Red Hat, Inc.
+ * Author: Jeff Moyer <jmoyer@redhat.com>
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/sysinfo.h>
+#include <poll.h>
+#include <assert.h>
+#include <sys/uio.h>
+#include <sys/mman.h>
+#include <linux/mman.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+
+#include "helpers.h"
+#include "liburing.h"
+#include "../src/syscall.h"
+
+static int pagesize;
+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)
+{
+ int ret;
+
+ ret = io_uring_register(fd, opcode, arg, nr_args);
+ if (ret >= 0) {
+ int ret2 = 0;
+
+ fprintf(stderr, "expected %s, but call succeeded\n", strerror(error));
+ if (opcode == IORING_REGISTER_BUFFERS) {
+ ret2 = io_uring_register(fd, IORING_UNREGISTER_BUFFERS,
+ 0, 0);
+ } else if (opcode == IORING_REGISTER_FILES) {
+ ret2 = io_uring_register(fd, IORING_UNREGISTER_FILES, 0,
+ 0);
+ }
+ if (ret2) {
+ fprintf(stderr, "internal error: failed to unregister\n");
+ exit(1);
+ }
+ return 1;
+ }
+
+ if (ret != error) {
+ fprintf(stderr, "expected %d, got %d\n", error, ret);
+ return 1;
+ }
+ return 0;
+}
+
+static int new_io_uring(int entries, struct io_uring_params *p)
+{
+ int fd;
+
+ fd = io_uring_setup(entries, p);
+ if (fd < 0) {
+ perror("io_uring_setup");
+ exit(1);
+ }
+ return fd;
+}
+
+#define MAXFDS (UINT_MAX * sizeof(int))
+
+static void *map_filebacked(size_t size)
+{
+ int fd, ret;
+ void *addr;
+ char template[32] = "io_uring_register-test-XXXXXXXX";
+
+ fd = mkstemp(template);
+ if (fd < 0) {
+ perror("mkstemp");
+ return NULL;
+ }
+ unlink(template);
+
+ ret = ftruncate(fd, size);
+ if (ret < 0) {
+ perror("ftruncate");
+ close(fd);
+ return NULL;
+ }
+
+ addr = mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
+ if (addr == MAP_FAILED) {
+ perror("mmap");
+ close(fd);
+ return NULL;
+ }
+
+ close(fd);
+ return addr;
+}
+
+/*
+ * NOTE: this is now limited by SCM_MAX_FD (253). Keep the code for now,
+ * but probably should augment it to test 253 and 254, specifically.
+ */
+static int test_max_fds(int uring_fd)
+{
+ int status = 1;
+ int ret;
+ void *fd_as; /* file descriptor address space */
+ int fdtable_fd; /* fd for the file that will be mapped over and over */
+ int io_fd; /* the valid fd for I/O -- /dev/null */
+ int *fds; /* used to map the file into the address space */
+ char template[32] = "io_uring_register-test-XXXXXXXX";
+ unsigned long long i, nr_maps, nr_fds;
+
+ /*
+ * First, mmap anonymous the full size. That will guarantee the
+ * mapping will fit in the memory area selected by mmap. Then,
+ * over-write that mapping using a file-backed mapping, 128MiB at
+ * a time using MAP_FIXED.
+ */
+ fd_as = mmap(NULL, UINT_MAX * sizeof(int), PROT_READ|PROT_WRITE,
+ MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
+ if (fd_as == MAP_FAILED) {
+ if (errno == ENOMEM)
+ return 0;
+ perror("mmap fd_as");
+ exit(1);
+ }
+
+ fdtable_fd = mkstemp(template);
+ if (fdtable_fd < 0) {
+ perror("mkstemp");
+ exit(1);
+ }
+ unlink(template);
+ ret = ftruncate(fdtable_fd, 128*1024*1024);
+ if (ret < 0) {
+ perror("ftruncate");
+ exit(1);
+ }
+
+ io_fd = open("/dev/null", O_RDWR);
+ if (io_fd < 0) {
+ perror("open /dev/null");
+ exit(1);
+ }
+ fds = mmap(fd_as, 128*1024*1024, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_FIXED, fdtable_fd, 0);
+ if (fds == MAP_FAILED) {
+ perror("mmap fdtable");
+ exit(1);
+ }
+
+ /* fill the fd table */
+ nr_fds = 128*1024*1024 / sizeof(int);
+ for (i = 0; i < nr_fds; i++)
+ fds[i] = io_fd;
+
+ /* map the file through the rest of the address space */
+ nr_maps = (UINT_MAX * sizeof(int)) / (128*1024*1024);
+ for (i = 0; i < nr_maps; i++) {
+ fds = &fds[nr_fds]; /* advance fds by 128MiB */
+ fds = mmap(fds, 128*1024*1024, PROT_READ|PROT_WRITE,
+ MAP_SHARED|MAP_FIXED, fdtable_fd, 0);
+ if (fds == MAP_FAILED) {
+ fprintf(stderr, "mmap failed at offset %lu\n",
+ (unsigned long)((char *)fd_as - (char *)fds));
+ exit(1);
+ }
+ }
+
+ /* Now fd_as points to the file descriptor array. */
+ /*
+ * We may not be able to map all of these files. Let's back off
+ * until success.
+ */
+ nr_fds = UINT_MAX;
+ while (nr_fds) {
+ ret = io_uring_register(uring_fd, IORING_REGISTER_FILES, fd_as,
+ nr_fds);
+ if (ret != 0) {
+ nr_fds /= 2;
+ continue;
+ }
+ status = 0;
+ ret = io_uring_register(uring_fd, IORING_UNREGISTER_FILES, 0, 0);
+ if (ret < 0) {
+ ret = errno;
+ errno = ret;
+ perror("io_uring_register UNREGISTER_FILES");
+ exit(1);
+ }
+ break;
+ }
+
+ close(io_fd);
+ close(fdtable_fd);
+ ret = munmap(fd_as, UINT_MAX * sizeof(int));
+ if (ret != 0) {
+ fprintf(stderr, "munmap(%zu) failed\n", UINT_MAX * sizeof(int));
+ exit(1);
+ }
+
+ return status;
+}
+
+static int test_memlock_exceeded(int fd)
+{
+ int ret;
+ void *buf;
+ struct iovec iov;
+
+ /* if limit is larger than 2gb, just skip this test */
+ if (mlock_limit >= 2 * 1024 * 1024 * 1024ULL)
+ return 0;
+
+ iov.iov_len = mlock_limit * 2;
+ buf = t_malloc(iov.iov_len);
+ iov.iov_base = buf;
+
+ 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);
+ 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);
+ free(buf);
+ return 1;
+ }
+ break;
+ }
+ if (!iov.iov_len)
+ printf("Unable to register buffers. Check memlock rlimit.\n");
+
+ free(buf);
+ return 0;
+}
+
+static int test_iovec_nr(int fd)
+{
+ int i, ret, status = 0;
+ unsigned int nr = 1000000;
+ struct iovec *iovs;
+ void *buf;
+
+ iovs = malloc(nr * sizeof(struct iovec));
+ if (!iovs) {
+ fprintf(stdout, "can't allocate iovecs, skip\n");
+ return 0;
+ }
+ buf = t_malloc(pagesize);
+
+ for (i = 0; i < nr; i++) {
+ iovs[i].iov_base = buf;
+ iovs[i].iov_len = pagesize;
+ }
+
+ status |= expect_fail(fd, IORING_REGISTER_BUFFERS, iovs, nr, -EINVAL);
+
+ /* reduce to UIO_MAXIOV */
+ nr = UIO_MAXIOV;
+ ret = io_uring_register(fd, IORING_REGISTER_BUFFERS, iovs, nr);
+ if (ret && (errno == ENOMEM || errno == 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);
+ status = 1;
+ } else {
+ io_uring_register(fd, IORING_UNREGISTER_BUFFERS, 0, 0);
+ }
+ free(buf);
+ free(iovs);
+ return status;
+}
+
+/*
+ * io_uring limit is 1G. iov_len limit is ~OUL, I think
+ */
+static int test_iovec_size(int fd)
+{
+ unsigned int status = 0;
+ int ret;
+ struct iovec iov;
+ void *buf;
+
+ /* NULL pointer for base */
+ iov.iov_base = 0;
+ iov.iov_len = 4096;
+ status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT);
+
+ /* valid base, 0 length */
+ iov.iov_base = &buf;
+ iov.iov_len = 0;
+ status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT);
+
+ /* valid base, length exceeds size */
+ /* this requires an unampped page directly after buf */
+ buf = mmap(NULL, 2 * pagesize, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
+ assert(buf != MAP_FAILED);
+ ret = munmap(buf + pagesize, pagesize);
+ assert(ret == 0);
+ iov.iov_base = buf;
+ iov.iov_len = 2 * pagesize;
+ status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EFAULT);
+ munmap(buf, pagesize);
+
+ /* huge page */
+ buf = mmap(NULL, 2*1024*1024, PROT_READ|PROT_WRITE,
+ MAP_PRIVATE | MAP_HUGETLB | MAP_HUGE_2MB | MAP_ANONYMOUS,
+ -1, 0);
+ if (buf == MAP_FAILED) {
+ printf("Unable to map a huge page. Try increasing "
+ "/proc/sys/vm/nr_hugepages by at least 1.\n");
+ printf("Skipping the hugepage test\n");
+ } else {
+ /*
+ * This should succeed, so long as RLIMIT_MEMLOCK is
+ * not exceeded
+ */
+ iov.iov_base = buf;
+ iov.iov_len = 2*1024*1024;
+ ret = io_uring_register(fd, IORING_REGISTER_BUFFERS, &iov, 1);
+ if (ret < 0) {
+ if (ret == -ENOMEM)
+ printf("Unable to test registering of a huge "
+ "page. Try increasing the "
+ "RLIMIT_MEMLOCK resource limit by at "
+ "least 2MB.");
+ else {
+ fprintf(stderr, "expected success, got %d\n", ret);
+ status = 1;
+ }
+ } else {
+ ret = io_uring_register(fd, IORING_UNREGISTER_BUFFERS,
+ 0, 0);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_unregister: %s\n",
+ strerror(-ret));
+ status = 1;
+ }
+ }
+ }
+ ret = munmap(iov.iov_base, iov.iov_len);
+ assert(ret == 0);
+
+ /* file-backed buffers -- not supported */
+ buf = map_filebacked(2*1024*1024);
+ if (!buf)
+ status = 1;
+ iov.iov_base = buf;
+ iov.iov_len = 2*1024*1024;
+ status |= expect_fail(fd, IORING_REGISTER_BUFFERS, &iov, 1, -EOPNOTSUPP);
+ munmap(buf, 2*1024*1024);
+
+ /* bump up against the soft limit and make sure we get EFAULT
+ * or whatever we're supposed to get. NOTE: this requires
+ * running the test as non-root. */
+ if (getuid() != 0)
+ status |= test_memlock_exceeded(fd);
+
+ return status;
+}
+
+static int ioring_poll(struct io_uring *ring, int fd, int fixed)
+{
+ int ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(ring);
+ memset(sqe, 0, sizeof(*sqe));
+ sqe->opcode = IORING_OP_POLL_ADD;
+ if (fixed)
+ sqe->flags = IOSQE_FIXED_FILE;
+ sqe->fd = fd;
+ sqe->poll_events = POLLIN|POLLOUT;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "failed to submit poll sqe: %d.\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_wait_cqe failed with %d\n", ret);
+ return 1;
+ }
+ ret = 0;
+ if (cqe->res != POLLOUT) {
+ fprintf(stderr, "io_uring_wait_cqe: expected 0x%.8x, got 0x%.8x\n",
+ POLLOUT, cqe->res);
+ ret = 1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static int test_poll_ringfd(void)
+{
+ int status = 0;
+ int ret;
+ int fd;
+ struct io_uring ring;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ perror("io_uring_queue_init");
+ return 1;
+ }
+ fd = ring.ring_fd;
+
+ /* try polling the ring fd */
+ status = ioring_poll(&ring, fd, 0);
+
+ /*
+ * now register the ring fd, and try the poll again. This should
+ * fail, because the kernel does not allow registering of the
+ * ring_fd.
+ */
+ status |= expect_fail(fd, IORING_REGISTER_FILES, &fd, 1, -EBADF);
+
+ /* tear down queue */
+ io_uring_queue_exit(&ring);
+
+ return status;
+}
+
+int main(int argc, char **argv)
+{
+ int fd, ret;
+ unsigned int status = 0;
+ struct io_uring_params p;
+ struct rlimit rlim;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ /* setup globals */
+ pagesize = getpagesize();
+ ret = getrlimit(RLIMIT_MEMLOCK, &rlim);
+ if (ret < 0) {
+ perror("getrlimit");
+ return T_EXIT_PASS;
+ }
+ mlock_limit = rlim.rlim_cur;
+ devnull = open("/dev/null", O_RDWR);
+ if (devnull < 0) {
+ perror("open /dev/null");
+ exit(T_EXIT_FAIL);
+ }
+
+ /* invalid fd */
+ status |= expect_fail(-1, 0, NULL, 0, -EBADF);
+ /* valid fd that is not an io_uring fd */
+ status |= expect_fail(devnull, 0, NULL, 0, -EOPNOTSUPP);
+
+ /* invalid opcode */
+ memset(&p, 0, sizeof(p));
+ fd = new_io_uring(1, &p);
+ ret = expect_fail(fd, ~0U, NULL, 0, -EINVAL);
+ if (ret) {
+ /* if this succeeds, tear down the io_uring instance
+ * and start clean for the next test. */
+ close(fd);
+ fd = new_io_uring(1, &p);
+ }
+
+ /* IORING_REGISTER_BUFFERS */
+ status |= test_iovec_size(fd);
+ status |= test_iovec_nr(fd);
+ /* IORING_REGISTER_FILES */
+ status |= test_max_fds(fd);
+ close(fd);
+ /* uring poll on the uring fd */
+ status |= test_poll_ringfd();
+
+ if (status)
+ fprintf(stderr, "FAIL\n");
+
+ return status;
+}
diff --git a/contrib/libs/liburing/test/io_uring_setup.c b/contrib/libs/liburing/test/io_uring_setup.c
new file mode 100644
index 00000000000..3d5a6c4bcaa
--- /dev/null
+++ b/contrib/libs/liburing/test/io_uring_setup.c
@@ -0,0 +1,188 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * io_uring_setup.c
+ *
+ * Description: Unit tests for the io_uring_setup system call.
+ *
+ * Copyright 2019, Red Hat, Inc.
+ * Author: Jeff Moyer <jmoyer@redhat.com>
+ */
+#include <stdio.h>
+#include <fcntl.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <errno.h>
+#include <sys/sysinfo.h>
+#include "liburing.h"
+#include "helpers.h"
+
+#include "../syscall.h"
+
+char *features_string(struct io_uring_params *p)
+{
+ static char flagstr[64];
+
+ if (!p || !p->features)
+ return "none";
+
+ if (p->features & ~IORING_FEAT_SINGLE_MMAP) {
+ snprintf(flagstr, 64, "0x%.8x", p->features);
+ return flagstr;
+ }
+
+ if (p->features & IORING_FEAT_SINGLE_MMAP)
+ strncat(flagstr, "IORING_FEAT_SINGLE_MMAP", 64 - strlen(flagstr));
+
+ return flagstr;
+}
+
+/*
+ * Attempt the call with the given args. Return 0 when expect matches
+ * the return value of the system call, 1 otherwise.
+ */
+char *
+flags_string(struct io_uring_params *p)
+{
+ static char flagstr[64];
+ int add_pipe = 0;
+
+ memset(flagstr, 0, sizeof(flagstr));
+
+ if (!p || p->flags == 0)
+ return "none";
+
+ /*
+ * If unsupported flags are present, just print the bitmask.
+ */
+ if (p->flags & ~(IORING_SETUP_IOPOLL | IORING_SETUP_SQPOLL |
+ IORING_SETUP_SQ_AFF)) {
+ snprintf(flagstr, 64, "0x%.8x", p->flags);
+ return flagstr;
+ }
+
+ if (p->flags & IORING_SETUP_IOPOLL) {
+ strncat(flagstr, "IORING_SETUP_IOPOLL", 64 - strlen(flagstr));
+ add_pipe = 1;
+ }
+ if (p->flags & IORING_SETUP_SQPOLL) {
+ if (add_pipe)
+ strncat(flagstr, "|", 64 - strlen(flagstr));
+ else
+ add_pipe = 1;
+ strncat(flagstr, "IORING_SETUP_SQPOLL", 64 - strlen(flagstr));
+ }
+ if (p->flags & IORING_SETUP_SQ_AFF) {
+ if (add_pipe)
+ strncat(flagstr, "|", 64 - strlen(flagstr));
+ strncat(flagstr, "IORING_SETUP_SQ_AFF", 64 - strlen(flagstr));
+ }
+
+ return flagstr;
+}
+
+char *
+dump_resv(struct io_uring_params *p)
+{
+ static char resvstr[4096];
+
+ if (!p)
+ return "";
+
+ sprintf(resvstr, "0x%.8x 0x%.8x 0x%.8x", p->resv[0],
+ p->resv[1], p->resv[2]);
+
+ return resvstr;
+}
+
+/* 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 */
+int
+try_io_uring_setup(unsigned entries, struct io_uring_params *p, int expect)
+{
+ int ret;
+
+ ret = io_uring_setup(entries, p);
+ if (ret != expect) {
+ fprintf(stderr, "expected %d, got %d\n", expect, ret);
+ /* if we got a valid uring, close it */
+ if (ret > 0)
+ close(ret);
+ return 1;
+ }
+
+ if (expect < 0 && expect != ret) {
+ if (ret == -EPERM && geteuid() != 0) {
+ printf("Needs root, not flagging as an error\n");
+ return 0;
+ }
+ fprintf(stderr, "expected errno %d, got %d\n", expect, ret);
+ return 1;
+ }
+
+ return 0;
+}
+
+int
+main(int argc, char **argv)
+{
+ int fd;
+ unsigned int status = 0;
+ struct io_uring_params p;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ memset(&p, 0, sizeof(p));
+ status |= try_io_uring_setup(0, &p, -EINVAL);
+ status |= try_io_uring_setup(1, NULL, -EFAULT);
+
+ /* resv array is non-zero */
+ memset(&p, 0, sizeof(p));
+ p.resv[0] = p.resv[1] = p.resv[2] = 1;
+ status |= try_io_uring_setup(1, &p, -EINVAL);
+
+ /* invalid flags */
+ memset(&p, 0, sizeof(p));
+ p.flags = ~0U;
+ status |= try_io_uring_setup(1, &p, -EINVAL);
+
+ /* IORING_SETUP_SQ_AFF set but not IORING_SETUP_SQPOLL */
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_SQ_AFF;
+ status |= try_io_uring_setup(1, &p, -EINVAL);
+
+ /* attempt to bind to invalid cpu */
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_SQPOLL | IORING_SETUP_SQ_AFF;
+ p.sq_thread_cpu = get_nprocs_conf();
+ status |= try_io_uring_setup(1, &p, -EINVAL);
+
+ /* I think we can limit a process to a set of cpus. I assume
+ * we shouldn't be able to setup a kernel thread outside of that.
+ * try to do that. (task->cpus_allowed) */
+
+ /* read/write on io_uring_fd */
+ memset(&p, 0, sizeof(p));
+ fd = io_uring_setup(1, &p);
+ if (fd < 0) {
+ fprintf(stderr, "io_uring_setup failed with %d, expected success\n",
+ -fd);
+ status = 1;
+ } else {
+ char buf[4096];
+ int ret;
+ ret = read(fd, buf, 4096);
+ if (ret >= 0) {
+ fprintf(stderr, "read from io_uring fd succeeded. expected fail\n");
+ status = 1;
+ }
+ }
+
+ if (!status)
+ return T_EXIT_PASS;
+
+ fprintf(stderr, "FAIL\n");
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/iopoll-leak.c b/contrib/libs/liburing/test/iopoll-leak.c
new file mode 100644
index 00000000000..01b98fb64ae
--- /dev/null
+++ b/contrib/libs/liburing/test/iopoll-leak.c
@@ -0,0 +1,86 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test a mem leak with IOPOLL
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024)
+#define BS 4096
+#define BUFFERS (FILE_SIZE / BS)
+
+static int do_iopoll(const char *fname)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ struct iovec *iov;
+ int fd;
+
+ fd = open(fname, O_RDONLY | O_DIRECT);
+ if (fd < 0) {
+ perror("open");
+ return T_EXIT_SKIP;
+ }
+
+ iov = t_create_buffers(1, 4096);
+
+ t_create_ring(2, &ring, IORING_SETUP_IOPOLL);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, iov->iov_base, iov->iov_len, 0);
+ io_uring_submit(&ring);
+
+ close(fd);
+ return T_EXIT_PASS;
+}
+
+static int test(const char *fname)
+{
+ if (fork()) {
+ int stat;
+
+ wait(&stat);
+ return WEXITSTATUS(stat);
+ } else {
+ int ret;
+
+ ret = do_iopoll(fname);
+ exit(ret);
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[256];
+ char *fname;
+ int i, ret;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ srand((unsigned)time(NULL));
+ snprintf(buf, sizeof(buf), ".iopoll-leak-%u-%u",
+ (unsigned)rand(), (unsigned)getpid());
+ fname = buf;
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ for (i = 0; i < 16; i++) {
+ ret = test(fname);
+ if (ret == T_EXIT_SKIP || ret == T_EXIT_FAIL)
+ break;
+ }
+
+ if (fname != argv[1])
+ unlink(fname);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/iopoll.c b/contrib/libs/liburing/test/iopoll.c
new file mode 100644
index 00000000000..dfb73265a85
--- /dev/null
+++ b/contrib/libs/liburing/test/iopoll.c
@@ -0,0 +1,380 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: basic read/write tests with polled IO
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+#include <sys/resource.h>
+#include "helpers.h"
+#include "liburing.h"
+#include "../src/syscall.h"
+
+#define FILE_SIZE (128 * 1024)
+#define BS 4096
+#define BUFFERS (FILE_SIZE / BS)
+
+static struct iovec *vecs;
+static int no_buf_select;
+static int no_iopoll;
+
+static int provide_buffers(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_provide_buffers(sqe, vecs[i].iov_base,
+ vecs[i].iov_len, 1, 1, i);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+static int __test_io(const char *file, struct io_uring *ring, int write, int sqthread,
+ int fixed, int buf_select)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int open_flags;
+ int i, fd = -1, ret;
+ off_t offset;
+
+ if (buf_select) {
+ write = 0;
+ fixed = 0;
+ }
+ if (buf_select && provide_buffers(ring))
+ return 1;
+
+ if (write)
+ open_flags = O_WRONLY;
+ else
+ open_flags = O_RDONLY;
+ open_flags |= O_DIRECT;
+
+ if (fixed) {
+ ret = t_register_buffers(ring, vecs, BUFFERS);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "buffer reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+ if (sqthread) {
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ offset = 0;
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ offset = BS * (rand() % BUFFERS);
+ if (write) {
+ int do_fixed = fixed;
+ int use_fd = fd;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else {
+ io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+ } else {
+ int do_fixed = fixed;
+ int use_fd = fd;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else {
+ io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+
+ }
+ if (sqthread)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ if (buf_select) {
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = buf_select;
+ sqe->user_data = i;
+ }
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (!ret && cqe->res == -EOPNOTSUPP) {
+ no_iopoll = 1;
+ io_uring_cqe_seen(ring, cqe);
+ goto out;
+ }
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ } else if (cqe->res == -EOPNOTSUPP) {
+ fprintf(stdout, "File/device/fs doesn't support polled IO\n");
+ no_iopoll = 1;
+ goto out;
+ } else if (cqe->res != BS) {
+ fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, BS);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (fixed) {
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "buffer unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ if (sqthread) {
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "file unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+out:
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+
+extern unsigned __io_uring_flush_sq(struct io_uring *ring);
+
+/*
+ * if we are polling io_uring_submit needs to always enter the
+ * kernel to fetch events
+ */
+static int test_io_uring_submit_enters(const char *file)
+{
+ struct io_uring ring;
+ int fd, i, ret, ring_flags, open_flags;
+ unsigned head;
+ struct io_uring_cqe *cqe;
+
+ if (no_iopoll)
+ return 0;
+
+ ring_flags = IORING_SETUP_IOPOLL;
+ ret = io_uring_queue_init(64, &ring, ring_flags);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ open_flags = O_WRONLY | O_DIRECT;
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ struct io_uring_sqe *sqe;
+ off_t offset = BS * (rand() % BUFFERS);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, fd, &vecs[i], 1, offset);
+ sqe->user_data = 1;
+ }
+
+ /* submit manually to avoid adding IORING_ENTER_GETEVENTS */
+ ret = __sys_io_uring_enter(ring.ring_fd, __io_uring_flush_sq(&ring), 0,
+ 0, NULL);
+ if (ret < 0)
+ goto err;
+
+ for (i = 0; i < 500; i++) {
+ ret = io_uring_submit(&ring);
+ if (ret != 0) {
+ fprintf(stderr, "still had %d sqes to submit, this is unexpected", ret);
+ goto err;
+ }
+
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ /* runs after test_io so should not have happened */
+ if (cqe->res == -EOPNOTSUPP) {
+ fprintf(stdout, "File/device/fs doesn't support polled IO\n");
+ goto err;
+ }
+ goto ok;
+ }
+ usleep(10000);
+ }
+err:
+ ret = 1;
+ if (fd != -1)
+ close(fd);
+
+ok:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int test_io(const char *file, int write, int sqthread, int fixed,
+ int buf_select, int defer)
+{
+ struct io_uring ring;
+ int ret, ring_flags = IORING_SETUP_IOPOLL;
+
+ if (no_iopoll)
+ return 0;
+
+ if (defer)
+ ring_flags |= IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ ret = t_create_ring(64, &ring, ring_flags);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+ ret = __test_io(file, &ring, write, sqthread, fixed, buf_select);
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int probe_buf_select(void)
+{
+ struct io_uring_probe *p;
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ p = io_uring_get_probe_ring(&ring);
+ if (!p || !io_uring_opcode_supported(p, IORING_OP_PROVIDE_BUFFERS)) {
+ no_buf_select = 1;
+ fprintf(stdout, "Buffer select not supported, skipping\n");
+ return 0;
+ }
+ io_uring_free_probe(p);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret, nr;
+ char buf[256];
+ char *fname;
+
+ if (probe_buf_select())
+ return T_EXIT_FAIL;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ srand((unsigned)time(NULL));
+ snprintf(buf, sizeof(buf), ".basic-rw-%u-%u",
+ (unsigned)rand(), (unsigned)getpid());
+ fname = buf;
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ nr = 32;
+ if (no_buf_select)
+ nr = 8;
+ else if (!t_probe_defer_taskrun())
+ nr = 16;
+ for (i = 0; i < nr; i++) {
+ int write = (i & 1) != 0;
+ int sqthread = (i & 2) != 0;
+ int fixed = (i & 4) != 0;
+ int buf_select = (i & 8) != 0;
+ int defer = (i & 16) != 0;
+
+ ret = test_io(fname, write, sqthread, fixed, buf_select, defer);
+ if (ret) {
+ fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
+ write, sqthread, fixed, buf_select, defer);
+ goto err;
+ }
+ if (no_iopoll)
+ break;
+ }
+
+ ret = test_io_uring_submit_enters(fname);
+ if (ret) {
+ fprintf(stderr, "test_io_uring_submit_enters failed\n");
+ goto err;
+ }
+
+ if (fname != argv[1])
+ unlink(fname);
+ return T_EXIT_PASS;
+err:
+ if (fname != argv[1])
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/lfs-openat-write.c b/contrib/libs/liburing/test/lfs-openat-write.c
new file mode 100644
index 00000000000..8e3c404de4f
--- /dev/null
+++ b/contrib/libs/liburing/test/lfs-openat-write.c
@@ -0,0 +1,122 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+
+#define _LARGEFILE_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <liburing.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "helpers.h"
+
+static const int RSIZE = 2;
+static const int OPEN_FLAGS = O_RDWR | O_CREAT;
+static const mode_t OPEN_MODE = S_IRUSR | S_IWUSR;
+
+#define DIE(...) do {\
+ fprintf(stderr, __VA_ARGS__);\
+ abort();\
+ } while(0);
+
+static int do_write(struct io_uring *ring, int fd, off_t offset)
+{
+ char buf[] = "some test write buf";
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int res, ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_write(sqe, fd, buf, sizeof(buf), offset);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "failed to submit write: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ res = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (res < 0) {
+ fprintf(stderr, "write failed: %s\n", strerror(-res));
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_open_write(struct io_uring *ring, int dfd, const char *fn)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fd = -1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ fd = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (fd < 0) {
+ fprintf(stderr, "openat failed: %s\n", strerror(-fd));
+ return 1;
+ }
+
+ return do_write(ring, fd, 1ULL << 32);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int dfd, ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ dfd = open("/tmp", O_RDONLY | O_DIRECTORY);
+ if (dfd < 0)
+ DIE("open /tmp: %s\n", strerror(errno));
+
+ ret = io_uring_queue_init(RSIZE, &ring, 0);
+ if (ret < 0)
+ DIE("failed to init io_uring: %s\n", strerror(-ret));
+
+ ret = test_open_write(&ring, dfd, "io_uring_openat_write_test1");
+
+ io_uring_queue_exit(&ring);
+ close(dfd);
+ unlink("/tmp/io_uring_openat_write_test1");
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/lfs-openat.c b/contrib/libs/liburing/test/lfs-openat.c
new file mode 100644
index 00000000000..1d93df7e4c6
--- /dev/null
+++ b/contrib/libs/liburing/test/lfs-openat.c
@@ -0,0 +1,276 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+
+#define _LARGEFILE_SOURCE
+#define _FILE_OFFSET_BITS 64
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/resource.h>
+#include <unistd.h>
+
+#include "liburing.h"
+
+#define DIE(...) do {\
+ fprintf(stderr, __VA_ARGS__);\
+ abort();\
+ } while(0);
+
+static const int RSIZE = 2;
+static const int OPEN_FLAGS = O_RDWR | O_CREAT;
+static const mode_t OPEN_MODE = S_IRUSR | S_IWUSR;
+
+static int open_io_uring(struct io_uring *ring, int dfd, const char *fn)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fd;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ fd = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe failed: %s\n", strerror(-ret));
+ return 1;
+ } else if (fd < 0) {
+ fprintf(stderr, "io_uring openat failed: %s\n", strerror(-fd));
+ return 1;
+ }
+
+ close(fd);
+ return 0;
+}
+
+static int prepare_file(int dfd, const char* fn)
+{
+ const char buf[] = "foo";
+ int fd, res;
+
+ fd = openat(dfd, fn, OPEN_FLAGS, OPEN_MODE);
+ if (fd < 0) {
+ fprintf(stderr, "prepare/open: %s\n", strerror(errno));
+ return -1;
+ }
+
+ res = pwrite(fd, buf, sizeof(buf), 1ull << 32);
+ if (res < 0)
+ fprintf(stderr, "prepare/pwrite: %s\n", strerror(errno));
+
+ close(fd);
+ return res < 0 ? res : 0;
+}
+
+static int test_linked_files(int dfd, const char *fn, bool async)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ char buffer[128];
+ struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
+ int ret, fd;
+ int fds[2];
+
+ ret = io_uring_queue_init(10, &ring, 0);
+ if (ret < 0)
+ DIE("failed to init io_uring: %s\n", strerror(-ret));
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return -1;
+ }
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ fd = dup(ring.ring_fd);
+ if (fd < 0) {
+ fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
+ return 1;
+ }
+
+ /* io_uring->flush() */
+ close(fd);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_drained_files(int dfd, const char *fn, bool linked, bool prepend)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ char buffer[128];
+ struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
+ int ret, fd, fds[2], to_cancel = 0;
+
+ ret = io_uring_queue_init(10, &ring, 0);
+ if (ret < 0)
+ DIE("failed to init io_uring: %s\n", strerror(-ret));
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ return -1;
+ }
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->user_data = 0;
+
+ if (prepend) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_DRAIN;
+ to_cancel++;
+ sqe->user_data = to_cancel;
+ }
+
+ if (linked) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_DRAIN | IOSQE_IO_LINK;
+ to_cancel++;
+ sqe->user_data = to_cancel;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+ io_uring_prep_openat(sqe, dfd, fn, OPEN_FLAGS, OPEN_MODE);
+ sqe->flags |= IOSQE_IO_DRAIN;
+ to_cancel++;
+ sqe->user_data = to_cancel;
+
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1 + to_cancel) {
+ fprintf(stderr, "failed to submit openat: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ fd = dup(ring.ring_fd);
+ if (fd < 0) {
+ fprintf(stderr, "dup() failed: %s\n", strerror(-fd));
+ return 1;
+ }
+
+ /*
+ * close(), which triggers ->flush(), and io_uring_queue_exit()
+ * should successfully return and not hang.
+ */
+ close(fd);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ const char *fn = "io_uring_openat_test";
+ struct io_uring ring;
+ int ret, dfd;
+
+ if (argc > 1)
+ return 0;
+
+ dfd = open("/tmp", O_PATH);
+ if (dfd < 0)
+ DIE("open /tmp: %s\n", strerror(errno));
+
+ ret = io_uring_queue_init(RSIZE, &ring, 0);
+ if (ret < 0)
+ DIE("failed to init io_uring: %s\n", strerror(-ret));
+
+ if (prepare_file(dfd, fn))
+ return 1;
+
+ ret = open_io_uring(&ring, dfd, fn);
+ if (ret) {
+ fprintf(stderr, "open_io_uring() failed\n");
+ goto out;
+ }
+
+ ret = test_linked_files(dfd, fn, false);
+ if (ret) {
+ fprintf(stderr, "test_linked_files() !async failed\n");
+ goto out;
+ }
+
+ ret = test_linked_files(dfd, fn, true);
+ if (ret) {
+ fprintf(stderr, "test_linked_files() async failed\n");
+ goto out;
+ }
+
+ ret = test_drained_files(dfd, fn, false, false);
+ if (ret) {
+ fprintf(stderr, "test_drained_files() failed\n");
+ goto out;
+ }
+
+ ret = test_drained_files(dfd, fn, false, true);
+ if (ret) {
+ fprintf(stderr, "test_drained_files() middle failed\n");
+ goto out;
+ }
+
+ ret = test_drained_files(dfd, fn, true, false);
+ if (ret) {
+ fprintf(stderr, "test_drained_files() linked failed\n");
+ goto out;
+ }
+out:
+ io_uring_queue_exit(&ring);
+ close(dfd);
+ unlink("/tmp/io_uring_openat_test");
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/link-timeout.c b/contrib/libs/liburing/test/link-timeout.c
new file mode 100644
index 00000000000..c59543c53f9
--- /dev/null
+++ b/contrib/libs/liburing/test/link-timeout.c
@@ -0,0 +1,1109 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various linked timeout cases
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int test_fail_lone_link_timeouts(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->user_data != 1) {
+ fprintf(stderr, "invalid user data %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "got %d, wanted -EINVAL\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_fail_two_link_timeouts(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i, nr_wait;
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+
+ /*
+ * sqe_1: write destined to fail
+ * use buf=NULL, to do that during the issuing stage
+ */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_writev(sqe, 0, NULL, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+
+ /* sqe_2: valid linked timeout */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+ sqe->flags |= IOSQE_IO_LINK;
+
+
+ /* sqe_3: invalid linked timeout */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 3;
+
+ /* sqe_4: invalid linked timeout */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 4;
+
+ ret = io_uring_submit(ring);
+ if (ret < 3) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+ nr_wait = ret;
+
+ for (i = 0; i < nr_wait; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -EFAULT && cqe->res != -ECANCELED) {
+ fprintf(stderr, "write got %d, wanted -EFAULT "
+ "or -ECANCELED\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Link timeout got %d, wanted -ECACNCELED\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ /* fall through */
+ case 4:
+ if (cqe->res != -ECANCELED && cqe->res != -EINVAL) {
+ fprintf(stderr, "Invalid link timeout got %d"
+ ", wanted -ECACNCELED || -EINVAL\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test linked timeout with timeout (timeoutception)
+ */
+static int test_single_link_timeout_ception(struct io_uring *ring)
+{
+ struct __kernel_timespec ts1, ts2;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts1.tv_sec = 1;
+ ts1.tv_nsec = 0;
+ io_uring_prep_timeout(sqe, &ts1, -1U, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts2.tv_sec = 2;
+ ts2.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts2, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ /* newer kernels allow timeout links */
+ if (cqe->res != -EINVAL && cqe->res != -ETIME) {
+ fprintf(stderr, "Timeout got %d, wanted "
+ "-EINVAL or -ETIME\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Link timeout got %d, wanted -ECANCELED\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test linked timeout with NOP
+ */
+static int test_single_link_timeout_nop(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res) {
+ fprintf(stderr, "NOP got %d, wanted 0\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Link timeout got %d, wanted -ECACNCELED\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test read that will not complete, with a linked timeout behind it that
+ * has errors in the SQE
+ */
+static int test_single_link_timeout_error(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+ struct iovec iov;
+ char buffer[128];
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ iov.iov_base = buffer;
+ iov.iov_len = sizeof(buffer);
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ /* set invalid field, it'll get failed */
+ sqe->ioprio = 89;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Read got %d, wanted -ECANCELED\n",
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "Link timeout got %d, wanted -EINVAL\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test read that will complete, with a linked timeout behind it
+ */
+static int test_single_link_no_timeout(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+ struct iovec iov;
+ char buffer[128];
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ iov.iov_base = buffer;
+ iov.iov_len = sizeof(buffer);
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ iov.iov_base = buffer;
+ iov.iov_len = sizeof(buffer);
+ io_uring_prep_writev(sqe, fds[1], &iov, 1, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ case 3:
+ if (cqe->res != sizeof(buffer)) {
+ fprintf(stderr, "R/W got %d, wanted %d\n", cqe->res,
+ (int) sizeof(buffer));
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Link timeout %d, wanted -ECANCELED\n",
+ cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test read that will not complete, with a linked timeout behind it
+ */
+static int test_single_link_timeout(struct io_uring *ring, unsigned nsec)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+ struct iovec iov;
+ char buffer[128];
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ iov.iov_base = buffer;
+ iov.iov_len = sizeof(buffer);
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ ts.tv_sec = 0;
+ ts.tv_nsec = nsec;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -EINTR && cqe->res != -ECANCELED) {
+ fprintf(stderr, "Read got %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -EALREADY && cqe->res != -ETIME &&
+ cqe->res != 0) {
+ fprintf(stderr, "Link timeout got %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_timeout_link_chain1(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+ struct iovec iov;
+ char buffer[128];
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ iov.iov_base = buffer;
+ iov.iov_len = sizeof(buffer);
+ io_uring_prep_readv(sqe, fds[0], &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -EINTR && cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ /* FASTPOLL kernels can cancel successfully */
+ if (cqe->res != -EALREADY && cqe->res != -ETIME) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_timeout_link_chain2(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 3;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 4;
+
+ ret = io_uring_submit(ring);
+ if (ret != 4) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ /* poll cancel really should return -ECANCEL... */
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ case 4:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_timeout_link_chain3(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 3;
+
+ /* POLL -> TIMEOUT -> NOP */
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 4;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 5;
+
+ /* poll on pipe + timeout */
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 6;
+
+ /* nop */
+
+ ret = io_uring_submit(ring);
+ if (ret != 6) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 6; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 2:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 1:
+ case 3:
+ case 4:
+ case 5:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 6:
+ if (cqe->res) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_timeout_link_chain4(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i;
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 1000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ /* poll cancel really should return -ECANCEL... */
+ case 1:
+ if (cqe->res) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_timeout_link_chain5(struct io_uring *ring)
+{
+ struct __kernel_timespec ts1, ts2;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts1.tv_sec = 1;
+ ts1.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ ts2.tv_sec = 2;
+ ts2.tv_nsec = 0;
+ io_uring_prep_link_timeout(sqe, &ts2, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ case 2:
+ if (cqe->res && cqe->res != -ECANCELED) {
+ fprintf(stderr, "Request got %d, wanted -EINVAL "
+ "or -ECANCELED\n",
+ cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != -ECANCELED && cqe->res != -EINVAL) {
+ fprintf(stderr, "Link timeout got %d, wanted -ECANCELED\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_timeout_link_chain1(&ring);
+ if (ret) {
+ printf("test_single_link_chain1 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_link_chain2(&ring);
+ if (ret) {
+ printf("test_single_link_chain2 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_link_chain3(&ring);
+ if (ret) {
+ printf("test_single_link_chain3 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_link_chain4(&ring);
+ if (ret) {
+ printf("test_single_link_chain4 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_link_chain5(&ring);
+ if (ret) {
+ printf("test_single_link_chain5 failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout(&ring, 10);
+ if (ret) {
+ printf("test_single_link_timeout 10 failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout(&ring, 100000ULL);
+ if (ret) {
+ printf("test_single_link_timeout 100000 failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout(&ring, 500000000ULL);
+ if (ret) {
+ printf("test_single_link_timeout 500000000 failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_no_timeout(&ring);
+ if (ret) {
+ printf("test_single_link_no_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout_error(&ring);
+ if (ret) {
+ printf("test_single_link_timeout_error failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout_nop(&ring);
+ if (ret) {
+ printf("test_single_link_timeout_nop failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_timeout_ception(&ring);
+ if (ret) {
+ printf("test_single_link_timeout_ception failed\n");
+ return ret;
+ }
+
+ ret = test_fail_lone_link_timeouts(&ring);
+ if (ret) {
+ printf("test_fail_lone_link_timeouts failed\n");
+ return ret;
+ }
+
+ ret = test_fail_two_link_timeouts(&ring);
+ if (ret) {
+ printf("test_fail_two_link_timeouts failed\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/link.c b/contrib/libs/liburing/test/link.c
new file mode 100644
index 00000000000..6f839394c31
--- /dev/null
+++ b/contrib/libs/liburing/test/link.c
@@ -0,0 +1,498 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various linked sqe tests
+ *
+ */
+#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_hardlink;
+
+/*
+ * Timer with single nop
+ */
+static int test_single_hardlink(struct io_uring *ring)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ ts.tv_sec = 0;
+ ts.tv_nsec = 10000000ULL;
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (!cqe) {
+ fprintf(stderr, "failed to get cqe\n");
+ goto err;
+ }
+ if (no_hardlink)
+ goto next;
+ if (cqe->user_data == 1 && cqe->res == -EINVAL) {
+ fprintf(stdout, "Hard links not supported, skipping\n");
+ no_hardlink = 1;
+ goto next;
+ }
+ if (cqe->user_data == 1 && cqe->res != -ETIME) {
+ fprintf(stderr, "timeout failed with %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->user_data == 2 && cqe->res) {
+ fprintf(stderr, "nop failed with %d\n", cqe->res);
+ goto err;
+ }
+next:
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Timer -> timer -> nop
+ */
+static int test_double_hardlink(struct io_uring *ring)
+{
+ struct __kernel_timespec ts1, ts2;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ if (no_hardlink)
+ return 0;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ ts1.tv_sec = 0;
+ ts1.tv_nsec = 10000000ULL;
+ io_uring_prep_timeout(sqe, &ts1, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ ts2.tv_sec = 0;
+ ts2.tv_nsec = 15000000ULL;
+ io_uring_prep_timeout(sqe, &ts2, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_IO_HARDLINK;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (!cqe) {
+ fprintf(stderr, "failed to get cqe\n");
+ goto err;
+ }
+ if (cqe->user_data == 1 && cqe->res != -ETIME) {
+ fprintf(stderr, "timeout failed with %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->user_data == 2 && cqe->res != -ETIME) {
+ fprintf(stderr, "timeout failed with %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->user_data == 3 && cqe->res) {
+ fprintf(stderr, "nop failed with %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+
+}
+
+/*
+ * Test failing head of chain, and dependent getting -ECANCELED
+ */
+static int test_single_link_fail(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_remove_buffers(sqe, 10, 1);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ if (!cqe) {
+ printf("failed to get cqe\n");
+ goto err;
+ }
+ if (i == 0 && cqe->res != -ENOENT) {
+ printf("sqe0 failed with %d, wanted -ENOENT\n", cqe->res);
+ goto err;
+ }
+ if (i == 1 && cqe->res != -ECANCELED) {
+ printf("sqe1 failed with %d, wanted -ECANCELED\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test two independent chains
+ */
+static int test_double_chain(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 4; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test multiple dependents
+ */
+static int test_double_link(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test single dependency
+ */
+static int test_single_link(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_early_fail_and_wait(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ int ret, invalid_fd = 42;
+ struct iovec iov = { .iov_base = NULL, .iov_len = 0 };
+
+ /* create a new ring as it leaves it dirty */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_readv(sqe, invalid_fd, &iov, 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit_and_wait(&ring, 2);
+ if (ret <= 0 && ret != -EAGAIN) {
+ printf("sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring, poll_ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+
+ }
+
+ ret = io_uring_queue_init(8, &poll_ring, IORING_SETUP_IOPOLL);
+ if (ret) {
+ printf("poll_ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_single_link(&ring);
+ if (ret) {
+ printf("test_single_link failed\n");
+ return ret;
+ }
+
+ ret = test_double_link(&ring);
+ if (ret) {
+ printf("test_double_link failed\n");
+ return ret;
+ }
+
+ ret = test_double_chain(&ring);
+ if (ret) {
+ printf("test_double_chain failed\n");
+ return ret;
+ }
+
+ ret = test_single_link_fail(&poll_ring);
+ if (ret) {
+ printf("test_single_link_fail failed\n");
+ return ret;
+ }
+
+ ret = test_single_hardlink(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_hardlink\n");
+ return ret;
+ }
+
+ ret = test_double_hardlink(&ring);
+ if (ret) {
+ fprintf(stderr, "test_double_hardlink\n");
+ return ret;
+ }
+
+ ret = test_early_fail_and_wait();
+ if (ret) {
+ fprintf(stderr, "test_early_fail_and_wait\n");
+ return ret;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/link_drain.c b/contrib/libs/liburing/test/link_drain.c
new file mode 100644
index 00000000000..ec8021f807a
--- /dev/null
+++ b/contrib/libs/liburing/test/link_drain.c
@@ -0,0 +1,230 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring link io with drain io
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int test_link_drain_one(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe[5];
+ struct iovec iovecs;
+ int i, fd, ret;
+ off_t off = 0;
+ char data[5] = {0};
+ char expect[5] = {0, 1, 2, 3, 4};
+
+ fd = open("testfile", O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ iovecs.iov_base = t_malloc(4096);
+ iovecs.iov_len = 4096;
+
+ for (i = 0; i < 5; i++) {
+ sqe[i] = io_uring_get_sqe(ring);
+ if (!sqe[i]) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ }
+
+ /* normal heavy io */
+ io_uring_prep_writev(sqe[0], fd, &iovecs, 1, off);
+ sqe[0]->user_data = 0;
+
+ /* link io */
+ io_uring_prep_nop(sqe[1]);
+ sqe[1]->flags |= IOSQE_IO_LINK;
+ sqe[1]->user_data = 1;
+
+ /* link drain io */
+ io_uring_prep_nop(sqe[2]);
+ sqe[2]->flags |= (IOSQE_IO_LINK | IOSQE_IO_DRAIN);
+ sqe[2]->user_data = 2;
+
+ /* link io */
+ io_uring_prep_nop(sqe[3]);
+ sqe[3]->user_data = 3;
+
+ /* normal nop io */
+ io_uring_prep_nop(sqe[4]);
+ sqe[4]->user_data = 4;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("sqe submit failed\n");
+ goto err;
+ } else if (ret < 5) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("child: wait completion %d\n", ret);
+ goto err;
+ }
+
+ data[i] = cqe->user_data;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (memcmp(data, expect, 5) != 0)
+ goto err;
+
+ free(iovecs.iov_base);
+ close(fd);
+ unlink("testfile");
+ return 0;
+err:
+ free(iovecs.iov_base);
+ close(fd);
+ unlink("testfile");
+ return 1;
+}
+
+int test_link_drain_multi(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe[9];
+ struct iovec iovecs;
+ int i, fd, ret;
+ off_t off = 0;
+ char data[9] = {0};
+ char expect[9] = {0, 1, 2, 3, 4, 5, 6, 7, 8};
+
+ fd = open("testfile", O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+ unlink("testfile");
+
+ iovecs.iov_base = t_malloc(4096);
+ iovecs.iov_len = 4096;
+
+ for (i = 0; i < 9; i++) {
+ sqe[i] = io_uring_get_sqe(ring);
+ if (!sqe[i]) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ }
+
+ /* normal heavy io */
+ io_uring_prep_writev(sqe[0], fd, &iovecs, 1, off);
+ sqe[0]->user_data = 0;
+
+ /* link1 io head */
+ io_uring_prep_nop(sqe[1]);
+ sqe[1]->flags |= IOSQE_IO_LINK;
+ sqe[1]->user_data = 1;
+
+ /* link1 drain io */
+ io_uring_prep_nop(sqe[2]);
+ sqe[2]->flags |= (IOSQE_IO_LINK | IOSQE_IO_DRAIN);
+ sqe[2]->user_data = 2;
+
+ /* link1 io end*/
+ io_uring_prep_nop(sqe[3]);
+ sqe[3]->user_data = 3;
+
+ /* link2 io head */
+ io_uring_prep_nop(sqe[4]);
+ sqe[4]->flags |= IOSQE_IO_LINK;
+ sqe[4]->user_data = 4;
+
+ /* link2 io */
+ io_uring_prep_nop(sqe[5]);
+ sqe[5]->flags |= IOSQE_IO_LINK;
+ sqe[5]->user_data = 5;
+
+ /* link2 drain io */
+ io_uring_prep_writev(sqe[6], fd, &iovecs, 1, off);
+ sqe[6]->flags |= (IOSQE_IO_LINK | IOSQE_IO_DRAIN);
+ sqe[6]->user_data = 6;
+
+ /* link2 io end */
+ io_uring_prep_nop(sqe[7]);
+ sqe[7]->user_data = 7;
+
+ /* normal io */
+ io_uring_prep_nop(sqe[8]);
+ sqe[8]->user_data = 8;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("sqe submit failed\n");
+ goto err;
+ } else if (ret < 9) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 9; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("child: wait completion %d\n", ret);
+ goto err;
+ }
+
+ data[i] = cqe->user_data;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (memcmp(data, expect, 9) != 0)
+ goto err;
+
+ free(iovecs.iov_base);
+ close(fd);
+ return 0;
+err:
+ free(iovecs.iov_base);
+ close(fd);
+ return 1;
+
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int i, ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(100, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ ret = test_link_drain_one(&ring);
+ if (ret) {
+ fprintf(stderr, "test_link_drain_one failed\n");
+ break;
+ }
+ ret = test_link_drain_multi(&ring);
+ if (ret) {
+ fprintf(stderr, "test_link_drain_multi failed\n");
+ break;
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/madvise.c b/contrib/libs/liburing/test/madvise.c
new file mode 100644
index 00000000000..bd44a9741ca
--- /dev/null
+++ b/contrib/libs/liburing/test/madvise.c
@@ -0,0 +1,196 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: basic madvise test
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/mman.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024)
+
+#define LOOPS 100
+#define MIN_LOOPS 10
+
+static unsigned long long utime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000000;
+ return sec + usec;
+}
+
+static unsigned long long utime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return utime_since(tv, &end);
+}
+
+static int do_madvise(struct io_uring *ring, void *addr, off_t len, int advice)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed to get sqe\n");
+ return 1;
+ }
+
+ io_uring_prep_madvise(sqe, addr, len, advice);
+ sqe->user_data = advice;
+ ret = io_uring_submit_and_wait(ring, 1);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ if (ret == -EINVAL || ret == -EBADF) {
+ fprintf(stdout, "Madvise not supported, skipping\n");
+ unlink(".madvise.tmp");
+ exit(0);
+ } else if (ret) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static long do_copy(int fd, char *buf, void *ptr)
+{
+ struct timeval tv;
+
+ gettimeofday(&tv, NULL);
+ memcpy(buf, ptr, FILE_SIZE);
+ return utime_since_now(&tv);
+}
+
+static int test_madvise(struct io_uring *ring, const char *filename)
+{
+ unsigned long cached_read, uncached_read, cached_read2;
+ int fd, ret;
+ char *buf;
+ void *ptr;
+
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ buf = t_malloc(FILE_SIZE);
+
+ ptr = mmap(NULL, FILE_SIZE, PROT_READ, MAP_PRIVATE, fd, 0);
+ if (ptr == MAP_FAILED) {
+ perror("mmap");
+ return 1;
+ }
+
+ cached_read = do_copy(fd, buf, ptr);
+ if (cached_read == -1)
+ return 1;
+
+ cached_read = do_copy(fd, buf, ptr);
+ if (cached_read == -1)
+ return 1;
+
+ ret = do_madvise(ring, ptr, FILE_SIZE, MADV_DONTNEED);
+ if (ret)
+ return 1;
+
+ uncached_read = do_copy(fd, buf, ptr);
+ if (uncached_read == -1)
+ return 1;
+
+ ret = do_madvise(ring, ptr, FILE_SIZE, MADV_DONTNEED);
+ if (ret)
+ return 1;
+
+ ret = do_madvise(ring, ptr, FILE_SIZE, MADV_WILLNEED);
+ if (ret)
+ return 1;
+
+ msync(ptr, FILE_SIZE, MS_SYNC);
+
+ cached_read2 = do_copy(fd, buf, ptr);
+ if (cached_read2 == -1)
+ return 1;
+
+ if (cached_read < uncached_read &&
+ cached_read2 < uncached_read)
+ return 0;
+
+ return 2;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, i, good, bad;
+ char *fname;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ fname = ".madvise.tmp";
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ if (io_uring_queue_init(8, &ring, 0)) {
+ fprintf(stderr, "ring creation failed\n");
+ goto err;
+ }
+
+ good = bad = 0;
+ for (i = 0; i < LOOPS; i++) {
+ ret = test_madvise(&ring, fname);
+ if (ret == 1) {
+ fprintf(stderr, "test_madvise failed\n");
+ goto err;
+ } else if (!ret)
+ good++;
+ else if (ret == 2)
+ bad++;
+ if (i >= MIN_LOOPS && !bad)
+ break;
+ }
+
+ /* too hard to reliably test, just ignore */
+ if (0 && bad > good)
+ fprintf(stderr, "Suspicious timings (%u > %u)\n", bad, good);
+ if (fname != argv[1])
+ unlink(fname);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ if (fname != argv[1])
+ unlink(fname);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/mkdir.c b/contrib/libs/liburing/test/mkdir.c
new file mode 100644
index 00000000000..630672cc931
--- /dev/null
+++ b/contrib/libs/liburing/test/mkdir.c
@@ -0,0 +1,113 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring mkdirat handling
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int do_mkdirat(struct io_uring *ring, const char *fn)
+{
+ int ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_mkdirat(sqe, AT_FDCWD, fn, 0700);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqes(ring, &cqe, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "wait_cqe failed: %d\n", ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+err:
+ return 1;
+}
+
+static int stat_file(const char *fn)
+{
+ struct stat sb;
+
+ if (!stat(fn, &sb))
+ return 0;
+
+ return errno;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char fn[] = "io_uring-mkdirat-test";
+ int ret;
+ struct io_uring ring;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = do_mkdirat(&ring, fn);
+ if (ret < 0) {
+ if (ret == -EBADF || ret == -EINVAL) {
+ fprintf(stdout, "mkdirat not supported, skipping\n");
+ goto skip;
+ }
+ fprintf(stderr, "mkdirat: %s\n", strerror(-ret));
+ goto err;
+ } else if (ret) {
+ goto err;
+ }
+
+ if (stat_file(fn)) {
+ perror("stat");
+ goto err;
+ }
+
+ ret = do_mkdirat(&ring, fn);
+ if (ret != -EEXIST) {
+ fprintf(stderr, "do_mkdirat already exists failed: %d\n", ret);
+ goto err1;
+ }
+
+ ret = do_mkdirat(&ring, "surely/this/wont/exist");
+ if (ret != -ENOENT) {
+ fprintf(stderr, "do_mkdirat no parent failed: %d\n", ret);
+ goto err1;
+ }
+
+ unlinkat(AT_FDCWD, fn, AT_REMOVEDIR);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+skip:
+ unlinkat(AT_FDCWD, fn, AT_REMOVEDIR);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_SKIP;
+err1:
+ unlinkat(AT_FDCWD, fn, AT_REMOVEDIR);
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
diff --git a/contrib/libs/liburing/test/msg-ring.c b/contrib/libs/liburing/test/msg-ring.c
new file mode 100644
index 00000000000..079c1609d05
--- /dev/null
+++ b/contrib/libs/liburing/test/msg-ring.c
@@ -0,0 +1,261 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test ring messaging command
+ *
+ */
+#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 test_own(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_msg_ring(sqe, ring->ring_fd, 0x10, 0x1234, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res == -EINVAL || cqe->res == -EOPNOTSUPP) {
+ no_msg = 1;
+ return 0;
+ }
+ if (cqe->res != 0) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return -1;
+ }
+ break;
+ case 0x1234:
+ if (cqe->res != 0x10) {
+ fprintf(stderr, "invalid len %x\n", cqe->res);
+ return -1;
+ }
+ break;
+ default:
+ fprintf(stderr, "Invalid user_data\n");
+ return -1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static void *wait_cqe_fn(void *data)
+{
+ struct io_uring *ring = data;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ goto err;
+ }
+
+ if (cqe->user_data != 0x5aa5) {
+ fprintf(stderr, "user_data %llx\n", (long long) cqe->user_data);
+ goto err;
+ }
+ if (cqe->res != 0x20) {
+ fprintf(stderr, "len %x\n", cqe->res);
+ goto err;
+ }
+
+ return NULL;
+err:
+ return (void *) (unsigned long) 1;
+}
+
+static int test_remote(struct io_uring *ring, struct io_uring *target)
+{
+ 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;
+ }
+
+ io_uring_prep_msg_ring(sqe, target->ring_fd, 0x20, 0x5aa5, 0);
+ sqe->user_data = 1;
+
+ 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;
+ }
+ if (cqe->res != 0) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return -1;
+ }
+ if (cqe->user_data != 1) {
+ fprintf(stderr, "user_data %llx\n", (long long) cqe->user_data);
+ return -1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_invalid(struct io_uring *ring, bool fixed)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, fd = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ if (fixed) {
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file register %d\n", ret);
+ return 1;
+ }
+ io_uring_prep_msg_ring(sqe, 0, 0, 0x8989, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ } else {
+ io_uring_prep_msg_ring(sqe, 1, 0, 0x8989, 0);
+ }
+
+ sqe->user_data = 1;
+
+ 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;
+ }
+ if (cqe->res != -EBADFD) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return -1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ if (fixed)
+ io_uring_unregister_files(ring);
+ return 0;
+err:
+ if (fixed)
+ io_uring_unregister_files(ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring, ring2, pring;
+ pthread_t thread;
+ void *tret;
+ int ret, i;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_queue_init(8, &ring2, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+ ret = io_uring_queue_init(8, &pring, IORING_SETUP_IOPOLL);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_own(&ring);
+ if (ret) {
+ fprintf(stderr, "test_own failed\n");
+ return ret;
+ }
+ if (no_msg) {
+ fprintf(stdout, "Skipped\n");
+ return T_EXIT_SKIP;
+ }
+ ret = test_own(&pring);
+ if (ret) {
+ fprintf(stderr, "test_own iopoll failed\n");
+ return ret;
+ }
+
+ ret = test_invalid(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "test_invalid failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = test_invalid(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "test_invalid fixed failed\n");
+ return ret;
+ }
+ }
+
+ pthread_create(&thread, NULL, wait_cqe_fn, &ring2);
+
+ ret = test_remote(&ring, &ring2);
+ if (ret) {
+ fprintf(stderr, "test_remote failed\n");
+ return ret;
+ }
+
+ pthread_join(thread, &tret);
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/multicqes_drain.c b/contrib/libs/liburing/test/multicqes_drain.c
new file mode 100644
index 00000000000..99e5fe1247b
--- /dev/null
+++ b/contrib/libs/liburing/test/multicqes_drain.c
@@ -0,0 +1,427 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: generic tests for io_uring drain io
+ *
+ * The main idea is to randomly generate different type of sqe to
+ * challenge the drain logic. There are some restrictions for the
+ * generated sqes, details in io_uring maillist:
+ * https://lore.kernel.org/io-uring/39a49b4c-27c2-1035-b250-51daeccaab9b@linux.alibaba.com/
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <poll.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+enum {
+ multi,
+ single,
+ nop,
+ cancel,
+ op_last,
+};
+
+struct sqe_info {
+ __u8 op;
+ unsigned flags;
+};
+
+#define max_entry 50
+
+/*
+ * sqe_flags: combination of sqe flags
+ * multi_sqes: record the user_data/index of all the multishot sqes
+ * cnt: how many entries there are in multi_sqes
+ * we can leverage multi_sqes array for cancellation: we randomly pick
+ * up an entry in multi_sqes when form a cancellation sqe.
+ * multi_cap: limitation of number of multishot sqes
+ */
+const unsigned sqe_flags[4] = {0, IOSQE_IO_LINK, IOSQE_IO_DRAIN,
+ IOSQE_IO_LINK | IOSQE_IO_DRAIN};
+int multi_sqes[max_entry], cnt = 0;
+int multi_cap = max_entry / 5;
+
+int write_pipe(int pipe, char *str)
+{
+ int ret;
+ do {
+ errno = 0;
+ ret = write(pipe, str, 3);
+ } while (ret == -1 && errno == EINTR);
+ return ret;
+}
+
+void read_pipe(int pipe)
+{
+ char str[4] = {0};
+ int ret;
+
+ ret = read(pipe, &str, 3);
+ if (ret < 0)
+ perror("read");
+}
+
+int trigger_event(int p[])
+{
+ int ret;
+ if ((ret = write_pipe(p[1], "foo")) != 3) {
+ fprintf(stderr, "bad write return %d\n", ret);
+ return 1;
+ }
+ read_pipe(p[0]);
+ return 0;
+}
+
+void io_uring_sqe_prep(int op, struct io_uring_sqe *sqe, unsigned sqe_flags, int arg)
+{
+ switch (op) {
+ case multi:
+ io_uring_prep_poll_add(sqe, arg, POLLIN);
+ sqe->len |= IORING_POLL_ADD_MULTI;
+ break;
+ case single:
+ io_uring_prep_poll_add(sqe, arg, POLLIN);
+ break;
+ case nop:
+ io_uring_prep_nop(sqe);
+ break;
+ case cancel:
+ io_uring_prep_poll_remove(sqe, arg);
+ break;
+ }
+ sqe->flags = sqe_flags;
+}
+
+__u8 generate_flags(int sqe_op)
+{
+ __u8 flags = 0;
+ /*
+ * drain sqe must be put after multishot sqes cancelled
+ */
+ do {
+ flags = sqe_flags[rand() % 4];
+ } while ((flags & IOSQE_IO_DRAIN) && cnt);
+
+ /*
+ * cancel req cannot have drain or link flag
+ */
+ if (sqe_op == cancel) {
+ flags &= ~(IOSQE_IO_DRAIN | IOSQE_IO_LINK);
+ }
+ /*
+ * avoid below case:
+ * sqe0(multishot, link)->sqe1(nop, link)->sqe2(nop)->sqe3(cancel_sqe0)
+ * sqe3 may execute before sqe0 so that sqe0 isn't cancelled
+ */
+ if (sqe_op == multi)
+ flags &= ~IOSQE_IO_LINK;
+
+ return flags;
+
+}
+
+/*
+ * function to generate opcode of a sqe
+ * several restrictions here:
+ * - cancel all the previous multishot sqes as soon as possible when
+ * we reach high watermark.
+ * - ensure there is some multishot sqe when generating a cancel sqe
+ * - ensure a cancel/multshot sqe is not in a linkchain
+ * - ensure number of multishot sqes doesn't exceed multi_cap
+ * - don't generate multishot sqes after high watermark
+ */
+int generate_opcode(int i, int pre_flags)
+{
+ int sqe_op;
+ int high_watermark = max_entry - max_entry / 5;
+ bool retry0 = false, retry1 = false, retry2 = false;
+
+ if ((i >= high_watermark) && cnt) {
+ sqe_op = cancel;
+ } else {
+ do {
+ sqe_op = rand() % op_last;
+ retry0 = (sqe_op == cancel) && (!cnt || (pre_flags & IOSQE_IO_LINK));
+ retry1 = (sqe_op == multi) && ((multi_cap - 1 < 0) || i >= high_watermark);
+ retry2 = (sqe_op == multi) && (pre_flags & IOSQE_IO_LINK);
+ } while (retry0 || retry1 || retry2);
+ }
+
+ if (sqe_op == multi)
+ multi_cap--;
+ return sqe_op;
+}
+
+static inline void add_multishot_sqe(int index)
+{
+ multi_sqes[cnt++] = index;
+}
+
+int remove_multishot_sqe()
+{
+ int ret;
+
+ int rem_index = rand() % cnt;
+ ret = multi_sqes[rem_index];
+ multi_sqes[rem_index] = multi_sqes[cnt - 1];
+ cnt--;
+
+ return ret;
+}
+
+static int test_generic_drain(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe[max_entry];
+ struct sqe_info si[max_entry];
+ int cqe_data[max_entry << 1], cqe_res[max_entry << 1];
+ int i, j, ret, arg = 0;
+ int pipes[max_entry][2];
+ int pre_flags = 0;
+
+ for (i = 0; i < max_entry; i++) {
+ if (pipe(pipes[i]) != 0) {
+ perror("pipe");
+ return 1;
+ }
+ }
+
+ srand((unsigned)time(NULL));
+ for (i = 0; i < max_entry; i++) {
+ sqe[i] = io_uring_get_sqe(ring);
+ if (!sqe[i]) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+
+ int sqe_op = generate_opcode(i, pre_flags);
+ __u8 flags = generate_flags(sqe_op);
+
+ if (sqe_op == cancel)
+ arg = remove_multishot_sqe();
+ if (sqe_op == multi || sqe_op == single)
+ arg = pipes[i][0];
+ io_uring_sqe_prep(sqe_op, sqe[i], flags, arg);
+ sqe[i]->user_data = i;
+ si[i].op = sqe_op;
+ si[i].flags = flags;
+ pre_flags = flags;
+ if (sqe_op == multi)
+ add_multishot_sqe(i);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("sqe submit failed\n");
+ goto err;
+ } else if (ret < max_entry) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ }
+
+ sleep(1);
+ // TODO: randomize event triggerring order
+ for (i = 0; i < max_entry; i++) {
+ if (si[i].op != multi && si[i].op != single)
+ continue;
+
+ if (trigger_event(pipes[i]))
+ goto err;
+
+ io_uring_get_events(ring);
+ }
+ sleep(1);
+ i = 0;
+ while (!io_uring_peek_cqe(ring, &cqe)) {
+ cqe_data[i] = cqe->user_data;
+ cqe_res[i++] = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ /*
+ * compl_bits is a bit map to record completions.
+ * eg. sqe[0], sqe[1], sqe[2] fully completed
+ * then compl_bits is 000...00111b
+ *
+ */
+ unsigned long long compl_bits = 0;
+ for (j = 0; j < i; j++) {
+ int index = cqe_data[j];
+ if ((si[index].flags & IOSQE_IO_DRAIN) && index) {
+ if ((~compl_bits) & ((1ULL << index) - 1)) {
+ printf("drain failed\n");
+ goto err;
+ }
+ }
+ /*
+ * for multishot sqes, record them only when it is cancelled
+ */
+ if ((si[index].op != multi) || (cqe_res[j] == -ECANCELED))
+ compl_bits |= (1ULL << index);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_simple_drain(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe[2];
+ int i, ret;
+ int pipe1[2], pipe2[2];
+
+ if (pipe(pipe1) != 0 || pipe(pipe2) != 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ sqe[i] = io_uring_get_sqe(ring);
+ if (!sqe[i]) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ }
+
+ io_uring_prep_poll_multishot(sqe[0], pipe1[0], POLLIN);
+ sqe[0]->user_data = 0;
+
+ io_uring_prep_poll_add(sqe[1], pipe2[0], POLLIN);
+ sqe[1]->user_data = 1;
+
+ /* This test relies on multishot poll to trigger events continually.
+ * however with IORING_SETUP_DEFER_TASKRUN this will only happen when
+ * triggered with a get_events. Hence we sprinkle get_events whenever
+ * there might be work to process in order to get the same result
+ */
+ ret = io_uring_submit_and_get_events(ring);
+ if (ret < 0) {
+ printf("sqe submit failed\n");
+ goto err;
+ } else if (ret < 2) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ if (trigger_event(pipe1))
+ goto err;
+ io_uring_get_events(ring);
+ }
+ if (trigger_event(pipe2))
+ goto err;
+ io_uring_get_events(ring);
+
+ for (i = 0; i < 2; i++) {
+ sqe[i] = io_uring_get_sqe(ring);
+ if (!sqe[i]) {
+ printf("get sqe failed\n");
+ goto err;
+ }
+ }
+
+ io_uring_prep_poll_remove(sqe[0], 0);
+ sqe[0]->user_data = 2;
+
+ io_uring_prep_nop(sqe[1]);
+ sqe[1]->flags |= IOSQE_IO_DRAIN;
+ sqe[1]->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ printf("sqe submit failed\n");
+ goto err;
+ } else if (ret < 2) {
+ printf("Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 6; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ printf("wait completion %d\n", ret);
+ goto err;
+ }
+ if ((i == 5) && (cqe->user_data != 3))
+ goto err;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(pipe1[0]);
+ close(pipe1[1]);
+ close(pipe2[0]);
+ close(pipe2[1]);
+ return 0;
+err:
+ return 1;
+}
+
+static int test(bool defer_taskrun)
+{
+ struct io_uring ring;
+ int i, ret;
+ unsigned int flags = 0;
+
+ if (defer_taskrun)
+ flags = IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ ret = io_uring_queue_init(1024, &ring, flags);
+ if (ret) {
+ printf("ring setup failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = test_simple_drain(&ring);
+ if (ret) {
+ fprintf(stderr, "test_simple_drain failed\n");
+ return T_EXIT_FAIL;
+ }
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = test_generic_drain(&ring);
+ if (ret) {
+ fprintf(stderr, "test_generic_drain failed\n");
+ return T_EXIT_FAIL;
+ }
+ }
+
+ 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(false);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(false) failed\n", argv[0]);
+ return ret;
+ }
+
+ if (t_probe_defer_taskrun()) {
+ ret = test(true);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(true) failed\n", argv[0]);
+ return ret;
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/nolibc.c b/contrib/libs/liburing/test/nolibc.c
new file mode 100644
index 00000000000..fd07fa13cda
--- /dev/null
+++ b/contrib/libs/liburing/test/nolibc.c
@@ -0,0 +1,61 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test liburing nolibc functionality.
+ *
+ * Currently, supported architectures are:
+ * 1) x86
+ * 2) x86-64
+ * 3) aarch64
+ *
+ */
+#include "helpers.h"
+
+#if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__)
+
+/*
+ * This arch doesn't support nolibc.
+ */
+int main(void)
+{
+ return T_EXIT_SKIP;
+}
+
+#else /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) */
+
+#ifndef CONFIG_NOLIBC
+#define CONFIG_NOLIBC
+#endif
+
+#include <stdio.h>
+#include <unistd.h>
+#include "../src/lib.h"
+
+static int test_get_page_size(void)
+{
+ long a, b;
+
+ a = sysconf(_SC_PAGESIZE);
+ b = get_page_size();
+ if (a != b) {
+ fprintf(stderr, "get_page_size() fails, %ld != %ld", a, b);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test_get_page_size();
+ if (ret)
+ return T_EXIT_FAIL;
+
+ return T_EXIT_PASS;
+}
+
+#endif /* #if !defined(__x86_64__) && !defined(__i386__) && !defined(__aarch64__) */
diff --git a/contrib/libs/liburing/test/nop-all-sizes.c b/contrib/libs/liburing/test/nop-all-sizes.c
new file mode 100644
index 00000000000..f7bc55b9105
--- /dev/null
+++ b/contrib/libs/liburing/test/nop-all-sizes.c
@@ -0,0 +1,100 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: exercise full filling of SQ and CQ ring
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+#define MAX_ENTRIES 32768
+
+static int fill_nops(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ int filled = 0;
+
+ do {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ break;
+
+ io_uring_prep_nop(sqe);
+ filled++;
+ } while (1);
+
+ return filled;
+}
+
+static int test_nops(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret, nr, total = 0, i;
+
+ nr = fill_nops(ring);
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ fprintf(stderr, "submit %d, wanted %d\n", ret, nr);
+ goto err;
+ }
+ total += ret;
+
+ nr = fill_nops(ring);
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ fprintf(stderr, "submit %d, wanted %d\n", ret, nr);
+ goto err;
+ }
+ total += ret;
+
+ for (i = 0; i < total; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, depth;
+
+ if (argc > 1)
+ return 0;
+
+ depth = 1;
+ while (depth <= MAX_ENTRIES) {
+ ret = io_uring_queue_init(depth, &ring, 0);
+ if (ret) {
+ if (ret == -ENOMEM)
+ break;
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test_nops(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_nop failed\n");
+ return ret;
+ }
+ depth <<= 1;
+ io_uring_queue_exit(&ring);
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/nop.c b/contrib/libs/liburing/test/nop.c
new file mode 100644
index 00000000000..a701b3d8eda
--- /dev/null
+++ b/contrib/libs/liburing/test/nop.c
@@ -0,0 +1,178 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various nop tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "test.h"
+
+static int seq;
+
+static int test_single_nop(struct io_uring *ring, unsigned req_flags)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+ bool cqe32 = (ring->flags & IORING_SETUP_CQE32);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ sqe->user_data = ++seq;
+ sqe->flags |= req_flags;
+
+ 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;
+ }
+ if (!cqe->user_data) {
+ fprintf(stderr, "Unexpected 0 user_data\n");
+ goto err;
+ }
+ if (cqe32) {
+ if (cqe->big_cqe[0] != 0) {
+ fprintf(stderr, "Unexpected extra1\n");
+ goto err;
+
+ }
+ if (cqe->big_cqe[1] != 0) {
+ fprintf(stderr, "Unexpected extra2\n");
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_barrier_nop(struct io_uring *ring, unsigned req_flags)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+ bool cqe32 = (ring->flags & IORING_SETUP_CQE32);
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+ if (i == 4)
+ sqe->flags = IOSQE_IO_DRAIN;
+ sqe->user_data = ++seq;
+ sqe->flags |= req_flags;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ } else if (ret < 8) {
+ fprintf(stderr, "Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 8; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (!cqe->user_data) {
+ fprintf(stderr, "Unexpected 0 user_data\n");
+ goto err;
+ }
+ if (cqe32) {
+ if (cqe->big_cqe[0] != 0) {
+ fprintf(stderr, "Unexpected extra1\n");
+ goto err;
+ }
+ if (cqe->big_cqe[1] != 0) {
+ fprintf(stderr, "Unexpected extra2\n");
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_ring(unsigned flags)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ int ret, i;
+
+ p.flags = flags;
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL)
+ return 0;
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 1000; i++) {
+ unsigned req_flags = (i & 1) ? IOSQE_ASYNC : 0;
+
+ ret = test_single_nop(&ring, req_flags);
+ if (ret) {
+ fprintf(stderr, "test_single_nop failed\n");
+ goto err;
+ }
+
+ ret = test_barrier_nop(&ring, req_flags);
+ if (ret) {
+ fprintf(stderr, "test_barrier_nop failed\n");
+ goto err;
+ }
+ }
+err:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ FOR_ALL_TEST_CONFIGS {
+ ret = test_ring(IORING_GET_TEST_CONFIG_FLAGS());
+ if (ret) {
+ fprintf(stderr, "Normal ring test failed: %s\n",
+ IORING_GET_TEST_CONFIG_DESCRIPTION());
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/nvme.h b/contrib/libs/liburing/test/nvme.h
new file mode 100644
index 00000000000..14dc338b85c
--- /dev/null
+++ b/contrib/libs/liburing/test/nvme.h
@@ -0,0 +1,168 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Helpers for NVMe uring passthrough commands
+ */
+#ifndef LIBURING_NVME_H
+#define LIBURING_NVME_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include <sys/ioctl.h>
+#include <linux/nvme_ioctl.h>
+
+/*
+ * If the uapi headers installed on the system lacks nvme uring command
+ * support, use the local version to prevent compilation issues.
+ */
+#ifndef CONFIG_HAVE_NVME_URING
+struct nvme_uring_cmd {
+ __u8 opcode;
+ __u8 flags;
+ __u16 rsvd1;
+ __u32 nsid;
+ __u32 cdw2;
+ __u32 cdw3;
+ __u64 metadata;
+ __u64 addr;
+ __u32 metadata_len;
+ __u32 data_len;
+ __u32 cdw10;
+ __u32 cdw11;
+ __u32 cdw12;
+ __u32 cdw13;
+ __u32 cdw14;
+ __u32 cdw15;
+ __u32 timeout_ms;
+ __u32 rsvd2;
+};
+
+#define NVME_URING_CMD_IO _IOWR('N', 0x80, struct nvme_uring_cmd)
+#define NVME_URING_CMD_IO_VEC _IOWR('N', 0x81, struct nvme_uring_cmd)
+#endif /* CONFIG_HAVE_NVME_URING */
+
+#define NVME_DEFAULT_IOCTL_TIMEOUT 0
+#define NVME_IDENTIFY_DATA_SIZE 4096
+#define NVME_IDENTIFY_CSI_SHIFT 24
+#define NVME_IDENTIFY_CNS_NS 0
+#define NVME_CSI_NVM 0
+
+enum nvme_admin_opcode {
+ nvme_admin_identify = 0x06,
+};
+
+enum nvme_io_opcode {
+ nvme_cmd_write = 0x01,
+ nvme_cmd_read = 0x02,
+};
+
+int nsid;
+__u32 lba_shift;
+
+struct nvme_lbaf {
+ __le16 ms;
+ __u8 ds;
+ __u8 rp;
+};
+
+struct nvme_id_ns {
+ __le64 nsze;
+ __le64 ncap;
+ __le64 nuse;
+ __u8 nsfeat;
+ __u8 nlbaf;
+ __u8 flbas;
+ __u8 mc;
+ __u8 dpc;
+ __u8 dps;
+ __u8 nmic;
+ __u8 rescap;
+ __u8 fpi;
+ __u8 dlfeat;
+ __le16 nawun;
+ __le16 nawupf;
+ __le16 nacwu;
+ __le16 nabsn;
+ __le16 nabo;
+ __le16 nabspf;
+ __le16 noiob;
+ __u8 nvmcap[16];
+ __le16 npwg;
+ __le16 npwa;
+ __le16 npdg;
+ __le16 npda;
+ __le16 nows;
+ __le16 mssrl;
+ __le32 mcl;
+ __u8 msrc;
+ __u8 rsvd81[11];
+ __le32 anagrpid;
+ __u8 rsvd96[3];
+ __u8 nsattr;
+ __le16 nvmsetid;
+ __le16 endgid;
+ __u8 nguid[16];
+ __u8 eui64[8];
+ struct nvme_lbaf lbaf[16];
+ __u8 rsvd192[192];
+ __u8 vs[3712];
+};
+
+static inline int ilog2(uint32_t i)
+{
+ int log = -1;
+
+ while (i) {
+ i >>= 1;
+ log++;
+ }
+ return log;
+}
+
+int nvme_get_info(const char *file)
+{
+ struct nvme_id_ns ns;
+ int fd, err;
+ __u32 lba_size;
+
+ fd = open(file, O_RDONLY);
+ if (fd < 0) {
+ perror("file open");
+ return -errno;
+ }
+
+ nsid = ioctl(fd, NVME_IOCTL_ID);
+ if (nsid < 0) {
+ close(fd);
+ return -errno;
+ }
+
+ struct nvme_passthru_cmd cmd = {
+ .opcode = nvme_admin_identify,
+ .nsid = nsid,
+ .addr = (__u64)(uintptr_t)&ns,
+ .data_len = NVME_IDENTIFY_DATA_SIZE,
+ .cdw10 = NVME_IDENTIFY_CNS_NS,
+ .cdw11 = NVME_CSI_NVM << NVME_IDENTIFY_CSI_SHIFT,
+ .timeout_ms = NVME_DEFAULT_IOCTL_TIMEOUT,
+ };
+
+ err = ioctl(fd, NVME_IOCTL_ADMIN_CMD, &cmd);
+ if (err) {
+ close(fd);
+ return err;
+ }
+
+ lba_size = 1 << ns.lbaf[(ns.flbas & 0x0f)].ds;
+ lba_shift = ilog2(lba_size);
+
+ close(fd);
+ return 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/libs/liburing/test/open-close.c b/contrib/libs/liburing/test/open-close.c
new file mode 100644
index 00000000000..6b236e463af
--- /dev/null
+++ b/contrib/libs/liburing/test/open-close.c
@@ -0,0 +1,262 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various openat(2) tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int submit_wait(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static inline int try_close(struct io_uring *ring, int fd, int slot)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_close(sqe, fd);
+ __io_uring_set_target_fixed_file(sqe, slot);
+ return submit_wait(ring);
+}
+
+static int test_close_fixed(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ int ret, fds[2];
+ char buf[1];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return -1;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+
+ ret = try_close(&ring, 0, 0);
+ if (ret == -EINVAL) {
+ fprintf(stderr, "close for fixed files is not supported\n");
+ return 0;
+ } else if (ret != -ENXIO) {
+ fprintf(stderr, "no table failed %i\n", ret);
+ return -1;
+ }
+
+ ret = try_close(&ring, 1, 0);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "set fd failed %i\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_register_files(&ring, fds, 2);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+
+ ret = try_close(&ring, 0, 2);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "out of table failed %i\n", ret);
+ return -1;
+ }
+
+ ret = try_close(&ring, 0, 0);
+ if (ret != 0) {
+ fprintf(stderr, "close failed %i\n", ret);
+ return -1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ ret = submit_wait(&ring);
+ if (ret != -EBADF) {
+ fprintf(stderr, "read failed %i\n", ret);
+ return -1;
+ }
+
+ ret = try_close(&ring, 0, 1);
+ if (ret != 0) {
+ fprintf(stderr, "close 2 failed %i\n", ret);
+ return -1;
+ }
+
+ ret = try_close(&ring, 0, 0);
+ if (ret != -EBADF) {
+ fprintf(stderr, "empty slot failed %i\n", ret);
+ return -1;
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_close(struct io_uring *ring, int fd, int is_ring_fd)
+{
+ 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;
+ }
+ io_uring_prep_close(sqe, fd);
+
+ 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) {
+ if (!(is_ring_fd && ret == -EBADF)) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ return ret;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+err:
+ return -1;
+}
+
+static int test_openat(struct io_uring *ring, const char *path, int dfd)
+{
+ 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;
+ }
+ io_uring_prep_openat(sqe, dfd, path, O_RDONLY, 0);
+
+ 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;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *path, *path_rel;
+ int ret, do_unlink;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ if (argc > 1) {
+ path = "/tmp/.open.close";
+ path_rel = argv[1];
+ do_unlink = 0;
+ } else {
+ path = "/tmp/.open.close";
+ path_rel = ".open.close";
+ do_unlink = 1;
+ }
+
+ t_create_file(path, 4096);
+
+ if (do_unlink)
+ t_create_file(path_rel, 4096);
+
+ ret = test_openat(&ring, path, -1);
+ if (ret < 0) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Open not supported, skipping\n");
+ goto done;
+ }
+ fprintf(stderr, "test_openat absolute failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = test_openat(&ring, path_rel, AT_FDCWD);
+ if (ret < 0) {
+ fprintf(stderr, "test_openat relative failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = test_close(&ring, ret, 0);
+ if (ret) {
+ fprintf(stderr, "test_close normal failed\n");
+ goto err;
+ }
+
+ ret = test_close(&ring, ring.ring_fd, 1);
+ if (ret != -EBADF) {
+ fprintf(stderr, "test_close ring_fd failed\n");
+ goto err;
+ }
+
+ ret = test_close_fixed();
+ if (ret) {
+ fprintf(stderr, "test_close_fixed failed\n");
+ goto err;
+ }
+
+done:
+ unlink(path);
+ if (do_unlink)
+ unlink(path_rel);
+ return 0;
+err:
+ unlink(path);
+ if (do_unlink)
+ unlink(path_rel);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/open-direct-link.c b/contrib/libs/liburing/test/open-direct-link.c
new file mode 100644
index 00000000000..ba3b8ea3683
--- /dev/null
+++ b/contrib/libs/liburing/test/open-direct-link.c
@@ -0,0 +1,189 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: link <open file><read from file><close file>
+ *
+ */
+#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 MAX_FILES 8
+#define FNAME ".link.direct"
+
+static int test(struct io_uring *ring, int skip_success, int drain, int async)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buf[4096];
+ int ret, i;
+
+ /* drain and cqe skip are mutually exclusive */
+ if (skip_success && drain)
+ return 1;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat_direct(sqe, AT_FDCWD, FNAME, O_RDONLY, 0, 0);
+ if (!drain)
+ sqe->flags |= IOSQE_IO_LINK;
+ if (skip_success)
+ sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, 0, buf, sizeof(buf), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+ if (drain)
+ sqe->flags |= IOSQE_IO_DRAIN;
+ else
+ sqe->flags |= IOSQE_IO_LINK;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_close_direct(sqe, 0);
+ sqe->user_data = 3;
+ if (skip_success)
+ sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+ if (drain)
+ sqe->flags |= IOSQE_IO_DRAIN;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+
+ ret = io_uring_submit(ring);
+ if (ret != 3) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (skip_success) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->user_data != 2) {
+ fprintf(stderr, "Unexpected cqe %lu/%d\n",
+ (unsigned long) cqe->user_data,
+ cqe->res);
+ goto err;
+ }
+ if (cqe->res != sizeof(buf)) {
+ fprintf(stderr, "bad read %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res) {
+ fprintf(stderr, "bad open %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != sizeof(buf)) {
+ fprintf(stderr, "bad read %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res) {
+ fprintf(stderr, "bad close %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ int ret, files[MAX_FILES];
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+ if (!(p.features & IORING_FEAT_CQE_SKIP))
+ return 0;
+
+ memset(files, -1, sizeof(files));
+ ret = io_uring_register_files(&ring, files, ARRAY_SIZE(files));
+ if (ret) {
+ fprintf(stderr, "Failed registering files\n");
+ return 1;
+ }
+
+ t_create_file(FNAME, 4096);
+
+ ret = test(&ring, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test 0 0 0 failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "test 0 1 0 failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, 0, 0, 1);
+ if (ret) {
+ fprintf(stderr, "test 0 0 1 failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "test 0 1 1 failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test 1 0 0 failed\n");
+ goto err;
+ }
+
+ ret = test(&ring, 1, 0, 1);
+ if (ret) {
+ fprintf(stderr, "test 1 0 1 failed\n");
+ goto err;
+ }
+
+ unlink(FNAME);
+ return 0;
+err:
+ unlink(FNAME);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/open-direct-pick.c b/contrib/libs/liburing/test/open-direct-pick.c
new file mode 100644
index 00000000000..0d04d8127e3
--- /dev/null
+++ b/contrib/libs/liburing/test/open-direct-pick.c
@@ -0,0 +1,181 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various openat(2) tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <limits.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FDS 800
+
+static int no_direct_pick;
+
+static int submit_wait(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static inline int try_close(struct io_uring *ring, int slot)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_close_direct(sqe, slot);
+ return submit_wait(ring);
+}
+
+static int do_opens(struct io_uring *ring, const char *path, int nr,
+ int expect_enfile)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int i, ret;
+
+ for (i = 0; i < nr; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_openat_direct(sqe, -1, path, O_RDONLY, 0, 0);
+ sqe->file_index = UINT_MAX;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ for (i = 0; i < nr; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ ret = cqe->res;
+ if (ret < 0) {
+ if (!expect_enfile || ret != -ENFILE) {
+ printf("open=%d, %d\n", cqe->res, i);
+ goto err;
+ }
+ if (!i && ret == -EINVAL) {
+ no_direct_pick = 1;
+ return 0;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_openat(struct io_uring *ring, const char *path)
+{
+ int ret, i;
+
+ /* open all */
+ ret = do_opens(ring, path, FDS, 0);
+ if (ret)
+ goto err;
+ if (no_direct_pick)
+ return 0;
+
+ /* now close 100 randomly */
+ for (i = 0; i < 100; i++) {
+ do {
+ int slot = rand() % FDS;
+ ret = try_close(ring, slot);
+ if (ret == -EBADF)
+ continue;
+ break;
+ } while (1);
+ }
+
+ /* opening 100 should work, we closed 100 */
+ ret = do_opens(ring, path, 100, 0);
+ if (ret)
+ goto err;
+
+ /* we should be full now, expect -ENFILE */
+ ret = do_opens(ring, path, 1, 1);
+ if (ret)
+ goto err;
+
+ return ret;
+err:
+ fprintf(stderr,"%s: err=%d\n", __FUNCTION__, ret);
+ return -1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *path;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = io_uring_register_files_sparse(&ring, FDS);
+ if (ret ) {
+ if (ret != -EINVAL) {
+ fprintf(stderr, "Sparse file registration failed\n");
+ return 1;
+ }
+ /* skip, kernel doesn't support sparse file array */
+ return 0;
+ }
+
+ path = "/tmp/.open.direct.pick";
+ t_create_file(path, 4096);
+
+ ret = test_openat(&ring, path);
+ if (ret < 0) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Open not supported, skipping\n");
+ goto done;
+ }
+ fprintf(stderr, "test_openat absolute failed: %d\n", ret);
+ goto err;
+ }
+
+done:
+ unlink(path);
+ return 0;
+err:
+ unlink(path);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/openat2.c b/contrib/libs/liburing/test/openat2.c
new file mode 100644
index 00000000000..a3ad70e1439
--- /dev/null
+++ b/contrib/libs/liburing/test/openat2.c
@@ -0,0 +1,309 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various openat(2) tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/uio.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int test_openat2(struct io_uring *ring, const char *path, int dfd,
+ bool direct, int fixed_index)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct open_how how;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return -1;
+ }
+ memset(&how, 0, sizeof(how));
+ how.flags = O_RDWR;
+
+ if (!direct)
+ io_uring_prep_openat2(sqe, dfd, path, &how);
+ else
+ io_uring_prep_openat2_direct(sqe, dfd, path, &how, fixed_index);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return -1;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+
+ if (direct && ret > 0) {
+ close(ret);
+ return -EINVAL;
+ }
+ return ret;
+}
+
+static int test_open_fixed(const char *path, int dfd)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ const char pattern = 0xac;
+ char buffer[] = { 0, 0 };
+ int i, ret, fd = -1;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return -1;
+ }
+ ret = io_uring_register_files(&ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ ret = test_openat2(&ring, path, dfd, true, 0);
+ if (ret == -EINVAL) {
+ printf("fixed open isn't supported\n");
+ return 1;
+ } else if (ret) {
+ fprintf(stderr, "direct open failed %d\n", ret);
+ return -1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_write(sqe, 0, &pattern, 1, 0);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_FIXED_FILE | IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, 0, buffer, 1, 0);
+ sqe->user_data = 2;
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: got %d, wanted 2\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return -1;
+ }
+ if (cqe->res != 1) {
+ fprintf(stderr, "unexpectetd ret %d\n", cqe->res);
+ return -1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ if (memcmp(&pattern, buffer, 1) != 0) {
+ fprintf(stderr, "buf validation failed\n");
+ return -1;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_open_fixed_fail(const char *path, int dfd)
+{
+ struct io_uring ring;
+ int ret, fd = -1;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return -1;
+ }
+
+ ret = test_openat2(&ring, path, dfd, true, 0);
+ if (ret != -ENXIO) {
+ fprintf(stderr, "install into not existing table, %i\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_register_files(&ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ ret = test_openat2(&ring, path, dfd, true, 1);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "install out of bounds, %i\n", ret);
+ return -1;
+ }
+
+ ret = test_openat2(&ring, path, dfd, true, (1u << 16));
+ if (ret != -EINVAL) {
+ fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
+ return -1;
+ }
+
+ ret = test_openat2(&ring, path, dfd, true, (1u << 16) + 1);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "install out of bounds or u16 overflow, %i\n", ret);
+ return -1;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_direct_reinstall(const char *path, int dfd)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ char buf[1] = { 0xfa };
+ struct io_uring ring;
+ int ret, pipe_fds[2];
+ ssize_t ret2;
+
+ if (pipe2(pipe_fds, O_NONBLOCK)) {
+ fprintf(stderr, "pipe() failed\n");
+ return -1;
+ }
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return -1;
+ }
+ ret = io_uring_register_files(&ring, pipe_fds, 2);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ return -1;
+ }
+
+ /* reinstall into the second slot */
+ ret = test_openat2(&ring, path, dfd, true, 1);
+ if (ret != 0) {
+ fprintf(stderr, "reinstall failed, %i\n", ret);
+ return -1;
+ }
+
+ /* verify it's reinstalled, first write into the slot... */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_write(sqe, 1, buf, sizeof(buf), 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return -1;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return ret;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(&ring, cqe);
+ if (ret != 1) {
+ fprintf(stderr, "invalid write %i\n", ret);
+ return -1;
+ }
+
+ /* ... and make sure nothing has been written to the pipe */
+ ret2 = read(pipe_fds[0], buf, 1);
+ if (ret2 != 0 && !(ret2 < 0 && errno == EAGAIN)) {
+ fprintf(stderr, "invalid pipe read, %d %d\n", errno, (int)ret2);
+ return -1;
+ }
+
+ close(pipe_fds[0]);
+ close(pipe_fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *path, *path_rel;
+ int ret, do_unlink;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ if (argc > 1) {
+ path = "/tmp/.open.at2";
+ path_rel = argv[1];
+ do_unlink = 0;
+ } else {
+ path = "/tmp/.open.at2";
+ path_rel = ".open.at2";
+ do_unlink = 1;
+ }
+
+ t_create_file(path, 4096);
+
+ if (do_unlink)
+ t_create_file(path_rel, 4096);
+
+ ret = test_openat2(&ring, path, -1, false, 0);
+ if (ret < 0) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "openat2 not supported, skipping\n");
+ goto done;
+ }
+ fprintf(stderr, "test_openat2 absolute failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = test_openat2(&ring, path_rel, AT_FDCWD, false, 0);
+ if (ret < 0) {
+ fprintf(stderr, "test_openat2 relative failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = test_open_fixed(path, -1);
+ if (ret > 0)
+ goto done;
+ if (ret) {
+ fprintf(stderr, "test_open_fixed failed\n");
+ goto err;
+ }
+ ret = test_open_fixed_fail(path, -1);
+ if (ret) {
+ fprintf(stderr, "test_open_fixed_fail failed\n");
+ goto err;
+ }
+
+ ret = test_direct_reinstall(path, -1);
+ if (ret) {
+ fprintf(stderr, "test_direct_reinstall failed\n");
+ goto err;
+ }
+
+done:
+ unlink(path);
+ if (do_unlink)
+ unlink(path_rel);
+ return 0;
+err:
+ unlink(path);
+ if (do_unlink)
+ unlink(path_rel);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/personality.c b/contrib/libs/liburing/test/personality.c
new file mode 100644
index 00000000000..577e356833f
--- /dev/null
+++ b/contrib/libs/liburing/test/personality.c
@@ -0,0 +1,205 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test if personalities work
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+#define FNAME "/tmp/.tmp.access"
+#define USE_UID 1000
+
+static int no_personality;
+
+static int open_file(struct io_uring *ring, int cred_id, int with_link)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i, to_submit = 1;
+
+ if (with_link) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+ to_submit++;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_openat(sqe, -1, FNAME, O_RDONLY, 0);
+ sqe->user_data = 2;
+
+ if (cred_id != -1)
+ sqe->personality = cred_id;
+
+ ret = io_uring_submit(ring);
+ if (ret != to_submit) {
+ fprintf(stderr, "submit got: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < to_submit; i++) {
+ 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);
+ }
+err:
+ return ret;
+}
+
+static int test_personality(struct io_uring *ring)
+{
+ int ret, cred_id;
+
+ ret = io_uring_register_personality(ring);
+ if (ret < 0) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Personalities not supported, skipping\n");
+ no_personality = 1;
+ goto out;
+ }
+ fprintf(stderr, "register_personality: %d\n", ret);
+ goto err;
+ }
+ cred_id = ret;
+
+ /* create file only owner can open */
+ ret = open(FNAME, O_RDONLY | O_CREAT, 0600);
+ if (ret < 0) {
+ perror("open");
+ goto err;
+ }
+ close(ret);
+
+ /* verify we can open it */
+ ret = open_file(ring, -1, 0);
+ if (ret < 0) {
+ fprintf(stderr, "current open got: %d\n", ret);
+ goto err;
+ }
+
+ if (seteuid(USE_UID) < 0) {
+ fprintf(stdout, "Can't switch to UID %u, skipping\n", USE_UID);
+ goto out;
+ }
+
+ /* verify we can't open it with current credentials */
+ ret = open_file(ring, -1, 0);
+ if (ret != -EACCES) {
+ fprintf(stderr, "open got: %d\n", ret);
+ goto err;
+ }
+
+ /* verify we can open with registered credentials */
+ ret = open_file(ring, cred_id, 0);
+ if (ret < 0) {
+ fprintf(stderr, "credential open: %d\n", ret);
+ goto err;
+ }
+ close(ret);
+
+ /* verify we can open with registered credentials and as a link */
+ ret = open_file(ring, cred_id, 1);
+ if (ret < 0) {
+ fprintf(stderr, "credential open: %d\n", ret);
+ goto err;
+ }
+
+ if (seteuid(0))
+ perror("seteuid");
+
+ ret = io_uring_unregister_personality(ring, cred_id);
+ if (ret) {
+ fprintf(stderr, "register_personality: %d\n", ret);
+ goto err;
+ }
+
+out:
+ unlink(FNAME);
+ return 0;
+err:
+ unlink(FNAME);
+ return 1;
+}
+
+static int test_invalid_personality(struct io_uring *ring)
+{
+ int ret;
+
+ ret = open_file(ring, 2, 0);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "invalid personality got: %d\n", ret);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_invalid_unregister(struct io_uring *ring)
+{
+ int ret;
+
+ ret = io_uring_unregister_personality(ring, 2);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "invalid personality unregister got: %d\n", ret);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ if (geteuid()) {
+ fprintf(stderr, "Not root, skipping\n");
+ return 0;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test_personality(&ring);
+ if (ret) {
+ fprintf(stderr, "test_personality failed\n");
+ return ret;
+ }
+ if (no_personality)
+ return 0;
+
+ ret = test_invalid_personality(&ring);
+ if (ret) {
+ fprintf(stderr, "test_invalid_personality failed\n");
+ return ret;
+ }
+
+ ret = test_invalid_unregister(&ring);
+ if (ret) {
+ fprintf(stderr, "test_invalid_unregister failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/pipe-eof.c b/contrib/libs/liburing/test/pipe-eof.c
new file mode 100644
index 00000000000..a917ae00a7b
--- /dev/null
+++ b/contrib/libs/liburing/test/pipe-eof.c
@@ -0,0 +1,84 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+
+/*
+ * Test that closed pipe reads returns 0, instead of waiting for more
+ * data.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include "liburing.h"
+
+#define BUFSIZE 512
+
+struct data {
+ char *str;
+ int fds[2];
+};
+
+static void *t(void *data)
+{
+ struct data *d = data;
+ int ret;
+
+ strcpy(d->str, "This is a test string");
+ ret = write(d->fds[1], d->str, strlen(d->str));
+ close(d->fds[1]);
+ if (ret < 0)
+ perror("write");
+
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ static char buf[BUFSIZE];
+ struct io_uring ring;
+ pthread_t thread;
+ struct data d;
+ int ret;
+
+ if (pipe(d.fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+ d.str = buf;
+
+ io_uring_queue_init(8, &ring, 0);
+
+ pthread_create(&thread, NULL, t, &d);
+
+ while (1) {
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, d.fds[0], buf, BUFSIZE, 0);
+ 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: %d\n", ret);
+ return 1;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "Read error: %s\n", strerror(-cqe->res));
+ return 1;
+ }
+ if (cqe->res == 0)
+ break;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ pthread_join(thread, NULL);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/pipe-reuse.c b/contrib/libs/liburing/test/pipe-reuse.c
new file mode 100644
index 00000000000..7005e597815
--- /dev/null
+++ b/contrib/libs/liburing/test/pipe-reuse.c
@@ -0,0 +1,106 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check split up read is handled correctly
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <string.h>
+#include "liburing.h"
+
+#define BUFSIZE 16384
+#define BUFFERS 16
+
+int main(int argc, char *argv[])
+{
+ char buf[BUFSIZE], wbuf[BUFSIZE];
+ struct iovec iov[BUFFERS];
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, fds[2];
+ void *ptr;
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ptr = buf;
+ for (i = 0; i < BUFFERS; i++) {
+ unsigned bsize = BUFSIZE / BUFFERS;
+
+ iov[i].iov_base = ptr;
+ iov[i].iov_len = bsize;
+ ptr += bsize;
+ }
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d\n", ret);
+ return 1;
+ }
+ if (!(p.features & IORING_FEAT_SUBMIT_STABLE)) {
+ fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
+ return 0;
+ }
+
+ ptr = wbuf;
+ memset(ptr, 0x11, sizeof(wbuf) / 2);
+ ptr += sizeof(wbuf) / 2;
+ memset(ptr, 0x22, sizeof(wbuf) / 2);
+
+ ret = write(fds[1], wbuf, sizeof(wbuf) / 2);
+ if (ret != sizeof(wbuf) / 2) {
+ fprintf(stderr, "Bad write\n");
+ ret = 1;
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, fds[0], iov, BUFFERS, 0);
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ iov[i].iov_base = NULL;
+ iov[i].iov_len = 1000000;
+ }
+
+ ret = write(fds[1], ptr, sizeof(wbuf) / 2);
+ if (ret != sizeof(wbuf) / 2) {
+ fprintf(stderr, "Bad write\n");
+ ret = 1;
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return 1;
+ }
+
+ if (cqe->res < 0) {
+ fprintf(stderr, "Read error: %s\n", strerror(-cqe->res));
+ return 1;
+ } else if (cqe->res != sizeof(wbuf)) {
+ /* ignore short read, not a failure */
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ ret = memcmp(wbuf, buf, sizeof(wbuf));
+ if (ret)
+ fprintf(stderr, "Read data mismatch\n");
+
+err:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/poll-cancel-all.c b/contrib/libs/liburing/test/poll-cancel-all.c
new file mode 100644
index 00000000000..83c48abeea9
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-cancel-all.c
@@ -0,0 +1,473 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test IORING_ASYNC_CANCEL_{ALL,FD}
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+
+#include "liburing.h"
+
+static int no_cancel_flags;
+
+static int test1(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i;
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 9; i++) {
+ if (no_cancel_flags)
+ break;
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ return 1;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res == -EINVAL) {
+ no_cancel_flags = 1;
+ break;
+ }
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ case 1 ... 8:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test2(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, fd2[2];
+
+ if (pipe(fd2) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (!(i & 1))
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ else
+ io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
+ sqe->user_data = i & 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 4) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 0:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ usleep(1000);
+
+ /*
+ * Should not have any pending CQEs now
+ */
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (!ret) {
+ fprintf(stderr, "Unexpected extra cancel cqe\n");
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_FD;
+ sqe->fd = fd2[0];
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 5; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 4) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fd2[0]);
+ close(fd2[1]);
+ return 0;
+err:
+ close(fd2[0]);
+ close(fd2[1]);
+ return 1;
+}
+
+static int test3(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, i, fd2[2];
+
+ if (pipe(fd2) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ if (!(i & 1)) {
+ io_uring_prep_poll_add(sqe, fd[0], POLLIN);
+ sqe->flags |= IOSQE_ASYNC;
+ } else
+ io_uring_prep_poll_add(sqe, fd2[0], POLLIN);
+ sqe->user_data = i & 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ usleep(10000);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
+ sqe->fd = 0;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 9; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 0:
+ case 1:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ close(fd2[0]);
+ close(fd2[1]);
+ return 0;
+err:
+ close(fd2[0]);
+ close(fd2[1]);
+ return 1;
+}
+
+static int test4(struct io_uring *ring, int *fd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char buffer[32];
+ int ret, i;
+
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ io_uring_prep_read(sqe, fd[0], &buffer, sizeof(buffer), 0);
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret < 8) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ usleep(10000);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+
+ /*
+ * Mark CANCEL_ALL to cancel all matching the key, and use
+ * CANCEL_FD to cancel requests matching the specified fd.
+ * This should cancel all the pending poll requests on the pipe
+ * input.
+ */
+ io_uring_prep_cancel(sqe, 0, IORING_ASYNC_CANCEL_ALL);
+ sqe->cancel_flags |= IORING_ASYNC_CANCEL_ANY;
+ sqe->fd = 0;
+ sqe->user_data = 100;
+
+ ret = io_uring_submit(ring);
+ if (ret < 1) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 9; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait=%d\n", ret);
+ goto err;
+ }
+ switch (cqe->user_data) {
+ case 100:
+ if (cqe->res != 8) {
+ fprintf(stderr, "canceled %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ case 1 ... 8:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "poll res %d\n", cqe->res);
+ goto err;
+ }
+ break;
+ default:
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, fd[2];
+
+ if (argc > 1)
+ return 0;
+
+ if (pipe(fd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test1(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test1 failed\n");
+ return ret;
+ }
+ if (no_cancel_flags)
+ return 0;
+
+ ret = test2(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test2 failed\n");
+ return ret;
+ }
+
+ ret = test3(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test3 failed\n");
+ return ret;
+ }
+
+ ret = test4(&ring, fd);
+ if (ret) {
+ fprintf(stderr, "test4 failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-cancel-ton.c b/contrib/libs/liburing/test/poll-cancel-ton.c
new file mode 100644
index 00000000000..5c77ce2b810
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-cancel-ton.c
@@ -0,0 +1,136 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test massive amounts of poll with cancel
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "liburing.h"
+
+#define POLL_COUNT 30000
+
+static void *sqe_index[POLL_COUNT];
+
+static int reap_events(struct io_uring *ring, unsigned nr_events, int nowait)
+{
+ struct io_uring_cqe *cqe;
+ int i, ret = 0;
+
+ for (i = 0; i < nr_events; i++) {
+ if (!i && !nowait)
+ ret = io_uring_wait_cqe(ring, &cqe);
+ else
+ ret = io_uring_peek_cqe(ring, &cqe);
+ if (ret) {
+ if (ret != -EAGAIN)
+ fprintf(stderr, "cqe peek failed: %d\n", ret);
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return i ? i : ret;
+}
+
+static int del_polls(struct io_uring *ring, int fd, int nr)
+{
+ int batch, i, ret;
+ struct io_uring_sqe *sqe;
+
+ while (nr) {
+ batch = 1024;
+ if (batch > nr)
+ batch = nr;
+
+ for (i = 0; i < batch; i++) {
+ void *data;
+
+ sqe = io_uring_get_sqe(ring);
+ data = sqe_index[lrand48() % nr];
+ io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)data);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != batch) {
+ fprintf(stderr, "%s: failed submit, %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ nr -= batch;
+ ret = reap_events(ring, 2 * batch, 0);
+ }
+ return 0;
+}
+
+static int add_polls(struct io_uring *ring, int fd, int nr)
+{
+ int batch, i, count, ret;
+ struct io_uring_sqe *sqe;
+
+ count = 0;
+ while (nr) {
+ batch = 1024;
+ if (batch > nr)
+ batch = nr;
+
+ for (i = 0; i < batch; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd, POLLIN);
+ sqe_index[count++] = sqe;
+ sqe->user_data = (unsigned long) sqe;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != batch) {
+ fprintf(stderr, "%s: failed submit, %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ nr -= batch;
+ reap_events(ring, batch, 1);
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ int pipe1[2];
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ p.flags = IORING_SETUP_CQSIZE;
+ p.cq_entries = 16384;
+ ret = io_uring_queue_init_params(1024, &ring, &p);
+ if (ret) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "No CQSIZE, trying without\n");
+ ret = io_uring_queue_init(1024, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+ }
+ }
+
+ add_polls(&ring, pipe1[0], 30000);
+ del_polls(&ring, pipe1[0], 30000);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-cancel.c b/contrib/libs/liburing/test/poll-cancel.c
new file mode 100644
index 00000000000..2d953b5676c
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-cancel.c
@@ -0,0 +1,229 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring poll cancel handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <signal.h>
+
+#include "liburing.h"
+
+struct poll_data {
+ unsigned is_poll;
+ unsigned is_cancel;
+};
+
+static void sig_alrm(int sig)
+{
+ fprintf(stderr, "Timed out!\n");
+ exit(1);
+}
+
+static int test_poll_cancel(void)
+{
+ struct io_uring ring;
+ int pipe1[2];
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct poll_data *pd, pds[2];
+ struct sigaction act;
+ int ret;
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_alrm;
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(1);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, pipe1[0], POLLIN);
+
+ pds[0].is_poll = 1;
+ pds[0].is_cancel = 0;
+ io_uring_sqe_set_data(sqe, &pds[0]);
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed\n");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ pds[1].is_poll = 0;
+ pds[1].is_cancel = 1;
+ io_uring_prep_poll_remove(sqe, (__u64)(uintptr_t)&pds[0]);
+ io_uring_sqe_set_data(sqe, &pds[1]);
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait cqe failed: %d\n", ret);
+ return 1;
+ }
+
+ pd = io_uring_cqe_get_data(cqe);
+ if (pd->is_poll && cqe->res != -ECANCELED) {
+ fprintf(stderr ,"sqe (add=%d/remove=%d) failed with %ld\n",
+ pd->is_poll, pd->is_cancel,
+ (long) cqe->res);
+ return 1;
+ } else if (pd->is_cancel && cqe->res) {
+ fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
+ pd->is_poll, pd->is_cancel,
+ (long) cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return 1;
+ }
+
+ pd = io_uring_cqe_get_data(cqe);
+ if (pd->is_poll && cqe->res != -ECANCELED) {
+ fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
+ pd->is_poll, pd->is_cancel,
+ (long) cqe->res);
+ return 1;
+ } else if (pd->is_cancel && cqe->res) {
+ fprintf(stderr, "sqe (add=%d/remove=%d) failed with %ld\n",
+ pd->is_poll, pd->is_cancel,
+ (long) cqe->res);
+ return 1;
+ }
+
+ close(pipe1[0]);
+ close(pipe1[1]);
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+
+static int __test_poll_cancel_with_timeouts(void)
+{
+ struct __kernel_timespec ts = { .tv_sec = 10, };
+ struct io_uring ring, ring2;
+ struct io_uring_sqe *sqe;
+ int ret, off_nr = 1000;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_queue_init(1, &ring2, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ /* test timeout-offset triggering path during cancellation */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts, off_nr, 0);
+
+ /* poll ring2 to trigger cancellation on exit() */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, ring2.ring_fd, POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "sqe submit failed\n");
+ return 1;
+ }
+
+ /* just drop all rings/etc. intact, exit() will clean them up */
+ return 0;
+}
+
+static int test_poll_cancel_with_timeouts(void)
+{
+ int ret;
+ pid_t p;
+
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork() failed\n");
+ return 1;
+ }
+
+ if (p == 0) {
+ ret = __test_poll_cancel_with_timeouts();
+ exit(ret);
+ } else {
+ int wstatus;
+
+ if (waitpid(p, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ return 1;
+ }
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ return 1;
+ }
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_poll_cancel();
+ if (ret) {
+ fprintf(stderr, "test_poll_cancel failed\n");
+ return -1;
+ }
+
+ ret = test_poll_cancel_with_timeouts();
+ if (ret) {
+ fprintf(stderr, "test_poll_cancel_with_timeouts failed\n");
+ return -1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-link.c b/contrib/libs/liburing/test/poll-link.c
new file mode 100644
index 00000000000..27346c65ae9
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-link.c
@@ -0,0 +1,222 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+#include <pthread.h>
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <poll.h>
+#include <arpa/inet.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
+
+static int recv_thread_ready = 0;
+static int recv_thread_done = 0;
+
+static void signal_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+ *var = 1;
+ pthread_cond_signal(&cond);
+ pthread_mutex_unlock(&mutex);
+}
+
+static void wait_for_var(int *var)
+{
+ pthread_mutex_lock(&mutex);
+
+ while (!*var)
+ pthread_cond_wait(&cond, &mutex);
+
+ pthread_mutex_unlock(&mutex);
+}
+
+struct data {
+ unsigned expected[2];
+ unsigned is_mask[2];
+ unsigned long timeout;
+ unsigned short port;
+ unsigned int addr;
+ int stop;
+};
+
+static void *send_thread(void *arg)
+{
+ struct sockaddr_in addr;
+ struct data *data = arg;
+ int s0;
+
+ wait_for_var(&recv_thread_ready);
+
+ s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = data->port;
+ addr.sin_addr.s_addr = data->addr;
+
+ if (connect(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1)
+ wait_for_var(&recv_thread_done);
+
+ close(s0);
+ return 0;
+}
+
+void *recv_thread(void *arg)
+{
+ struct sockaddr_in addr = { };
+ struct data *data = arg;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int i, ret;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ assert(ret == 0);
+
+ int s0 = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ assert(s0 != -1);
+
+ int32_t val = 1;
+ ret = setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ addr.sin_family = AF_INET;
+ data->addr = inet_addr("127.0.0.1");
+ addr.sin_addr.s_addr = data->addr;
+
+ if (t_bind_ephemeral_port(s0, &addr)) {
+ perror("bind");
+ data->stop = 1;
+ signal_var(&recv_thread_ready);
+ goto err;
+ }
+ data->port = addr.sin_port;
+
+ ret = listen(s0, 128);
+ assert(ret != -1);
+
+ signal_var(&recv_thread_ready);
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ io_uring_prep_poll_add(sqe, s0, POLLIN | POLLHUP | POLLERR);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ assert(sqe != NULL);
+
+ struct __kernel_timespec ts;
+ ts.tv_sec = data->timeout / 1000000000;
+ ts.tv_nsec = data->timeout % 1000000000;
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ assert(ret == 2);
+
+ for (i = 0; i < 2; i++) {
+ struct io_uring_cqe *cqe;
+ int idx;
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "wait cqe failed\n");
+ goto err;
+ }
+ idx = cqe->user_data - 1;
+ if (data->is_mask[idx] && !(data->expected[idx] & cqe->res)) {
+ fprintf(stderr, "cqe %" PRIu64 " got %x, wanted mask %x\n",
+ (uint64_t) cqe->user_data, cqe->res,
+ data->expected[idx]);
+ goto err;
+ } else if (!data->is_mask[idx] && cqe->res != data->expected[idx]) {
+ fprintf(stderr, "cqe %" PRIu64 " got %d, wanted %d\n",
+ (uint64_t) cqe->user_data, cqe->res,
+ data->expected[idx]);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ signal_var(&recv_thread_done);
+ close(s0);
+ io_uring_queue_exit(&ring);
+ return NULL;
+err:
+ signal_var(&recv_thread_done);
+ close(s0);
+ io_uring_queue_exit(&ring);
+ return (void *) 1;
+}
+
+static int test_poll_timeout(int do_connect, unsigned long timeout)
+{
+ pthread_t t1, t2;
+ struct data d;
+ void *tret;
+ int ret = 0;
+
+ recv_thread_ready = 0;
+ recv_thread_done = 0;
+
+ memset(&d, 0, sizeof(d));
+ d.timeout = timeout;
+ if (!do_connect) {
+ d.expected[0] = -ECANCELED;
+ d.expected[1] = -ETIME;
+ } else {
+ d.expected[0] = POLLIN;
+ d.is_mask[0] = 1;
+ d.expected[1] = -ECANCELED;
+ }
+
+ pthread_create(&t1, NULL, recv_thread, &d);
+
+ if (do_connect)
+ pthread_create(&t2, NULL, send_thread, &d);
+
+ pthread_join(t1, &tret);
+ if (tret)
+ ret++;
+
+ if (do_connect) {
+ pthread_join(t2, &tret);
+ if (tret)
+ ret++;
+ }
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return 0;
+
+ srand(getpid());
+
+ if (test_poll_timeout(0, 200000000)) {
+ fprintf(stderr, "poll timeout 0 failed\n");
+ return 1;
+ }
+
+ if (test_poll_timeout(1, 1000000000)) {
+ fprintf(stderr, "poll timeout 1 failed\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-many.c b/contrib/libs/liburing/test/poll-many.c
new file mode 100644
index 00000000000..8f6a89efdd9
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-many.c
@@ -0,0 +1,209 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test many files being polled for
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+#define NFILES 5000
+#define BATCH 500
+#define NLOOPS 1000
+
+#define RING_SIZE 512
+
+struct p {
+ int fd[2];
+ int triggered;
+};
+
+static struct p p[NFILES];
+
+static int arm_poll(struct io_uring *ring, int off)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed getting sqe\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, p[off].fd[0], POLLIN);
+ sqe->user_data = off;
+ return 0;
+}
+
+static int reap_polls(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int i, ret, off;
+ char c;
+
+ for (i = 0; i < BATCH; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ return ret;
+ }
+ off = cqe->user_data;
+ p[off].triggered = 0;
+ ret = read(p[off].fd[0], &c, 1);
+ if (ret != 1) {
+ fprintf(stderr, "read got %d/%d\n", ret, errno);
+ break;
+ }
+ if (arm_poll(ring, off))
+ break;
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (i != BATCH) {
+ fprintf(stderr, "gave up at %d\n", i);
+ return 1;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BATCH) {
+ fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int trigger_polls(void)
+{
+ char c = 89;
+ int i, ret;
+
+ for (i = 0; i < BATCH; i++) {
+ int off;
+
+ do {
+ off = rand() % NFILES;
+ if (!p[off].triggered)
+ break;
+ } while (1);
+
+ p[off].triggered = 1;
+ ret = write(p[off].fd[1], &c, 1);
+ if (ret != 1) {
+ fprintf(stderr, "write got %d/%d\n", ret, errno);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int arm_polls(struct io_uring *ring)
+{
+ int ret, to_arm = NFILES, i, off;
+
+ off = 0;
+ while (to_arm) {
+ int this_arm;
+
+ this_arm = to_arm;
+ if (this_arm > RING_SIZE)
+ this_arm = RING_SIZE;
+
+ for (i = 0; i < this_arm; i++) {
+ if (arm_poll(ring, off)) {
+ fprintf(stderr, "arm failed at %d\n", off);
+ return 1;
+ }
+ off++;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != this_arm) {
+ fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
+ return 1;
+ }
+ to_arm -= this_arm;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_params params = { };
+ struct rlimit rlim;
+ int i, ret;
+
+ if (argc > 1)
+ return 0;
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ perror("getrlimit");
+ goto err_noring;
+ }
+
+ if (rlim.rlim_cur < (2 * NFILES + 5)) {
+ rlim.rlim_cur = (2 * NFILES + 5);
+ rlim.rlim_max = rlim.rlim_cur;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ if (errno == EPERM)
+ goto err_nofail;
+ perror("setrlimit");
+ goto err_noring;
+ }
+ }
+
+ for (i = 0; i < NFILES; i++) {
+ if (pipe(p[i].fd) < 0) {
+ perror("pipe");
+ goto err_noring;
+ }
+ }
+
+ params.flags = IORING_SETUP_CQSIZE;
+ params.cq_entries = 4096;
+ ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
+ if (ret) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "No CQSIZE, trying without\n");
+ ret = io_uring_queue_init(RING_SIZE, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+ }
+ }
+
+ if (arm_polls(&ring))
+ goto err;
+
+ for (i = 0; i < NLOOPS; i++) {
+ trigger_polls();
+ ret = reap_polls(&ring);
+ if (ret)
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+err_noring:
+ fprintf(stderr, "poll-many failed\n");
+ return 1;
+err_nofail:
+ fprintf(stderr, "poll-many: not enough files available (and not root), "
+ "skipped\n");
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-mshot-overflow.c b/contrib/libs/liburing/test/poll-mshot-overflow.c
new file mode 100644
index 00000000000..b31633d507d
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-mshot-overflow.c
@@ -0,0 +1,163 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int check_final_cqe(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int count = 0;
+ bool signalled_no_more = false;
+
+ while (!io_uring_peek_cqe(ring, &cqe)) {
+ if (cqe->user_data == 1) {
+ count++;
+ if (signalled_no_more) {
+ fprintf(stderr, "signalled no more!\n");
+ return T_EXIT_FAIL;
+ }
+ if (!(cqe->flags & IORING_CQE_F_MORE))
+ signalled_no_more = true;
+ } else if (cqe->user_data != 3) {
+ fprintf(stderr, "%d: got unexpected %d\n", count, (int)cqe->user_data);
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (!count) {
+ fprintf(stderr, "no cqe\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
+
+static int test(bool defer_taskrun)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int pipe1[2];
+ int ret, i;
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return T_EXIT_FAIL;
+ }
+
+ struct io_uring_params params = {
+ /* cheat using SINGLE_ISSUER existence to know if this behaviour
+ * is updated
+ */
+ .flags = IORING_SETUP_CQSIZE | IORING_SETUP_SINGLE_ISSUER,
+ .cq_entries = 2
+ };
+
+ if (defer_taskrun)
+ params.flags |= IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ ret = io_uring_queue_init_params(2, &ring, &params);
+ if (ret)
+ return T_EXIT_SKIP;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_prep_poll_multishot(sqe, pipe1[0], POLLIN);
+ io_uring_sqe_set_data64(sqe, 1);
+
+ if (io_uring_cq_ready(&ring)) {
+ fprintf(stderr, "unexpected cqe\n");
+ return T_EXIT_FAIL;
+ }
+
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data64(sqe, 2);
+ io_uring_submit(&ring);
+ }
+
+ do {
+ errno = 0;
+ ret = write(pipe1[1], "foo", 3);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret <= 0) {
+ fprintf(stderr, "write failed: %d\n", errno);
+ return T_EXIT_FAIL;
+ }
+
+ /* should have 2 cqe + 1 overflow now, so take out two cqes */
+ for (i = 0; i < 2; i++) {
+ if (io_uring_peek_cqe(&ring, &cqe)) {
+ fprintf(stderr, "unexpectedly no cqe\n");
+ return T_EXIT_FAIL;
+ }
+ if (cqe->user_data != 2) {
+ fprintf(stderr, "unexpected user_data\n");
+ return T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ /* make sure everything is processed */
+ io_uring_get_events(&ring);
+
+ /* now remove the poll */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_remove(sqe, 1);
+ io_uring_sqe_set_data64(sqe, 3);
+ ret = io_uring_submit(&ring);
+
+ if (ret != 1) {
+ fprintf(stderr, "bad poll remove\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = check_final_cqe(&ring);
+
+ close(pipe1[0]);
+ close(pipe1[1]);
+ io_uring_queue_exit(&ring);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test(false);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(false) failed\n", argv[0]);
+ return ret;
+ }
+
+ if (t_probe_defer_taskrun()) {
+ ret = test(true);
+ if (ret != T_EXIT_PASS) {
+ fprintf(stderr, "%s: test(true) failed\n", argv[0]);
+ return ret;
+ }
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/poll-mshot-update.c b/contrib/libs/liburing/test/poll-mshot-update.c
new file mode 100644
index 00000000000..c3b687260c1
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-mshot-update.c
@@ -0,0 +1,324 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test many files being polled for and updated
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/resource.h>
+#include <fcntl.h>
+#include <pthread.h>
+
+#include "liburing.h"
+
+#define NFILES 5000
+#define BATCH 500
+#define NLOOPS 1000
+
+#define RING_SIZE 512
+
+struct p {
+ int fd[2];
+ int triggered;
+};
+
+static struct p p[NFILES];
+
+static int has_poll_update(void)
+{
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ bool has_update = false;
+ int ret;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret)
+ return -1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_update(sqe, 0, 0, POLLIN, IORING_TIMEOUT_UPDATE);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1)
+ return -1;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (!ret) {
+ if (cqe->res == -ENOENT)
+ has_update = true;
+ else if (cqe->res != -EINVAL)
+ return -1;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ io_uring_queue_exit(&ring);
+ return has_update;
+}
+
+static int arm_poll(struct io_uring *ring, int off)
+{
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "failed getting sqe\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_multishot(sqe, p[off].fd[0], POLLIN);
+ sqe->user_data = off;
+ return 0;
+}
+
+static int submit_arm_poll(struct io_uring *ring, int off)
+{
+ int ret;
+
+ ret = arm_poll(ring, off);
+ if (ret)
+ return ret;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0)
+ return ret;
+ return ret == 1 ? 0 : -1;
+}
+
+static int reap_polls(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int i, ret, off;
+ char c;
+
+ for (i = 0; i < BATCH; i++) {
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ /* update event */
+ io_uring_prep_poll_update(sqe, i, 0, POLLIN,
+ IORING_POLL_UPDATE_EVENTS);
+ sqe->user_data = 0x12345678;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BATCH) {
+ fprintf(stderr, "submitted %d, %d\n", ret, BATCH);
+ return 1;
+ }
+
+ for (i = 0; i < 2 * BATCH; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe %d\n", ret);
+ return ret;
+ }
+ off = cqe->user_data;
+ if (off == 0x12345678)
+ goto seen;
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ /* need to re-arm poll */
+ ret = submit_arm_poll(ring, off);
+ if (ret)
+ break;
+ if (cqe->res <= 0) {
+ /* retry this one */
+ i--;
+ goto seen;
+ }
+ }
+
+ ret = read(p[off].fd[0], &c, 1);
+ if (ret != 1) {
+ if (ret == -1 && errno == EAGAIN)
+ goto seen;
+ fprintf(stderr, "read got %d/%d\n", ret, errno);
+ break;
+ }
+seen:
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (i != 2 * BATCH) {
+ fprintf(stderr, "gave up at %d\n", i);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int trigger_polls(void)
+{
+ char c = 89;
+ int i, ret;
+
+ for (i = 0; i < BATCH; i++) {
+ int off;
+
+ do {
+ off = rand() % NFILES;
+ if (!p[off].triggered)
+ break;
+ } while (1);
+
+ p[off].triggered = 1;
+ ret = write(p[off].fd[1], &c, 1);
+ if (ret != 1) {
+ fprintf(stderr, "write got %d/%d\n", ret, errno);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static void *trigger_polls_fn(void *data)
+{
+ trigger_polls();
+ return NULL;
+}
+
+static int arm_polls(struct io_uring *ring)
+{
+ int ret, to_arm = NFILES, i, off;
+
+ off = 0;
+ while (to_arm) {
+ int this_arm;
+
+ this_arm = to_arm;
+ if (this_arm > RING_SIZE)
+ this_arm = RING_SIZE;
+
+ for (i = 0; i < this_arm; i++) {
+ if (arm_poll(ring, off)) {
+ fprintf(stderr, "arm failed at %d\n", off);
+ return 1;
+ }
+ off++;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != this_arm) {
+ fprintf(stderr, "submitted %d, %d\n", ret, this_arm);
+ return 1;
+ }
+ to_arm -= this_arm;
+ }
+
+ return 0;
+}
+
+static int run(int cqe)
+{
+ struct io_uring ring;
+ struct io_uring_params params = { };
+ pthread_t thread;
+ int i, j, ret;
+
+ for (i = 0; i < NFILES; i++) {
+ if (pipe(p[i].fd) < 0) {
+ perror("pipe");
+ return 1;
+ }
+ fcntl(p[i].fd[0], F_SETFL, O_NONBLOCK);
+ }
+
+ params.flags = IORING_SETUP_CQSIZE;
+ params.cq_entries = cqe;
+ ret = io_uring_queue_init_params(RING_SIZE, &ring, &params);
+ if (ret) {
+ if (ret == -EINVAL) {
+ fprintf(stdout, "No CQSIZE, trying without\n");
+ ret = io_uring_queue_init(RING_SIZE, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+ }
+ }
+
+ if (arm_polls(&ring))
+ goto err;
+
+ for (i = 0; i < NLOOPS; i++) {
+ pthread_create(&thread, NULL, trigger_polls_fn, NULL);
+ ret = reap_polls(&ring);
+ if (ret)
+ goto err;
+ pthread_join(thread, NULL);
+
+ for (j = 0; j < NFILES; j++)
+ p[j].triggered = 0;
+ }
+
+ io_uring_queue_exit(&ring);
+ for (i = 0; i < NFILES; i++) {
+ close(p[i].fd[0]);
+ close(p[i].fd[1]);
+ }
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct rlimit rlim;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = has_poll_update();
+ if (ret < 0) {
+ fprintf(stderr, "poll update check failed %i\n", ret);
+ return -1;
+ } else if (!ret) {
+ fprintf(stderr, "no poll update, skip\n");
+ return 0;
+ }
+
+ if (getrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ perror("getrlimit");
+ goto err;
+ }
+
+ if (rlim.rlim_cur < (2 * NFILES + 5)) {
+ rlim.rlim_cur = (2 * NFILES + 5);
+ rlim.rlim_max = rlim.rlim_cur;
+ if (setrlimit(RLIMIT_NOFILE, &rlim) < 0) {
+ if (errno == EPERM)
+ goto err_nofail;
+ perror("setrlimit");
+ goto err;
+ }
+ }
+
+ ret = run(1024);
+ if (ret) {
+ fprintf(stderr, "run(1024) failed\n");
+ goto err;
+ }
+
+ ret = run(8192);
+ if (ret) {
+ fprintf(stderr, "run(8192) failed\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ fprintf(stderr, "poll-many failed\n");
+ return 1;
+err_nofail:
+ fprintf(stderr, "poll-many: not enough files available (and not root), "
+ "skipped\n");
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-ring.c b/contrib/libs/liburing/test/poll-ring.c
new file mode 100644
index 00000000000..0c580461794
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-ring.c
@@ -0,0 +1,49 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test poll against ring itself. A buggy kernel will end up
+ * having io_wq_* workers pending, as the circular reference
+ * will prevent full exit.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <poll.h>
+
+#include "liburing.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
+ io_uring_sqe_set_data(sqe, sqe);
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/poll-v-poll.c b/contrib/libs/liburing/test/poll-v-poll.c
new file mode 100644
index 00000000000..207f7bd5dae
--- /dev/null
+++ b/contrib/libs/liburing/test/poll-v-poll.c
@@ -0,0 +1,354 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring poll handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/select.h>
+#include <pthread.h>
+#include <sys/epoll.h>
+
+#include "liburing.h"
+
+struct thread_data {
+ struct io_uring *ring;
+ int fd;
+ int events;
+ const char *test;
+ int out[2];
+};
+
+static void *epoll_wait_fn(void *data)
+{
+ struct thread_data *td = data;
+ struct epoll_event ev;
+
+ if (epoll_wait(td->fd, &ev, 1, -1) < 0) {
+ perror("epoll_wait");
+ goto err;
+ }
+
+ return NULL;
+err:
+ return (void *) 1;
+}
+
+static void *iou_poll(void *data)
+{
+ struct thread_data *td = data;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(td->ring);
+ io_uring_prep_poll_add(sqe, td->fd, td->events);
+
+ ret = io_uring_submit(td->ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit got %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(td->ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+
+ td->out[0] = cqe->res & 0x3f;
+ io_uring_cqe_seen(td->ring, cqe);
+ return NULL;
+err:
+ return (void *) 1;
+}
+
+static void *poll_pipe(void *data)
+{
+ struct thread_data *td = data;
+ struct pollfd pfd;
+ int ret;
+
+ pfd.fd = td->fd;
+ pfd.events = td->events;
+
+ ret = poll(&pfd, 1, -1);
+ if (ret < 0)
+ perror("poll");
+
+ td->out[1] = pfd.revents;
+ return NULL;
+}
+
+static int do_pipe_pollin_test(struct io_uring *ring)
+{
+ struct thread_data td;
+ pthread_t threads[2];
+ int ret, pipe1[2];
+ char buf;
+
+ if (pipe(pipe1) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ td.ring = ring;
+ td.fd = pipe1[0];
+ td.events = POLLIN;
+ td.test = __FUNCTION__;
+
+ pthread_create(&threads[1], NULL, iou_poll, &td);
+ pthread_create(&threads[0], NULL, poll_pipe, &td);
+ usleep(100000);
+
+ buf = 0x89;
+ ret = write(pipe1[1], &buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ fprintf(stderr, "write failed: %d\n", ret);
+ return 1;
+ }
+
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+
+ if (td.out[0] != td.out[1]) {
+ fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
+ td.out[0], td.out[1]);
+ return 1;
+ }
+ return 0;
+}
+
+static int do_pipe_pollout_test(struct io_uring *ring)
+{
+ struct thread_data td;
+ pthread_t threads[2];
+ int ret, pipe1[2];
+ char buf;
+
+ if (pipe(pipe1) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ td.ring = ring;
+ td.fd = pipe1[1];
+ td.events = POLLOUT;
+ td.test = __FUNCTION__;
+
+ pthread_create(&threads[0], NULL, poll_pipe, &td);
+ pthread_create(&threads[1], NULL, iou_poll, &td);
+ usleep(100000);
+
+ buf = 0x89;
+ ret = write(pipe1[1], &buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ fprintf(stderr, "write failed: %d\n", ret);
+ return 1;
+ }
+
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+
+ if (td.out[0] != td.out[1]) {
+ fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
+ td.out[0], td.out[1]);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int do_fd_test(struct io_uring *ring, const char *fname, int events)
+{
+ struct thread_data td;
+ pthread_t threads[2];
+ int fd;
+
+ fd = open(fname, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ td.ring = ring;
+ td.fd = fd;
+ td.events = events;
+ td.test = __FUNCTION__;
+
+ pthread_create(&threads[0], NULL, poll_pipe, &td);
+ pthread_create(&threads[1], NULL, iou_poll, &td);
+
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+
+ if (td.out[0] != td.out[1]) {
+ fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
+ td.out[0], td.out[1]);
+ return 1;
+ }
+
+ return 0;
+}
+
+static int iou_epoll_ctl(struct io_uring *ring, int epfd, int fd,
+ struct epoll_event *ev)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Failed to get sqe\n");
+ return 1;
+ }
+
+ io_uring_prep_epoll_ctl(sqe, epfd, fd, EPOLL_CTL_ADD, ev);
+
+ 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: %d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static int do_test_epoll(struct io_uring *ring, int iou_epoll_add)
+{
+ struct epoll_event ev;
+ struct thread_data td;
+ pthread_t threads[2];
+ int ret, pipe1[2];
+ char buf;
+ int fd;
+
+ fd = epoll_create1(0);
+ if (fd < 0) {
+ perror("epoll_create");
+ return 1;
+ }
+
+ if (pipe(pipe1) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ev.events = EPOLLIN;
+ ev.data.fd = pipe1[0];
+
+ if (!iou_epoll_add) {
+ if (epoll_ctl(fd, EPOLL_CTL_ADD, pipe1[0], &ev) < 0) {
+ perror("epoll_ctrl");
+ return 1;
+ }
+ } else {
+ ret = iou_epoll_ctl(ring, fd, pipe1[0], &ev);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "epoll not supported, skipping\n");
+ return 0;
+ } else if (ret < 0) {
+ return 1;
+ }
+ }
+
+ td.ring = ring;
+ td.fd = fd;
+ td.events = POLLIN;
+ td.test = __FUNCTION__;
+
+ pthread_create(&threads[0], NULL, iou_poll, &td);
+ pthread_create(&threads[1], NULL, epoll_wait_fn, &td);
+ usleep(100000);
+
+ buf = 0x89;
+ ret = write(pipe1[1], &buf, sizeof(buf));
+ if (ret != sizeof(buf)) {
+ fprintf(stderr, "write failed: %d\n", ret);
+ return 1;
+ }
+
+ pthread_join(threads[0], NULL);
+ pthread_join(threads[1], NULL);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *fname;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = do_pipe_pollin_test(&ring);
+ if (ret) {
+ fprintf(stderr, "pipe pollin test failed\n");
+ return ret;
+ }
+
+ ret = do_pipe_pollout_test(&ring);
+ if (ret) {
+ fprintf(stderr, "pipe pollout test failed\n");
+ return ret;
+ }
+
+ ret = do_test_epoll(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "epoll test 0 failed\n");
+ return ret;
+ }
+
+ ret = do_test_epoll(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "epoll test 1 failed\n");
+ return ret;
+ }
+
+ if (argc > 1)
+ fname = argv[1];
+ else
+ fname = argv[0];
+
+ ret = do_fd_test(&ring, fname, POLLIN);
+ if (ret) {
+ fprintf(stderr, "fd test IN failed\n");
+ return ret;
+ }
+
+ ret = do_fd_test(&ring, fname, POLLOUT);
+ if (ret) {
+ fprintf(stderr, "fd test OUT failed\n");
+ return ret;
+ }
+
+ ret = do_fd_test(&ring, fname, POLLOUT | POLLIN);
+ if (ret) {
+ fprintf(stderr, "fd test IN|OUT failed\n");
+ return ret;
+ }
+
+ return 0;
+
+}
diff --git a/contrib/libs/liburing/test/poll.c b/contrib/libs/liburing/test/poll.c
new file mode 100644
index 00000000000..cfba077af69
--- /dev/null
+++ b/contrib/libs/liburing/test/poll.c
@@ -0,0 +1,110 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring poll handling
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+
+static void sig_alrm(int sig)
+{
+ fprintf(stderr, "Timed out!\n");
+ exit(1);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int pipe1[2];
+ pid_t p;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ p = fork();
+ switch (p) {
+ case -1:
+ perror("fork");
+ exit(2);
+ case 0: {
+ struct sigaction act;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_alrm;
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(1);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_poll_add(sqe, pipe1[0], POLLIN);
+ io_uring_sqe_set_data(sqe, sqe);
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "child: sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ do {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "child: wait completion %d\n", ret);
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ } while (ret != 0);
+
+ if (ret < 0)
+ return 1;
+ if (cqe->user_data != (unsigned long) sqe) {
+ fprintf(stderr, "child: cqe doesn't match sqe\n");
+ return 1;
+ }
+ if ((cqe->res & POLLIN) != POLLIN) {
+ fprintf(stderr, "child: bad return value %ld\n",
+ (long) cqe->res);
+ return 1;
+ }
+ exit(0);
+ }
+ default:
+ do {
+ errno = 0;
+ ret = write(pipe1[1], "foo", 3);
+ } while (ret == -1 && errno == EINTR);
+
+ if (ret != 3) {
+ fprintf(stderr, "parent: bad write return %d\n", ret);
+ return 1;
+ }
+ return 0;
+ }
+}
diff --git a/contrib/libs/liburing/test/pollfree.c b/contrib/libs/liburing/test/pollfree.c
new file mode 100644
index 00000000000..4ed61e3091c
--- /dev/null
+++ b/contrib/libs/liburing/test/pollfree.c
@@ -0,0 +1,427 @@
+#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()
+{
+ 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
+
+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, 0x32ul, -1, 0ul);
+ if (ret == MAP_FAILED)
+ return 0;
+ ret = mmap((void *)0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
+ if (ret == MAP_FAILED)
+ return 0;
+ ret = mmap((void *)0x21000000ul, 0x1000ul, 0ul, 0x32ul, -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/probe.c b/contrib/libs/liburing/test/probe.c
new file mode 100644
index 00000000000..5a40e289e48
--- /dev/null
+++ b/contrib/libs/liburing/test/probe.c
@@ -0,0 +1,136 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test IORING_REGISTER_PROBE
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int no_probe;
+
+static int verify_probe(struct io_uring_probe *p, int full)
+{
+ if (!full && p->ops_len) {
+ fprintf(stderr, "Got ops_len=%u\n", p->ops_len);
+ return 1;
+ }
+ if (!p->last_op) {
+ fprintf(stderr, "Got last_op=%u\n", p->last_op);
+ return 1;
+ }
+ if (!full)
+ return 0;
+ /* check a few ops that must be supported */
+ if (!(p->ops[IORING_OP_NOP].flags & IO_URING_OP_SUPPORTED)) {
+ fprintf(stderr, "NOP not supported!?\n");
+ return 1;
+ }
+ if (!(p->ops[IORING_OP_READV].flags & IO_URING_OP_SUPPORTED)) {
+ fprintf(stderr, "READV not supported!?\n");
+ return 1;
+ }
+ if (!(p->ops[IORING_OP_WRITE].flags & IO_URING_OP_SUPPORTED)) {
+ fprintf(stderr, "WRITE not supported!?\n");
+ return 1;
+ }
+
+ return 0;
+}
+
+static int test_probe_helper(struct io_uring *ring)
+{
+ int ret;
+ struct io_uring_probe *p;
+
+ p = io_uring_get_probe_ring(ring);
+ if (!p) {
+ fprintf(stderr, "Failed getting probe data\n");
+ return 1;
+ }
+
+ ret = verify_probe(p, 1);
+ io_uring_free_probe(p);
+ return ret;
+}
+
+static int test_probe(struct io_uring *ring)
+{
+ struct io_uring_probe *p;
+ size_t len;
+ int ret;
+
+ len = sizeof(*p) + 256 * sizeof(struct io_uring_probe_op);
+ p = t_calloc(1, len);
+ ret = io_uring_register_probe(ring, p, 0);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Probe not supported, skipping\n");
+ no_probe = 1;
+ goto out;
+ } else if (ret) {
+ fprintf(stdout, "Probe returned %d\n", ret);
+ goto err;
+ }
+
+ if (verify_probe(p, 0))
+ goto err;
+
+ /* now grab for all entries */
+ memset(p, 0, len);
+ ret = io_uring_register_probe(ring, p, 256);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Probe not supported, skipping\n");
+ goto err;
+ } else if (ret) {
+ fprintf(stdout, "Probe returned %d\n", ret);
+ goto err;
+ }
+
+ if (verify_probe(p, 1))
+ goto err;
+
+out:
+ free(p);
+ return 0;
+err:
+ free(p);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_probe(&ring);
+ if (ret) {
+ fprintf(stderr, "test_probe failed\n");
+ return ret;
+ }
+ if (no_probe)
+ return 0;
+
+ ret = test_probe_helper(&ring);
+ if (ret) {
+ fprintf(stderr, "test_probe failed\n");
+ return ret;
+ }
+
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/read-before-exit.c b/contrib/libs/liburing/test/read-before-exit.c
new file mode 100644
index 00000000000..6510a09263a
--- /dev/null
+++ b/contrib/libs/liburing/test/read-before-exit.c
@@ -0,0 +1,113 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test if issuing IO from thread and immediately exiting will
+ * proceed correctly.
+ *
+ * Original test case from: https://github.com/axboe/liburing/issues/582
+ */
+#include <unistd.h>
+#include <pthread.h>
+#include <sys/timerfd.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+struct data {
+ struct io_uring *ring;
+ int timer_fd1;
+ int timer_fd2;
+ uint64_t buf1;
+ uint64_t buf2;
+};
+
+void *submit(void *data)
+{
+ struct io_uring_sqe *sqe;
+ struct data *d = data;
+ int ret;
+
+ sqe = io_uring_get_sqe(d->ring);
+ io_uring_prep_read(sqe, d->timer_fd1, &d->buf1, sizeof(d->buf1), 0);
+
+ sqe = io_uring_get_sqe(d->ring);
+ io_uring_prep_read(sqe, d->timer_fd2, &d->buf2, sizeof(d->buf2), 0);
+
+ ret = io_uring_submit(d->ring);
+ if (ret != 2)
+ return (void *) (uintptr_t) 1;
+
+ /* Exit suddenly. */
+ return NULL;
+}
+
+static int test(int flags)
+{
+ struct io_uring_params params = { .flags = flags, };
+ struct io_uring ring;
+ struct data d = { .ring = &ring, };
+ pthread_t thread;
+ void *res;
+ int ret;
+
+ ret = t_create_ring_params(8, &ring, &params);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret != T_SETUP_OK)
+ return 1;
+
+ d.timer_fd1 = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (d.timer_fd1 < 0) {
+ perror("timerfd_create");
+ return 1;
+ }
+ d.timer_fd2 = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
+ if (d.timer_fd2 < 0) {
+ perror("timerfd_create");
+ return 1;
+ }
+
+ pthread_create(&thread, NULL, submit, &d);
+ pthread_join(thread, &res);
+
+ /** Wait for completions and do stuff ... **/
+
+ io_uring_queue_exit(&ring);
+
+ close(d.timer_fd1);
+ close(d.timer_fd2);
+ return !!res;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret, i;
+
+ for (i = 0; i < 1000; i++) {
+ ret = test(0);
+ if (ret) {
+ fprintf(stderr, "Test failed\n");
+ return ret;
+ }
+ }
+
+ for (i = 0; i < 1000; i++) {
+ ret = test(IORING_SETUP_IOPOLL);
+ if (ret) {
+ fprintf(stderr, "Test IOPOLL failed\n");
+ return ret;
+ }
+ }
+
+ for (i = 0; i < 100; i++) {
+ ret = test(IORING_SETUP_SQPOLL);
+ if (ret) {
+ fprintf(stderr, "Test SQPOLL failed\n");
+ return ret;
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/read-write.c b/contrib/libs/liburing/test/read-write.c
new file mode 100644
index 00000000000..953b0afc532
--- /dev/null
+++ b/contrib/libs/liburing/test/read-write.c
@@ -0,0 +1,959 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: basic read/write tests with buffered, O_DIRECT, and SQPOLL
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+#include <sys/resource.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (256 * 1024)
+#define BS 8192
+#define BUFFERS (FILE_SIZE / BS)
+
+static struct iovec *vecs;
+static int no_read;
+static int no_buf_select;
+static int warned;
+
+static int create_nonaligned_buffers(void)
+{
+ int i;
+
+ vecs = t_malloc(BUFFERS * sizeof(struct iovec));
+ for (i = 0; i < BUFFERS; i++) {
+ char *p = t_malloc(3 * BS);
+
+ if (!p)
+ return 1;
+ vecs[i].iov_base = p + (rand() % BS);
+ vecs[i].iov_len = 1 + (rand() % BS);
+ }
+
+ return 0;
+}
+
+static int __test_io(const char *file, struct io_uring *ring, int write,
+ int buffered, int sqthread, int fixed, int nonvec,
+ int buf_select, int seq, int exp_len)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int open_flags;
+ int i, fd = -1, ret;
+ off_t offset;
+
+#ifdef VERBOSE
+ fprintf(stdout, "%s: start %d/%d/%d/%d/%d: ", __FUNCTION__, write,
+ buffered, sqthread,
+ fixed, nonvec);
+#endif
+ if (write)
+ open_flags = O_WRONLY;
+ else
+ open_flags = O_RDONLY;
+ if (!buffered)
+ open_flags |= O_DIRECT;
+
+ if (fixed) {
+ ret = t_register_buffers(ring, vecs, BUFFERS);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "buffer reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ fd = open(file, open_flags);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ if (sqthread) {
+ ret = io_uring_register_files(ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ offset = 0;
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ if (!seq)
+ offset = BS * (rand() % BUFFERS);
+ if (write) {
+ int do_fixed = fixed;
+ int use_fd = fd;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else if (nonvec) {
+ io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ } else {
+ io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+ } else {
+ int do_fixed = fixed;
+ int use_fd = fd;
+
+ if (sqthread)
+ use_fd = 0;
+ if (fixed && (i & 1))
+ do_fixed = 0;
+ if (do_fixed) {
+ io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len,
+ offset, i);
+ } else if (nonvec) {
+ io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
+ vecs[i].iov_len, offset);
+ } else {
+ io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
+ offset);
+ }
+
+ }
+ sqe->user_data = i;
+ if (sqthread)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ if (buf_select) {
+ if (nonvec)
+ sqe->addr = 0;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = buf_select;
+ }
+ if (seq)
+ offset += BS;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
+ goto err;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL && nonvec) {
+ if (!warned) {
+ fprintf(stdout, "Non-vectored IO not "
+ "supported, skipping\n");
+ warned = 1;
+ no_read = 1;
+ }
+ } else if (exp_len == -1) {
+ int iov_len = vecs[cqe->user_data].iov_len;
+
+ if (cqe->res != iov_len) {
+ fprintf(stderr, "cqe res %d, wanted %d\n",
+ cqe->res, iov_len);
+ goto err;
+ }
+ } else if (cqe->res != exp_len) {
+ fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, exp_len);
+ goto err;
+ }
+ if (buf_select && exp_len == BS) {
+ int bid = cqe->flags >> 16;
+ unsigned char *ptr = vecs[bid].iov_base;
+ int j;
+
+ for (j = 0; j < BS; j++) {
+ if (ptr[j] == cqe->user_data)
+ continue;
+
+ fprintf(stderr, "Data mismatch! bid=%d, "
+ "wanted=%d, got=%d\n", bid,
+ (int)cqe->user_data, ptr[j]);
+ return 1;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (fixed) {
+ ret = io_uring_unregister_buffers(ring);
+ if (ret) {
+ fprintf(stderr, "buffer unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+ if (sqthread) {
+ ret = io_uring_unregister_files(ring);
+ if (ret) {
+ fprintf(stderr, "file unreg failed: %d\n", ret);
+ goto err;
+ }
+ }
+
+ close(fd);
+#ifdef VERBOSE
+ fprintf(stdout, "PASS\n");
+#endif
+ return 0;
+err:
+#ifdef VERBOSE
+ fprintf(stderr, "FAILED\n");
+#endif
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+static int test_io(const char *file, int write, int buffered, int sqthread,
+ int fixed, int nonvec, int exp_len)
+{
+ struct io_uring ring;
+ int ret, ring_flags = 0;
+
+ if (sqthread)
+ ring_flags = IORING_SETUP_SQPOLL;
+
+ ret = t_create_ring(64, &ring, ring_flags);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = __test_io(file, &ring, write, buffered, sqthread, fixed, nonvec,
+ 0, 0, exp_len);
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int read_poll_link(const char *file)
+{
+ struct __kernel_timespec ts;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int i, fd, ret, fds[2];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret)
+ return ret;
+
+ fd = open(file, O_WRONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ if (pipe(fds)) {
+ perror("pipe");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, fd, &vecs[0], 1, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 2;
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "submitted %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ return 0;
+}
+
+static int has_nonvec_read(void)
+{
+ struct io_uring_probe *p;
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ exit(ret);
+ }
+
+ p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
+ ret = io_uring_register_probe(&ring, p, 256);
+ /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
+ if (ret == -EINVAL) {
+out:
+ io_uring_queue_exit(&ring);
+ return 0;
+ } else if (ret) {
+ fprintf(stderr, "register_probe: %d\n", ret);
+ goto out;
+ }
+
+ if (p->ops_len <= IORING_OP_READ)
+ goto out;
+ if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
+ goto out;
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+static int test_eventfd_read(void)
+{
+ struct io_uring ring;
+ int fd, ret;
+ eventfd_t event;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ if (no_read)
+ return 0;
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret)
+ return ret;
+
+ fd = eventfd(1, 0);
+ if (fd < 0) {
+ perror("eventfd");
+ return 1;
+ }
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, &event, sizeof(eventfd_t), 0);
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submitted %d\n", ret);
+ return 1;
+ }
+ eventfd_write(fd, 1);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "eventfd IO not supported, skipping\n");
+ } else if (cqe->res != sizeof(eventfd_t)) {
+ fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res,
+ (int) sizeof(eventfd_t));
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ return 0;
+}
+
+static int test_buf_select_short(const char *filename, int nonvec)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, i, exp_len;
+
+ if (no_buf_select)
+ return 0;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ exp_len = 0;
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, vecs[i].iov_base,
+ vecs[i].iov_len / 2, 1, 1, i);
+ if (!exp_len)
+ exp_len = vecs[i].iov_len / 2;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return -1;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ ret = __test_io(filename, &ring, 0, 0, 0, 0, nonvec, 1, 1, exp_len);
+
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int provide_buffers_iovec(struct io_uring *ring, int bgid)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int i, ret;
+
+ for (i = 0; i < BUFFERS; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_provide_buffers(sqe, vecs[i].iov_base,
+ vecs[i].iov_len, 1, bgid, i);
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != BUFFERS) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return -1;
+ }
+
+ for (i = 0; i < BUFFERS; i++) {
+ ret = io_uring_wait_cqe(ring, &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;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+static int test_buf_select_pipe(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, i;
+ int fds[2];
+
+ if (no_buf_select)
+ return 0;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = provide_buffers_iovec(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "provide buffers failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = pipe(fds);
+ if (ret) {
+ fprintf(stderr, "pipe failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 5; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fds[0], NULL, 1 /* max read 1 per go */, -1);
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = 0;
+ }
+ io_uring_submit(&ring);
+
+ ret = write(fds[1], "01234", 5);
+ if (ret != 5) {
+ fprintf(stderr, "pipe write failed %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 5; i++) {
+ const char *buff;
+
+ if (io_uring_wait_cqe(&ring, &cqe)) {
+ fprintf(stderr, "bad wait %d\n", i);
+ return 1;
+ }
+ if (cqe->res != 1) {
+ fprintf(stderr, "expected read %d\n", cqe->res);
+ return 1;
+ }
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "no buffer %d\n", cqe->res);
+ return 1;
+ }
+ buff = vecs[cqe->flags >> 16].iov_base;
+ if (*buff != '0' + i) {
+ fprintf(stderr, "%d: expected %c, got %c\n", i, '0' + i, *buff);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_buf_select(const char *filename, int nonvec)
+{
+ struct io_uring_probe *p;
+ struct io_uring ring;
+ int ret, i;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ p = io_uring_get_probe_ring(&ring);
+ if (!p || !io_uring_opcode_supported(p, IORING_OP_PROVIDE_BUFFERS)) {
+ no_buf_select = 1;
+ fprintf(stdout, "Buffer select not supported, skipping\n");
+ return 0;
+ }
+ io_uring_free_probe(p);
+
+ /*
+ * Write out data with known pattern
+ */
+ for (i = 0; i < BUFFERS; i++)
+ memset(vecs[i].iov_base, i, vecs[i].iov_len);
+
+ ret = __test_io(filename, &ring, 1, 0, 0, 0, 0, 0, 1, BS);
+ if (ret) {
+ fprintf(stderr, "failed writing data\n");
+ return 1;
+ }
+
+ for (i = 0; i < BUFFERS; i++)
+ memset(vecs[i].iov_base, 0x55, vecs[i].iov_len);
+
+ ret = provide_buffers_iovec(&ring, 1);
+ if (ret)
+ return ret;
+
+ ret = __test_io(filename, &ring, 0, 0, 0, 0, nonvec, 1, 1, BS);
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int test_rem_buf(int batch, int sqe_flags)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int left, ret, nr = 0;
+ int bgid = 1;
+
+ if (no_buf_select)
+ return 0;
+
+ ret = io_uring_queue_init(64, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = provide_buffers_iovec(&ring, bgid);
+ if (ret)
+ return ret;
+
+ left = BUFFERS;
+ while (left) {
+ int to_rem = (left < batch) ? left : batch;
+
+ left -= to_rem;
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_remove_buffers(sqe, to_rem, bgid);
+ sqe->user_data = to_rem;
+ sqe->flags |= sqe_flags;
+ ++nr;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != nr) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return -1;
+ }
+
+ for (; nr > 0; nr--) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res != cqe->user_data) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int test_io_link(const char *file)
+{
+ const int nr_links = 100;
+ const int link_len = 100;
+ const int nr_sqes = nr_links * link_len;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int i, j, fd, ret;
+
+ fd = open(file, O_WRONLY);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+
+ ret = io_uring_queue_init(nr_sqes, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < nr_links; ++i) {
+ for (j = 0; j < link_len; ++j) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_writev(sqe, fd, &vecs[0], 1, 0);
+ sqe->flags |= IOSQE_ASYNC;
+ if (j != link_len - 1)
+ sqe->flags |= IOSQE_IO_LINK;
+ }
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != nr_sqes) {
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (!ret && cqe->res == -EINVAL) {
+ fprintf(stdout, "IOSQE_ASYNC not supported, skipped\n");
+ goto out;
+ }
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, nr_sqes);
+ goto err;
+ }
+
+ for (i = 0; i < nr_sqes; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL) {
+ if (!warned) {
+ fprintf(stdout, "Non-vectored IO not "
+ "supported, skipping\n");
+ warned = 1;
+ no_read = 1;
+ }
+ } else if (cqe->res != BS) {
+ fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, BS);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+out:
+ io_uring_queue_exit(&ring);
+ close(fd);
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+
+static int test_write_efbig(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ struct rlimit rlim, old_rlim;
+ int i, fd, ret;
+ loff_t off;
+
+ if (geteuid()) {
+ fprintf(stdout, "Not root, skipping %s\n", __FUNCTION__);
+ return 0;
+ }
+
+ if (getrlimit(RLIMIT_FSIZE, &old_rlim) < 0) {
+ perror("getrlimit");
+ return 1;
+ }
+ rlim = old_rlim;
+ rlim.rlim_cur = 128 * 1024;
+ rlim.rlim_max = 128 * 1024;
+ if (setrlimit(RLIMIT_FSIZE, &rlim) < 0) {
+ perror("setrlimit");
+ return 1;
+ }
+
+ fd = open(".efbig", O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("file open");
+ goto err;
+ }
+ unlink(".efbig");
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ goto err;
+ }
+
+ off = 0;
+ for (i = 0; i < 32; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_writev(sqe, fd, &vecs[i], 1, off);
+ io_uring_sqe_set_data64(sqe, i);
+ off += BS;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != 32) {
+ fprintf(stderr, "submit got %d, wanted %d\n", ret, 32);
+ goto err;
+ }
+
+ for (i = 0; i < 32; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->user_data < 16) {
+ if (cqe->res != BS) {
+ fprintf(stderr, "bad write: %d\n", cqe->res);
+ goto err;
+ }
+ } else {
+ if (cqe->res != -EFBIG) {
+ fprintf(stderr, "Expected -EFBIG: %d\n", cqe->res);
+ goto err;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ close(fd);
+ unlink(".efbig");
+
+ if (setrlimit(RLIMIT_FSIZE, &old_rlim) < 0) {
+ perror("setrlimit");
+ return 1;
+ }
+ return 0;
+err:
+ if (fd != -1)
+ close(fd);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret, nr;
+ char buf[256];
+ char *fname;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ srand((unsigned)time(NULL));
+ snprintf(buf, sizeof(buf), ".basic-rw-%u-%u",
+ (unsigned)rand(), (unsigned)getpid());
+ fname = buf;
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ signal(SIGXFSZ, SIG_IGN);
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ /* if we don't have nonvec read, skip testing that */
+ nr = has_nonvec_read() ? 32 : 16;
+
+ for (i = 0; i < nr; i++) {
+ int write = (i & 1) != 0;
+ int buffered = (i & 2) != 0;
+ int sqthread = (i & 4) != 0;
+ int fixed = (i & 8) != 0;
+ int nonvec = (i & 16) != 0;
+
+ ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
+ BS);
+ if (ret) {
+ fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
+ write, buffered, sqthread, fixed, nonvec);
+ goto err;
+ }
+ }
+
+ ret = test_buf_select(fname, 1);
+ if (ret) {
+ fprintf(stderr, "test_buf_select nonvec failed\n");
+ goto err;
+ }
+
+ ret = test_buf_select(fname, 0);
+ if (ret) {
+ fprintf(stderr, "test_buf_select vec failed\n");
+ goto err;
+ }
+
+ ret = test_buf_select_short(fname, 1);
+ if (ret) {
+ fprintf(stderr, "test_buf_select_short nonvec failed\n");
+ goto err;
+ }
+
+ ret = test_buf_select_short(fname, 0);
+ if (ret) {
+ fprintf(stderr, "test_buf_select_short vec failed\n");
+ goto err;
+ }
+
+ ret = test_buf_select_pipe();
+ if (ret) {
+ fprintf(stderr, "test_buf_select_pipe failed\n");
+ goto err;
+ }
+
+ ret = test_eventfd_read();
+ if (ret) {
+ fprintf(stderr, "test_eventfd_read failed\n");
+ goto err;
+ }
+
+ ret = read_poll_link(fname);
+ if (ret) {
+ fprintf(stderr, "read_poll_link failed\n");
+ goto err;
+ }
+
+ ret = test_io_link(fname);
+ if (ret) {
+ fprintf(stderr, "test_io_link failed\n");
+ goto err;
+ }
+
+ ret = test_write_efbig();
+ if (ret) {
+ fprintf(stderr, "test_write_efbig failed\n");
+ goto err;
+ }
+
+ ret = test_rem_buf(1, 0);
+ if (ret) {
+ fprintf(stderr, "test_rem_buf by 1 failed\n");
+ goto err;
+ }
+
+ ret = test_rem_buf(10, 0);
+ if (ret) {
+ fprintf(stderr, "test_rem_buf by 10 failed\n");
+ goto err;
+ }
+
+ ret = test_rem_buf(2, IOSQE_IO_LINK);
+ if (ret) {
+ fprintf(stderr, "test_rem_buf link failed\n");
+ goto err;
+ }
+
+ ret = test_rem_buf(2, IOSQE_ASYNC);
+ if (ret) {
+ fprintf(stderr, "test_rem_buf async failed\n");
+ goto err;
+ }
+
+ srand((unsigned)time(NULL));
+ if (create_nonaligned_buffers()) {
+ fprintf(stderr, "file creation failed\n");
+ goto err;
+ }
+
+ /* test fixed bufs with non-aligned len/offset */
+ for (i = 0; i < nr; i++) {
+ int write = (i & 1) != 0;
+ int buffered = (i & 2) != 0;
+ int sqthread = (i & 4) != 0;
+ int fixed = (i & 8) != 0;
+ int nonvec = (i & 16) != 0;
+
+ /* direct IO requires alignment, skip it */
+ if (!buffered || !fixed || nonvec)
+ continue;
+
+ ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
+ -1);
+ if (ret) {
+ fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
+ write, buffered, sqthread, fixed, nonvec);
+ goto err;
+ }
+ }
+
+ if (fname != argv[1])
+ unlink(fname);
+ return 0;
+err:
+ if (fname != argv[1])
+ unlink(fname);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/recv-msgall-stream.c b/contrib/libs/liburing/test/recv-msgall-stream.c
new file mode 100644
index 00000000000..355b1f5cfe2
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-msgall-stream.c
@@ -0,0 +1,399 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test MSG_WAITALL for recv/recvmsg and include normal sync versions just
+ * for comparison.
+ */
+#include <assert.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define MAX_MSG 128
+
+struct recv_data {
+ pthread_mutex_t mutex;
+ int use_recvmsg;
+ int use_sync;
+ __be16 port;
+};
+
+static int get_conn_sock(struct recv_data *rd, int *sockout)
+{
+ struct sockaddr_in saddr;
+ int sockfd, ret, val;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ if (sockfd < 0) {
+ perror("socket");
+ goto err;
+ }
+
+ val = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+
+ if (t_bind_ephemeral_port(sockfd, &saddr)) {
+ perror("bind");
+ goto err;
+ }
+ rd->port = saddr.sin_port;
+
+ ret = listen(sockfd, 16);
+ if (ret < 0) {
+ perror("listen");
+ goto err;
+ }
+
+ pthread_mutex_unlock(&rd->mutex);
+
+ ret = accept(sockfd, NULL, NULL);
+ if (ret < 0) {
+ perror("accept");
+ return -1;
+ }
+
+ *sockout = sockfd;
+ return ret;
+err:
+ pthread_mutex_unlock(&rd->mutex);
+ return -1;
+}
+
+static int recv_prep(struct io_uring *ring, struct iovec *iov, int *sock,
+ struct recv_data *rd)
+{
+ struct io_uring_sqe *sqe;
+ struct msghdr msg = { };
+ int sockfd, sockout = -1, ret;
+
+ sockfd = get_conn_sock(rd, &sockout);
+ if (sockfd < 0)
+ goto err;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!rd->use_recvmsg) {
+ io_uring_prep_recv(sqe, sockfd, iov->iov_base, iov->iov_len,
+ MSG_WAITALL);
+ } else {
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ io_uring_prep_recvmsg(sqe, sockfd, &msg, MSG_WAITALL);
+ }
+
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ *sock = sockfd;
+ return 0;
+err:
+ if (sockout != -1) {
+ shutdown(sockout, SHUT_RDWR);
+ close(sockout);
+ }
+ if (sockfd != -1) {
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+ }
+ return 1;
+}
+
+static int do_recv(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stdout, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "recv not supported, skipping\n");
+ return 0;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->res != MAX_MSG * sizeof(int)) {
+ fprintf(stderr, "got wrong length: %d\n", cqe->res);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+err:
+ return 1;
+}
+
+static int recv_sync(struct recv_data *rd)
+{
+ int buf[MAX_MSG];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf),
+ };
+ int i, ret, sockfd, sockout = -1;
+
+ sockfd = get_conn_sock(rd, &sockout);
+
+ if (rd->use_recvmsg) {
+ struct msghdr msg = { };
+
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ ret = recvmsg(sockfd, &msg, MSG_WAITALL);
+ } else {
+ ret = recv(sockfd, buf, sizeof(buf), MSG_WAITALL);
+ }
+
+ if (ret < 0) {
+ perror("receive");
+ goto err;
+ }
+
+ if (ret != sizeof(buf)) {
+ ret = -1;
+ goto err;
+ }
+
+ for (i = 0; i < MAX_MSG; i++) {
+ if (buf[i] != i)
+ goto err;
+ }
+ ret = 0;
+err:
+ shutdown(sockout, SHUT_RDWR);
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockout);
+ close(sockfd);
+ return ret;
+}
+
+static int recv_uring(struct recv_data *rd)
+{
+ int buf[MAX_MSG];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf),
+ };
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ int ret, sock = -1, sockout = -1;
+
+ ret = t_create_ring_params(1, &ring, &p);
+ if (ret == T_SETUP_SKIP) {
+ pthread_mutex_unlock(&rd->mutex);
+ ret = 0;
+ goto err;
+ } else if (ret < 0) {
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+
+ sock = recv_prep(&ring, &iov, &sockout, rd);
+ if (ret) {
+ fprintf(stderr, "recv_prep failed: %d\n", ret);
+ goto err;
+ }
+ ret = do_recv(&ring);
+ if (!ret) {
+ int i;
+
+ for (i = 0; i < MAX_MSG; i++) {
+ if (buf[i] != i) {
+ fprintf(stderr, "found %d at %d\n", buf[i], i);
+ ret = 1;
+ break;
+ }
+ }
+ }
+
+ shutdown(sockout, SHUT_RDWR);
+ shutdown(sock, SHUT_RDWR);
+ close(sock);
+ close(sockout);
+ io_uring_queue_exit(&ring);
+err:
+ if (sock != -1) {
+ shutdown(sock, SHUT_RDWR);
+ close(sock);
+ }
+ if (sockout != -1) {
+ shutdown(sockout, SHUT_RDWR);
+ close(sockout);
+ }
+ return ret;
+}
+
+static void *recv_fn(void *data)
+{
+ struct recv_data *rd = data;
+
+ if (rd->use_sync)
+ return (void *) (uintptr_t) recv_sync(rd);
+
+ return (void *) (uintptr_t) recv_uring(rd);
+}
+
+static int do_send(struct recv_data *rd)
+{
+ struct sockaddr_in saddr;
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, i;
+ struct iovec iov;
+ int *buf;
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return 1;
+ }
+
+ buf = malloc(MAX_MSG * sizeof(int));
+ for (i = 0; i < MAX_MSG; i++)
+ buf[i] = i;
+
+ sockfd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ pthread_mutex_lock(&rd->mutex);
+ assert(rd->port != 0);
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = rd->port;
+ inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr);
+
+ ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = MAX_MSG * sizeof(int) / 2;
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+ usleep(10000);
+ iov.iov_base += iov.iov_len;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "send not supported, skipping\n");
+ close(sockfd);
+ return 0;
+ }
+ if (cqe->res != iov.iov_len) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+ return 0;
+err:
+ shutdown(sockfd, SHUT_RDWR);
+ close(sockfd);
+ return 1;
+}
+
+static int test(int use_recvmsg, int use_sync)
+{
+ pthread_mutexattr_t attr;
+ pthread_t recv_thread;
+ struct recv_data rd;
+ int ret;
+ void *retval;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, 1);
+ pthread_mutex_init(&rd.mutex, &attr);
+ pthread_mutex_lock(&rd.mutex);
+ rd.use_recvmsg = use_recvmsg;
+ rd.use_sync = use_sync;
+ rd.port = 0;
+
+ ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
+ if (ret) {
+ fprintf(stderr, "Thread create failed: %d\n", ret);
+ pthread_mutex_unlock(&rd.mutex);
+ return 1;
+ }
+
+ do_send(&rd);
+ pthread_join(recv_thread, &retval);
+ return (intptr_t)retval;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test(0, 0);
+ if (ret) {
+ fprintf(stderr, "test recv failed\n");
+ return ret;
+ }
+
+ ret = test(1, 0);
+ if (ret) {
+ fprintf(stderr, "test recvmsg failed\n");
+ return ret;
+ }
+
+ ret = test(0, 1);
+ if (ret) {
+ fprintf(stderr, "test sync recv failed\n");
+ return ret;
+ }
+
+ ret = test(1, 1);
+ if (ret) {
+ fprintf(stderr, "test sync recvmsg failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/recv-msgall.c b/contrib/libs/liburing/test/recv-msgall.c
new file mode 100644
index 00000000000..89b12b72689
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-msgall.c
@@ -0,0 +1,266 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test MSG_WAITALL with datagram sockets, with a send splice into two.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define MAX_MSG 128
+#define HOST "127.0.0.1"
+static __be16 bind_port;
+
+static int recv_prep(struct io_uring *ring, struct iovec *iov, int *sock,
+ int use_recvmsg)
+{
+ struct sockaddr_in saddr;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, val;
+ struct msghdr msg = { };
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ val = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ if (t_bind_ephemeral_port(sockfd, &saddr)) {
+ perror("bind");
+ goto err;
+ }
+ bind_port = saddr.sin_port;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!use_recvmsg) {
+ io_uring_prep_recv(sqe, sockfd, iov->iov_base, iov->iov_len,
+ MSG_WAITALL);
+ } else {
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = 1;
+ io_uring_prep_recvmsg(sqe, sockfd, &msg, MSG_WAITALL);
+ }
+
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ *sock = sockfd;
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int do_recv(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stdout, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "recv not supported, skipping\n");
+ return 0;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->res != MAX_MSG * sizeof(int) / 2) {
+ fprintf(stderr, "got wrong length: %d\n", cqe->res);
+ goto err;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+err:
+ return 1;
+}
+
+struct recv_data {
+ pthread_mutex_t mutex;
+ int use_recvmsg;
+};
+
+static void *recv_fn(void *data)
+{
+ struct recv_data *rd = data;
+ int buf[MAX_MSG];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf),
+ };
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ int ret, sock;
+
+ ret = t_create_ring_params(1, &ring, &p);
+ if (ret == T_SETUP_SKIP) {
+ pthread_mutex_unlock(&rd->mutex);
+ ret = 0;
+ goto err;
+ } else if (ret < 0) {
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+
+ ret = recv_prep(&ring, &iov, &sock, rd->use_recvmsg);
+ if (ret) {
+ fprintf(stderr, "recv_prep failed: %d\n", ret);
+ goto err;
+ }
+ pthread_mutex_unlock(&rd->mutex);
+ ret = do_recv(&ring);
+ close(sock);
+ io_uring_queue_exit(&ring);
+err:
+ return (void *)(intptr_t)ret;
+}
+
+static int do_send(void)
+{
+ struct sockaddr_in saddr;
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, i;
+ struct iovec iov;
+ int *buf;
+
+ ret = io_uring_queue_init(2, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return 1;
+ }
+
+ buf = malloc(MAX_MSG * sizeof(int));
+ for (i = 0; i < MAX_MSG; i++)
+ buf[i] = i;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = bind_port;
+ inet_pton(AF_INET, HOST, &saddr.sin_addr);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = MAX_MSG * sizeof(int) / 2;
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+ usleep(10000);
+ iov.iov_base += iov.iov_len;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "send not supported, skipping\n");
+ close(sockfd);
+ return 0;
+ }
+ if (cqe->res != iov.iov_len) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ close(sockfd);
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int test(int use_recvmsg)
+{
+ pthread_mutexattr_t attr;
+ pthread_t recv_thread;
+ struct recv_data rd;
+ int ret;
+ void *retval;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, 1);
+ pthread_mutex_init(&rd.mutex, &attr);
+ pthread_mutex_lock(&rd.mutex);
+ rd.use_recvmsg = use_recvmsg;
+
+ ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
+ if (ret) {
+ fprintf(stderr, "Thread create failed: %d\n", ret);
+ pthread_mutex_unlock(&rd.mutex);
+ return 1;
+ }
+
+ pthread_mutex_lock(&rd.mutex);
+ do_send();
+ pthread_join(recv_thread, &retval);
+ return (intptr_t)retval;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test(0);
+ if (ret) {
+ fprintf(stderr, "test recv failed\n");
+ return ret;
+ }
+
+ ret = test(1);
+ if (ret) {
+ fprintf(stderr, "test recvmsg failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/recv-multishot.c b/contrib/libs/liburing/test/recv-multishot.c
new file mode 100644
index 00000000000..67a53567b7f
--- /dev/null
+++ b/contrib/libs/liburing/test/recv-multishot.c
@@ -0,0 +1,506 @@
+#include "../config-host.h"
+// SPDX-License-Identifier: MIT
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define ENORECVMULTISHOT 9999
+
+enum early_error_t {
+ ERROR_NONE = 0,
+ ERROR_NOT_ENOUGH_BUFFERS,
+ ERROR_EARLY_CLOSE_SENDER,
+ ERROR_EARLY_CLOSE_RECEIVER,
+ ERROR_EARLY_OVERFLOW,
+ ERROR_EARLY_LAST
+};
+
+struct args {
+ bool stream;
+ bool wait_each;
+ bool recvmsg;
+ enum early_error_t early_error;
+ bool defer;
+};
+
+static int check_sockaddr(struct sockaddr_in *in)
+{
+ struct in_addr expected;
+
+ inet_pton(AF_INET, "127.0.0.1", &expected);
+ if (in->sin_family != AF_INET) {
+ fprintf(stderr, "bad family %d\n", (int)htons(in->sin_family));
+ return -1;
+ }
+ if (memcmp(&expected, &in->sin_addr, sizeof(in->sin_addr))) {
+ char buff[256];
+ const char *addr = inet_ntop(AF_INET, &in->sin_addr, buff, sizeof(buff));
+
+ fprintf(stderr, "unexpected address %s\n", addr ? addr : "INVALID");
+ return -1;
+ }
+ return 0;
+}
+
+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 NAME_LEN = sizeof(struct sockaddr_storage);
+ int const CONTROL_LEN = CMSG_ALIGN(sizeof(struct sockaddr_storage))
+ + sizeof(struct cmsghdr);
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int fds[2], ret, i, j;
+ int total_sent_bytes = 0, total_recv_bytes = 0, total_dropped_bytes = 0;
+ int send_buff[256];
+ int *sent_buffs[N_BUFFS];
+ int *recv_buffs[N_BUFFS];
+ int *at;
+ struct io_uring_cqe recv_cqe[N_BUFFS];
+ int recv_cqes = 0;
+ bool early_error = false;
+ bool early_error_started = false;
+ struct __kernel_timespec timeout = {
+ .tv_sec = 1,
+ };
+ struct msghdr msg;
+ struct io_uring_params params = { };
+ int n_sqe = 32;
+
+ memset(recv_buffs, 0, sizeof(recv_buffs));
+
+ if (args->defer)
+ params.flags |= IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_DEFER_TASKRUN;
+
+ if (args->early_error == ERROR_EARLY_OVERFLOW) {
+ params.flags |= IORING_SETUP_CQSIZE;
+ params.cq_entries = N_CQE_OVERFLOW;
+ n_sqe = N_CQE_OVERFLOW;
+ }
+
+ ret = io_uring_queue_init_params(n_sqe, &ring, &params);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = t_create_socket_pair(fds, args->stream);
+ if (ret) {
+ fprintf(stderr, "t_create_socket_pair failed: %d\n", ret);
+ return ret;
+ }
+
+ if (!args->stream) {
+ bool val = true;
+
+ /* force some cmsgs to come back to us */
+ ret = setsockopt(fds[0], IPPROTO_IP, IP_RECVORIGDSTADDR, &val,
+ sizeof(val));
+ if (ret) {
+ fprintf(stderr, "setsockopt failed %d\n", errno);
+ goto cleanup;
+ }
+ }
+
+ for (i = 0; i < ARRAY_SIZE(send_buff); i++)
+ send_buff[i] = i;
+
+ for (i = 0; i < ARRAY_SIZE(recv_buffs); i++) {
+ /* prepare some different sized buffers */
+ int buffer_size = (i % 2 == 0 && (args->stream || args->recvmsg)) ? 1 : N;
+
+ buffer_size *= sizeof(int);
+ if (args->recvmsg) {
+ buffer_size +=
+ sizeof(struct io_uring_recvmsg_out) +
+ NAME_LEN +
+ CONTROL_LEN;
+ }
+
+ recv_buffs[i] = malloc(buffer_size);
+
+ if (i > 2 && args->early_error == ERROR_NOT_ENOUGH_BUFFERS)
+ continue;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, recv_buffs[i],
+ buffer_size, 1, 7, i);
+ io_uring_sqe_set_data64(sqe, 0x999);
+ memset(recv_buffs[i], 0xcc, buffer_size);
+ if (io_uring_submit_and_wait_timeout(&ring, &cqe, 1, &timeout, NULL) < 0) {
+ fprintf(stderr, "provide buffers failed: %d\n", ret);
+ ret = -1;
+ goto cleanup;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (args->recvmsg) {
+ unsigned int flags = 0;
+
+ if (!args->stream)
+ flags |= MSG_TRUNC;
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = NAME_LEN;
+ msg.msg_controllen = CONTROL_LEN;
+ io_uring_prep_recvmsg_multishot(sqe, fds[0], &msg, flags);
+ } else {
+ io_uring_prep_recv_multishot(sqe, fds[0], NULL, 0, 0);
+ }
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = 7;
+ io_uring_sqe_set_data64(sqe, 1234);
+ io_uring_submit(&ring);
+
+ at = &send_buff[0];
+ total_sent_bytes = 0;
+ for (i = 0; i < N; i++) {
+ int to_send = sizeof(*at) * (i+1);
+
+ total_sent_bytes += to_send;
+ sent_buffs[i] = at;
+ if (send(fds[1], at, to_send, 0) != to_send) {
+ if (early_error_started)
+ break;
+ fprintf(stderr, "send failed %d\n", errno);
+ ret = -1;
+ goto cleanup;
+ }
+
+ if (i == 2) {
+ if (args->early_error == ERROR_EARLY_CLOSE_RECEIVER) {
+ /* allow previous sends to complete */
+ usleep(1000);
+ io_uring_get_events(&ring);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recv(sqe, fds[0], NULL, 0, 0);
+ io_uring_prep_cancel64(sqe, 1234, 0);
+ io_uring_sqe_set_data64(sqe, 0x888);
+ sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+ io_uring_submit(&ring);
+ early_error_started = true;
+
+ /* allow the cancel to complete */
+ usleep(1000);
+ io_uring_get_events(&ring);
+ }
+ if (args->early_error == ERROR_EARLY_CLOSE_SENDER) {
+ early_error_started = true;
+ shutdown(fds[1], SHUT_RDWR);
+ close(fds[1]);
+ }
+ }
+ at += (i+1);
+
+ if (args->wait_each) {
+ ret = io_uring_wait_cqes(&ring, &cqe, 1, &timeout, NULL);
+ if (ret) {
+ fprintf(stderr, "wait_each failed: %d\n", ret);
+ ret = -1;
+ goto cleanup;
+ }
+ while (io_uring_peek_cqe(&ring, &cqe) == 0) {
+ recv_cqe[recv_cqes++] = *cqe;
+ if (cqe->flags & IORING_CQE_F_MORE) {
+ io_uring_cqe_seen(&ring, cqe);
+ } else {
+ early_error = true;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ }
+ if (early_error)
+ break;
+ }
+ }
+
+ close(fds[1]);
+
+ /* allow sends to finish */
+ usleep(1000);
+
+ if ((args->stream && !early_error) || recv_cqes < min_cqes) {
+ ret = io_uring_wait_cqes(&ring, &cqe, 1, &timeout, NULL);
+ if (ret && ret != -ETIME) {
+ fprintf(stderr, "wait final failed: %d\n", ret);
+ ret = -1;
+ goto cleanup;
+ }
+ }
+
+ while (io_uring_peek_cqe(&ring, &cqe) == 0) {
+ recv_cqe[recv_cqes++] = *cqe;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ ret = -1;
+ at = &send_buff[0];
+ if (recv_cqes < min_cqes) {
+ if (recv_cqes > 0 && recv_cqe[0].res == -EINVAL) {
+ return -ENORECVMULTISHOT;
+ }
+ /* some kernels apparently don't check ->ioprio, skip */
+ ret = -ENORECVMULTISHOT;
+ goto cleanup;
+ }
+ for (i = 0; i < recv_cqes; i++) {
+ cqe = &recv_cqe[i];
+
+ bool const is_last = i == recv_cqes - 1;
+
+ bool const should_be_last =
+ (cqe->res <= 0) ||
+ (args->stream && is_last) ||
+ (args->early_error == ERROR_EARLY_OVERFLOW &&
+ !args->wait_each && i == N_CQE_OVERFLOW);
+ int *this_recv;
+ int orig_payload_size = cqe->res;
+
+
+ if (should_be_last) {
+ int used_res = cqe->res;
+
+ if (!is_last) {
+ fprintf(stderr, "not last cqe had error %d\n", i);
+ goto cleanup;
+ }
+
+ switch (args->early_error) {
+ case ERROR_NOT_ENOUGH_BUFFERS:
+ if (cqe->res != -ENOBUFS) {
+ fprintf(stderr,
+ "ERROR_NOT_ENOUGH_BUFFERS: res %d\n", cqe->res);
+ goto cleanup;
+ }
+ break;
+ case ERROR_EARLY_OVERFLOW:
+ if (cqe->res < 0) {
+ fprintf(stderr,
+ "ERROR_EARLY_OVERFLOW: res %d\n", cqe->res);
+ goto cleanup;
+ }
+ break;
+ case ERROR_EARLY_CLOSE_RECEIVER:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr,
+ "ERROR_EARLY_CLOSE_RECEIVER: res %d\n", cqe->res);
+ goto cleanup;
+ }
+ break;
+ case ERROR_NONE:
+ case ERROR_EARLY_CLOSE_SENDER:
+ if (args->recvmsg && (cqe->flags & IORING_CQE_F_BUFFER)) {
+ void *buff = recv_buffs[cqe->flags >> 16];
+ struct io_uring_recvmsg_out *o =
+ io_uring_recvmsg_validate(buff, cqe->res, &msg);
+
+ if (!o) {
+ fprintf(stderr, "invalid buff\n");
+ goto cleanup;
+ }
+ if (o->payloadlen != 0) {
+ fprintf(stderr, "expected 0 payloadlen, got %u\n",
+ o->payloadlen);
+ goto cleanup;
+ }
+ used_res = 0;
+ } else if (cqe->res != 0) {
+ fprintf(stderr, "early error: res %d\n", cqe->res);
+ goto cleanup;
+ }
+ break;
+ case ERROR_EARLY_LAST:
+ fprintf(stderr, "bad error_early\n");
+ goto cleanup;
+ };
+
+ if (cqe->res <= 0 && cqe->flags & IORING_CQE_F_BUFFER) {
+ fprintf(stderr, "final BUFFER flag set\n");
+ goto cleanup;
+ }
+
+ if (cqe->flags & IORING_CQE_F_MORE) {
+ fprintf(stderr, "final MORE flag set\n");
+ goto cleanup;
+ }
+
+ if (used_res <= 0)
+ continue;
+ } else {
+ if (!(cqe->flags & IORING_CQE_F_MORE)) {
+ fprintf(stderr, "MORE flag not set\n");
+ goto cleanup;
+ }
+ }
+
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "BUFFER flag not set\n");
+ goto cleanup;
+ }
+
+ this_recv = recv_buffs[cqe->flags >> 16];
+
+ if (args->recvmsg) {
+ struct io_uring_recvmsg_out *o = io_uring_recvmsg_validate(
+ this_recv, cqe->res, &msg);
+
+ if (!o) {
+ fprintf(stderr, "bad recvmsg\n");
+ goto cleanup;
+ }
+ orig_payload_size = o->payloadlen;
+
+ if (!args->stream) {
+ orig_payload_size = o->payloadlen;
+
+ struct cmsghdr *cmsg;
+
+ if (o->namelen < sizeof(struct sockaddr_in)) {
+ fprintf(stderr, "bad addr len %d",
+ o->namelen);
+ goto cleanup;
+ }
+ if (check_sockaddr((struct sockaddr_in *)io_uring_recvmsg_name(o)))
+ goto cleanup;
+
+ cmsg = io_uring_recvmsg_cmsg_firsthdr(o, &msg);
+ if (!cmsg ||
+ cmsg->cmsg_level != IPPROTO_IP ||
+ cmsg->cmsg_type != IP_RECVORIGDSTADDR) {
+ fprintf(stderr, "bad cmsg");
+ goto cleanup;
+ }
+ if (check_sockaddr((struct sockaddr_in *)CMSG_DATA(cmsg)))
+ goto cleanup;
+ cmsg = io_uring_recvmsg_cmsg_nexthdr(o, &msg, cmsg);
+ if (cmsg) {
+ fprintf(stderr, "unexpected extra cmsg\n");
+ goto cleanup;
+ }
+
+ }
+
+ this_recv = (int *)io_uring_recvmsg_payload(o, &msg);
+ cqe->res = io_uring_recvmsg_payload_length(o, cqe->res, &msg);
+ if (o->payloadlen != cqe->res) {
+ if (!(o->flags & MSG_TRUNC)) {
+ fprintf(stderr, "expected truncated flag\n");
+ goto cleanup;
+ }
+ total_dropped_bytes += (o->payloadlen - cqe->res);
+ }
+ }
+
+ total_recv_bytes += cqe->res;
+
+ if (cqe->res % 4 != 0) {
+ /*
+ * doesn't seem to happen in practice, would need some
+ * work to remove this requirement
+ */
+ fprintf(stderr, "unexpectedly aligned buffer cqe->res=%d\n", cqe->res);
+ goto cleanup;
+ }
+
+ /*
+ * for tcp: check buffer arrived in order
+ * for udp: based on size validate data based on size
+ */
+ if (!args->stream) {
+ int sent_idx = orig_payload_size / sizeof(*at) - 1;
+
+ if (sent_idx < 0 || sent_idx > N) {
+ fprintf(stderr, "Bad sent idx: %d\n", sent_idx);
+ goto cleanup;
+ }
+ at = sent_buffs[sent_idx];
+ }
+ for (j = 0; j < cqe->res / 4; j++) {
+ int sent = *at++;
+ int recv = *this_recv++;
+
+ if (sent != recv) {
+ fprintf(stderr, "recv=%d sent=%d\n", recv, sent);
+ goto cleanup;
+ }
+ }
+ }
+
+ if (args->early_error == ERROR_NONE &&
+ total_recv_bytes + total_dropped_bytes < total_sent_bytes) {
+ fprintf(stderr,
+ "missing recv: recv=%d dropped=%d sent=%d\n",
+ total_recv_bytes, total_sent_bytes, total_dropped_bytes);
+ goto cleanup;
+ }
+
+ ret = 0;
+cleanup:
+ for (i = 0; i < ARRAY_SIZE(recv_buffs); i++)
+ free(recv_buffs[i]);
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+ int loop;
+ int early_error = 0;
+ bool has_defer;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ has_defer = t_probe_defer_taskrun();
+
+ for (loop = 0; loop < 16; loop++) {
+ struct args a = {
+ .stream = loop & 0x01,
+ .wait_each = loop & 0x2,
+ .recvmsg = loop & 0x04,
+ .defer = loop & 0x08,
+ };
+ if (a.defer && !has_defer)
+ continue;
+ for (early_error = 0; early_error < ERROR_EARLY_LAST; early_error++) {
+ a.early_error = (enum early_error_t)early_error;
+ ret = test(&a);
+ if (ret) {
+ if (ret == -ENORECVMULTISHOT) {
+ if (loop == 0)
+ return T_EXIT_SKIP;
+ fprintf(stderr,
+ "ENORECVMULTISHOT received but loop>0\n");
+ }
+ fprintf(stderr,
+ "test stream=%d wait_each=%d recvmsg=%d early_error=%d "
+ " defer=%d failed\n",
+ a.stream, a.wait_each, a.recvmsg, a.early_error, a.defer);
+ return T_EXIT_FAIL;
+ }
+ }
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/register-restrictions.c b/contrib/libs/liburing/test/register-restrictions.c
new file mode 100644
index 00000000000..af35db4b4a0
--- /dev/null
+++ b/contrib/libs/liburing/test/register-restrictions.c
@@ -0,0 +1,634 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test restrictions
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+
+#include "liburing.h"
+
+enum {
+ TEST_OK,
+ TEST_SKIPPED,
+ TEST_FAILED
+};
+
+static int test_restrictions_sqe_op(void)
+{
+ struct io_uring_restriction res[2];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, pipe1[2];
+
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_WRITEV;
+
+ res[1].opcode = IORING_RESTRICTION_SQE_OP;
+ res[1].sqe_op = IORING_OP_WRITE;
+
+ ret = io_uring_register_restrictions(&ring, res, 2);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+
+ fprintf(stderr, "failed to register restrictions: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_enable_rings(&ring);
+ if (ret) {
+ fprintf(stderr, "ring enabling failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, pipe1[0], &vec, 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ for (int i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* writev */
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "write res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+
+ break;
+ case 2: /* readv should be denied */
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "read res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_register_op(void)
+{
+ struct io_uring_restriction res[1];
+ struct io_uring ring;
+ int ret, pipe1[2];
+
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ res[0].opcode = IORING_RESTRICTION_REGISTER_OP;
+ res[0].register_op = IORING_REGISTER_BUFFERS;
+
+ ret = io_uring_register_restrictions(&ring, res, 1);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+
+ fprintf(stderr, "failed to register restrictions: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_enable_rings(&ring);
+ if (ret) {
+ fprintf(stderr, "ring enabling failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_buffers(&ring, &vec, 1);
+ if (ret) {
+ fprintf(stderr, "io_uring_register_buffers failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_files(&ring, pipe1, 2);
+ if (ret != -EACCES) {
+ fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_fixed_file(void)
+{
+ struct io_uring_restriction res[4];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, pipe1[2];
+
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_WRITEV;
+
+ res[1].opcode = IORING_RESTRICTION_SQE_OP;
+ res[1].sqe_op = IORING_OP_READV;
+
+ res[2].opcode = IORING_RESTRICTION_SQE_FLAGS_REQUIRED;
+ res[2].sqe_flags = IOSQE_FIXED_FILE;
+
+ res[3].opcode = IORING_RESTRICTION_REGISTER_OP;
+ res[3].register_op = IORING_REGISTER_FILES;
+
+ ret = io_uring_register_restrictions(&ring, res, 4);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+
+ fprintf(stderr, "failed to register restrictions: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_enable_rings(&ring);
+ if (ret) {
+ fprintf(stderr, "ring enabling failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_files(&ring, pipe1, 2);
+ if (ret) {
+ fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, 1, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, 0, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ for (int i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* writev */
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "write res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+
+ break;
+ case 2: /* readv */
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "read res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+ break;
+ case 3: /* writev without fixed_file should be denied */
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "write res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_flags(void)
+{
+ struct io_uring_restriction res[3];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, pipe1[2];
+
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_WRITEV;
+
+ res[1].opcode = IORING_RESTRICTION_SQE_FLAGS_ALLOWED;
+ res[1].sqe_flags = IOSQE_ASYNC | IOSQE_IO_LINK;
+
+ res[2].opcode = IORING_RESTRICTION_SQE_FLAGS_REQUIRED;
+ res[2].sqe_flags = IOSQE_FIXED_FILE;
+
+ ret = io_uring_register_restrictions(&ring, res, 3);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+
+ fprintf(stderr, "failed to register restrictions: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_files(&ring, pipe1, 2);
+ if (ret) {
+ fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_enable_rings(&ring);
+ if (ret) {
+ fprintf(stderr, "ring enabling failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, 1, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, 1, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_ASYNC);
+ sqe->user_data = 2;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, 1, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_IO_LINK);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 3) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, 1, &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_FIXED_FILE | IOSQE_IO_DRAIN);
+ sqe->user_data = 4;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_IO_DRAIN);
+ sqe->user_data = 5;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
+ sqe->user_data = 6;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+ sqe->user_data = 7;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ for (int i = 0; i < 7; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ switch (cqe->user_data) {
+ case 1: /* writev - flags = IOSQE_FIXED_FILE */
+ case 2: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_ASYNC */
+ case 3: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_IO_LINK */
+ if (cqe->res != sizeof(ptr)) {
+ fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
+ cqe->res, (uint64_t) cqe->user_data);
+ return TEST_FAILED;
+ }
+
+ break;
+ case 4: /* writev - flags = IOSQE_FIXED_FILE | IOSQE_IO_DRAIN */
+ case 5: /* writev - flags = IOSQE_IO_DRAIN */
+ case 6: /* writev - flags = IOSQE_ASYNC */
+ case 7: /* writev - flags = 0 */
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
+ cqe->res, (uint64_t) cqe->user_data);
+ return TEST_FAILED;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_empty(void)
+{
+ struct io_uring_restriction res[0];
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, pipe1[2];
+
+ uint64_t ptr;
+ struct iovec vec = {
+ .iov_base = &ptr,
+ .iov_len = sizeof(ptr)
+ };
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_restrictions(&ring, res, 0);
+ if (ret) {
+ if (ret == -EINVAL)
+ return TEST_SKIPPED;
+
+ fprintf(stderr, "failed to register restrictions: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_enable_rings(&ring);
+ if (ret) {
+ fprintf(stderr, "ring enabling failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_buffers(&ring, &vec, 1);
+ if (ret != -EACCES) {
+ fprintf(stderr, "io_uring_register_buffers ret: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_register_files(&ring, pipe1, 2);
+ if (ret != -EACCES) {
+ fprintf(stderr, "io_uring_register_files ret: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_writev(sqe, pipe1[1], &vec, 1, 0);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ if (cqe->res != -EACCES) {
+ fprintf(stderr, "write res: %d\n", cqe->res);
+ return TEST_FAILED;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_rings_not_disabled(void)
+{
+ struct io_uring_restriction res[1];
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ res[0].opcode = IORING_RESTRICTION_SQE_OP;
+ res[0].sqe_op = IORING_OP_WRITEV;
+
+ ret = io_uring_register_restrictions(&ring, res, 1);
+ if (ret != -EBADFD) {
+ fprintf(stderr, "io_uring_register_restrictions ret: %d\n",
+ ret);
+ return TEST_FAILED;
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+static int test_restrictions_rings_disabled(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_R_DISABLED);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(&ring);
+ if (ret != -EBADFD) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return TEST_FAILED;
+ }
+
+ io_uring_queue_exit(&ring);
+ return TEST_OK;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_restrictions_sqe_op();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_sqe_op: skipped\n");
+ return 0;
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_sqe_op failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_register_op();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_register_op: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_register_op failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_fixed_file();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_fixed_file: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_fixed_file failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_flags();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_flags: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_flags failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_empty();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_empty: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_empty failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_rings_not_disabled();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_rings_not_disabled: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_rings_not_disabled failed\n");
+ return ret;
+ }
+
+ ret = test_restrictions_rings_disabled();
+ if (ret == TEST_SKIPPED) {
+ printf("test_restrictions_rings_disabled: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_restrictions_rings_disabled failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/rename.c b/contrib/libs/liburing/test/rename.c
new file mode 100644
index 00000000000..c72a36ea1b2
--- /dev/null
+++ b/contrib/libs/liburing/test/rename.c
@@ -0,0 +1,133 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various nop tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "liburing.h"
+
+static int test_rename(struct io_uring *ring, const char *old, const char *new)
+{
+ 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_rename(sqe, old, new);
+
+ 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 stat_file(const char *buf)
+{
+ struct stat sb;
+
+ if (!stat(buf, &sb))
+ return 0;
+
+ return errno;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ char src[32] = "./XXXXXX";
+ char dst[32] = "./XXXXXX";
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = mkstemp(src);
+ if (ret < 0) {
+ perror("mkstemp");
+ return 1;
+ }
+ close(ret);
+
+ ret = mkstemp(dst);
+ if (ret < 0) {
+ perror("mkstemp");
+ return 1;
+ }
+ close(ret);
+
+ if (stat_file(src) != 0) {
+ perror("stat");
+ return 1;
+ }
+ if (stat_file(dst) != 0) {
+ perror("stat");
+ return 1;
+ }
+
+ ret = test_rename(&ring, src, dst);
+ if (ret < 0) {
+ if (ret == -EBADF || ret == -EINVAL) {
+ fprintf(stdout, "Rename not supported, skipping\n");
+ goto out;
+ }
+ fprintf(stderr, "rename: %s\n", strerror(-ret));
+ goto err;
+ } else if (ret)
+ goto err;
+
+ if (stat_file(src) != ENOENT) {
+ fprintf(stderr, "stat got %s\n", strerror(ret));
+ return 1;
+ }
+
+ if (stat_file(dst) != 0) {
+ perror("stat");
+ return 1;
+ }
+
+ ret = test_rename(&ring, "/x/y/1/2", "/2/1/y/x");
+ if (ret != -ENOENT) {
+ fprintf(stderr, "test_rename invalid failed: %d\n", ret);
+ return ret;
+ }
+out:
+ unlink(dst);
+ return 0;
+err:
+ unlink(src);
+ unlink(dst);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/ring-leak.c b/contrib/libs/liburing/test/ring-leak.c
new file mode 100644
index 00000000000..26338a48f1e
--- /dev/null
+++ b/contrib/libs/liburing/test/ring-leak.c
@@ -0,0 +1,271 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Based on description from Al Viro - this demonstrates a leak of the
+ * io_uring instance, by sending the io_uring fd over a UNIX socket.
+ *
+ * See:
+ *
+ * https://lore.kernel.org/linux-block/20190129192702.3605-1-axboe@kernel.dk/T/#m6c87fc64e4d063786af6ec6fadce3ac1e95d3184
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stddef.h>
+#include <signal.h>
+#include <inttypes.h>
+#include <sys/types.h>
+#include <sys/syscall.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <linux/fs.h>
+
+#include "liburing.h"
+#include "../src/syscall.h"
+
+static int __io_uring_register_files(int ring_fd, int fd1, int fd2)
+{
+ __s32 fds[2] = { fd1, fd2 };
+
+ return __sys_io_uring_register(ring_fd, IORING_REGISTER_FILES, fds, 2);
+}
+
+static int get_ring_fd(void)
+{
+ struct io_uring_params p;
+ int fd;
+
+ memset(&p, 0, sizeof(p));
+
+ fd = __sys_io_uring_setup(2, &p);
+ if (fd < 0) {
+ perror("io_uring_setup");
+ return -1;
+ }
+
+ return fd;
+}
+
+static void send_fd(int socket, int fd)
+{
+ char buf[CMSG_SPACE(sizeof(fd))];
+ struct cmsghdr *cmsg;
+ struct msghdr msg;
+
+ memset(buf, 0, sizeof(buf));
+ memset(&msg, 0, sizeof(msg));
+
+ msg.msg_control = buf;
+ msg.msg_controllen = sizeof(buf);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ cmsg->cmsg_level = SOL_SOCKET;
+ cmsg->cmsg_type = SCM_RIGHTS;
+ cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+
+ memmove(CMSG_DATA(cmsg), &fd, sizeof(fd));
+
+ msg.msg_controllen = CMSG_SPACE(sizeof(fd));
+
+ if (sendmsg(socket, &msg, 0) < 0)
+ perror("sendmsg");
+}
+
+static int test_iowq_request_cancel(void)
+{
+ char buffer[128];
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ int ret, fds[2];
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+ ret = io_uring_register_files(&ring, fds, 2);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+ close(fds[1]);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ /* potentially sitting in internal polling */
+ io_uring_prep_read(sqe, 0, buffer, 10, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: failed to get sqe\n", __FUNCTION__);
+ return 1;
+ }
+ /* staying in io-wq */
+ io_uring_prep_read(sqe, 0, buffer, 10, 0);
+ sqe->flags |= IOSQE_FIXED_FILE | IOSQE_ASYNC;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ /* should unregister files and close the write fd */
+ io_uring_queue_exit(&ring);
+
+ /*
+ * We're trying to wait for the ring to "really" exit, that will be
+ * done async. For that rely on the registered write end to be closed
+ * after ring quiesce, so failing read from the other pipe end.
+ */
+ ret = read(fds[0], buffer, 10);
+ if (ret < 0)
+ perror("read");
+ close(fds[0]);
+ return 0;
+}
+
+static void trigger_unix_gc(void)
+{
+ int fd;
+
+ fd = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (fd < 0)
+ perror("socket dgram");
+ else
+ close(fd);
+}
+
+static int test_scm_cycles(bool update)
+{
+ char buffer[128];
+ struct io_uring ring;
+ int i, ret;
+ int sp[2], fds[2], reg_fds[4];
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
+ perror("Failed to create Unix-domain socket pair\n");
+ return 1;
+ }
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret < 0) {
+ fprintf(stderr, "failed to init io_uring: %s\n", strerror(-ret));
+ return ret;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+ send_fd(sp[0], ring.ring_fd);
+
+ /* register an empty set for updates */
+ if (update) {
+ for (i = 0; i < 4; i++)
+ reg_fds[i] = -1;
+ ret = io_uring_register_files(&ring, reg_fds, 4);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+ }
+
+ reg_fds[0] = fds[0];
+ reg_fds[1] = fds[1];
+ reg_fds[2] = sp[0];
+ reg_fds[3] = sp[1];
+ if (update) {
+ ret = io_uring_register_files_update(&ring, 0, reg_fds, 4);
+ if (ret != 4) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+ } else {
+ ret = io_uring_register_files(&ring, reg_fds, 4);
+ if (ret) {
+ fprintf(stderr, "file_register: %d\n", ret);
+ return ret;
+ }
+ }
+
+ close(fds[1]);
+ close(sp[0]);
+ close(sp[1]);
+
+ /* should unregister files and close the write fd */
+ io_uring_queue_exit(&ring);
+
+ trigger_unix_gc();
+
+ /*
+ * We're trying to wait for the ring to "really" exit, that will be
+ * done async. For that rely on the registered write end to be closed
+ * after ring quiesce, so failing read from the other pipe end.
+ */
+ ret = read(fds[0], buffer, 10);
+ if (ret < 0)
+ perror("read");
+ close(fds[0]);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int sp[2], pid, ring_fd, ret;
+ int i;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_iowq_request_cancel();
+ if (ret) {
+ fprintf(stderr, "test_iowq_request_cancel() failed\n");
+ return 1;
+ }
+
+ for (i = 0; i < 2; i++) {
+ bool update = !!(i & 1);
+
+ ret = test_scm_cycles(update);
+ if (ret) {
+ fprintf(stderr, "test_scm_cycles() failed %i\n",
+ update);
+ return 1;
+ }
+ break;
+ }
+
+ if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sp) != 0) {
+ perror("Failed to create Unix-domain socket pair\n");
+ return 1;
+ }
+
+ ring_fd = get_ring_fd();
+ if (ring_fd < 0)
+ return 1;
+
+ ret = __io_uring_register_files(ring_fd, sp[0], sp[1]);
+ if (ret < 0) {
+ perror("register files");
+ return 1;
+ }
+
+ pid = fork();
+ if (pid)
+ send_fd(sp[0], ring_fd);
+
+ close(ring_fd);
+ close(sp[0]);
+ close(sp[1]);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/ring-leak2.c b/contrib/libs/liburing/test/ring-leak2.c
new file mode 100644
index 00000000000..b0b43413ef8
--- /dev/null
+++ b/contrib/libs/liburing/test/ring-leak2.c
@@ -0,0 +1,250 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test two ring deadlock. A buggy kernel will end up
+ * having io_wq_* workers pending, as the circular reference
+ * will prevent full exit.
+ *
+ * Based on a test case from Josef <josef.grieb@gmail.com>
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <strings.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <unistd.h>
+#include <sys/eventfd.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "../src/syscall.h"
+
+enum {
+ ACCEPT,
+ READ,
+ WRITE,
+ POLLING_IN,
+ POLLING_RDHUP,
+ CLOSE,
+ EVENTFD_READ,
+};
+
+typedef struct conn_info {
+ __u32 fd;
+ __u16 type;
+ __u16 bid;
+} conn_info;
+
+static char read_eventfd_buffer[8];
+
+static pthread_mutex_t lock;
+static struct io_uring *client_ring;
+
+static int client_eventfd = -1;
+
+int setup_io_uring(struct io_uring *ring)
+{
+ struct io_uring_params p = { };
+ int ret;
+
+ ret = io_uring_queue_init_params(8, ring, &p);
+ if (ret) {
+ fprintf(stderr, "Unable to setup io_uring: %s\n",
+ strerror(-ret));
+ return 1;
+ }
+ return 0;
+}
+
+static void add_socket_eventfd_read(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+ conn_info conn_i = {
+ .fd = fd,
+ .type = EVENTFD_READ,
+ };
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fd, &read_eventfd_buffer, 8, 0);
+ io_uring_sqe_set_flags(sqe, IOSQE_ASYNC);
+
+ memcpy(&sqe->user_data, &conn_i, sizeof(conn_i));
+}
+
+static void add_socket_pollin(struct io_uring *ring, int fd)
+{
+ struct io_uring_sqe *sqe;
+ conn_info conn_i = {
+ .fd = fd,
+ .type = POLLING_IN,
+ };
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_poll_add(sqe, fd, POLL_IN);
+
+ memcpy(&sqe->user_data, &conn_i, sizeof(conn_i));
+}
+
+static void *server_thread(void *arg)
+{
+ struct sockaddr_in serv_addr;
+ int port = 0;
+ int sock_listen_fd, evfd;
+ const int val = 1;
+ struct io_uring ring;
+
+ sock_listen_fd = socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK, 0);
+ setsockopt(sock_listen_fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ memset(&serv_addr, 0, sizeof(serv_addr));
+ serv_addr.sin_family = AF_INET;
+ serv_addr.sin_port = htons(port);
+ serv_addr.sin_addr.s_addr = INADDR_ANY;
+
+ evfd = eventfd(0, EFD_CLOEXEC);
+
+ // bind and listen
+ if (bind(sock_listen_fd, (struct sockaddr *)&serv_addr, sizeof(serv_addr)) < 0) {
+ perror("Error binding socket...\n");
+ exit(1);
+ }
+ if (listen(sock_listen_fd, 1) < 0) {
+ perror("Error listening on socket...\n");
+ exit(1);
+ }
+
+ setup_io_uring(&ring);
+ add_socket_eventfd_read(&ring, evfd);
+ add_socket_pollin(&ring, sock_listen_fd);
+
+ while (1) {
+ struct io_uring_cqe *cqe;
+ unsigned head;
+ unsigned count = 0;
+
+ io_uring_submit_and_wait(&ring, 1);
+
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ struct conn_info conn_i;
+
+ count++;
+ memcpy(&conn_i, &cqe->user_data, sizeof(conn_i));
+
+ if (conn_i.type == ACCEPT) {
+ int sock_conn_fd = cqe->res;
+ // only read when there is no error, >= 0
+ if (sock_conn_fd > 0) {
+ add_socket_pollin(&ring, sock_listen_fd);
+
+ pthread_mutex_lock(&lock);
+ io_uring_submit(client_ring);
+ pthread_mutex_unlock(&lock);
+
+ }
+ } else if (conn_i.type == POLLING_IN) {
+ break;
+ }
+ }
+ io_uring_cq_advance(&ring, count);
+ }
+}
+
+static void *client_thread(void *arg)
+{
+ struct io_uring ring;
+ int ret;
+
+ setup_io_uring(&ring);
+ client_ring = &ring;
+
+ client_eventfd = eventfd(0, EFD_CLOEXEC);
+ pthread_mutex_lock(&lock);
+ add_socket_eventfd_read(&ring, client_eventfd);
+ pthread_mutex_unlock(&lock);
+
+ while (1) {
+ struct io_uring_cqe *cqe;
+ unsigned head;
+ unsigned count = 0;
+
+ pthread_mutex_lock(&lock);
+ io_uring_submit(&ring);
+ pthread_mutex_unlock(&lock);
+
+ ret = __sys_io_uring_enter(ring.ring_fd, 0, 1, IORING_ENTER_GETEVENTS, NULL);
+ if (ret < 0) {
+ perror("Error io_uring_enter...\n");
+ exit(1);
+ }
+
+ // go through all CQEs
+ io_uring_for_each_cqe(&ring, head, cqe) {
+ struct conn_info conn_i;
+ int type;
+
+ count++;
+ memcpy(&conn_i, &cqe->user_data, sizeof(conn_i));
+
+ type = conn_i.type;
+ if (type == READ) {
+ pthread_mutex_lock(&lock);
+
+ if (cqe->res <= 0) {
+ // connection closed or error
+ shutdown(conn_i.fd, SHUT_RDWR);
+ } else {
+ pthread_mutex_unlock(&lock);
+ break;
+ }
+ add_socket_pollin(&ring, conn_i.fd);
+ pthread_mutex_unlock(&lock);
+ } else if (type == WRITE) {
+ } else if (type == POLLING_IN) {
+ break;
+ } else if (type == POLLING_RDHUP) {
+ break;
+ } else if (type == CLOSE) {
+ } else if (type == EVENTFD_READ) {
+ add_socket_eventfd_read(&ring, client_eventfd);
+ }
+ }
+
+ io_uring_cq_advance(&ring, count);
+ }
+}
+
+static void sig_alrm(int sig)
+{
+ exit(0);
+}
+
+int main(int argc, char *argv[])
+{
+ pthread_t server_thread_t, client_thread_t;
+ struct sigaction act;
+
+ if (argc > 1)
+ return 0;
+
+ if (pthread_mutex_init(&lock, NULL) != 0) {
+ printf("\n mutex init failed\n");
+ return 1;
+ }
+
+ pthread_create(&server_thread_t, NULL, &server_thread, NULL);
+ pthread_create(&client_thread_t, NULL, &client_thread, NULL);
+
+ memset(&act, 0, sizeof(act));
+ act.sa_handler = sig_alrm;
+ act.sa_flags = SA_RESTART;
+ sigaction(SIGALRM, &act, NULL);
+ alarm(1);
+
+ pthread_join(server_thread_t, NULL);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/ringbuf-read.c b/contrib/libs/liburing/test/ringbuf-read.c
new file mode 100644
index 00000000000..2eede182130
--- /dev/null
+++ b/contrib/libs/liburing/test/ringbuf-read.c
@@ -0,0 +1,201 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: ring mapped provided buffers with reads
+ *
+ */
+#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 4096
+#define NR_BUFS 64
+#define FSIZE (BUF_SIZE * NR_BUFS)
+
+#define BR_MASK (NR_BUFS - 1)
+
+static int no_buf_ring;
+
+static int verify_buffer(char *buf, char val)
+{
+ int i;
+
+ for (i = 0; i < BUF_SIZE; i++) {
+ if (buf[i] != val) {
+ fprintf(stderr, "got %d, wanted %d\n", buf[i], val);
+ return 1;
+ }
+ }
+
+ return 0;
+}
+
+static int test(const char *filename, int dio, int async)
+{
+ struct io_uring_buf_reg reg = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ struct io_uring_buf_ring *br;
+ int ret, fd, i;
+ char *buf;
+ void *ptr;
+
+ ret = io_uring_queue_init(NR_BUFS, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ if (dio)
+ fd = open(filename, O_DIRECT | O_RDONLY);
+ else
+ fd = open(filename, O_RDONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ posix_fadvise(fd, 0, FSIZE, POSIX_FADV_DONTNEED);
+
+ if (posix_memalign((void **) &buf, 4096, FSIZE))
+ return 1;
+ if (posix_memalign((void **) &br, 4096, 4096))
+ return 1;
+
+ reg.ring_addr = (unsigned long) br;
+ reg.ring_entries = NR_BUFS;
+ reg.bgid = 1;
+
+ ret = io_uring_register_buf_ring(&ring, &reg, 0);
+ if (ret) {
+ 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);
+
+ for (i = 0; i < NR_BUFS; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fd, NULL, BUF_SIZE, i * BUF_SIZE);
+ sqe->buf_group = 1;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ if (async && !(i & 1))
+ sqe->flags |= IOSQE_ASYNC;
+ sqe->user_data = i + 1;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != NR_BUFS) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < NR_BUFS; i++) {
+ int bid, ud;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait cqe failed %d\n", ret);
+ return 1;
+ }
+ if (cqe->res != BUF_SIZE) {
+ fprintf(stderr, "cqe res %d\n", cqe->res);
+ return 1;
+ }
+ if (!(cqe->flags & IORING_CQE_F_BUFFER)) {
+ fprintf(stderr, "no buffer selected\n");
+ return 1;
+ }
+ bid = cqe->flags >> IORING_CQE_BUFFER_SHIFT;
+ ud = cqe->user_data;
+ io_uring_cqe_seen(&ring, cqe);
+ if (verify_buffer(buf + ((bid - 1) * BUF_SIZE), ud))
+ return 1;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ char buf[BUF_SIZE];
+ char fname[80];
+ int ret, fd, i, do_unlink;
+
+ if (argc > 1) {
+ strcpy(fname, argv[1]);
+ do_unlink = 0;
+ } else {
+ sprintf(fname, ".ringbuf-read.%d", getpid());
+ t_create_file(fname, FSIZE);
+ do_unlink = 1;
+ }
+
+ fd = open(fname, O_WRONLY);
+ if (fd < 0) {
+ perror("open");
+ goto err;
+ }
+ for (i = 0; i < NR_BUFS; i++) {
+ memset(buf, i + 1, BUF_SIZE);
+ ret = write(fd, buf, BUF_SIZE);
+ if (ret != BUF_SIZE) {
+ fprintf(stderr, "bad file prep write\n");
+ close(fd);
+ goto err;
+ }
+ }
+ close(fd);
+
+ ret = test(fname, 1, 0);
+ if (ret) {
+ fprintf(stderr, "dio test failed\n");
+ goto err;
+ }
+ if (no_buf_ring)
+ goto pass;
+
+ ret = test(fname, 0, 0);
+ if (ret) {
+ fprintf(stderr, "buffered test failed\n");
+ goto err;
+ }
+
+ ret = test(fname, 1, 1);
+ if (ret) {
+ fprintf(stderr, "dio async test failed\n");
+ goto err;
+ }
+
+ ret = test(fname, 0, 1);
+ if (ret) {
+ fprintf(stderr, "buffered async test failed\n");
+ goto err;
+ }
+
+pass:
+ ret = T_EXIT_PASS;
+ goto out;
+err:
+ ret = T_EXIT_FAIL;
+out:
+ if (do_unlink)
+ unlink(fname);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/rsrc_tags.c b/contrib/libs/liburing/test/rsrc_tags.c
new file mode 100644
index 00000000000..8d3fe9307b7
--- /dev/null
+++ b/contrib/libs/liburing/test/rsrc_tags.c
@@ -0,0 +1,462 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various file registration tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "../src/syscall.h"
+#include "helpers.h"
+#include "liburing.h"
+
+static int pipes[2];
+
+enum {
+ TEST_IORING_RSRC_FILE = 0,
+ TEST_IORING_RSRC_BUFFER = 1,
+};
+
+static bool check_cq_empty(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe = NULL;
+ int ret;
+
+ usleep(1000); /* doesn't happen immediately, so wait */
+ ret = io_uring_peek_cqe(ring, &cqe); /* nothing should be there */
+ return ret == -EAGAIN;
+}
+
+/*
+ * There are io_uring_register_buffers_tags() and other wrappers,
+ * but they may change, so hand-code to specifically test this ABI.
+ */
+static int register_rsrc(struct io_uring *ring, int type, int nr,
+ const void *arg, const __u64 *tags)
+{
+ struct io_uring_rsrc_register reg;
+ int reg_type;
+
+ memset(&reg, 0, sizeof(reg));
+ reg.nr = nr;
+ reg.data = (__u64)(uintptr_t)arg;
+ reg.tags = (__u64)(uintptr_t)tags;
+
+ reg_type = IORING_REGISTER_FILES2;
+ if (type != TEST_IORING_RSRC_FILE)
+ reg_type = IORING_REGISTER_BUFFERS2;
+
+ return __sys_io_uring_register(ring->ring_fd, reg_type, &reg,
+ sizeof(reg));
+}
+
+/*
+ * There are io_uring_register_buffers_update_tag() and other wrappers,
+ * but they may change, so hand-code to specifically test this ABI.
+ */
+static int update_rsrc(struct io_uring *ring, int type, int nr, int off,
+ const void *arg, const __u64 *tags)
+{
+ struct io_uring_rsrc_update2 up;
+ int up_type;
+
+ memset(&up, 0, sizeof(up));
+ up.offset = off;
+ up.data = (__u64)(uintptr_t)arg;
+ up.tags = (__u64)(uintptr_t)tags;
+ up.nr = nr;
+
+ up_type = IORING_REGISTER_FILES_UPDATE2;
+ if (type != TEST_IORING_RSRC_FILE)
+ up_type = IORING_REGISTER_BUFFERS_UPDATE;
+ return __sys_io_uring_register(ring->ring_fd, up_type, &up, sizeof(up));
+}
+
+static bool has_rsrc_update(void)
+{
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_init() failed, %d\n", ret);
+ exit(1);
+ }
+
+ ret = ring.features & IORING_FEAT_RSRC_TAGS;
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+static int test_tags_generic(int nr, int type, void *rsrc, int ring_flags)
+{
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+ int i, ret;
+ __u64 *tags;
+
+ tags = malloc(nr * sizeof(*tags));
+ if (!tags)
+ return 1;
+ for (i = 0; i < nr; i++)
+ tags[i] = i + 1;
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ ret = register_rsrc(&ring, type, nr, rsrc, tags);
+ if (ret) {
+ fprintf(stderr, "rsrc register failed %i\n", ret);
+ return 1;
+ }
+
+ /* test that tags are set */
+ tags[0] = 666;
+ ret = update_rsrc(&ring, type, 1, 0, rsrc, &tags[0]);
+ assert(ret == 1);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == 1);
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* test that tags are updated */
+ tags[0] = 0;
+ ret = update_rsrc(&ring, type, 1, 0, rsrc, &tags[0]);
+ assert(ret == 1);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == 666);
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* test tag=0 doesn't emit CQE */
+ tags[0] = 1;
+ ret = update_rsrc(&ring, type, 1, 0, rsrc, &tags[0]);
+ assert(ret == 1);
+ assert(check_cq_empty(&ring));
+
+ free(tags);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_buffers_update(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+ const int nr = 5;
+ int buf_idx = 1, i, ret;
+ int pipes[2];
+ char tmp_buf[1024];
+ char tmp_buf2[1024];
+ struct iovec vecs[nr];
+ __u64 tags[nr];
+
+ for (i = 0; i < nr; i++) {
+ vecs[i].iov_base = tmp_buf;
+ vecs[i].iov_len = 1024;
+ tags[i] = i + 1;
+ }
+
+ ret = test_tags_generic(nr, TEST_IORING_RSRC_BUFFER, vecs, 0);
+ if (ret)
+ return 1;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+ if (pipe(pipes) < 0) {
+ perror("pipe");
+ return 1;
+ }
+ ret = register_rsrc(&ring, TEST_IORING_RSRC_BUFFER, nr, vecs, tags);
+ if (ret) {
+ fprintf(stderr, "rsrc register failed %i\n", ret);
+ return 1;
+ }
+
+ /* test that CQE is not emitted before we're done with a buffer */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read_fixed(sqe, pipes[0], tmp_buf, 10, 0, 0);
+ sqe->user_data = 100;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ assert(ret == -EAGAIN);
+
+ vecs[buf_idx].iov_base = tmp_buf2;
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_BUFFER, 1, buf_idx,
+ &vecs[buf_idx], &tags[buf_idx]);
+ if (ret != 1) {
+ fprintf(stderr, "rsrc update failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ ret = io_uring_peek_cqe(&ring, &cqe); /* nothing should be there */
+ assert(ret == -EAGAIN);
+ close(pipes[0]);
+ close(pipes[1]);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == 100);
+ io_uring_cqe_seen(&ring, cqe);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == buf_idx + 1);
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_buffers_empty_buffers(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+ const int nr = 5;
+ int ret, i;
+ char tmp_buf[1024];
+ struct iovec vecs[nr];
+
+ for (i = 0; i < nr; i++) {
+ vecs[i].iov_base = 0;
+ vecs[i].iov_len = 0;
+ }
+ vecs[0].iov_base = tmp_buf;
+ vecs[0].iov_len = 10;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+
+ ret = register_rsrc(&ring, TEST_IORING_RSRC_BUFFER, nr, vecs, NULL);
+ if (ret) {
+ fprintf(stderr, "rsrc register failed %i\n", ret);
+ return 1;
+ }
+
+ /* empty to buffer */
+ vecs[1].iov_base = tmp_buf;
+ vecs[1].iov_len = 10;
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_BUFFER, 1, 1, &vecs[1], NULL);
+ if (ret != 1) {
+ fprintf(stderr, "rsrc update failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ /* buffer to empty */
+ vecs[0].iov_base = 0;
+ vecs[0].iov_len = 0;
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_BUFFER, 1, 0, &vecs[0], NULL);
+ if (ret != 1) {
+ fprintf(stderr, "rsrc update failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ /* zero to zero is ok */
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_BUFFER, 1, 2, &vecs[2], NULL);
+ if (ret != 1) {
+ fprintf(stderr, "rsrc update failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ /* empty buf with non-zero len fails */
+ vecs[3].iov_base = 0;
+ vecs[3].iov_len = 1;
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_BUFFER, 1, 3, &vecs[3], NULL);
+ if (ret >= 0) {
+ fprintf(stderr, "rsrc update failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ /* test rw on empty ubuf is failed */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read_fixed(sqe, pipes[0], tmp_buf, 10, 0, 2);
+ sqe->user_data = 100;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == 100);
+ assert(cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read_fixed(sqe, pipes[0], tmp_buf, 0, 0, 2);
+ sqe->user_data = 100;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret && cqe->user_data == 100);
+ assert(cqe->res);
+ io_uring_cqe_seen(&ring, cqe);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+
+static int test_files(int ring_flags)
+{
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+ const int nr = 50;
+ int off = 5, i, ret, fd;
+ __s32 files[nr];
+ __u64 tags[nr], tag;
+
+ for (i = 0; i < nr; ++i) {
+ files[i] = pipes[0];
+ tags[i] = i + 1;
+ }
+
+ ret = test_tags_generic(nr, TEST_IORING_RSRC_FILE, files, ring_flags);
+ if (ret)
+ return 1;
+
+ ret = io_uring_queue_init(1, &ring, ring_flags);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+ ret = register_rsrc(&ring, TEST_IORING_RSRC_FILE, nr, files, tags);
+ if (ret) {
+ fprintf(stderr, "rsrc register failed %i\n", ret);
+ return 1;
+ }
+
+ /* check update did update tag */
+ fd = -1;
+ ret = io_uring_register_files_update(&ring, off, &fd, 1);
+ assert(ret == 1);
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring wait ret=%d\n", ret);
+ return 1;
+ }
+ if (cqe->user_data != tags[off]) {
+ fprintf(stderr, "data %lx != %lx\n",
+ (unsigned long) cqe->user_data,
+ (unsigned long) tags[off]);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ /* remove removed file, shouldn't emit old tag */
+ ret = io_uring_register_files_update(&ring, off, &fd, 1);
+ assert(ret <= 1);
+ assert(check_cq_empty(&ring));
+
+ /* non-zero tag with remove update is disallowed */
+ tag = 1;
+ fd = -1;
+ ret = update_rsrc(&ring, TEST_IORING_RSRC_FILE, 1, off + 1, &fd, &tag);
+ assert(ret);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+static int test_notag(void)
+{
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+ int i, ret, fd;
+ const int nr = 50;
+ int files[nr];
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return 1;
+ }
+ for (i = 0; i < nr; ++i)
+ files[i] = pipes[0];
+
+ ret = io_uring_register_files(&ring, files, nr);
+ assert(!ret);
+
+ /* default register, update shouldn't emit CQE */
+ fd = -1;
+ ret = io_uring_register_files_update(&ring, 0, &fd, 1);
+ assert(ret == 1);
+ assert(check_cq_empty(&ring));
+
+ ret = io_uring_unregister_files(&ring);
+ assert(!ret);
+ ret = io_uring_peek_cqe(&ring, &cqe); /* nothing should be there */
+ assert(ret);
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ring_flags[] = {0, IORING_SETUP_IOPOLL, IORING_SETUP_SQPOLL,
+ IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN};
+ int i, ret;
+
+ if (argc > 1)
+ return 0;
+ if (!has_rsrc_update()) {
+ fprintf(stderr, "doesn't support rsrc tags, skip\n");
+ return 0;
+ }
+
+ if (pipe(pipes) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = test_notag();
+ if (ret) {
+ printf("test_notag failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < sizeof(ring_flags) / sizeof(ring_flags[0]); i++) {
+ int flag = ring_flags[i];
+
+ if (flag & IORING_SETUP_DEFER_TASKRUN && !t_probe_defer_taskrun())
+ continue;
+
+ ret = test_files(flag);
+ if (ret) {
+ printf("test_tag failed, type %i\n", i);
+ return ret;
+ }
+ }
+
+ ret = test_buffers_update();
+ if (ret) {
+ printf("test_buffers_update failed\n");
+ return ret;
+ }
+
+ ret = test_buffers_empty_buffers();
+ if (ret) {
+ printf("test_buffers_empty_buffers failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/rw_merge_test.c b/contrib/libs/liburing/test/rw_merge_test.c
new file mode 100644
index 00000000000..e771eb73975
--- /dev/null
+++ b/contrib/libs/liburing/test/rw_merge_test.c
@@ -0,0 +1,99 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Regression test for incorrect async_list io_should_merge() logic
+ * Bug was fixed in 5.5 by (commit: 561fb04 io_uring: replace workqueue usage with io-wq")
+ * Affects 5.4 lts branch, at least 5.4.106 is affected.
+ */
+#include <stdio.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <assert.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret, fd, pipe1[2];
+ char buf[4096];
+ struct iovec vec = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf)
+ };
+ struct __kernel_timespec ts = {.tv_sec = 3, .tv_nsec = 0};
+
+ if (argc > 1)
+ return 0;
+
+ ret = pipe(pipe1);
+ assert(!ret);
+
+ fd = open("testfile", O_RDWR | O_CREAT, 0644);
+ assert(fd >= 0);
+ unlink("testfile");
+ ret = ftruncate(fd, 4096);
+ assert(!ret);
+
+ ret = t_create_ring(4, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret < 0)
+ return 1;
+
+ /* REQ1 */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, pipe1[0], &vec, 1, 0);
+ sqe->user_data = 1;
+
+ /* REQ2 */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, fd, &vec, 1, 4096);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ assert(ret == 2);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret);
+ assert(cqe->res == 0);
+ assert(cqe->user_data == 2);
+ io_uring_cqe_seen(&ring, cqe);
+
+ /*
+ * REQ3
+ * Prepare request adjacent to previous one, so merge logic may want to
+ * link it to previous request, but because of a bug in merge logic
+ * it may be merged with <REQ1> request
+ */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, fd, &vec, 1, 2048);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(&ring);
+ assert(ret == 1);
+
+ /*
+ * Read may stuck because of bug there request was be incorrectly
+ * merged with <REQ1> request
+ */
+ ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
+ if (ret == -ETIME) {
+ printf("TEST_FAIL: readv req3 stuck\n");
+ return 1;
+ }
+ assert(!ret);
+
+ assert(cqe->res == 2048);
+ assert(cqe->user_data == 3);
+
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/self.c b/contrib/libs/liburing/test/self.c
new file mode 100644
index 00000000000..a62cc7380e0
--- /dev/null
+++ b/contrib/libs/liburing/test/self.c
@@ -0,0 +1,92 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test that pathname resolution works from async context when
+ * using /proc/self/ which should be the original submitting task, not the
+ * async worker.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+static int io_openat2(struct io_uring *ring, const char *path, int dfd)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct open_how how;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ memset(&how, 0, sizeof(how));
+ how.flags = O_RDONLY;
+ io_uring_prep_openat2(sqe, dfd, path, &how);
+
+ 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;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ char buf[64];
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = io_openat2(&ring, "/proc/self/comm", -1);
+ if (ret < 0) {
+ if (ret == -EOPNOTSUPP)
+ return 0;
+ if (ret == -EINVAL) {
+ fprintf(stdout, "openat2 not supported, skipping\n");
+ return 0;
+ }
+ fprintf(stderr, "openat2 failed: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ memset(buf, 0, sizeof(buf));
+ ret = read(ret, buf, sizeof(buf));
+ if (ret < 0) {
+ perror("read");
+ return 1;
+ }
+
+ if (strncmp(buf, "self", 4)) {
+ fprintf(stderr, "got comm=<%s>, wanted <self>\n", buf);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/send-zerocopy.c b/contrib/libs/liburing/test/send-zerocopy.c
new file mode 100644
index 00000000000..b201e68317d
--- /dev/null
+++ b/contrib/libs/liburing/test/send-zerocopy.c
@@ -0,0 +1,685 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+#include <errno.h>
+#include <error.h>
+#include <limits.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <arpa/inet.h>
+#include <linux/errqueue.h>
+#include <linux/if_packet.h>
+#include <linux/ipv6.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <net/ethernet.h>
+#include <net/if.h>
+#include <netinet/ip.h>
+#include <netinet/in.h>
+#include <netinet/ip6.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/un.h>
+#include <sys/ioctl.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define MAX_MSG 128
+
+#define PORT 10200
+#define HOST "127.0.0.1"
+#define HOSTV6 "::1"
+
+#define CORK_REQS 5
+#define RX_TAG 10000
+#define BUFFER_OFFSET 41
+
+#ifndef ARRAY_SIZE
+ #define ARRAY_SIZE(a) (sizeof(a)/sizeof((a)[0]))
+#endif
+
+enum {
+ BUF_T_NORMAL,
+ BUF_T_SMALL,
+ BUF_T_NONALIGNED,
+ BUF_T_LARGE,
+};
+
+static char *tx_buffer, *rx_buffer;
+static struct iovec buffers_iov[4];
+static bool has_sendmsg;
+
+static bool check_cq_empty(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe = NULL;
+ int ret;
+
+ ret = io_uring_peek_cqe(ring, &cqe); /* nothing should be there */
+ return ret == -EAGAIN;
+}
+
+static int test_basic_send(struct io_uring *ring, 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;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_send_zc(sqe, sock_tx, tx_buffer, payload_size,
+ msg_flags, zc_flags);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ assert(ret == 1);
+
+ 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) {
+ fprintf(stderr, "send failed %i\n", cqe->res);
+ return T_EXIT_FAIL;
+ }
+
+ assert(cqe->flags & IORING_CQE_F_MORE);
+ io_uring_cqe_seen(ring, cqe);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ assert(!ret);
+ assert(cqe->user_data == 1);
+ assert(cqe->flags & IORING_CQE_F_NOTIF);
+ assert(!(cqe->flags & IORING_CQE_F_MORE));
+ io_uring_cqe_seen(ring, cqe);
+ assert(check_cq_empty(ring));
+
+ ret = recv(sock_rx, rx_buffer, payload_size, MSG_TRUNC);
+ assert(ret == payload_size);
+ return T_EXIT_PASS;
+}
+
+static int test_send_faults(struct io_uring *ring, 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 = 2;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_send_zc(sqe, sock_tx, (void *)1UL, payload_size,
+ msg_flags, zc_flags);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_send_zc(sqe, sock_tx, tx_buffer, payload_size,
+ msg_flags, zc_flags);
+ sqe->user_data = 2;
+ io_uring_prep_send_set_addr(sqe, (const struct sockaddr *)1UL,
+ sizeof(struct sockaddr_in6));
+
+ ret = io_uring_submit(ring);
+ assert(ret == 2);
+
+ for (i = 0; i < nr_cqes; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ assert(!ret);
+ assert(cqe->user_data <= 2);
+
+ if (!(cqe->flags & IORING_CQE_F_NOTIF)) {
+ assert(cqe->res == -EFAULT);
+ if (cqe->flags & IORING_CQE_F_MORE)
+ nr_cqes++;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ assert(check_cq_empty(ring));
+ return T_EXIT_PASS;
+}
+
+static int create_socketpair_ip(struct sockaddr_storage *addr,
+ int *sock_client, int *sock_server,
+ bool ipv6, bool client_connect,
+ bool msg_zc, bool tcp)
+{
+ int family, addr_size;
+ int ret, val;
+ int listen_sock = -1;
+ int sock;
+
+ memset(addr, 0, sizeof(*addr));
+ if (ipv6) {
+ struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
+
+ family = AF_INET6;
+ saddr->sin6_family = family;
+ saddr->sin6_port = htons(PORT);
+ addr_size = sizeof(*saddr);
+ } else {
+ struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
+
+ family = AF_INET;
+ saddr->sin_family = family;
+ saddr->sin_port = htons(PORT);
+ saddr->sin_addr.s_addr = htonl(INADDR_ANY);
+ addr_size = sizeof(*saddr);
+ }
+
+ /* server sock setup */
+ if (tcp) {
+ sock = listen_sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ } else {
+ sock = *sock_server = socket(family, SOCK_DGRAM, 0);
+ }
+ if (sock < 0) {
+ perror("socket");
+ return 1;
+ }
+ val = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ val = 1;
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+
+ ret = bind(sock, (struct sockaddr *)addr, addr_size);
+ if (ret < 0) {
+ perror("bind");
+ return 1;
+ }
+ if (tcp) {
+ ret = listen(sock, 128);
+ assert(ret != -1);
+ }
+
+ if (ipv6) {
+ struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
+
+ inet_pton(AF_INET6, HOSTV6, &(saddr->sin6_addr));
+ } else {
+ struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
+
+ inet_pton(AF_INET, HOST, &saddr->sin_addr);
+ }
+
+ /* client sock setup */
+ if (tcp) {
+ *sock_client = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ assert(client_connect);
+ } else {
+ *sock_client = socket(family, SOCK_DGRAM, 0);
+ }
+ if (*sock_client < 0) {
+ perror("socket");
+ return 1;
+ }
+ if (client_connect) {
+ ret = connect(*sock_client, (struct sockaddr *)addr, addr_size);
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+ }
+ if (msg_zc) {
+ val = 1;
+ if (setsockopt(*sock_client, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
+ perror("setsockopt zc");
+ return 1;
+ }
+ }
+ if (tcp) {
+ *sock_server = accept(listen_sock, NULL, NULL);
+ if (!*sock_server) {
+ fprintf(stderr, "can't accept\n");
+ return 1;
+ }
+ close(listen_sock);
+ }
+ return 0;
+}
+
+static int do_test_inet_send(struct io_uring *ring, int sock_client, int sock_server,
+ bool fixed_buf, struct sockaddr_storage *addr,
+ bool cork, bool mix_register,
+ int buf_idx, bool force_async, bool use_sendmsg)
+{
+ struct iovec iov[CORK_REQS];
+ struct msghdr msghdr[CORK_REQS];
+ const unsigned zc_flags = 0;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int nr_reqs = cork ? CORK_REQS : 1;
+ int i, ret, nr_cqes, addr_len = 0;
+ size_t send_size = buffers_iov[buf_idx].iov_len;
+ size_t chunk_size = send_size / nr_reqs;
+ size_t chunk_size_last = send_size - chunk_size * (nr_reqs - 1);
+ char *buf = buffers_iov[buf_idx].iov_base;
+
+ if (addr) {
+ sa_family_t fam = ((struct sockaddr_in *)addr)->sin_family;
+
+ addr_len = (fam == AF_INET) ? sizeof(struct sockaddr_in) :
+ sizeof(struct sockaddr_in6);
+ }
+
+ memset(rx_buffer, 0, send_size);
+
+ for (i = 0; i < nr_reqs; i++) {
+ bool real_fixed_buf = fixed_buf;
+ size_t cur_size = chunk_size;
+ int msg_flags = MSG_WAITALL;
+
+ if (mix_register)
+ real_fixed_buf = rand() & 1;
+
+ if (cork && i != nr_reqs - 1)
+ msg_flags |= MSG_MORE;
+ if (i == nr_reqs - 1)
+ cur_size = chunk_size_last;
+
+ sqe = io_uring_get_sqe(ring);
+
+ if (!use_sendmsg) {
+ io_uring_prep_send_zc(sqe, sock_client, buf + i * chunk_size,
+ cur_size, msg_flags, zc_flags);
+ if (real_fixed_buf) {
+ sqe->ioprio |= IORING_RECVSEND_FIXED_BUF;
+ sqe->buf_index = buf_idx;
+ }
+ if (addr)
+ io_uring_prep_send_set_addr(sqe, (const struct sockaddr *)addr,
+ addr_len);
+ } else {
+ io_uring_prep_sendmsg_zc(sqe, sock_client, &msghdr[i], msg_flags);
+
+ memset(&msghdr[i], 0, sizeof(msghdr[i]));
+ iov[i].iov_len = cur_size;
+ iov[i].iov_base = buf + i * chunk_size;
+ msghdr[i].msg_iov = &iov[i];
+ msghdr[i].msg_iovlen = 1;
+ if (addr) {
+ msghdr[i].msg_name = addr;
+ msghdr[i].msg_namelen = addr_len;
+ }
+ }
+ sqe->user_data = i;
+ if (force_async)
+ sqe->flags |= IOSQE_ASYNC;
+ if (i != nr_reqs - 1)
+ sqe->flags |= IOSQE_IO_LINK;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_recv(sqe, sock_server, rx_buffer, send_size, MSG_WAITALL);
+ sqe->user_data = RX_TAG;
+
+ ret = io_uring_submit(ring);
+ if (ret != nr_reqs + 1) {
+ fprintf(stderr, "submit failed, got %i expected %i\n", ret, nr_reqs);
+ return 1;
+ }
+
+ nr_cqes = 2 * nr_reqs + 1;
+ for (i = 0; i < nr_cqes; i++) {
+ int expected = chunk_size;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe failed %i\n", ret);
+ return 1;
+ }
+ if (cqe->user_data == RX_TAG) {
+ if (cqe->res != send_size) {
+ fprintf(stderr, "rx failed %i\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+
+ if (cqe->user_data >= nr_reqs) {
+ fprintf(stderr, "invalid user_data %lu\n",
+ (unsigned long)cqe->user_data);
+ return 1;
+ }
+ if (!(cqe->flags & IORING_CQE_F_NOTIF)) {
+ if (cqe->user_data == nr_reqs - 1)
+ expected = chunk_size_last;
+ if (cqe->res != expected) {
+ fprintf(stderr, "invalid cqe->res %d expected %d\n",
+ cqe->res, expected);
+ return 1;
+ }
+ }
+ if ((cqe->flags & IORING_CQE_F_MORE) ==
+ (cqe->flags & IORING_CQE_F_NOTIF)) {
+ fprintf(stderr, "unexpected cflags %i res %i\n",
+ cqe->flags, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ for (i = 0; i < send_size; i++) {
+ if (buf[i] != rx_buffer[i]) {
+ fprintf(stderr, "botched data, first mismated byte %i, "
+ "%u vs %u\n", i, buf[i], rx_buffer[i]);
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int test_inet_send(struct io_uring *ring)
+{
+ struct sockaddr_storage addr;
+ int sock_client = -1, sock_server = -1;
+ int ret, j, i;
+
+ for (j = 0; j < 16; j++) {
+ bool ipv6 = j & 1;
+ bool client_connect = j & 2;
+ bool msg_zc_set = j & 4;
+ bool tcp = j & 8;
+
+ if (tcp && !client_connect)
+ continue;
+
+ ret = create_socketpair_ip(&addr, &sock_client, &sock_server, ipv6,
+ client_connect, msg_zc_set, tcp);
+ if (ret) {
+ fprintf(stderr, "sock prep failed %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < 256; i++) {
+ int buf_flavour = i & 3;
+ bool fixed_buf = i & 4;
+ struct sockaddr_storage *addr_arg = (i & 8) ? &addr : NULL;
+ bool cork = i & 16;
+ bool mix_register = i & 32;
+ bool force_async = i & 64;
+ bool use_sendmsg = i & 128;
+
+ if (buf_flavour == BUF_T_LARGE && !tcp)
+ continue;
+ if (!buffers_iov[buf_flavour].iov_base)
+ continue;
+ if (tcp && (cork || addr_arg))
+ continue;
+ if (mix_register && (!cork || fixed_buf))
+ continue;
+ if (!client_connect && addr_arg == NULL)
+ continue;
+ if (use_sendmsg && (mix_register || fixed_buf || !has_sendmsg))
+ continue;
+
+ ret = do_test_inet_send(ring, sock_client, sock_server, fixed_buf,
+ addr_arg, cork, mix_register,
+ buf_flavour, force_async, use_sendmsg);
+ if (ret) {
+ fprintf(stderr, "send failed fixed buf %i, conn %i, addr %i, "
+ "cork %i\n",
+ fixed_buf, client_connect, !!addr_arg,
+ cork);
+ return 1;
+ }
+ }
+
+ close(sock_client);
+ close(sock_server);
+ }
+ return 0;
+}
+
+static int test_async_addr(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct sockaddr_storage addr;
+ int sock_tx = -1, sock_rx = -1;
+ struct __kernel_timespec ts;
+ int ret;
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ ret = create_socketpair_ip(&addr, &sock_tx, &sock_rx, true, false, false, false);
+ if (ret) {
+ fprintf(stderr, "sock prep failed %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_timeout(sqe, &ts, 0, IORING_TIMEOUT_ETIME_SUCCESS);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_send_zc(sqe, sock_tx, tx_buffer, 1, 0, 0);
+ sqe->user_data = 2;
+ io_uring_prep_send_set_addr(sqe, (const struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in6));
+
+ ret = io_uring_submit(ring);
+ assert(ret == 2);
+ memset(&addr, 0, sizeof(addr));
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe failed %i\n", ret);
+ return 1;
+ }
+ if (cqe->user_data != 1 || cqe->res != -ETIME) {
+ fprintf(stderr, "invalid timeout res %i %i\n",
+ (int)cqe->user_data, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe failed %i\n", ret);
+ return 1;
+ }
+ if (cqe->user_data != 2 || cqe->res != 1) {
+ fprintf(stderr, "invalid send %i %i\n",
+ (int)cqe->user_data, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ ret = recv(sock_rx, rx_buffer, 1, MSG_TRUNC);
+ assert(ret == 1);
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe failed %i\n", ret);
+ return 1;
+ }
+ assert(cqe->flags & IORING_CQE_F_NOTIF);
+ io_uring_cqe_seen(ring, cqe);
+
+ close(sock_tx);
+ close(sock_rx);
+ 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])
+{
+ struct io_uring ring;
+ int ret;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ bool notif = false;
+
+ if (!has_sendmsg)
+ return 0;
+
+ ret = t_create_ring(8, &ring, 0);
+ if (ret)
+ return ret;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_sendmsg(sqe, fds[0], NULL, MSG_WAITALL);
+ sqe->opcode = IORING_OP_SENDMSG_ZC;
+ sqe->flags |= IOSQE_ASYNC;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed %i\n", ret);
+ return ret;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret)
+ return 1;
+ if (cqe->flags & IORING_CQE_F_MORE)
+ notif = true;
+ io_uring_cqe_seen(&ring, cqe);
+
+ if (notif) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret)
+ return 1;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct sockaddr_storage addr;
+ struct io_uring ring;
+ int i, ret, sp[2];
+ size_t len;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ /* 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 T_EXIT_FAIL;
+ }
+
+ len = 1U << 25; /* 32MB, should be enough to trigger a short send */
+ tx_buffer = aligned_alloc(4096, len);
+ rx_buffer = aligned_alloc(4096, len);
+ if (tx_buffer && rx_buffer) {
+ buffers_iov[BUF_T_LARGE].iov_base = tx_buffer;
+ buffers_iov[BUF_T_LARGE].iov_len = len;
+ } else {
+ printf("skip large buffer tests, can't alloc\n");
+
+ len = 8192;
+ tx_buffer = aligned_alloc(4096, len);
+ rx_buffer = aligned_alloc(4096, len);
+ }
+ if (!tx_buffer || !rx_buffer) {
+ fprintf(stderr, "can't allocate buffers\n");
+ return T_EXIT_FAIL;
+ }
+
+ buffers_iov[BUF_T_NORMAL].iov_base = tx_buffer + 4096;
+ buffers_iov[BUF_T_NORMAL].iov_len = 4096;
+ buffers_iov[BUF_T_SMALL].iov_base = tx_buffer;
+ buffers_iov[BUF_T_SMALL].iov_len = 137;
+ buffers_iov[BUF_T_NONALIGNED].iov_base = tx_buffer + BUFFER_OFFSET;
+ buffers_iov[BUF_T_NONALIGNED].iov_len = 8192 - BUFFER_OFFSET - 13;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ srand((unsigned)time(NULL));
+ for (i = 0; i < len; i++)
+ tx_buffer[i] = i;
+ memset(rx_buffer, 0, len);
+
+ 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");
+ return T_EXIT_FAIL;
+ }
+
+ has_sendmsg = io_check_zc_sendmsg(&ring);
+
+ ret = test_send_faults(&ring, sp[0], sp[1]);
+ if (ret) {
+ fprintf(stderr, "test_send_faults() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_invalid_zc(sp);
+ if (ret) {
+ fprintf(stderr, "test_invalid_zc() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ close(sp[0]);
+ close(sp[1]);
+
+ ret = test_async_addr(&ring);
+ if (ret) {
+ fprintf(stderr, "test_async_addr() failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ 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\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
new file mode 100644
index 00000000000..5200494a6cb
--- /dev/null
+++ b/contrib/libs/liburing/test/send_recv.c
@@ -0,0 +1,334 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Simple test case showing using send and recv through io_uring
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static char str[] = "This is a test of send and recv over io_uring!";
+
+#define MAX_MSG 128
+
+#define PORT 10202
+#define HOST "127.0.0.1"
+
+static int recv_prep(struct io_uring *ring, struct iovec *iov, int *sock,
+ int registerfiles)
+{
+ struct sockaddr_in saddr;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, val, use_fd;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(PORT);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ val = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ goto err;
+ }
+
+ if (registerfiles) {
+ ret = io_uring_register_files(ring, &sockfd, 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed\n");
+ goto err;
+ }
+ use_fd = 0;
+ } else {
+ use_fd = sockfd;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_recv(sqe, use_fd, iov->iov_base, iov->iov_len, 0);
+ if (registerfiles)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ *sock = sockfd;
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int do_recv(struct io_uring *ring, struct iovec *iov)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stdout, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "recv not supported, skipping\n");
+ return 0;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+
+ if (cqe->res -1 != strlen(str)) {
+ fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
+ (int) strlen(str) + 1);
+ goto err;
+ }
+
+ if (strcmp(str, iov->iov_base)) {
+ fprintf(stderr, "string mismatch\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+struct recv_data {
+ pthread_mutex_t mutex;
+ int use_sqthread;
+ int registerfiles;
+};
+
+static void *recv_fn(void *data)
+{
+ struct recv_data *rd = data;
+ char buf[MAX_MSG + 1];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf) - 1,
+ };
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ int ret, sock;
+
+ if (rd->use_sqthread)
+ p.flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring_params(1, &ring, &p);
+ if (ret == T_SETUP_SKIP) {
+ pthread_mutex_unlock(&rd->mutex);
+ ret = 0;
+ goto err;
+ } else if (ret < 0) {
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+
+ if (rd->use_sqthread && !rd->registerfiles) {
+ if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
+ fprintf(stdout, "Non-registered SQPOLL not available, skipping\n");
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+ }
+
+ ret = recv_prep(&ring, &iov, &sock, rd->registerfiles);
+ if (ret) {
+ fprintf(stderr, "recv_prep failed: %d\n", ret);
+ goto err;
+ }
+ pthread_mutex_unlock(&rd->mutex);
+ ret = do_recv(&ring, &iov);
+
+ close(sock);
+ io_uring_queue_exit(&ring);
+err:
+ return (void *)(intptr_t)ret;
+}
+
+static int do_send(void)
+{
+ struct sockaddr_in saddr;
+ struct iovec iov = {
+ .iov_base = str,
+ .iov_len = sizeof(str),
+ };
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return 1;
+ }
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(PORT);
+ inet_pton(AF_INET, HOST, &saddr.sin_addr);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ ret = connect(sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "send not supported, skipping\n");
+ close(sockfd);
+ return 0;
+ }
+ if (cqe->res != iov.iov_len) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+
+ close(sockfd);
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int test(int use_sqthread, int regfiles)
+{
+ pthread_mutexattr_t attr;
+ pthread_t recv_thread;
+ struct recv_data rd;
+ int ret;
+ void *retval;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, 1);
+ pthread_mutex_init(&rd.mutex, &attr);
+ pthread_mutex_lock(&rd.mutex);
+ rd.use_sqthread = use_sqthread;
+ rd.registerfiles = regfiles;
+
+ ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
+ if (ret) {
+ fprintf(stderr, "Thread create failed: %d\n", ret);
+ pthread_mutex_unlock(&rd.mutex);
+ return 1;
+ }
+
+ pthread_mutex_lock(&rd.mutex);
+ do_send();
+ pthread_join(recv_thread, &retval);
+ return (intptr_t)retval;
+}
+
+static int test_invalid(void)
+{
+ struct io_uring ring;
+ int ret, i;
+ int fds[2];
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+
+ ret = t_create_ring(8, &ring, 0);
+ if (ret)
+ return ret;
+
+ ret = t_create_socket_pair(fds, true);
+ if (ret)
+ return ret;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_sendmsg(sqe, fds[0], NULL, MSG_WAITALL);
+ sqe->flags |= IOSQE_ASYNC;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_recvmsg(sqe, fds[1], NULL, 0);
+ sqe->flags |= IOSQE_ASYNC;
+
+ ret = io_uring_submit_and_wait(&ring, 2);
+ if (ret != 2)
+ return ret;
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_peek_cqe(&ring, &cqe);
+ if (ret || cqe->res != -EFAULT)
+ return -1;
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ close(fds[0]);
+ close(fds[1]);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_invalid();
+ if (ret) {
+ fprintf(stderr, "test_invalid failed\n");
+ return ret;
+ }
+
+ ret = test(0, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=0 failed\n");
+ return ret;
+ }
+
+ ret = test(1, 1);
+ if (ret) {
+ fprintf(stderr, "test sqthread=1 reg=1 failed\n");
+ return ret;
+ }
+
+ ret = test(1, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=1 reg=0 failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/send_recvmsg.c b/contrib/libs/liburing/test/send_recvmsg.c
new file mode 100644
index 00000000000..da64a0fb06c
--- /dev/null
+++ b/contrib/libs/liburing/test/send_recvmsg.c
@@ -0,0 +1,456 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Simple test case showing using sendmsg and recvmsg through io_uring
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "liburing.h"
+
+static char str[] = "This is a test of sendmsg and recvmsg over io_uring!";
+
+static int ud;
+
+#define MAX_MSG 128
+
+#define PORT 10203
+#define HOST "127.0.0.1"
+
+#define BUF_BGID 10
+#define BUF_BID 89
+
+#define MAX_IOV_COUNT 10
+
+static int no_pbuf_ring;
+
+static int recv_prep(struct io_uring *ring, int *sockfd, struct iovec iov[],
+ int iov_count, int bgid, int async)
+{
+ struct sockaddr_in saddr;
+ struct msghdr msg;
+ struct io_uring_sqe *sqe;
+ int ret, val = 1;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+ saddr.sin_port = htons(PORT);
+
+ *sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (*sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ val = 1;
+ setsockopt(*sockfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ setsockopt(*sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ ret = bind(*sockfd, (struct sockaddr *)&saddr, sizeof(saddr));
+ if (ret < 0) {
+ perror("bind");
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "io_uring_get_sqe failed\n");
+ return 1;
+ }
+
+ io_uring_prep_recvmsg(sqe, *sockfd, &msg, 0);
+ if (bgid) {
+ iov->iov_base = NULL;
+ sqe->flags |= IOSQE_BUFFER_SELECT;
+ sqe->buf_group = bgid;
+ iov_count = 1;
+ }
+ sqe->user_data = ++ud;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = iov;
+ msg.msg_iovlen = iov_count;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+err:
+ close(*sockfd);
+ return 1;
+}
+
+struct recv_data {
+ pthread_mutex_t *mutex;
+ int buf_select;
+ int buf_ring;
+ int no_buf_add;
+ int iov_count;
+ int async;
+};
+
+static int do_recvmsg(struct io_uring *ring, char buf[MAX_MSG + 1],
+ struct recv_data *rd)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stdout, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ if (rd->no_buf_add && (rd->buf_select || rd->buf_ring))
+ return 0;
+ fprintf(stderr, "%s: failed cqe: %d\n", __FUNCTION__, cqe->res);
+ goto err;
+ }
+ if (cqe->flags & IORING_CQE_F_BUFFER) {
+ int bid = cqe->flags >> 16;
+ if (bid != BUF_BID)
+ fprintf(stderr, "Buffer ID mismatch %d\n", bid);
+ }
+
+ if (rd->no_buf_add && (rd->buf_ring || rd->buf_select)) {
+ fprintf(stderr, "Expected -ENOBUFS: %d\n", cqe->res);
+ goto err;
+ }
+
+ if (cqe->res -1 != strlen(str)) {
+ fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
+ (int) strlen(str) + 1);
+ goto err;
+ }
+
+ if (strncmp(str, buf, MAX_MSG + 1)) {
+ fprintf(stderr, "string mismatch\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static void init_iov(struct iovec iov[MAX_IOV_COUNT], int iov_to_use,
+ char buf[MAX_MSG + 1])
+{
+ int i, last_idx = iov_to_use - 1;
+
+ assert(0 < iov_to_use && iov_to_use <= MAX_IOV_COUNT);
+ for (i = 0; i < last_idx; ++i) {
+ iov[i].iov_base = buf + i;
+ iov[i].iov_len = 1;
+ }
+
+ iov[last_idx].iov_base = buf + last_idx;
+ iov[last_idx].iov_len = MAX_MSG - last_idx;
+}
+
+static void *recv_fn(void *data)
+{
+ struct recv_data *rd = data;
+ pthread_mutex_t *mutex = rd->mutex;
+ struct io_uring_buf_ring *br = NULL;
+ char buf[MAX_MSG + 1];
+ struct iovec iov[MAX_IOV_COUNT];
+ struct io_uring ring;
+ int ret, sockfd;
+
+ if (rd->buf_ring && no_pbuf_ring)
+ goto out_no_ring;
+
+ init_iov(iov, rd->iov_count, buf);
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ goto err;
+ }
+
+ if ((rd->buf_ring || rd->buf_select) && !rd->no_buf_add) {
+ if (rd->buf_ring) {
+ struct io_uring_buf_reg reg = { };
+ void *ptr;
+
+ if (posix_memalign(&ptr, 4096, 4096))
+ goto err;
+
+ reg.ring_addr = (unsigned long) ptr;
+ reg.ring_entries = 1;
+ reg.bgid = BUF_BGID;
+ if (io_uring_register_buf_ring(&ring, &reg, 0)) {
+ no_pbuf_ring = 1;
+ goto out;
+ }
+
+ br = ptr;
+ io_uring_buf_ring_init(br);
+ io_uring_buf_ring_add(br, buf, sizeof(buf), BUF_BID,
+ io_uring_buf_ring_mask(1), 0);
+ io_uring_buf_ring_advance(br, 1);
+ } else {
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_provide_buffers(sqe, buf, sizeof(buf) -1,
+ 1, BUF_BGID, BUF_BID);
+ sqe->user_data = ++ud;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit ret=%d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(&ring, cqe);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "PROVIDE_BUFFERS not supported, skip\n");
+ goto out;
+ } else if (ret < 0) {
+ fprintf(stderr, "PROVIDER_BUFFERS %d\n", ret);
+ goto err;
+ }
+ }
+ }
+
+ ret = recv_prep(&ring, &sockfd, iov, rd->iov_count,
+ (rd->buf_ring || rd->buf_select) ? BUF_BGID : 0,
+ rd->async);
+ if (ret) {
+ fprintf(stderr, "recv_prep failed: %d\n", ret);
+ goto err;
+ }
+
+ pthread_mutex_unlock(mutex);
+ ret = do_recvmsg(&ring, buf, rd);
+ close(sockfd);
+
+ io_uring_queue_exit(&ring);
+ if (br)
+ free(br);
+err:
+ return (void *)(intptr_t)ret;
+out:
+ io_uring_queue_exit(&ring);
+out_no_ring:
+ pthread_mutex_unlock(mutex);
+ if (br)
+ free(br);
+ return NULL;
+}
+
+static int do_sendmsg(void)
+{
+ struct sockaddr_in saddr;
+ struct iovec iov = {
+ .iov_base = str,
+ .iov_len = sizeof(str),
+ };
+ struct msghdr msg;
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return 1;
+ }
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = htons(PORT);
+ inet_pton(AF_INET, HOST, &saddr.sin_addr);
+
+ memset(&msg, 0, sizeof(msg));
+ msg.msg_name = &saddr;
+ msg.msg_namelen = sizeof(struct sockaddr_in);
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ usleep(10000);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_sendmsg(sqe, sockfd, &msg, 0);
+ sqe->user_data = ++ud;
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res < 0) {
+ fprintf(stderr, "%s: failed cqe: %d\n", __FUNCTION__, cqe->res);
+ goto err;
+ }
+
+ close(sockfd);
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int test(int buf_select, int buf_ring, int no_buf_add, int iov_count,
+ int async)
+{
+ struct recv_data rd;
+ pthread_mutexattr_t attr;
+ pthread_t recv_thread;
+ pthread_mutex_t mutex;
+ int ret;
+ void *retval;
+
+ if (buf_select || buf_ring)
+ assert(iov_count == 1);
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, 1);
+ pthread_mutex_init(&mutex, &attr);
+ pthread_mutex_lock(&mutex);
+
+ rd.mutex = &mutex;
+ rd.buf_select = buf_select;
+ rd.buf_ring = buf_ring;
+ rd.no_buf_add = no_buf_add;
+ rd.iov_count = iov_count;
+ rd.async = async;
+ ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
+ if (ret) {
+ pthread_mutex_unlock(&mutex);
+ fprintf(stderr, "Thread create failed\n");
+ return 1;
+ }
+
+ pthread_mutex_lock(&mutex);
+ do_sendmsg();
+ pthread_join(recv_thread, &retval);
+ ret = (intptr_t)retval;
+
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test(0, 0, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 0 0 0 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(0, 0, 0, 10, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg multi iov failed\n");
+ return 1;
+ }
+
+ ret = test(1, 0, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 1 0 0 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 0, 1, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 1 0 1 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(0, 1, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 0 1 0 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 1, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 1 1 0 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 1, 1, 1, 0);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg 1 1 1 1 0 failed\n");
+ return 1;
+ }
+
+ ret = test(0, 0, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 0 0 0 1 1 failed\n");
+ return 1;
+ }
+
+ ret = test(0, 0, 0, 10, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async multi iov failed\n");
+ return 1;
+ }
+
+ ret = test(1, 0, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 1 0 0 1 1 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 0, 1, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 1 0 1 1 1 failed\n");
+ return 1;
+ }
+
+ ret = test(0, 1, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 0 1 0 1 1 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 1, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 1 1 0 1 1 failed\n");
+ return 1;
+ }
+
+ ret = test(1, 1, 1, 1, 1);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg async 1 1 1 1 1 failed\n");
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sendmsg_fs_cve.c b/contrib/libs/liburing/test/sendmsg_fs_cve.c
new file mode 100644
index 00000000000..3829a5c0852
--- /dev/null
+++ b/contrib/libs/liburing/test/sendmsg_fs_cve.c
@@ -0,0 +1,201 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * repro-CVE-2020-29373 -- Reproducer for CVE-2020-29373.
+ *
+ * Copyright (c) 2021 SUSE
+ * Author: Nicolai Stange <nstange@suse.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <inttypes.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include "liburing.h"
+
+/*
+ * This attempts to make the kernel issue a sendmsg() to
+ * path from io_uring's async io_sq_wq_submit_work().
+ *
+ * Unfortunately, IOSQE_ASYNC is available only from kernel version
+ * 5.6 onwards. To still force io_uring to process the request
+ * asynchronously from io_sq_wq_submit_work(), queue a couple of
+ * auxiliary requests all failing with EAGAIN before. This is
+ * implemented by writing repeatedly to an auxiliary O_NONBLOCK
+ * AF_UNIX socketpair with a small SO_SNDBUF.
+ */
+static int try_sendmsg_async(const char * const path)
+{
+ int snd_sock, r;
+ struct io_uring ring;
+ char sbuf[16] = {};
+ struct iovec siov = { .iov_base = &sbuf, .iov_len = sizeof(sbuf) };
+ struct sockaddr_un addr = {};
+ struct msghdr msg = {
+ .msg_name = &addr,
+ .msg_namelen = sizeof(addr),
+ .msg_iov = &siov,
+ .msg_iovlen = 1,
+ };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+
+ snd_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (snd_sock < 0) {
+ perror("socket(AF_UNIX)");
+ return -1;
+ }
+
+ addr.sun_family = AF_UNIX;
+ strcpy(addr.sun_path, path);
+
+ r = io_uring_queue_init(512, &ring, 0);
+ if (r < 0) {
+ fprintf(stderr, "ring setup failed: %d\n", r);
+ goto close_iour;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ r = -EFAULT;
+ goto close_iour;
+ }
+
+ /* the actual one supposed to fail with -ENOENT. */
+ io_uring_prep_sendmsg(sqe, snd_sock, &msg, 0);
+ sqe->flags = IOSQE_ASYNC;
+ sqe->user_data = 255;
+
+ r = io_uring_submit(&ring);
+ if (r != 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", r);
+ r = -EFAULT;
+ goto close_iour;
+ }
+
+ r = io_uring_wait_cqe(&ring, &cqe);
+ if (r < 0) {
+ fprintf(stderr, "wait completion %d\n", r);
+ r = -EFAULT;
+ goto close_iour;
+ }
+ if (cqe->user_data != 255) {
+ fprintf(stderr, "user data %d\n", r);
+ r = -EFAULT;
+ goto close_iour;
+ }
+ if (cqe->res != -ENOENT) {
+ r = 3;
+ fprintf(stderr,
+ "error: cqe %i: res=%i, but expected -ENOENT\n",
+ (int)cqe->user_data, (int)cqe->res);
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+close_iour:
+ io_uring_queue_exit(&ring);
+ close(snd_sock);
+ return r;
+}
+
+int main(int argc, char *argv[])
+{
+ int r;
+ char tmpdir[] = "/tmp/tmp.XXXXXX";
+ int rcv_sock;
+ struct sockaddr_un addr = {};
+ pid_t c;
+ int wstatus;
+
+ if (!mkdtemp(tmpdir)) {
+ perror("mkdtemp()");
+ return 1;
+ }
+
+ rcv_sock = socket(AF_UNIX, SOCK_DGRAM, 0);
+ if (rcv_sock < 0) {
+ perror("socket(AF_UNIX)");
+ r = 1;
+ goto rmtmpdir;
+ }
+
+ addr.sun_family = AF_UNIX;
+ snprintf(addr.sun_path, sizeof(addr.sun_path), "%s/sock", tmpdir);
+
+ r = bind(rcv_sock, (struct sockaddr *)&addr,
+ sizeof(addr));
+ if (r < 0) {
+ perror("bind()");
+ close(rcv_sock);
+ r = 1;
+ goto rmtmpdir;
+ }
+
+ c = fork();
+ if (!c) {
+ close(rcv_sock);
+
+ r = chroot(tmpdir);
+ if (r) {
+ if (errno == EPERM) {
+ fprintf(stderr, "chroot not allowed, skip\n");
+ return 0;
+ }
+
+ perror("chroot()");
+ return 1;
+ }
+
+ r = try_sendmsg_async(addr.sun_path);
+ if (r < 0) {
+ /* system call failure */
+ r = 1;
+ } else if (r) {
+ /* test case failure */
+ r += 1;
+ }
+ return r;
+ }
+
+ if (waitpid(c, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ r = 1;
+ goto rmsock;
+ }
+ if (!WIFEXITED(wstatus)) {
+ fprintf(stderr, "child got terminated\n");
+ r = 1;
+ goto rmsock;
+ }
+ r = WEXITSTATUS(wstatus);
+ if (r)
+ fprintf(stderr, "error: Test failed\n");
+rmsock:
+ close(rcv_sock);
+ unlink(addr.sun_path);
+rmtmpdir:
+ rmdir(tmpdir);
+ return r;
+}
diff --git a/contrib/libs/liburing/test/shared-wq.c b/contrib/libs/liburing/test/shared-wq.c
new file mode 100644
index 00000000000..d4b721c7e0c
--- /dev/null
+++ b/contrib/libs/liburing/test/shared-wq.c
@@ -0,0 +1,85 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test wq sharing
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+static int test_attach_invalid(int ringfd)
+{
+ struct io_uring_params p;
+ struct io_uring ring;
+ int ret;
+
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_ATTACH_WQ;
+ p.wq_fd = ringfd;
+ ret = io_uring_queue_init_params(1, &ring, &p);
+ if (ret != -EINVAL) {
+ fprintf(stderr, "Attach to zero: %d\n", ret);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_attach(int ringfd)
+{
+ struct io_uring_params p;
+ struct io_uring ring2;
+ int ret;
+
+ memset(&p, 0, sizeof(p));
+ p.flags = IORING_SETUP_ATTACH_WQ;
+ p.wq_fd = ringfd;
+ ret = io_uring_queue_init_params(1, &ring2, &p);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Sharing not supported, skipping\n");
+ return 0;
+ } else if (ret) {
+ fprintf(stderr, "Attach to id: %d\n", ret);
+ goto err;
+ }
+ io_uring_queue_exit(&ring2);
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ /* stdout is definitely not an io_uring descriptor */
+ ret = test_attach_invalid(2);
+ if (ret) {
+ fprintf(stderr, "test_attach_invalid failed\n");
+ return ret;
+ }
+
+ ret = test_attach(ring.ring_fd);
+ if (ret) {
+ fprintf(stderr, "test_attach failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/short-read.c b/contrib/libs/liburing/test/short-read.c
new file mode 100644
index 00000000000..cd8c1644f85
--- /dev/null
+++ b/contrib/libs/liburing/test/short-read.c
@@ -0,0 +1,76 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <poll.h>
+
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define BUF_SIZE 4096
+#define FILE_SIZE 1024
+
+int main(int argc, char *argv[])
+{
+ int ret, fd, save_errno;
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct iovec vec;
+
+ if (argc > 1)
+ return 0;
+
+ vec.iov_base = t_malloc(BUF_SIZE);
+ vec.iov_len = BUF_SIZE;
+
+ t_create_file(".short-read", FILE_SIZE);
+
+ fd = open(".short-read", O_RDONLY);
+ save_errno = errno;
+ unlink(".short-read");
+ errno = save_errno;
+ if (fd < 0) {
+ perror("file open");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return ret;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ return 1;
+ }
+ io_uring_prep_readv(sqe, fd, &vec, 1, 0);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqes(&ring, &cqe, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "wait_cqe failed: %d\n", ret);
+ return 1;
+ }
+
+ if (cqe->res != FILE_SIZE) {
+ fprintf(stderr, "Read failed: %d\n", cqe->res);
+ return 1;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/shutdown.c b/contrib/libs/liburing/test/shutdown.c
new file mode 100644
index 00000000000..e2c59c76668
--- /dev/null
+++ b/contrib/libs/liburing/test/shutdown.c
@@ -0,0 +1,165 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that writev on a socket that has been shutdown(2) fails
+ *
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static void sig_pipe(int sig)
+{
+}
+
+int main(int argc, char *argv[])
+{
+ int p_fd[2], ret;
+ int32_t recv_s0;
+ int32_t val = 1;
+ struct sockaddr_in addr = { };
+
+ if (argc > 1)
+ return 0;
+
+ srand(getpid());
+
+ recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ 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 = listen(recv_s0, 128);
+ assert(ret != -1);
+
+ p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ val = 1;
+ ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret == -1);
+
+ flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags &= ~O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ p_fd[0] = accept(recv_s0, NULL, NULL);
+ assert(p_fd[0] != -1);
+
+ signal(SIGPIPE, sig_pipe);
+
+ while (1) {
+ int32_t code;
+ socklen_t code_len = sizeof(code);
+
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
+
+ if (!code)
+ break;
+ }
+
+ struct io_uring m_io_uring;
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ {
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int res;
+
+ sqe = io_uring_get_sqe(&m_io_uring);
+ io_uring_prep_shutdown(sqe, p_fd[1], SHUT_WR);
+ sqe->user_data = 1;
+
+ res = io_uring_submit_and_wait(&m_io_uring, 1);
+ assert(res != -1);
+
+ res = io_uring_wait_cqe(&m_io_uring, &cqe);
+ if (res < 0) {
+ fprintf(stderr, "wait: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ if (cqe->res) {
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "Shutdown not supported, skipping\n");
+ goto done;
+ }
+ fprintf(stderr, "writev: %d\n", cqe->res);
+ goto err;
+ }
+
+ io_uring_cqe_seen(&m_io_uring, cqe);
+ }
+
+ {
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec iov[1];
+ char send_buff[128];
+ int res;
+
+ iov[0].iov_base = send_buff;
+ iov[0].iov_len = sizeof(send_buff);
+
+ sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_writev(sqe, p_fd[1], iov, 1, 0);
+ res = io_uring_submit_and_wait(&m_io_uring, 1);
+ assert(res != -1);
+
+ res = io_uring_wait_cqe(&m_io_uring, &cqe);
+ if (res < 0) {
+ fprintf(stderr, "wait: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ if (cqe->res != -EPIPE) {
+ fprintf(stderr, "writev: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(&m_io_uring, cqe);
+ }
+
+done:
+ io_uring_queue_exit(&m_io_uring);
+ return 0;
+err:
+ io_uring_queue_exit(&m_io_uring);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sigfd-deadlock.c b/contrib/libs/liburing/test/sigfd-deadlock.c
new file mode 100644
index 00000000000..a2a9ad94883
--- /dev/null
+++ b/contrib/libs/liburing/test/sigfd-deadlock.c
@@ -0,0 +1,89 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test that sigfd reading/polling works. A regression test for
+ * the upstream commit:
+ *
+ * fd7d6de22414 ("io_uring: don't recurse on tsk->sighand->siglock with signalfd")
+ */
+#include <unistd.h>
+#include <sys/signalfd.h>
+#include <sys/epoll.h>
+#include <poll.h>
+#include <stdio.h>
+#include "liburing.h"
+#include "helpers.h"
+
+static int setup_signal(void)
+{
+ sigset_t mask;
+ int sfd;
+
+ sigemptyset(&mask);
+ sigaddset(&mask, SIGINT);
+
+ sigprocmask(SIG_BLOCK, &mask, NULL);
+ sfd = signalfd(-1, &mask, SFD_NONBLOCK);
+ if (sfd < 0)
+ perror("signalfd");
+ return sfd;
+}
+
+static int test_uring(int sfd)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct io_uring ring;
+ int ret;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret)
+ return T_EXIT_FAIL;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, sfd, POLLIN);
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ ret = T_EXIT_FAIL;
+ goto err_exit;
+ }
+
+ kill(getpid(), SIGINT);
+
+ io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res == -EOPNOTSUPP) {
+ fprintf(stderr, "signalfd poll not supported\n");
+ ret = T_EXIT_SKIP;
+ } else if (cqe->res < 0) {
+ fprintf(stderr, "poll failed: %d\n", cqe->res);
+ ret = T_EXIT_FAIL;
+ } else if (cqe->res & POLLIN) {
+ ret = T_EXIT_PASS;
+ } else {
+ fprintf(stderr, "Unexpected poll mask %x\n", cqe->res);
+ ret = T_EXIT_FAIL;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+err_exit:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ int sfd, ret;
+
+ if (argc > 1)
+ return T_EXIT_PASS;
+
+ sfd = setup_signal();
+ if (sfd < 0)
+ return T_EXIT_FAIL;
+
+ ret = test_uring(sfd);
+ if (ret == T_EXIT_FAIL)
+ fprintf(stderr, "test_uring signalfd failed\n");
+
+ close(sfd);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/single-issuer.c b/contrib/libs/liburing/test/single-issuer.c
new file mode 100644
index 00000000000..22f104e94ef
--- /dev/null
+++ b/contrib/libs/liburing/test/single-issuer.c
@@ -0,0 +1,172 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <error.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+
+#include "liburing.h"
+#include "test.h"
+#include "helpers.h"
+
+static pid_t pid;
+
+static pid_t fork_t(void)
+{
+ pid = fork();
+ if (pid == -1) {
+ fprintf(stderr, "fork failed\n");
+ exit(T_EXIT_FAIL);
+ }
+ return pid;
+}
+
+static void wait_child_t(void)
+{
+ int wstatus;
+
+ if (waitpid(pid, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ exit(T_EXIT_FAIL);
+ }
+ if (!WIFEXITED(wstatus)) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ exit(T_EXIT_FAIL);
+ }
+ if (WEXITSTATUS(wstatus))
+ exit(T_EXIT_FAIL);
+}
+
+static int try_submit(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 42;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0)
+ return ret;
+
+ if (ret != 1)
+ error(1, ret, "submit %i", ret);
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret)
+ error(1, ret, "wait fail %i", ret);
+
+ if (cqe->res || cqe->user_data != 42)
+ error(1, ret, "invalid cqe");
+
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER);
+ if (ret == -EINVAL) {
+ fprintf(stderr, "SETUP_SINGLE_ISSUER is not supported, skip\n");
+ return T_EXIT_SKIP;
+ } else if (ret) {
+ fprintf(stderr, "io_uring_queue_init() failed %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* test that the creator iw allowed to submit */
+ ret = try_submit(&ring);
+ if (ret) {
+ fprintf(stderr, "the creator can't submit %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* test that a second submitter doesn't succeed */
+ if (!fork_t()) {
+ ret = try_submit(&ring);
+ if (ret != -EEXIST)
+ fprintf(stderr, "1: not owner child could submit %i\n", ret);
+ return ret != -EEXIST;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+
+ /* test that the first submitter but not creator can submit */
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_R_DISABLED);
+ if (ret)
+ error(1, ret, "ring init (2) %i", ret);
+
+ if (!fork_t()) {
+ io_uring_enable_rings(&ring);
+ ret = try_submit(&ring);
+ if (ret)
+ fprintf(stderr, "2: not owner child could submit %i\n", ret);
+ return !!ret;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+
+ /* test that only the first enabler can submit */
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER |
+ IORING_SETUP_R_DISABLED);
+ if (ret)
+ error(1, ret, "ring init (3) %i", ret);
+
+ io_uring_enable_rings(&ring);
+ if (!fork_t()) {
+ ret = try_submit(&ring);
+ if (ret != -EEXIST)
+ fprintf(stderr, "3: not owner child could submit %i\n", ret);
+ return ret != -EEXIST;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+
+ /* test that anyone can submit to a SQPOLL|SINGLE_ISSUER ring */
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_SQPOLL);
+ if (ret)
+ error(1, ret, "ring init (4) %i", ret);
+
+ ret = try_submit(&ring);
+ if (ret) {
+ fprintf(stderr, "SQPOLL submit failed (creator) %i\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (!fork_t()) {
+ ret = try_submit(&ring);
+ if (ret)
+ fprintf(stderr, "SQPOLL submit failed (child) %i\n", ret);
+ return !!ret;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+
+ /* test that IORING_ENTER_REGISTERED_RING doesn't break anything */
+ ret = io_uring_queue_init(8, &ring, IORING_SETUP_SINGLE_ISSUER);
+ if (ret)
+ error(1, ret, "ring init (5) %i", ret);
+
+ if (!fork_t()) {
+ ret = try_submit(&ring);
+ if (ret != -EEXIST)
+ fprintf(stderr, "4: not owner child could submit %i\n", ret);
+ return ret != -EEXIST;
+ }
+ wait_child_t();
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/skip-cqe.c b/contrib/libs/liburing/test/skip-cqe.c
new file mode 100644
index 00000000000..c73e2b8dea7
--- /dev/null
+++ b/contrib/libs/liburing/test/skip-cqe.c
@@ -0,0 +1,430 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "liburing.h"
+
+#define LINK_SIZE 6
+#define TIMEOUT_USER_DATA (-1)
+
+static int fds[2];
+
+/* should be successfully submitted but fails during execution */
+static void prep_exec_fail_req(struct io_uring_sqe *sqe)
+{
+ io_uring_prep_write(sqe, fds[1], NULL, 100, 0);
+}
+
+static int test_link_success(struct io_uring *ring, int nr, bool skip_last)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ for (i = 0; i < nr; ++i) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ if (i != nr - 1 || skip_last)
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ if (!skip_last) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret != 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res != 0) {
+ fprintf(stderr, "nop failed: res %d\n", cqe->res);
+ goto err;
+ }
+ if (cqe->user_data != nr - 1) {
+ fprintf(stderr, "invalid user_data %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (io_uring_peek_cqe(ring, &cqe) >= 0) {
+ fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_link_fail(struct io_uring *ring, int nr, int fail_idx)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ for (i = 0; i < nr; ++i) {
+ sqe = io_uring_get_sqe(ring);
+ if (i == fail_idx)
+ prep_exec_fail_req(sqe);
+ else
+ io_uring_prep_nop(sqe);
+
+ if (i != nr - 1)
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ 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;
+ }
+ if (!cqe->res || cqe->user_data != fail_idx) {
+ fprintf(stderr, "got: user_data %d res %d, expected data: %d\n",
+ (int)cqe->user_data, cqe->res, fail_idx);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ if (io_uring_peek_cqe(ring, &cqe) >= 0) {
+ fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_ltimeout_cancel(struct io_uring *ring, int nr, int tout_idx,
+ bool async, int fail_idx)
+{
+ struct __kernel_timespec ts = {.tv_sec = 1, .tv_nsec = 0};
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+ int e_res = 0, e_idx = nr - 1;
+
+ if (fail_idx >= 0) {
+ e_res = -EFAULT;
+ e_idx = fail_idx;
+ }
+
+ for (i = 0; i < nr; ++i) {
+ sqe = io_uring_get_sqe(ring);
+ if (i == fail_idx)
+ prep_exec_fail_req(sqe);
+ else
+ io_uring_prep_nop(sqe);
+ sqe->user_data = i;
+ sqe->flags |= IOSQE_IO_LINK;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ if (i != nr - 1)
+ sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+
+ if (i == tout_idx) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= IOSQE_IO_LINK | IOSQE_CQE_SKIP_SUCCESS;
+ sqe->user_data = TIMEOUT_USER_DATA;
+ }
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != nr + 1) {
+ 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;
+ }
+ if (cqe->user_data != e_idx) {
+ fprintf(stderr, "invalid user_data %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ if (cqe->res != e_res) {
+ fprintf(stderr, "unexpected res: %d\n", cqe->res);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ if (io_uring_peek_cqe(ring, &cqe) >= 0) {
+ fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_ltimeout_fire(struct io_uring *ring, bool async,
+ bool skip_main, bool skip_tout)
+{
+ char buf[1];
+ struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 1000000};
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+ int nr = 1 + !skip_tout;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->flags |= async ? IOSQE_ASYNC : 0;
+ sqe->flags |= skip_main ? IOSQE_CQE_SKIP_SUCCESS : 0;
+ sqe->user_data = 0;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ sqe->flags |= skip_tout ? IOSQE_CQE_SKIP_SUCCESS : 0;
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < nr; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret != 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ return 1;
+ }
+ switch (cqe->user_data) {
+ case 0:
+ if (cqe->res != -ECANCELED && cqe->res != -EINTR) {
+ fprintf(stderr, "unexpected read return: %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ case 1:
+ if (skip_tout) {
+ fprintf(stderr, "extra timeout cqe, %d\n", cqe->res);
+ return 1;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+
+ if (io_uring_peek_cqe(ring, &cqe) >= 0) {
+ fprintf(stderr, "single CQE expected: got data: %i res: %i\n",
+ (int)cqe->user_data, cqe->res);
+ return 1;
+ }
+ return 0;
+}
+
+static int test_hardlink(struct io_uring *ring, int nr, int fail_idx,
+ int skip_idx, bool hardlink_last)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret, i;
+
+ assert(fail_idx < nr);
+ assert(skip_idx < nr);
+
+ for (i = 0; i < nr; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (i == fail_idx)
+ prep_exec_fail_req(sqe);
+ else
+ io_uring_prep_nop(sqe);
+ if (i != nr - 1 || hardlink_last)
+ sqe->flags |= IOSQE_IO_HARDLINK;
+ if (i == skip_idx)
+ sqe->flags |= IOSQE_CQE_SKIP_SUCCESS;
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < nr; i++) {
+ if (i == skip_idx && fail_idx != skip_idx)
+ continue;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret != 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->user_data != i) {
+ fprintf(stderr, "invalid user_data %d (%i)\n",
+ (int)cqe->user_data, i);
+ goto err;
+ }
+ if (i == fail_idx) {
+ if (cqe->res >= 0) {
+ fprintf(stderr, "req should've failed %d %d\n",
+ (int)cqe->user_data, cqe->res);
+ goto err;
+ }
+ } else {
+ if (cqe->res) {
+ fprintf(stderr, "req error %d %d\n",
+ (int)cqe->user_data, cqe->res);
+ goto err;
+ }
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ if (io_uring_peek_cqe(ring, &cqe) >= 0) {
+ fprintf(stderr, "single CQE expected %i\n", (int)cqe->user_data);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret, i, j, k;
+ int mid_idx = LINK_SIZE / 2;
+ int last_idx = LINK_SIZE - 1;
+
+ if (argc > 1)
+ return 0;
+
+ if (pipe(fds)) {
+ fprintf(stderr, "pipe() failed\n");
+ return 1;
+ }
+ ret = io_uring_queue_init(16, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ if (!(ring.features & IORING_FEAT_CQE_SKIP)) {
+ printf("IOSQE_CQE_SKIP_SUCCESS is not supported, skip\n");
+ return 0;
+ }
+
+ for (i = 0; i < 4; i++) {
+ bool skip_last = i & 1;
+ int sz = (i & 2) ? LINK_SIZE : 1;
+
+ ret = test_link_success(&ring, sz, skip_last);
+ if (ret) {
+ fprintf(stderr, "test_link_success sz %d, %d last\n",
+ skip_last, sz);
+ return ret;
+ }
+ }
+
+ ret = test_link_fail(&ring, LINK_SIZE, mid_idx);
+ if (ret) {
+ fprintf(stderr, "test_link_fail mid failed\n");
+ return ret;
+ }
+
+ ret = test_link_fail(&ring, LINK_SIZE, last_idx);
+ if (ret) {
+ fprintf(stderr, "test_link_fail last failed\n");
+ return ret;
+ }
+
+ for (i = 0; i < 2; i++) {
+ bool async = i & 1;
+
+ ret = test_ltimeout_cancel(&ring, 1, 0, async, -1);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel 1 failed, %i\n",
+ async);
+ return ret;
+ }
+ ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, -1);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel mid failed, %i\n",
+ async);
+ return ret;
+ }
+ ret = test_ltimeout_cancel(&ring, LINK_SIZE, last_idx, async, -1);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel last failed, %i\n",
+ async);
+ return ret;
+ }
+ ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel fail mid failed, %i\n",
+ async);
+ return ret;
+ }
+ ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx - 1);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel fail2 mid failed, %i\n",
+ async);
+ return ret;
+ }
+ ret = test_ltimeout_cancel(&ring, LINK_SIZE, mid_idx, async, mid_idx + 1);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_cancel fail3 mid failed, %i\n",
+ async);
+ return ret;
+ }
+ }
+
+ for (i = 0; i < 8; i++) {
+ bool async = i & 1;
+ bool skip1 = i & 2;
+ bool skip2 = i & 4;
+
+ ret = test_ltimeout_fire(&ring, async, skip1, skip2);
+ if (ret) {
+ fprintf(stderr, "test_ltimeout_fire failed\n");
+ return ret;
+ }
+ }
+
+ /* test 3 positions, start/middle/end of the link, i.e. indexes 0, 3, 6 */
+ for (i = 0; i < 3; i++) {
+ for (j = 0; j < 3; j++) {
+ for (k = 0; k < 2; k++) {
+ bool mark_last = k & 1;
+
+ ret = test_hardlink(&ring, 7, i * 3, j * 3, mark_last);
+ if (ret) {
+ fprintf(stderr, "test_hardlink failed"
+ "fail %i skip %i mark last %i\n",
+ i * 3, j * 3, k);
+ return 1;
+ }
+ }
+ }
+ }
+
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/socket-rw-eagain.c b/contrib/libs/liburing/test/socket-rw-eagain.c
new file mode 100644
index 00000000000..e762da531f0
--- /dev/null
+++ b/contrib/libs/liburing/test/socket-rw-eagain.c
@@ -0,0 +1,149 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that a readv on a nonblocking socket queued before a writev doesn't
+ * wait for data to arrive.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ int p_fd[2], ret;
+ int32_t recv_s0;
+ int32_t val = 1;
+ struct sockaddr_in addr;
+ struct iovec iov_r[1], iov_w[1];
+
+ if (argc > 1)
+ return 0;
+
+ srand(getpid());
+
+ recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ 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 = listen(recv_s0, 128);
+ assert(ret != -1);
+
+ p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ val = 1;
+ ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret == -1);
+
+ p_fd[0] = accept(recv_s0, NULL, NULL);
+ assert(p_fd[0] != -1);
+
+ flags = fcntl(p_fd[0], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(p_fd[0], F_SETFL, flags);
+ assert(ret != -1);
+
+ while (1) {
+ int32_t code;
+ socklen_t code_len = sizeof(code);
+
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
+
+ if (!code)
+ break;
+ }
+
+ struct io_uring m_io_uring;
+ struct io_uring_params p = { };
+
+ ret = io_uring_queue_init_params(32, &m_io_uring, &p);
+ assert(ret >= 0);
+
+ if (p.features & IORING_FEAT_FAST_POLL)
+ return 0;
+
+ char recv_buff[128];
+ char send_buff[128];
+
+ {
+ iov_r[0].iov_base = recv_buff;
+ iov_r[0].iov_len = sizeof(recv_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_readv(sqe, p_fd[0], iov_r, 1, 0);
+ sqe->user_data = 1;
+ }
+
+ {
+ iov_w[0].iov_base = send_buff;
+ iov_w[0].iov_len = sizeof(send_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_writev(sqe, p_fd[1], iov_w, 1, 0);
+ sqe->user_data = 2;
+ }
+
+ ret = io_uring_submit_and_wait(&m_io_uring, 2);
+ assert(ret != -1);
+
+ struct io_uring_cqe* cqe;
+ uint32_t head;
+ uint32_t count = 0;
+
+ while (count != 2) {
+ io_uring_for_each_cqe(&m_io_uring, head, cqe) {
+ if (cqe->user_data == 2 && cqe->res != 128) {
+ fprintf(stderr, "write=%d\n", cqe->res);
+ goto err;
+ } else if (cqe->user_data == 1 && cqe->res != -EAGAIN) {
+ fprintf(stderr, "read=%d\n", cqe->res);
+ goto err;
+ }
+ count++;
+ }
+
+ assert(count <= 2);
+ io_uring_cq_advance(&m_io_uring, count);
+ }
+
+ io_uring_queue_exit(&m_io_uring);
+ return 0;
+err:
+ io_uring_queue_exit(&m_io_uring);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/socket-rw-offset.c b/contrib/libs/liburing/test/socket-rw-offset.c
new file mode 100644
index 00000000000..26173876ef1
--- /dev/null
+++ b/contrib/libs/liburing/test/socket-rw-offset.c
@@ -0,0 +1,149 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that a readv on a socket queued before a writev doesn't hang
+ * the processing.
+ *
+ * From Hrvoje Zeba <zeba.hrvoje@gmail.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ int p_fd[2], ret;
+ int32_t recv_s0;
+ int32_t val = 1;
+ struct sockaddr_in addr;
+ struct iovec iov_r[1], iov_w[1];
+
+ if (argc > 1)
+ return 0;
+
+ srand(getpid());
+
+ recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ 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 = listen(recv_s0, 128);
+ assert(ret != -1);
+
+
+ p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ val = 1;
+ ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret == -1);
+
+ flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags &= ~O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ p_fd[0] = accept(recv_s0, NULL, NULL);
+ assert(p_fd[0] != -1);
+
+ while (1) {
+ int32_t code;
+ socklen_t code_len = sizeof(code);
+
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
+
+ if (!code)
+ break;
+ }
+
+ struct io_uring m_io_uring;
+ struct io_uring_params p = { };
+
+ ret = io_uring_queue_init_params(32, &m_io_uring, &p);
+ assert(ret >= 0);
+
+ /* skip for kernels without cur position read/write */
+ if (!(p.features & IORING_FEAT_RW_CUR_POS))
+ return 0;
+
+ char recv_buff[128];
+ char send_buff[128];
+
+ {
+ iov_r[0].iov_base = recv_buff;
+ iov_r[0].iov_len = sizeof(recv_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_readv(sqe, p_fd[0], iov_r, 1, -1);
+ }
+
+ {
+ iov_w[0].iov_base = send_buff;
+ iov_w[0].iov_len = sizeof(send_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_writev(sqe, p_fd[1], iov_w, 1, 0);
+ }
+
+ ret = io_uring_submit_and_wait(&m_io_uring, 2);
+ assert(ret != -1);
+
+ struct io_uring_cqe* cqe;
+ uint32_t head;
+ uint32_t count = 0;
+
+ ret = 0;
+ while (count != 2) {
+ io_uring_for_each_cqe(&m_io_uring, head, cqe) {
+ if (cqe->res != 128) {
+ fprintf(stderr, "Got %d, expected 128\n", cqe->res);
+ ret = 1;
+ goto err;
+ }
+ assert(cqe->res == 128);
+ count++;
+ }
+
+ assert(count <= 2);
+ io_uring_cq_advance(&m_io_uring, count);
+ }
+
+err:
+ io_uring_queue_exit(&m_io_uring);
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/socket-rw.c b/contrib/libs/liburing/test/socket-rw.c
new file mode 100644
index 00000000000..5546714e659
--- /dev/null
+++ b/contrib/libs/liburing/test/socket-rw.c
@@ -0,0 +1,137 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that a readv on a socket queued before a writev doesn't hang
+ * the processing.
+ *
+ * From Hrvoje Zeba <zeba.hrvoje@gmail.com>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+int main(int argc, char *argv[])
+{
+ int p_fd[2], ret;
+ int32_t recv_s0;
+ int32_t val = 1;
+ struct sockaddr_in addr;
+ struct iovec iov_r[1], iov_w[1];
+
+ if (argc > 1)
+ return 0;
+
+ srand(getpid());
+
+ recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
+ assert(ret != -1);
+ ret = setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+ assert(ret != -1);
+
+ 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 = listen(recv_s0, 128);
+ assert(ret != -1);
+
+
+ p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
+
+ val = 1;
+ ret = setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val));
+ assert(ret != -1);
+
+ int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags |= O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ ret = connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret == -1);
+
+ flags = fcntl(p_fd[1], F_GETFL, 0);
+ assert(flags != -1);
+
+ flags &= ~O_NONBLOCK;
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
+
+ p_fd[0] = accept(recv_s0, NULL, NULL);
+ assert(p_fd[0] != -1);
+
+ while (1) {
+ int32_t code;
+ socklen_t code_len = sizeof(code);
+
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
+
+ if (!code)
+ break;
+ }
+
+ struct io_uring m_io_uring;
+
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
+
+ char recv_buff[128];
+ char send_buff[128];
+
+ {
+ iov_r[0].iov_base = recv_buff;
+ iov_r[0].iov_len = sizeof(recv_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_readv(sqe, p_fd[0], iov_r, 1, 0);
+ }
+
+ {
+ iov_w[0].iov_base = send_buff;
+ iov_w[0].iov_len = sizeof(send_buff);
+
+ struct io_uring_sqe* sqe = io_uring_get_sqe(&m_io_uring);
+ assert(sqe != NULL);
+
+ io_uring_prep_writev(sqe, p_fd[1], iov_w, 1, 0);
+ }
+
+ ret = io_uring_submit_and_wait(&m_io_uring, 2);
+ assert(ret != -1);
+
+ struct io_uring_cqe* cqe;
+ uint32_t head;
+ uint32_t count = 0;
+
+ while (count != 2) {
+ io_uring_for_each_cqe(&m_io_uring, head, cqe) {
+ assert(cqe->res == 128);
+ count++;
+ }
+
+ assert(count <= 2);
+ io_uring_cq_advance(&m_io_uring, count);
+ }
+
+ io_uring_queue_exit(&m_io_uring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/socket.c b/contrib/libs/liburing/test/socket.c
new file mode 100644
index 00000000000..c3250c8dc08
--- /dev/null
+++ b/contrib/libs/liburing/test/socket.c
@@ -0,0 +1,410 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Simple test case using the socket op
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <arpa/inet.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <pthread.h>
+#include <assert.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static char str[] = "This is a test of send and recv over io_uring!";
+
+#define MAX_MSG 128
+
+#define HOST "127.0.0.1"
+
+static int no_socket;
+static __be32 g_port;
+
+static int recv_prep(struct io_uring *ring, struct iovec *iov, int *sock,
+ int registerfiles)
+{
+ struct sockaddr_in saddr;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, val, use_fd;
+
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_addr.s_addr = htonl(INADDR_ANY);
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ val = 1;
+ setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val));
+
+ if (t_bind_ephemeral_port(sockfd, &saddr)) {
+ perror("bind");
+ goto err;
+ }
+ g_port = saddr.sin_port;
+
+ if (registerfiles) {
+ ret = io_uring_register_files(ring, &sockfd, 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed\n");
+ goto err;
+ }
+ use_fd = 0;
+ } else {
+ use_fd = sockfd;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_recv(sqe, use_fd, iov->iov_base, iov->iov_len, 0);
+ if (registerfiles)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ *sock = sockfd;
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int do_recv(struct io_uring *ring, struct iovec *iov)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stdout, "wait_cqe: %d\n", ret);
+ goto err;
+ }
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "recv not supported, skipping\n");
+ return 0;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+
+ if (cqe->res -1 != strlen(str)) {
+ fprintf(stderr, "got wrong length: %d/%d\n", cqe->res,
+ (int) strlen(str) + 1);
+ goto err;
+ }
+
+ if (strcmp(str, iov->iov_base)) {
+ fprintf(stderr, "string mismatch\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+struct recv_data {
+ pthread_mutex_t mutex;
+ int use_sqthread;
+ int registerfiles;
+};
+
+static void *recv_fn(void *data)
+{
+ struct recv_data *rd = data;
+ char buf[MAX_MSG + 1];
+ struct iovec iov = {
+ .iov_base = buf,
+ .iov_len = sizeof(buf) - 1,
+ };
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ int ret, sock;
+
+ if (rd->use_sqthread)
+ p.flags = IORING_SETUP_SQPOLL;
+ ret = t_create_ring_params(1, &ring, &p);
+ if (ret == T_SETUP_SKIP) {
+ pthread_mutex_unlock(&rd->mutex);
+ ret = 0;
+ goto err;
+ } else if (ret < 0) {
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+
+ if (rd->use_sqthread && !rd->registerfiles) {
+ if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
+ fprintf(stdout, "Non-registered SQPOLL not available, skipping\n");
+ pthread_mutex_unlock(&rd->mutex);
+ goto err;
+ }
+ }
+
+ ret = recv_prep(&ring, &iov, &sock, rd->registerfiles);
+ if (ret) {
+ fprintf(stderr, "recv_prep failed: %d\n", ret);
+ goto err;
+ }
+ pthread_mutex_unlock(&rd->mutex);
+ ret = do_recv(&ring, &iov);
+
+ close(sock);
+ io_uring_queue_exit(&ring);
+err:
+ return (void *)(intptr_t)ret;
+}
+
+static int fallback_send(struct io_uring *ring, struct sockaddr_in *saddr)
+{
+ struct iovec iov = {
+ .iov_base = str,
+ .iov_len = sizeof(str),
+ };
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret;
+
+ sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+ if (sockfd < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ ret = connect(sockfd, (struct sockaddr *)saddr, sizeof(*saddr));
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "send not supported, skipping\n");
+ close(sockfd);
+ return 0;
+ }
+ if (cqe->res != iov.iov_len) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+
+ close(sockfd);
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int do_send(int socket_direct, int alloc)
+{
+ struct sockaddr_in saddr;
+ struct iovec iov = {
+ .iov_base = str,
+ .iov_len = sizeof(str),
+ };
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int sockfd, ret, fd = -1;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return 1;
+ }
+
+ if (socket_direct) {
+ ret = io_uring_register_files(&ring, &fd, 1);
+ if (ret) {
+ fprintf(stderr, "file register %d\n", ret);
+ return 1;
+ }
+ }
+
+ assert(g_port != 0);
+ memset(&saddr, 0, sizeof(saddr));
+ saddr.sin_family = AF_INET;
+ saddr.sin_port = g_port;
+ inet_pton(AF_INET, HOST, &saddr.sin_addr);
+
+ sqe = io_uring_get_sqe(&ring);
+ if (socket_direct) {
+ unsigned file_index = 0;
+ if (alloc)
+ file_index = IORING_FILE_INDEX_ALLOC - 1;
+ io_uring_prep_socket_direct(sqe, AF_INET, SOCK_DGRAM, 0,
+ file_index, 0);
+ } else {
+ io_uring_prep_socket(sqe, AF_INET, SOCK_DGRAM, 0, 0);
+ }
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "socket submit: %d\n", ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "No socket support, skipping\n");
+ no_socket = 1;
+ io_uring_cqe_seen(&ring, cqe);
+ return fallback_send(&ring, &saddr);
+ }
+
+ fprintf(stderr, "socket res: %d\n", ret);
+ return 1;
+ }
+
+ sockfd = cqe->res;
+ if (socket_direct && !alloc)
+ sockfd = 0;
+ io_uring_cqe_seen(&ring, cqe);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_connect(sqe, sockfd, (struct sockaddr *) &saddr,
+ sizeof(saddr));
+ if (socket_direct)
+ sqe->flags |= IOSQE_FIXED_FILE;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "connect submit: %d\n", ret);
+ return 1;
+ }
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe: %d\n", ret);
+ return 1;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "connect res: %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_send(sqe, sockfd, iov.iov_base, iov.iov_len, 0);
+ sqe->user_data = 1;
+ if (socket_direct)
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (cqe->res == -EINVAL) {
+ fprintf(stdout, "send not supported, skipping\n");
+ close(sockfd);
+ return 0;
+ }
+ if (cqe->res != iov.iov_len) {
+ fprintf(stderr, "failed cqe: %d\n", cqe->res);
+ goto err;
+ }
+
+ close(sockfd);
+ return 0;
+err:
+ close(sockfd);
+ return 1;
+}
+
+static int test(int use_sqthread, int regfiles, int socket_direct, int alloc)
+{
+ pthread_mutexattr_t attr;
+ pthread_t recv_thread;
+ struct recv_data rd;
+ int ret;
+ void *retval;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_setpshared(&attr, 1);
+ pthread_mutex_init(&rd.mutex, &attr);
+ pthread_mutex_lock(&rd.mutex);
+ rd.use_sqthread = use_sqthread;
+ rd.registerfiles = regfiles;
+
+ ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
+ if (ret) {
+ fprintf(stderr, "Thread create failed: %d\n", ret);
+ pthread_mutex_unlock(&rd.mutex);
+ return 1;
+ }
+
+ pthread_mutex_lock(&rd.mutex);
+ do_send(socket_direct, alloc);
+ pthread_join(recv_thread, &retval);
+ return (intptr_t)retval;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test(0, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=0 failed\n");
+ return ret;
+ }
+ if (no_socket)
+ return 0;
+
+ ret = test(1, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=1 reg=1 failed\n");
+ return ret;
+ }
+
+ ret = test(1, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=1 reg=0 failed\n");
+ return ret;
+ }
+
+ ret = test(0, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "test sqthread=0 direct=1 failed\n");
+ return ret;
+ }
+
+ ret = test(0, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "test sqthread=0 direct=alloc failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/splice.c b/contrib/libs/liburing/test/splice.c
new file mode 100644
index 00000000000..d4042a8fdb0
--- /dev/null
+++ b/contrib/libs/liburing/test/splice.c
@@ -0,0 +1,513 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define BUF_SIZE (16 * 4096)
+
+struct test_ctx {
+ int real_pipe1[2];
+ int real_pipe2[2];
+ int real_fd_in;
+ int real_fd_out;
+
+ /* fds or for registered files */
+ int pipe1[2];
+ int pipe2[2];
+ int fd_in;
+ int fd_out;
+
+ void *buf_in;
+ void *buf_out;
+};
+
+static unsigned int splice_flags = 0;
+static unsigned int sqe_flags = 0;
+static int has_splice = 0;
+static int has_tee = 0;
+
+static int read_buf(int fd, void *buf, int len)
+{
+ int ret;
+
+ while (len) {
+ ret = read(fd, buf, len);
+ if (ret < 0)
+ return ret;
+ len -= ret;
+ buf += ret;
+ }
+ return 0;
+}
+
+static int write_buf(int fd, const void *buf, int len)
+{
+ int ret;
+
+ while (len) {
+ ret = write(fd, buf, len);
+ if (ret < 0)
+ return ret;
+ len -= ret;
+ buf += ret;
+ }
+ return 0;
+}
+
+static int check_content(int fd, void *buf, int len, const void *src)
+{
+ int ret;
+
+ ret = read_buf(fd, buf, len);
+ if (ret)
+ return ret;
+
+ ret = memcmp(buf, src, len);
+ return (ret != 0) ? -1 : 0;
+}
+
+static int create_file(const char *filename)
+{
+ int fd, save_errno;
+
+ fd = open(filename, O_RDWR | O_CREAT, 0644);
+ save_errno = errno;
+ unlink(filename);
+ errno = save_errno;
+ return fd;
+}
+
+static int init_splice_ctx(struct test_ctx *ctx)
+{
+ int ret, rnd_fd;
+
+ ctx->buf_in = t_calloc(BUF_SIZE, 1);
+ ctx->buf_out = t_calloc(BUF_SIZE, 1);
+
+ ctx->fd_in = create_file(".splice-test-in");
+ if (ctx->fd_in < 0) {
+ perror("file open");
+ return 1;
+ }
+
+ ctx->fd_out = create_file(".splice-test-out");
+ if (ctx->fd_out < 0) {
+ perror("file open");
+ return 1;
+ }
+
+ /* get random data */
+ rnd_fd = open("/dev/urandom", O_RDONLY);
+ if (rnd_fd < 0)
+ return 1;
+
+ ret = read_buf(rnd_fd, ctx->buf_in, BUF_SIZE);
+ if (ret != 0)
+ return 1;
+ close(rnd_fd);
+
+ /* populate file */
+ ret = write_buf(ctx->fd_in, ctx->buf_in, BUF_SIZE);
+ if (ret)
+ return ret;
+
+ if (pipe(ctx->pipe1) < 0)
+ return 1;
+ if (pipe(ctx->pipe2) < 0)
+ return 1;
+
+ ctx->real_pipe1[0] = ctx->pipe1[0];
+ ctx->real_pipe1[1] = ctx->pipe1[1];
+ ctx->real_pipe2[0] = ctx->pipe2[0];
+ ctx->real_pipe2[1] = ctx->pipe2[1];
+ ctx->real_fd_in = ctx->fd_in;
+ ctx->real_fd_out = ctx->fd_out;
+ return 0;
+}
+
+static int do_splice_op(struct io_uring *ring,
+ int fd_in, loff_t off_in,
+ int fd_out, loff_t off_out,
+ unsigned int len,
+ __u8 opcode)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret = -1;
+
+ do {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ return -1;
+ }
+ io_uring_prep_splice(sqe, fd_in, off_in, fd_out, off_out,
+ len, splice_flags);
+ sqe->flags |= sqe_flags;
+ sqe->user_data = 42;
+ sqe->opcode = opcode;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", cqe->res);
+ return ret;
+ }
+
+ if (cqe->res <= 0) {
+ io_uring_cqe_seen(ring, cqe);
+ return cqe->res;
+ }
+
+ len -= cqe->res;
+ if (off_in != -1)
+ off_in += cqe->res;
+ if (off_out != -1)
+ off_out += cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ } while (len);
+
+ return 0;
+}
+
+static int do_splice(struct io_uring *ring,
+ int fd_in, loff_t off_in,
+ int fd_out, loff_t off_out,
+ unsigned int len)
+{
+ return do_splice_op(ring, fd_in, off_in, fd_out, off_out, len,
+ IORING_OP_SPLICE);
+}
+
+static int do_tee(struct io_uring *ring, int fd_in, int fd_out,
+ unsigned int len)
+{
+ return do_splice_op(ring, fd_in, 0, fd_out, 0, len, IORING_OP_TEE);
+}
+
+static void check_splice_support(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_splice(ring, -1, 0, -1, 0, BUF_SIZE);
+ has_splice = (ret == -EBADF);
+}
+
+static void check_tee_support(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_tee(ring, -1, -1, BUF_SIZE);
+ has_tee = (ret == -EBADF);
+}
+
+static int check_zero_splice(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_splice(ring, ctx->fd_in, -1, ctx->pipe1[1], -1, 0);
+ if (ret)
+ return ret;
+
+ ret = do_splice(ring, ctx->pipe2[0], -1, ctx->pipe1[1], -1, 0);
+ if (ret)
+ return ret;
+
+ return 0;
+}
+
+static int splice_to_pipe(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = lseek(ctx->real_fd_in, 0, SEEK_SET);
+ if (ret)
+ return ret;
+
+ /* implicit file offset */
+ ret = do_splice(ring, ctx->fd_in, -1, ctx->pipe1[1], -1, BUF_SIZE);
+ if (ret)
+ return ret;
+
+ ret = check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+ if (ret)
+ return ret;
+
+ /* explicit file offset */
+ ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], -1, BUF_SIZE);
+ if (ret)
+ return ret;
+
+ return check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+}
+
+static int splice_from_pipe(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = write_buf(ctx->real_pipe1[1], ctx->buf_in, BUF_SIZE);
+ if (ret)
+ return ret;
+ ret = do_splice(ring, ctx->pipe1[0], -1, ctx->fd_out, 0, BUF_SIZE);
+ if (ret)
+ return ret;
+ ret = check_content(ctx->real_fd_out, ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+ if (ret)
+ return ret;
+
+ ret = ftruncate(ctx->real_fd_out, 0);
+ if (ret)
+ return ret;
+ return lseek(ctx->real_fd_out, 0, SEEK_SET);
+}
+
+static int splice_pipe_to_pipe(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], -1, BUF_SIZE);
+ if (ret)
+ return ret;
+ ret = do_splice(ring, ctx->pipe1[0], -1, ctx->pipe2[1], -1, BUF_SIZE);
+ if (ret)
+ return ret;
+
+ return check_content(ctx->real_pipe2[0], ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+}
+
+static int fail_splice_pipe_offset(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_splice(ring, ctx->fd_in, 0, ctx->pipe1[1], 0, BUF_SIZE);
+ if (ret != -ESPIPE && ret != -EINVAL)
+ return ret;
+
+ ret = do_splice(ring, ctx->pipe1[0], 0, ctx->fd_out, 0, BUF_SIZE);
+ if (ret != -ESPIPE && ret != -EINVAL)
+ return ret;
+
+ return 0;
+}
+
+static int fail_tee_nonpipe(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_tee(ring, ctx->fd_in, ctx->pipe1[1], BUF_SIZE);
+ if (ret != -ESPIPE && ret != -EINVAL)
+ return ret;
+
+ return 0;
+}
+
+static int fail_tee_offset(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = do_splice_op(ring, ctx->pipe2[0], -1, ctx->pipe1[1], 0,
+ BUF_SIZE, IORING_OP_TEE);
+ if (ret != -ESPIPE && ret != -EINVAL)
+ return ret;
+
+ ret = do_splice_op(ring, ctx->pipe2[0], 0, ctx->pipe1[1], -1,
+ BUF_SIZE, IORING_OP_TEE);
+ if (ret != -ESPIPE && ret != -EINVAL)
+ return ret;
+
+ return 0;
+}
+
+static int check_tee(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ ret = write_buf(ctx->real_pipe1[1], ctx->buf_in, BUF_SIZE);
+ if (ret)
+ return ret;
+ ret = do_tee(ring, ctx->pipe1[0], ctx->pipe2[1], BUF_SIZE);
+ if (ret)
+ return ret;
+
+ ret = check_content(ctx->real_pipe1[0], ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+ if (ret) {
+ fprintf(stderr, "tee(), invalid src data\n");
+ return ret;
+ }
+
+ ret = check_content(ctx->real_pipe2[0], ctx->buf_out, BUF_SIZE,
+ ctx->buf_in);
+ if (ret) {
+ fprintf(stderr, "tee(), invalid dst data\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int check_zero_tee(struct io_uring *ring, struct test_ctx *ctx)
+{
+ return do_tee(ring, ctx->pipe2[0], ctx->pipe1[1], 0);
+}
+
+static int test_splice(struct io_uring *ring, struct test_ctx *ctx)
+{
+ int ret;
+
+ if (has_splice) {
+ ret = check_zero_splice(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "check_zero_splice failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = splice_to_pipe(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "splice_to_pipe failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = splice_from_pipe(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "splice_from_pipe failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = splice_pipe_to_pipe(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "splice_pipe_to_pipe failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = fail_splice_pipe_offset(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "fail_splice_pipe_offset failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+ }
+
+ if (has_tee) {
+ ret = check_zero_tee(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "check_zero_tee() failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = fail_tee_nonpipe(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "fail_tee_nonpipe() failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = fail_tee_offset(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "fail_tee_offset failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+
+ ret = check_tee(ring, ctx);
+ if (ret) {
+ fprintf(stderr, "check_tee() failed %i %i\n",
+ ret, errno);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ struct test_ctx ctx;
+ int ret;
+ int reg_fds[6];
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+ if (!(p.features & IORING_FEAT_FAST_POLL)) {
+ fprintf(stdout, "No splice support, skipping\n");
+ return 0;
+ }
+
+ ret = init_splice_ctx(&ctx);
+ if (ret) {
+ fprintf(stderr, "init failed %i %i\n", ret, errno);
+ return 1;
+ }
+
+ check_splice_support(&ring, &ctx);
+ if (!has_splice)
+ fprintf(stdout, "skip, doesn't support splice()\n");
+ check_tee_support(&ring, &ctx);
+ if (!has_tee)
+ fprintf(stdout, "skip, doesn't support tee()\n");
+
+ ret = test_splice(&ring, &ctx);
+ if (ret) {
+ fprintf(stderr, "basic splice tests failed\n");
+ return ret;
+ }
+
+ reg_fds[0] = ctx.real_pipe1[0];
+ reg_fds[1] = ctx.real_pipe1[1];
+ reg_fds[2] = ctx.real_pipe2[0];
+ reg_fds[3] = ctx.real_pipe2[1];
+ reg_fds[4] = ctx.real_fd_in;
+ reg_fds[5] = ctx.real_fd_out;
+ ret = io_uring_register_files(&ring, reg_fds, 6);
+ if (ret) {
+ fprintf(stderr, "%s: register ret=%d\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ /* remap fds to registered */
+ ctx.pipe1[0] = 0;
+ ctx.pipe1[1] = 1;
+ ctx.pipe2[0] = 2;
+ ctx.pipe2[1] = 3;
+ ctx.fd_in = 4;
+ ctx.fd_out = 5;
+
+ splice_flags = SPLICE_F_FD_IN_FIXED;
+ sqe_flags = IOSQE_FIXED_FILE;
+ ret = test_splice(&ring, &ctx);
+ if (ret) {
+ fprintf(stderr, "registered fds splice tests failed\n");
+ return ret;
+ }
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sq-full-cpp.cc b/contrib/libs/liburing/test/sq-full-cpp.cc
new file mode 100644
index 00000000000..f825596bdf1
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-full-cpp.cc
@@ -0,0 +1,46 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQ queue full condition
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret, i;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+
+ }
+
+ i = 0;
+ while ((sqe = io_uring_get_sqe(&ring)) != NULL)
+ i++;
+
+ if (i != 8) {
+ fprintf(stderr, "Got %d SQEs, wanted 8\n", i);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sq-full.c b/contrib/libs/liburing/test/sq-full.c
new file mode 100644
index 00000000000..f825596bdf1
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-full.c
@@ -0,0 +1,46 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQ queue full condition
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret, i;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+
+ }
+
+ i = 0;
+ while ((sqe = io_uring_get_sqe(&ring)) != NULL)
+ i++;
+
+ if (i != 8) {
+ fprintf(stderr, "Got %d SQEs, wanted 8\n", i);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sq-poll-dup.c b/contrib/libs/liburing/test/sq-poll-dup.c
new file mode 100644
index 00000000000..bbeb63e6cb7
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-poll-dup.c
@@ -0,0 +1,205 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQPOLL with IORING_SETUP_ATTACH_WQ and closing of
+ * the original ring descriptor.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/eventfd.h>
+#include <sys/resource.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024 * 1024)
+#define BS 4096
+#define BUFFERS 64
+
+#define NR_RINGS 4
+
+static struct iovec *vecs;
+static struct io_uring rings[NR_RINGS];
+
+static int wait_io(struct io_uring *ring, int nr_ios)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ while (nr_ios) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_ret=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res != BS) {
+ fprintf(stderr, "Unexpected ret %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ nr_ios--;
+ }
+
+ return 0;
+}
+
+static int queue_io(struct io_uring *ring, int fd, int nr_ios)
+{
+ unsigned long off;
+ int i;
+
+ i = 0;
+ off = 0;
+ while (nr_ios) {
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ break;
+ io_uring_prep_read(sqe, fd, vecs[i].iov_base, vecs[i].iov_len, off);
+ nr_ios--;
+ i++;
+ off += BS;
+ }
+
+ io_uring_submit(ring);
+ return i;
+}
+
+static int do_io(int fd, int ring_start, int ring_end)
+{
+ int i, rets[NR_RINGS];
+ unsigned ios = 0;
+
+ while (ios < 32) {
+ for (i = ring_start; i < ring_end; i++) {
+ int ret = queue_io(&rings[i], fd, BUFFERS);
+ if (ret < 0)
+ goto err;
+ rets[i] = ret;
+ }
+ for (i = ring_start; i < ring_end; i++) {
+ if (wait_io(&rings[i], rets[i]))
+ goto err;
+ }
+ ios += BUFFERS;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test(int fd, int do_dup_and_close, int close_ring)
+{
+ int i, ret, ring_fd;
+
+ for (i = 0; i < NR_RINGS; i++) {
+ struct io_uring_params p = { };
+
+ p.flags = IORING_SETUP_SQPOLL;
+ p.sq_thread_idle = 100;
+ if (i) {
+ p.wq_fd = rings[0].ring_fd;
+ p.flags |= IORING_SETUP_ATTACH_WQ;
+ }
+ ret = io_uring_queue_init_params(BUFFERS, &rings[i], &p);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d/%d\n", ret, i);
+ goto err;
+ }
+ /* no sharing for non-fixed either */
+ if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
+ fprintf(stdout, "No SQPOLL sharing, skipping\n");
+ return 0;
+ }
+ }
+
+ /* test all rings */
+ if (do_io(fd, 0, NR_RINGS))
+ goto err;
+
+ /* dup and close original ring fd */
+ ring_fd = dup(rings[0].ring_fd);
+ if (close_ring)
+ close(rings[0].ring_fd);
+ rings[0].ring_fd = rings[0].enter_ring_fd = ring_fd;
+ if (do_dup_and_close)
+ goto done;
+
+ /* test all but closed one */
+ if (do_io(fd, 1, NR_RINGS))
+ goto err;
+
+ /* test closed one */
+ if (do_io(fd, 0, 1))
+ goto err;
+
+ /* make sure thread is idle so we enter the kernel */
+ usleep(200000);
+
+ /* test closed one */
+ if (do_io(fd, 0, 1))
+ goto err;
+
+
+done:
+ for (i = 0; i < NR_RINGS; i++)
+ io_uring_queue_exit(&rings[i]);
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ char *fname;
+ int ret, fd;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ fname = ".basic-rw-poll-dup";
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ fd = open(fname, O_RDONLY | O_DIRECT);
+ if (fname != argv[1])
+ unlink(fname);
+
+ if (fd < 0) {
+ perror("open");
+ return -1;
+ }
+
+ ret = test(fd, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test 0 0 failed\n");
+ goto err;
+ }
+
+ ret = test(fd, 0, 1);
+ if (ret) {
+ fprintf(stderr, "test 0 1 failed\n");
+ goto err;
+ }
+
+
+ ret = test(fd, 1, 0);
+ if (ret) {
+ fprintf(stderr, "test 1 0 failed\n");
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sq-poll-kthread.c b/contrib/libs/liburing/test/sq-poll-kthread.c
new file mode 100644
index 00000000000..4ec43a93231
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-poll-kthread.c
@@ -0,0 +1,170 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test if io_uring SQ poll kthread is stopped when the userspace
+ * process ended with or without closing the io_uring fd
+ *
+ */
+#include <errno.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <pthread.h>
+#include <stdbool.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <poll.h>
+#include <sys/wait.h>
+#include <sys/epoll.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define SQ_THREAD_IDLE 2000
+#define BUF_SIZE 128
+#define KTHREAD_NAME "io_uring-sq"
+
+enum {
+ TEST_OK = 0,
+ TEST_SKIPPED = 1,
+ TEST_FAILED = 2,
+};
+
+static int do_test_sq_poll_kthread_stopped(bool do_exit)
+{
+ int ret = 0, pipe1[2];
+ struct io_uring_params param;
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ uint8_t buf[BUF_SIZE];
+ struct iovec iov;
+
+ if (pipe(pipe1) != 0) {
+ perror("pipe");
+ return TEST_FAILED;
+ }
+
+ memset(&param, 0, sizeof(param));
+ param.flags |= IORING_SETUP_SQPOLL;
+ param.sq_thread_idle = SQ_THREAD_IDLE;
+
+ ret = t_create_ring_params(16, &ring, &param);
+ if (ret == T_SETUP_SKIP) {
+ ret = TEST_FAILED;
+ goto err_pipe;
+ } else if (ret != T_SETUP_OK) {
+ fprintf(stderr, "ring setup failed\n");
+ ret = TEST_FAILED;
+ goto err_pipe;
+ }
+
+ ret = io_uring_register_files(&ring, &pipe1[1], 1);
+ if (ret) {
+ fprintf(stderr, "file reg failed: %d\n", ret);
+ ret = TEST_FAILED;
+ goto err_uring;
+ }
+
+ iov.iov_base = buf;
+ iov.iov_len = BUF_SIZE;
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "io_uring_get_sqe failed\n");
+ ret = TEST_FAILED;
+ goto err_uring;
+ }
+
+ io_uring_prep_writev(sqe, 0, &iov, 1, 0);
+ sqe->flags |= IOSQE_FIXED_FILE;
+
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_submit failed - ret: %d\n",
+ ret);
+ ret = TEST_FAILED;
+ goto err_uring;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "io_uring_wait_cqe - ret: %d\n",
+ ret);
+ ret = TEST_FAILED;
+ goto err_uring;
+ }
+
+ if (cqe->res != BUF_SIZE) {
+ fprintf(stderr, "unexpected cqe->res %d [expected %d]\n",
+ cqe->res, BUF_SIZE);
+ ret = TEST_FAILED;
+ goto err_uring;
+
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+
+ ret = TEST_OK;
+
+err_uring:
+ if (do_exit)
+ io_uring_queue_exit(&ring);
+err_pipe:
+ close(pipe1[0]);
+ close(pipe1[1]);
+
+ return ret;
+}
+
+int test_sq_poll_kthread_stopped(bool do_exit)
+{
+ pid_t pid;
+ int status = 0;
+
+ pid = fork();
+
+ if (pid == 0) {
+ int ret = do_test_sq_poll_kthread_stopped(do_exit);
+ exit(ret);
+ }
+
+ pid = wait(&status);
+ if (status != 0)
+ return WEXITSTATUS(status);
+
+ sleep(1);
+ if (system("ps --ppid 2 | grep " KTHREAD_NAME) == 0) {
+ fprintf(stderr, "%s kthread still running!\n", KTHREAD_NAME);
+ return TEST_FAILED;
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_sq_poll_kthread_stopped(true);
+ if (ret == TEST_SKIPPED) {
+ printf("test_sq_poll_kthread_stopped_exit: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_sq_poll_kthread_stopped_exit failed\n");
+ return ret;
+ }
+
+ ret = test_sq_poll_kthread_stopped(false);
+ if (ret == TEST_SKIPPED) {
+ printf("test_sq_poll_kthread_stopped_noexit: skipped\n");
+ } else if (ret == TEST_FAILED) {
+ fprintf(stderr, "test_sq_poll_kthread_stopped_noexit failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sq-poll-share.c b/contrib/libs/liburing/test/sq-poll-share.c
new file mode 100644
index 00000000000..a2af97543f1
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-poll-share.c
@@ -0,0 +1,138 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQPOLL with IORING_SETUP_ATTACH_WQ
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <sys/eventfd.h>
+#include <sys/resource.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define FILE_SIZE (128 * 1024 * 1024)
+#define BS 4096
+#define BUFFERS 64
+
+#define NR_RINGS 4
+
+static struct iovec *vecs;
+
+static int wait_io(struct io_uring *ring, int nr_ios)
+{
+ struct io_uring_cqe *cqe;
+
+ while (nr_ios) {
+ int ret = io_uring_wait_cqe(ring, &cqe);
+
+ if (ret == -EAGAIN) {
+ continue;
+ } else if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe failed %i\n", ret);
+ return 1;
+ }
+ if (cqe->res != BS) {
+ fprintf(stderr, "Unexpected ret %d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ nr_ios--;
+ }
+
+ return 0;
+}
+
+static int queue_io(struct io_uring *ring, int fd, int nr_ios)
+{
+ unsigned long off;
+ int i;
+
+ i = 0;
+ off = 0;
+ while (nr_ios) {
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe)
+ break;
+ io_uring_prep_read(sqe, fd, vecs[i].iov_base, vecs[i].iov_len, off);
+ nr_ios--;
+ i++;
+ off += BS;
+ }
+
+ io_uring_submit(ring);
+ return i;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring rings[NR_RINGS];
+ int rets[NR_RINGS];
+ unsigned long ios;
+ int i, ret, fd;
+ char *fname;
+
+ if (argc > 1) {
+ fname = argv[1];
+ } else {
+ fname = ".basic-rw-poll-share";
+ t_create_file(fname, FILE_SIZE);
+ }
+
+ vecs = t_create_buffers(BUFFERS, BS);
+
+ fd = open(fname, O_RDONLY | O_DIRECT);
+ if (fname != argv[1])
+ unlink(fname);
+ if (fd < 0) {
+ perror("open");
+ return -1;
+ }
+
+ for (i = 0; i < NR_RINGS; i++) {
+ struct io_uring_params p = { };
+
+ p.flags = IORING_SETUP_SQPOLL;
+ if (i) {
+ p.wq_fd = rings[0].ring_fd;
+ p.flags |= IORING_SETUP_ATTACH_WQ;
+ }
+ ret = io_uring_queue_init_params(BUFFERS, &rings[i], &p);
+ if (ret) {
+ fprintf(stderr, "queue_init: %d/%d\n", ret, i);
+ goto err;
+ }
+ /* no sharing for non-fixed either */
+ if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
+ fprintf(stdout, "No SQPOLL sharing, skipping\n");
+ return 0;
+ }
+ }
+
+ ios = 0;
+ while (ios < (FILE_SIZE / BS)) {
+ for (i = 0; i < NR_RINGS; i++) {
+ ret = queue_io(&rings[i], fd, BUFFERS);
+ if (ret < 0)
+ goto err;
+ rets[i] = ret;
+ }
+ for (i = 0; i < NR_RINGS; i++) {
+ if (wait_io(&rings[i], rets[i]))
+ goto err;
+ }
+ ios += BUFFERS;
+ }
+
+ return 0;
+err:
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sq-space_left.c b/contrib/libs/liburing/test/sq-space_left.c
new file mode 100644
index 00000000000..9402377df7f
--- /dev/null
+++ b/contrib/libs/liburing/test/sq-space_left.c
@@ -0,0 +1,160 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test SQ queue space left
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+
+static int test_left(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret, i = 0, s;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+
+ }
+
+ if ((s = io_uring_sq_space_left(&ring)) != 8) {
+ fprintf(stderr, "Got %d SQEs left, expected %d\n", s, 8);
+ goto err;
+ }
+
+ i = 0;
+ while ((sqe = io_uring_get_sqe(&ring)) != NULL) {
+ i++;
+ if ((s = io_uring_sq_space_left(&ring)) != 8 - i) {
+ fprintf(stderr, "Got %d SQEs left, expected %d\n", s, 8 - i);
+ goto err;
+ }
+ }
+
+ if (i != 8) {
+ fprintf(stderr, "Got %d SQEs, expected %d\n", i, 8);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+static int test_sync(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring ring;
+ int ret, i;
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+
+ }
+
+ /* prep 8 NOPS */
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ }
+
+ /* prep known bad command, this should terminate submission */
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->opcode = 0xfe;
+
+ /* prep 8 NOPS */
+ for (i = 0; i < 8; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ }
+
+ /* we should have 8 + 1 + 8 pending now */
+ ret = io_uring_sq_ready(&ring);
+ if (ret != 17) {
+ fprintf(stderr, "%d ready, wanted 17\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_submit(&ring);
+
+ /* should submit 8 successfully, then error #9 and stop */
+ if (ret != 9) {
+ fprintf(stderr, "submitted %d, wanted 9\n", ret);
+ goto err;
+ }
+
+ /* should now have 8 ready, with 9 gone */
+ ret = io_uring_sq_ready(&ring);
+ if (ret != 8) {
+ fprintf(stderr, "%d ready, wanted 8\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_submit(&ring);
+
+ /* the last 8 should submit fine */
+ if (ret != 8) {
+ fprintf(stderr, "submitted %d, wanted 8\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_sq_ready(&ring);
+ if (ret) {
+ fprintf(stderr, "%d ready, wanted 0\n", ret);
+ goto err;
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_left();
+ if (ret) {
+ fprintf(stderr, "test_left failed\n");
+ return ret;
+ }
+
+ ret = test_sync();
+ if (ret) {
+ fprintf(stderr, "test_sync failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sqpoll-cancel-hang.c b/contrib/libs/liburing/test/sqpoll-cancel-hang.c
new file mode 100644
index 00000000000..81a30e27de3
--- /dev/null
+++ b/contrib/libs/liburing/test/sqpoll-cancel-hang.c
@@ -0,0 +1,158 @@
+#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 "../src/syscall.h"
+
+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
+
+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;
+}
+
+
+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, 0x32ul, -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;
+}
+
+
+
diff --git a/contrib/libs/liburing/test/sqpoll-disable-exit.c b/contrib/libs/liburing/test/sqpoll-disable-exit.c
new file mode 100644
index 00000000000..b2a4160c589
--- /dev/null
+++ b/contrib/libs/liburing/test/sqpoll-disable-exit.c
@@ -0,0 +1,197 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+// https://syzkaller.appspot.com/bug?id=99f4ea77bb9b9ef24cefb66469be319f4aa9f162
+// autogenerated by syzkaller (https://github.com/google/syzkaller)
+
+#include <dirent.h>
+#include <endian.h>
+#include <errno.h>
+#include <fcntl.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/types.h>
+#include <sys/wait.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "liburing.h"
+#include "../src/syscall.h"
+
+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 bool write_file(const char* file, const char* what, ...)
+{
+ char buf[1024];
+ va_list args;
+ va_start(args, what);
+ vsnprintf(buf, sizeof(buf), what, args);
+ va_end(args);
+ buf[sizeof(buf) - 1] = 0;
+ int len = strlen(buf);
+ int fd = open(file, O_WRONLY | O_CLOEXEC);
+ if (fd == -1)
+ return false;
+ if (write(fd, buf, len) != len) {
+ int err = errno;
+ close(fd);
+ errno = err;
+ return false;
+ }
+ close(fd);
+ return true;
+}
+
+#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
+
+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 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()
+{
+ prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0);
+ setpgrp();
+ write_file("/proc/self/oom_score_adj", "1000");
+}
+
+static void execute_one(void);
+
+#define WAIT_FLAGS __WALL
+
+static void loop(void)
+{
+ int iter = 0;
+ for (; iter < 100; 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;
+ }
+ }
+}
+
+void execute_one(void)
+{
+ *(uint32_t*)0x20000044 = 0;
+ *(uint32_t*)0x20000048 = 0x42;
+ *(uint32_t*)0x2000004c = 0;
+ *(uint32_t*)0x20000050 = 0;
+ *(uint32_t*)0x20000058 = -1;
+ *(uint32_t*)0x2000005c = 0;
+ *(uint32_t*)0x20000060 = 0;
+ *(uint32_t*)0x20000064 = 0;
+ syz_io_uring_setup(0x74bc, 0x20000040, 0x20ffb000, 0x20ffc000, 0, 0);
+}
+int main(void)
+{
+ mmap((void *)0x1ffff000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
+ mmap((void *)0x20000000ul, 0x1000000ul, 7ul, 0x32ul, -1, 0ul);
+ mmap((void *)0x21000000ul, 0x1000ul, 0ul, 0x32ul, -1, 0ul);
+ loop();
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sqpoll-exit-hang.c b/contrib/libs/liburing/test/sqpoll-exit-hang.c
new file mode 100644
index 00000000000..7f5e5399032
--- /dev/null
+++ b/contrib/libs/liburing/test/sqpoll-exit-hang.c
@@ -0,0 +1,79 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test that we exit properly with SQPOLL and having a request that
+ * adds a circular reference to the ring itself.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <poll.h>
+#include "liburing.h"
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct timeval tv;
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ p.flags = IORING_SETUP_SQPOLL;
+ p.sq_thread_idle = 100;
+
+ ret = io_uring_queue_init_params(1, &ring, &p);
+ if (ret) {
+ if (geteuid()) {
+ printf("%s: skipped, not root\n", argv[0]);
+ return 0;
+ }
+ fprintf(stderr, "queue_init=%d\n", ret);
+ return 1;
+ }
+
+ if (!(p.features & IORING_FEAT_SQPOLL_NONFIXED)) {
+ fprintf(stdout, "Skipping\n");
+ return 0;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, ring.ring_fd, POLLIN);
+ io_uring_submit(&ring);
+
+ gettimeofday(&tv, NULL);
+ do {
+ usleep(1000);
+ } while (mtime_since_now(&tv) < 1000);
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/sqpoll-sleep.c b/contrib/libs/liburing/test/sqpoll-sleep.c
new file mode 100644
index 00000000000..f98d733ea4f
--- /dev/null
+++ b/contrib/libs/liburing/test/sqpoll-sleep.c
@@ -0,0 +1,70 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test that the sqthread goes to sleep around the specified time, and that
+ * the NEED_WAKEUP flag is then set.
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include "liburing.h"
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring_params p = {};
+ struct timeval tv;
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ p.flags = IORING_SETUP_SQPOLL;
+ p.sq_thread_idle = 100;
+
+ ret = io_uring_queue_init_params(1, &ring, &p);
+ if (ret) {
+ if (geteuid()) {
+ printf("%s: skipped, not root\n", argv[0]);
+ return 0;
+ }
+ fprintf(stderr, "queue_init=%d\n", ret);
+ return 1;
+ }
+
+ gettimeofday(&tv, NULL);
+ do {
+ usleep(1000);
+ if ((*ring.sq.kflags) & IORING_SQ_NEED_WAKEUP)
+ return 0;
+ } while (mtime_since_now(&tv) < 1000);
+
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/stdout.c b/contrib/libs/liburing/test/stdout.c
new file mode 100644
index 00000000000..02b0456955c
--- /dev/null
+++ b/contrib/libs/liburing/test/stdout.c
@@ -0,0 +1,233 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: check that STDOUT write works
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int test_pipe_io_fixed(struct io_uring *ring)
+{
+ const char str[] = "This is a fixed pipe test\n";
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec vecs[2];
+ char buffer[128];
+ int i, ret, fds[2];
+
+ t_posix_memalign(&vecs[0].iov_base, 4096, 4096);
+ memcpy(vecs[0].iov_base, str, strlen(str));
+ vecs[0].iov_len = strlen(str);
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_register_buffers(ring, vecs, 1);
+ if (ret) {
+ fprintf(stderr, "Failed to register buffers: %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_write_fixed(sqe, fds[1], vecs[0].iov_base,
+ vecs[0].iov_len, 0, 0);
+ sqe->user_data = 1;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ vecs[1].iov_base = buffer;
+ vecs[1].iov_len = sizeof(buffer);
+ io_uring_prep_readv(sqe, fds[0], &vecs[1], 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ } else if (ret != 2) {
+ fprintf(stderr, "Submitted only %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "I/O write error on %lu: %s\n",
+ (unsigned long) cqe->user_data,
+ strerror(-cqe->res));
+ goto err;
+ }
+ if (cqe->res != strlen(str)) {
+ fprintf(stderr, "Got %d bytes, wanted %d on %lu\n",
+ cqe->res, (int)strlen(str),
+ (unsigned long) cqe->user_data);
+ goto err;
+ }
+ if (cqe->user_data == 2 && memcmp(str, buffer, strlen(str))) {
+ fprintf(stderr, "read data mismatch\n");
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ io_uring_unregister_buffers(ring);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_stdout_io_fixed(struct io_uring *ring)
+{
+ const char str[] = "This is a fixed pipe test\n";
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec vecs;
+ int ret;
+
+ t_posix_memalign(&vecs.iov_base, 4096, 4096);
+ memcpy(vecs.iov_base, str, strlen(str));
+ vecs.iov_len = strlen(str);
+
+ ret = io_uring_register_buffers(ring, &vecs, 1);
+ if (ret) {
+ fprintf(stderr, "Failed to register buffers: %d\n", ret);
+ return 1;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_write_fixed(sqe, STDOUT_FILENO, vecs.iov_base, vecs.iov_len, 0, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ } else if (ret < 1) {
+ fprintf(stderr, "Submitted only %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "STDOUT write error: %s\n", strerror(-cqe->res));
+ goto err;
+ }
+ if (cqe->res != vecs.iov_len) {
+ fprintf(stderr, "Got %d write, wanted %d\n", cqe->res, (int)vecs.iov_len);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ io_uring_unregister_buffers(ring);
+ return 0;
+err:
+ return 1;
+}
+
+static int test_stdout_io(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct iovec vecs;
+ int ret;
+
+ vecs.iov_base = "This is a pipe test\n";
+ vecs.iov_len = strlen(vecs.iov_base);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed\n");
+ goto err;
+ }
+ io_uring_prep_writev(sqe, STDOUT_FILENO, &vecs, 1, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ } else if (ret < 1) {
+ fprintf(stderr, "Submitted only %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "STDOUT write error: %s\n",
+ strerror(-cqe->res));
+ goto err;
+ }
+ if (cqe->res != vecs.iov_len) {
+ fprintf(stderr, "Got %d write, wanted %d\n", cqe->res,
+ (int)vecs.iov_len);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_stdout_io(&ring);
+ if (ret) {
+ fprintf(stderr, "test_pipe_io failed\n");
+ return ret;
+ }
+
+ ret = test_stdout_io_fixed(&ring);
+ if (ret) {
+ fprintf(stderr, "test_pipe_io_fixed failed\n");
+ return ret;
+ }
+
+ ret = test_pipe_io_fixed(&ring);
+ if (ret) {
+ fprintf(stderr, "test_pipe_io_fixed failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/submit-and-wait.c b/contrib/libs/liburing/test/submit-and-wait.c
new file mode 100644
index 00000000000..ab841ca6b19
--- /dev/null
+++ b/contrib/libs/liburing/test/submit-and-wait.c
@@ -0,0 +1,109 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test that io_uring_submit_and_wait_timeout() returns the
+ * right value (submit count) and that it doesn't end up waiting twice.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+
+#include "liburing.h"
+#include "test.h"
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+static int test(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ struct timeval tv;
+ int ret, i;
+
+ for (i = 0; i < 1; i++) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "get sqe failed at %d\n", i);
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ }
+
+ ts.tv_sec = 1;
+ ts.tv_nsec = 0;
+ gettimeofday(&tv, NULL);
+ ret = io_uring_submit_and_wait_timeout(ring, &cqe, 2, &ts, NULL);
+ if (ret < 0) {
+ fprintf(stderr, "submit_and_wait_timeout: %d\n", ret);
+ goto err;
+ }
+ ret = mtime_since_now(&tv);
+ /* allow some slack, should be around 1s */
+ if (ret > 1200) {
+ fprintf(stderr, "wait took too long: %d\n", ret);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_ring(void)
+{
+ struct io_uring ring;
+ struct io_uring_params p = { };
+ int ret;
+
+ p.flags = 0;
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test(&ring);
+ if (ret) {
+ fprintf(stderr, "test failed\n");
+ goto err;
+ }
+err:
+ io_uring_queue_exit(&ring);
+ return ret;
+}
+
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return 0;
+
+ return test_ring();
+}
diff --git a/contrib/libs/liburing/test/submit-link-fail.c b/contrib/libs/liburing/test/submit-link-fail.c
new file mode 100644
index 00000000000..3cbc4c1278a
--- /dev/null
+++ b/contrib/libs/liburing/test/submit-link-fail.c
@@ -0,0 +1,157 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: tests linked requests failing during submission
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <assert.h>
+
+#include "liburing.h"
+
+#define DRAIN_USER_DATA 42
+
+static int test_underprep_fail(bool hardlink, bool drain, bool link_last,
+ int link_size, int fail_idx)
+{
+ const int invalid_fd = 42;
+ int link_flags = IOSQE_IO_LINK;
+ int total_submit = link_size;
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ char buffer[1] = { };
+ int i, ret, fds[2];
+
+ if (drain)
+ link_flags |= IOSQE_IO_DRAIN;
+ if (hardlink)
+ link_flags |= IOSQE_IO_HARDLINK;
+
+ assert(fail_idx < link_size);
+ assert(link_size < 40);
+
+ /* create a new ring as it leaves it dirty */
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ printf("ring setup failed\n");
+ return -1;
+ }
+ if (pipe(fds)) {
+ perror("pipe");
+ return -1;
+ }
+
+ if (drain) {
+ /* clog drain, so following reqs sent to draining */
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
+ sqe->user_data = DRAIN_USER_DATA;
+ sqe->flags |= IOSQE_IO_DRAIN;
+ total_submit++;
+ }
+
+ for (i = 0; i < link_size; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (i == fail_idx) {
+ io_uring_prep_read(sqe, invalid_fd, buffer, 1, 0);
+ sqe->ioprio = (short) -1;
+ } else {
+ io_uring_prep_nop(sqe);
+ }
+
+ if (i != link_size - 1 || !link_last)
+ sqe->flags |= link_flags;
+ sqe->user_data = i;
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != total_submit) {
+ /* Old behaviour, failed early and under-submitted */
+ if (ret == fail_idx + 1 + drain)
+ goto out;
+ fprintf(stderr, "submit failed: %d\n", ret);
+ return -1;
+ }
+
+ if (drain) {
+ /* unclog drain */
+ ret = write(fds[1], buffer, sizeof(buffer));
+ if (ret < 0) {
+ perror("write");
+ return 1;
+ }
+ }
+
+ for (i = 0; i < total_submit; i++) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+
+ ret = cqe->res;
+ if (cqe->user_data == DRAIN_USER_DATA) {
+ if (ret != 1) {
+ fprintf(stderr, "drain failed %d\n", ret);
+ return 1;
+ }
+ } else if (cqe->user_data == fail_idx) {
+ if (ret == 0 || ret == -ECANCELED) {
+ fprintf(stderr, "half-prep req unexpected return %d\n", ret);
+ return 1;
+ }
+ } else {
+ if (ret != -ECANCELED) {
+ fprintf(stderr, "cancel failed %d, ud %d\n", ret, (int)cqe->user_data);
+ return 1;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+out:
+ close(fds[0]);
+ close(fds[1]);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret, link_size, fail_idx, i;
+
+ if (argc > 1)
+ return 0;
+
+ /*
+ * hardlink, size=3, fail_idx=1, drain=false -- kernel fault
+ * link, size=3, fail_idx=0, drain=true -- kernel fault
+ * link, size=3, fail_idx=1, drain=true -- invalid cqe->res
+ */
+ for (link_size = 0; link_size < 3; link_size++) {
+ for (fail_idx = 0; fail_idx < link_size; fail_idx++) {
+ for (i = 0; i < 8; i++) {
+ bool hardlink = (i & 1) != 0;
+ bool drain = (i & 2) != 0;
+ bool link_last = (i & 4) != 0;
+
+ ret = test_underprep_fail(hardlink, drain, link_last,
+ link_size, fail_idx);
+ if (!ret)
+ continue;
+
+ fprintf(stderr, "failed %d, hard %d, drain %d,"
+ "link_last %d, size %d, idx %d\n",
+ ret, hardlink, drain, link_last,
+ link_size, fail_idx);
+ return 1;
+ }
+ }
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/submit-reuse.c b/contrib/libs/liburing/test/submit-reuse.c
new file mode 100644
index 00000000000..92f6b2f5d03
--- /dev/null
+++ b/contrib/libs/liburing/test/submit-reuse.c
@@ -0,0 +1,238 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test reads that will punt to blocking context, with immediate overwrite
+ * of iovec->iov_base to NULL. If the kernel doesn't properly handle
+ * reuse of the iovec, we should get -EFAULT.
+ */
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <pthread.h>
+#include <sys/time.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define STR_SIZE 32768
+#define FILE_SIZE 65536
+
+struct thread_data {
+ int fd1, fd2;
+ volatile int do_exit;
+};
+
+static void *flusher(void *__data)
+{
+ struct thread_data *data = __data;
+
+ while (!data->do_exit) {
+ posix_fadvise(data->fd1, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
+ posix_fadvise(data->fd2, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
+ usleep(10);
+ }
+
+ return NULL;
+}
+
+static char str1[STR_SIZE];
+static char str2[STR_SIZE];
+
+static struct io_uring ring;
+
+static int no_stable;
+
+static int prep(int fd, char *str, int split, int async)
+{
+ struct io_uring_sqe *sqe;
+ struct iovec iovs[16];
+ int ret, i;
+
+ if (split) {
+ int vsize = STR_SIZE / 16;
+ void *ptr = str;
+
+ for (i = 0; i < 16; i++) {
+ iovs[i].iov_base = ptr;
+ iovs[i].iov_len = vsize;
+ ptr += vsize;
+ }
+ } else {
+ iovs[0].iov_base = str;
+ iovs[0].iov_len = STR_SIZE;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_readv(sqe, fd, iovs, split ? 16 : 1, 0);
+ sqe->user_data = fd;
+ if (async)
+ sqe->flags = IOSQE_ASYNC;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit got %d\n", ret);
+ return 1;
+ }
+ if (split) {
+ for (i = 0; i < 16; i++)
+ iovs[i].iov_base = NULL;
+ } else {
+ iovs[0].iov_base = NULL;
+ }
+ return 0;
+}
+
+static int wait_nr(int nr)
+{
+ int i, ret;
+
+ for (i = 0; i < nr; i++) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret)
+ return ret;
+ if (cqe->res < 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ return 0;
+}
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+static int test_reuse(int argc, char *argv[], int split, int async)
+{
+ struct thread_data data;
+ struct io_uring_params p = { };
+ int fd1, fd2, ret, i;
+ struct timeval tv;
+ pthread_t thread;
+ char *fname1 = ".reuse.1";
+ int do_unlink = 1;
+ void *tret;
+
+ ret = io_uring_queue_init_params(32, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "io_uring_queue_init: %d\n", ret);
+ return 1;
+ }
+
+ if (!(p.features & IORING_FEAT_SUBMIT_STABLE)) {
+ fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
+ io_uring_queue_exit(&ring);
+ no_stable = 1;
+ return 0;
+ }
+
+ if (argc > 1) {
+ fname1 = argv[1];
+ do_unlink = 0;
+ } else {
+ t_create_file(fname1, FILE_SIZE);
+ }
+
+ fd1 = open(fname1, O_RDONLY);
+ if (do_unlink)
+ unlink(fname1);
+ if (fd1 < 0) {
+ perror("open fname1");
+ goto err;
+ }
+
+ t_create_file(".reuse.2", FILE_SIZE);
+ fd2 = open(".reuse.2", O_RDONLY);
+ unlink(".reuse.2");
+ if (fd2 < 0) {
+ perror("open .reuse.2");
+ goto err;
+ }
+
+ data.fd1 = fd1;
+ data.fd2 = fd2;
+ data.do_exit = 0;
+ pthread_create(&thread, NULL, flusher, &data);
+ usleep(10000);
+
+ gettimeofday(&tv, NULL);
+ for (i = 0; i < 1000; i++) {
+ ret = prep(fd1, str1, split, async);
+ if (ret) {
+ fprintf(stderr, "prep1 failed: %d\n", ret);
+ goto err;
+ }
+ ret = prep(fd2, str2, split, async);
+ if (ret) {
+ fprintf(stderr, "prep1 failed: %d\n", ret);
+ goto err;
+ }
+ ret = wait_nr(2);
+ if (ret) {
+ fprintf(stderr, "wait_nr: %d\n", ret);
+ goto err;
+ }
+ if (mtime_since_now(&tv) > 5000)
+ break;
+ }
+
+ data.do_exit = 1;
+ pthread_join(thread, &tret);
+
+ close(fd2);
+ close(fd1);
+ io_uring_queue_exit(&ring);
+ return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+
+}
+
+int main(int argc, char *argv[])
+{
+ int ret, i;
+
+ for (i = 0; i < 4; i++) {
+ int split, async;
+
+ split = (i & 1) != 0;
+ async = (i & 2) != 0;
+
+ ret = test_reuse(argc, argv, split, async);
+ if (ret) {
+ fprintf(stderr, "test_reuse %d %d failed\n", split, async);
+ return ret;
+ }
+ if (no_stable)
+ break;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/symlink.c b/contrib/libs/liburing/test/symlink.c
new file mode 100644
index 00000000000..755b51d152b
--- /dev/null
+++ b/contrib/libs/liburing/test/symlink.c
@@ -0,0 +1,117 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring symlinkat handling
+ */
+#include <fcntl.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+
+#include "liburing.h"
+
+
+static int do_symlinkat(struct io_uring *ring, const char *oldname, const char *newname)
+{
+ int ret;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "sqe get failed\n");
+ goto err;
+ }
+ io_uring_prep_symlinkat(sqe, oldname, AT_FDCWD, newname);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqes(ring, &cqe, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "wait_cqe failed: %d\n", ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+err:
+ return 1;
+}
+
+int test_link_contents(const char* linkname, const char *expected_contents)
+{
+ char buf[128];
+ int ret = readlink(linkname, buf, 127);
+ if (ret < 0) {
+ perror("readlink");
+ return ret;
+ }
+ buf[ret] = 0;
+ if (strncmp(buf, expected_contents, 128)) {
+ fprintf(stderr, "link contents differs from expected: '%s' vs '%s'",
+ buf, expected_contents);
+ return -1;
+ }
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ static const char target[] = "io_uring-symlinkat-test-target";
+ static const char linkname[] = "io_uring-symlinkat-test-link";
+ int ret;
+ struct io_uring ring;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = do_symlinkat(&ring, target, linkname);
+ if (ret < 0) {
+ if (ret == -EBADF || ret == -EINVAL) {
+ fprintf(stdout, "symlinkat not supported, skipping\n");
+ goto out;
+ }
+ fprintf(stderr, "symlinkat: %s\n", strerror(-ret));
+ goto err;
+ } else if (ret) {
+ goto err;
+ }
+
+ ret = test_link_contents(linkname, target);
+ if (ret < 0)
+ goto err1;
+
+ ret = do_symlinkat(&ring, target, linkname);
+ if (ret != -EEXIST) {
+ fprintf(stderr, "test_symlinkat linkname already exists failed: %d\n", ret);
+ goto err1;
+ }
+
+ ret = do_symlinkat(&ring, target, "surely/this/does/not/exist");
+ if (ret != -ENOENT) {
+ fprintf(stderr, "test_symlinkat no parent failed: %d\n", ret);
+ goto err1;
+ }
+
+out:
+ unlinkat(AT_FDCWD, linkname, 0);
+ io_uring_queue_exit(&ring);
+ return 0;
+err1:
+ unlinkat(AT_FDCWD, linkname, 0);
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/sync-cancel.c b/contrib/libs/liburing/test/sync-cancel.c
new file mode 100644
index 00000000000..096d210ffb0
--- /dev/null
+++ b/contrib/libs/liburing/test/sync-cancel.c
@@ -0,0 +1,236 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test io_uring_register_sync_cancel()
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int no_sync_cancel;
+
+static int test_sync_cancel_timeout(struct io_uring *ring, int async)
+{
+ struct io_uring_sync_cancel_reg reg = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fds[2], to_prep;
+ char buf[32];
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ to_prep = 1;
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
+ sqe->user_data = 0x89;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+
+ ret = io_uring_submit(ring);
+ if (ret != to_prep) {
+ fprintf(stderr, "submit=%d\n", ret);
+ return 1;
+ }
+
+ usleep(10000);
+
+ reg.addr = 0x89;
+ reg.timeout.tv_nsec = 1;
+ ret = io_uring_register_sync_cancel(ring, &reg);
+ if (async) {
+ /* we expect -ETIME here, but can race and get 0 */
+ if (ret != -ETIME && ret != 0) {
+ fprintf(stderr, "sync_cancel=%d\n", ret);
+ return 1;
+ }
+ } else {
+ if (ret < 0) {
+ fprintf(stderr, "sync_cancel=%d\n", ret);
+ return 1;
+ }
+ }
+
+ /*
+ * we could _almost_ use peek_cqe() here, but there is still
+ * a small gap where io-wq is done with the request and on
+ * its way to posting a completion, but hasn't done it just
+ * yet. the request is canceled and won't be doing any IO
+ * to buffers etc, but the cqe may not have quite arrived yet.
+ */
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "peek=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res >= 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+static int test_sync_cancel(struct io_uring *ring, int async, int nr_all,
+ int use_fd)
+{
+ struct io_uring_sync_cancel_reg reg = { };
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, fds[2], to_prep, i;
+ char buf[32];
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ to_prep = 1;
+ if (nr_all)
+ to_prep = 4;
+ for (i = 0; i < to_prep; i++) {
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
+ sqe->user_data = 0x89;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ }
+
+ ret = io_uring_submit(ring);
+ if (ret != to_prep) {
+ fprintf(stderr, "submit=%d\n", ret);
+ return 1;
+ }
+
+ usleep(10000);
+
+ if (!use_fd)
+ reg.addr = 0x89;
+ else
+ reg.fd = fds[0];
+ reg.timeout.tv_sec = 200;
+ if (nr_all)
+ reg.flags |= IORING_ASYNC_CANCEL_ALL;
+ if (use_fd)
+ reg.flags |= IORING_ASYNC_CANCEL_FD;
+ ret = io_uring_register_sync_cancel(ring, &reg);
+ if (ret < 0) {
+ if (ret == -EINVAL && !no_sync_cancel) {
+ no_sync_cancel = 1;
+ return 0;
+ }
+ fprintf(stderr, "sync_cancel=%d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < to_prep; i++) {
+ /*
+ * we could _almost_ use peek_cqe() here, but there is still
+ * a small gap where io-wq is done with the request and on
+ * its way to posting a completion, but hasn't done it just
+ * yet. the request is canceled and won't be doing any IO
+ * to buffers etc, but the cqe may not have quite arrived yet.
+ */
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "peek=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res >= 0) {
+ fprintf(stderr, "cqe->res=%d\n", cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = t_create_ring(7, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return T_EXIT_SKIP;
+ else if (ret != T_SETUP_OK)
+ return ret;
+
+ ret = test_sync_cancel(&ring, 0, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 0 0 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+ if (no_sync_cancel)
+ return T_EXIT_SKIP;
+
+ ret = test_sync_cancel(&ring, 1, 0, 0);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 1 0 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 0, 1, 0);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 0 1 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 1, 1, 0);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 1 1 0 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 0, 0, 1);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 0 0 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 1, 0, 1);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 1 0 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 0, 1, 1);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 0 1 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel(&ring, 1, 1, 1);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel 1 1 1 failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ ret = test_sync_cancel_timeout(&ring, 0);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel_timeout 0\n");
+ return T_EXIT_FAIL;
+ }
+
+ /* must be last, leaves request */
+ ret = test_sync_cancel_timeout(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "test_sync_cancel_timeout 1\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/teardowns.c b/contrib/libs/liburing/test/teardowns.c
new file mode 100644
index 00000000000..4dfb20ef89f
--- /dev/null
+++ b/contrib/libs/liburing/test/teardowns.c
@@ -0,0 +1,59 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <unistd.h>
+#include <errno.h>
+
+#include "liburing.h"
+
+static void loop(void)
+{
+ int i, ret = 0;
+
+ for (i = 0; i < 100; i++) {
+ struct io_uring ring;
+ int fd;
+
+ memset(&ring, 0, sizeof(ring));
+ fd = io_uring_queue_init(0xa4, &ring, 0);
+ if (fd >= 0) {
+ close(fd);
+ continue;
+ }
+ if (fd != -ENOMEM)
+ ret++;
+ }
+ exit(ret);
+}
+
+int main(int argc, char *argv[])
+{
+ int i, ret, status;
+
+ if (argc > 1)
+ return 0;
+
+ for (i = 0; i < 12; i++) {
+ if (!fork()) {
+ loop();
+ break;
+ }
+ }
+
+ ret = 0;
+ for (i = 0; i < 12; i++) {
+ if (waitpid(-1, &status, 0) < 0) {
+ perror("waitpid");
+ return 1;
+ }
+ if (WEXITSTATUS(status))
+ ret++;
+ }
+
+ return ret;
+}
diff --git a/contrib/libs/liburing/test/test.h b/contrib/libs/liburing/test/test.h
new file mode 100644
index 00000000000..3628163afe4
--- /dev/null
+++ b/contrib/libs/liburing/test/test.h
@@ -0,0 +1,35 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: Test configs for tests.
+ */
+#ifndef LIBURING_TEST_H
+#define LIBURING_TEST_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct io_uring_test_config {
+ unsigned int flags;
+ const char *description;
+} io_uring_test_config;
+
+io_uring_test_config io_uring_test_configs[] = {
+ { 0, "default" },
+ { IORING_SETUP_SQE128, "large SQE"},
+ { IORING_SETUP_CQE32, "large CQE"},
+ { IORING_SETUP_SQE128 | IORING_SETUP_CQE32, "large SQE/CQE" },
+};
+
+#define FOR_ALL_TEST_CONFIGS \
+ for (int i = 0; i < sizeof(io_uring_test_configs) / sizeof(io_uring_test_configs[0]); i++)
+
+#define IORING_GET_TEST_CONFIG_FLAGS() (io_uring_test_configs[i].flags)
+#define IORING_GET_TEST_CONFIG_DESCRIPTION() (io_uring_test_configs[i].description)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/contrib/libs/liburing/test/thread-exit.c b/contrib/libs/liburing/test/thread-exit.c
new file mode 100644
index 00000000000..282b1e8461c
--- /dev/null
+++ b/contrib/libs/liburing/test/thread-exit.c
@@ -0,0 +1,144 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: test that thread pool issued requests don't cancel on thread
+ * exit, but do get canceled once the parent exits. Do both
+ * writes that finish and a poll request that sticks around.
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <pthread.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+#define NR_IOS 8
+#define WSIZE 512
+
+struct d {
+ int fd;
+ struct io_uring *ring;
+ unsigned long off;
+ int pipe_fd;
+ int err;
+ int i;
+};
+
+static char *g_buf[NR_IOS] = {NULL};
+
+static void free_g_buf(void)
+{
+ int i;
+ for (i = 0; i < NR_IOS; i++)
+ free(g_buf[i]);
+}
+
+static void *do_io(void *data)
+{
+ struct d *d = data;
+ struct io_uring_sqe *sqe;
+ char *buffer;
+ int ret;
+
+ buffer = t_malloc(WSIZE);
+ g_buf[d->i] = buffer;
+ memset(buffer, 0x5a, WSIZE);
+ sqe = io_uring_get_sqe(d->ring);
+ if (!sqe) {
+ d->err++;
+ return NULL;
+ }
+ io_uring_prep_write(sqe, d->fd, buffer, WSIZE, d->off);
+ sqe->user_data = d->off;
+
+ sqe = io_uring_get_sqe(d->ring);
+ if (!sqe) {
+ d->err++;
+ return NULL;
+ }
+ io_uring_prep_poll_add(sqe, d->pipe_fd, POLLIN);
+
+ ret = io_uring_submit(d->ring);
+ if (ret != 2)
+ d->err++;
+ return NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ const char *fname;
+ pthread_t thread;
+ int ret, do_unlink, i, fd;
+ struct d d;
+ int fds[2];
+
+ if (pipe(fds) < 0) {
+ perror("pipe");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(32, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ if (argc > 1) {
+ fname = argv[1];
+ do_unlink = 0;
+ } else {
+ fname = ".thread.exit";
+ do_unlink = 1;
+ t_create_file(fname, 4096);
+ }
+
+ fd = open(fname, O_WRONLY);
+ if (do_unlink)
+ unlink(fname);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ d.fd = fd;
+ d.ring = &ring;
+ d.off = 0;
+ d.pipe_fd = fds[0];
+ d.err = 0;
+ for (i = 0; i < NR_IOS; i++) {
+ d.i = i;
+ memset(&thread, 0, sizeof(thread));
+ pthread_create(&thread, NULL, do_io, &d);
+ pthread_join(thread, NULL);
+ d.off += WSIZE;
+ }
+
+ for (i = 0; i < NR_IOS; i++) {
+ struct io_uring_cqe *cqe;
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "io_uring_wait_cqe=%d\n", ret);
+ goto err;
+ }
+ if (cqe->res != WSIZE) {
+ fprintf(stderr, "cqe->res=%d, Expected %d\n", cqe->res,
+ WSIZE);
+ goto err;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ free_g_buf();
+ return d.err;
+err:
+ free_g_buf();
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/timeout-new.c b/contrib/libs/liburing/test/timeout-new.c
new file mode 100644
index 00000000000..d4aeced46b1
--- /dev/null
+++ b/contrib/libs/liburing/test/timeout-new.c
@@ -0,0 +1,253 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: tests for getevents timeout
+ *
+ */
+#include <stdio.h>
+#include <sys/time.h>
+#include <unistd.h>
+#include <pthread.h>
+#include "liburing.h"
+
+#define TIMEOUT_MSEC 200
+#define TIMEOUT_SEC 10
+
+int thread_ret0, thread_ret1;
+int cnt = 0;
+pthread_mutex_t mutex;
+
+static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
+{
+ ts->tv_sec = msec / 1000;
+ ts->tv_nsec = (msec % 1000) * 1000000;
+}
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+
+static int test_return_before_timeout(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int ret;
+ bool retried = false;
+ struct __kernel_timespec ts;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+again:
+ ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
+ if (ret == -ETIME && (ring->flags & IORING_SETUP_SQPOLL) && !retried) {
+ /*
+ * there is a small chance SQPOLL hasn't been waked up yet,
+ * give it one more try.
+ */
+ printf("warning: funky SQPOLL timing\n");
+ sleep(1);
+ retried = true;
+ goto again;
+ } else if (ret < 0) {
+ fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ return 0;
+}
+
+static int test_return_after_timeout(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ int ret;
+ struct __kernel_timespec ts;
+ struct timeval tv;
+ unsigned long long exp;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ gettimeofday(&tv, NULL);
+ ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
+ exp = mtime_since_now(&tv);
+ if (ret != -ETIME) {
+ fprintf(stderr, "%s: timeout error: %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ if (exp < TIMEOUT_MSEC / 2 || exp > (TIMEOUT_MSEC * 3) / 2) {
+ fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
+ return 1;
+ }
+
+ return 0;
+}
+
+int __reap_thread_fn(void *data) {
+ struct io_uring *ring = (struct io_uring *)data;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+
+ msec_to_ts(&ts, TIMEOUT_SEC);
+ pthread_mutex_lock(&mutex);
+ cnt++;
+ pthread_mutex_unlock(&mutex);
+ return io_uring_wait_cqe_timeout(ring, &cqe, &ts);
+}
+
+void *reap_thread_fn0(void *data) {
+ thread_ret0 = __reap_thread_fn(data);
+ return NULL;
+}
+
+void *reap_thread_fn1(void *data) {
+ thread_ret1 = __reap_thread_fn(data);
+ return NULL;
+}
+
+/*
+ * This is to test issuing a sqe in main thread and reaping it in two child-thread
+ * at the same time. To see if timeout feature works or not.
+ */
+int test_multi_threads_timeout() {
+ struct io_uring ring;
+ int ret;
+ bool both_wait = false;
+ pthread_t reap_thread0, reap_thread1;
+ struct io_uring_sqe *sqe;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "%s: ring setup failed: %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ pthread_create(&reap_thread0, NULL, reap_thread_fn0, &ring);
+ pthread_create(&reap_thread1, NULL, reap_thread_fn1, &ring);
+
+ /*
+ * make two threads both enter io_uring_wait_cqe_timeout() before issuing the sqe
+ * as possible as we can. So that there are two threads in the ctx->wait queue.
+ * In this way, we can test if a cqe wakes up two threads at the same time.
+ */
+ while(!both_wait) {
+ pthread_mutex_lock(&mutex);
+ if (cnt == 2)
+ both_wait = true;
+ pthread_mutex_unlock(&mutex);
+ sleep(1);
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ io_uring_prep_nop(sqe);
+
+ ret = io_uring_submit(&ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ pthread_join(reap_thread0, NULL);
+ pthread_join(reap_thread1, NULL);
+
+ if ((thread_ret0 && thread_ret0 != -ETIME) || (thread_ret1 && thread_ret1 != -ETIME)) {
+ fprintf(stderr, "%s: thread wait cqe timeout failed: %d %d\n",
+ __FUNCTION__, thread_ret0, thread_ret1);
+ goto err;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring_normal, ring_sq;
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(8, &ring_normal, 0);
+ if (ret) {
+ fprintf(stderr, "ring_normal setup failed: %d\n", ret);
+ return 1;
+ }
+ if (!(ring_normal.features & IORING_FEAT_EXT_ARG)) {
+ fprintf(stderr, "feature IORING_FEAT_EXT_ARG not supported, skipping.\n");
+ return 0;
+ }
+
+ ret = test_return_before_timeout(&ring_normal);
+ if (ret) {
+ fprintf(stderr, "ring_normal: test_return_before_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_return_after_timeout(&ring_normal);
+ if (ret) {
+ fprintf(stderr, "ring_normal: test_return_after_timeout failed\n");
+ return ret;
+ }
+
+ ret = io_uring_queue_init(8, &ring_sq, IORING_SETUP_SQPOLL);
+ if (ret) {
+ fprintf(stderr, "ring_sq setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = test_return_before_timeout(&ring_sq);
+ if (ret) {
+ fprintf(stderr, "ring_sq: test_return_before_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_return_after_timeout(&ring_sq);
+ if (ret) {
+ fprintf(stderr, "ring_sq: test_return_after_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_multi_threads_timeout();
+ if (ret) {
+ fprintf(stderr, "test_multi_threads_timeout failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/timeout-overflow.c b/contrib/libs/liburing/test/timeout-overflow.c
new file mode 100644
index 00000000000..8c57ff31b12
--- /dev/null
+++ b/contrib/libs/liburing/test/timeout-overflow.c
@@ -0,0 +1,205 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run timeout overflow test
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <limits.h>
+#include <string.h>
+#include <sys/time.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define TIMEOUT_MSEC 200
+static int not_supported;
+
+static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
+{
+ ts->tv_sec = msec / 1000;
+ ts->tv_nsec = (msec % 1000) * 1000000;
+}
+
+static int check_timeout_support(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ struct io_uring_params p;
+ struct io_uring ring;
+ int ret;
+
+ memset(&p, 0, sizeof(p));
+ ret = io_uring_queue_init_params(1, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ /* not really a match, but same kernel added batched completions */
+ if (p.features & IORING_FEAT_POLL_32BITS) {
+ not_supported = 1;
+ return T_EXIT_SKIP;
+ }
+
+ sqe = io_uring_get_sqe(&ring);
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, 1, 0);
+
+ 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;
+ }
+
+ if (cqe->res == -EINVAL) {
+ not_supported = 1;
+ fprintf(stdout, "Timeout not supported, ignored\n");
+ return 0;
+ }
+
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return T_EXIT_PASS;
+err:
+ io_uring_queue_exit(&ring);
+ return T_EXIT_FAIL;
+}
+
+/*
+ * We first setup 4 timeout requests, which require a count value of 1, 1, 2,
+ * UINT_MAX, so the sequence is 1, 2, 4, 2. Before really timeout, this 4
+ * requests will not lead the change of cq_cached_tail, so as sq_dropped.
+ *
+ * And before this patch. The order of this four requests will be req1->req2->
+ * req4->req3. Actually, it should be req1->req2->req3->req4.
+ *
+ * Then, if there is 2 nop req. All timeout requests expect req4 will completed
+ * successful after the patch. And req1/req2 will completed successful with
+ * req3/req4 return -ETIME without this patch!
+ */
+static int test_timeout_overflow(void)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ struct io_uring ring;
+ int i, ret;
+
+ ret = io_uring_queue_init(16, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ for (i = 0; i < 4; i++) {
+ unsigned num = 0;
+ sqe = io_uring_get_sqe(&ring);
+ switch (i) {
+ case 0:
+ case 1:
+ num = 1;
+ break;
+ case 2:
+ num = 2;
+ break;
+ case 3:
+ num = UINT_MAX;
+ break;
+ }
+ io_uring_prep_timeout(sqe, &ts, num, 0);
+ }
+
+ for (i = 0; i < 2; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+ }
+ ret = io_uring_submit(&ring);
+ if (ret < 0) {
+ fprintf(stderr, "sqe submit failed: %d\n", ret);
+ goto err;
+ }
+
+ i = 0;
+ while (i < 6) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "wait completion %d\n", ret);
+ goto err;
+ }
+
+ /*
+ * cqe1: first nop req
+ * cqe2: first timeout req, because of cqe1
+ * cqe3: second timeout req because of cqe1 + cqe2
+ * cqe4: second nop req
+ * cqe5~cqe6: the left three timeout req
+ */
+ switch (i) {
+ case 0:
+ case 3:
+ if (io_uring_cqe_get_data(cqe) != (void *) 1) {
+ fprintf(stderr, "nop not seen as 1 or 2\n");
+ goto err;
+ }
+ break;
+ case 1:
+ case 2:
+ case 4:
+ if (cqe->res == -ETIME) {
+ fprintf(stderr, "expected not return -ETIME "
+ "for the #%d timeout req\n", i - 1);
+ goto err;
+ }
+ break;
+ case 5:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "expected return -ETIME for "
+ "the #%d timeout req\n", i - 1);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ i++;
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = check_timeout_support();
+ if (ret == T_EXIT_FAIL) {
+ fprintf(stderr, "check_timeout_support failed: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (not_supported)
+ return T_EXIT_SKIP;
+
+ ret = test_timeout_overflow();
+ if (ret) {
+ fprintf(stderr, "test_timeout_overflow failed\n");
+ return T_EXIT_FAIL;
+ }
+
+ return T_EXIT_PASS;
+}
diff --git a/contrib/libs/liburing/test/timeout.c b/contrib/libs/liburing/test/timeout.c
new file mode 100644
index 00000000000..41d86279387
--- /dev/null
+++ b/contrib/libs/liburing/test/timeout.c
@@ -0,0 +1,1524 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various timeout tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/time.h>
+#include <sys/wait.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "liburing.h"
+#include "../src/syscall.h"
+
+#define TIMEOUT_MSEC 200
+static int not_supported;
+static int no_modify;
+
+static void msec_to_ts(struct __kernel_timespec *ts, unsigned int msec)
+{
+ ts->tv_sec = msec / 1000;
+ ts->tv_nsec = (msec % 1000) * 1000000;
+}
+
+static unsigned long long mtime_since(const struct timeval *s,
+ const struct timeval *e)
+{
+ long long sec, usec;
+
+ sec = e->tv_sec - s->tv_sec;
+ usec = (e->tv_usec - s->tv_usec);
+ if (sec > 0 && usec < 0) {
+ sec--;
+ usec += 1000000;
+ }
+
+ sec *= 1000;
+ usec /= 1000;
+ return sec + usec;
+}
+
+static unsigned long long mtime_since_now(struct timeval *tv)
+{
+ struct timeval end;
+
+ gettimeofday(&end, NULL);
+ return mtime_since(tv, &end);
+}
+
+/*
+ * Test that we return to userspace if a timeout triggers, even if we
+ * don't satisfy the number of events asked for.
+ */
+static int test_single_timeout_many(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ unsigned long long exp;
+ struct __kernel_timespec ts;
+ struct timeval tv;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ gettimeofday(&tv, NULL);
+ ret = __sys_io_uring_enter(ring->ring_fd, 0, 4, IORING_ENTER_GETEVENTS,
+ NULL);
+ if (ret < 0) {
+ fprintf(stderr, "%s: io_uring_enter %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Timeout not supported, ignored\n");
+ not_supported = 1;
+ return 0;
+ } else if (ret != -ETIME) {
+ fprintf(stderr, "Timeout: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ exp = mtime_since_now(&tv);
+ if (exp >= TIMEOUT_MSEC / 2 && exp <= (TIMEOUT_MSEC * 3) / 2)
+ return 0;
+ fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
+err:
+ return 1;
+}
+
+/*
+ * Test numbered trigger of timeout
+ */
+static int test_single_timeout_nr(struct io_uring *ring, int nr)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ int i, ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, nr, 0);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+
+ ret = io_uring_submit_and_wait(ring, 3);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ i = 0;
+ while (i < 3) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = cqe->res;
+
+ /*
+ * NOP commands have user_data as 1. Check that we get the
+ * at least 'nr' NOPs first, then the successfully removed timeout.
+ */
+ if (io_uring_cqe_get_data(cqe) == NULL) {
+ if (i < nr) {
+ fprintf(stderr, "%s: timeout received too early\n", __FUNCTION__);
+ goto err;
+ }
+ if (ret) {
+ fprintf(stderr, "%s: timeout triggered by passage of"
+ " time, not by events completed\n", __FUNCTION__);
+ goto err;
+ }
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ if (ret) {
+ fprintf(stderr, "res: %d\n", ret);
+ goto err;
+ }
+ i++;
+ };
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_single_timeout_wait(struct io_uring *ring,
+ struct io_uring_params *p)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ int i, ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+
+ /* no implied submit for newer kernels */
+ if (p->features & IORING_FEAT_EXT_ARG) {
+ ret = io_uring_submit(ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: submit %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+ }
+
+ msec_to_ts(&ts, 1000);
+
+ i = 0;
+ do {
+ ret = io_uring_wait_cqes(ring, &cqe, 2, &ts, NULL);
+ if (ret == -ETIME)
+ break;
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait timeout failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (ret < 0) {
+ fprintf(stderr, "res: %d\n", ret);
+ goto err;
+ }
+ i++;
+ } while (1);
+
+ if (i != 2) {
+ fprintf(stderr, "got %d completions\n", i);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test single timeout waking us up
+ */
+static int test_single_timeout(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ unsigned long long exp;
+ struct __kernel_timespec ts;
+ struct timeval tv;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ gettimeofday(&tv, NULL);
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "%s: Timeout not supported, ignored\n", __FUNCTION__);
+ not_supported = 1;
+ return 0;
+ } else if (ret != -ETIME) {
+ fprintf(stderr, "%s: Timeout: %s\n", __FUNCTION__, strerror(-ret));
+ goto err;
+ }
+
+ exp = mtime_since_now(&tv);
+ if (exp >= TIMEOUT_MSEC / 2 && exp <= (TIMEOUT_MSEC * 3) / 2)
+ return 0;
+ fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
+err:
+ return 1;
+}
+
+static int test_single_timeout_remove_notfound(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ if (no_modify)
+ return 0;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, 2, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ io_uring_prep_timeout_remove(sqe, 2, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ /*
+ * We should get two completions. One is our modify request, which should
+ * complete with -ENOENT. The other is the timeout that will trigger after
+ * TIMEOUT_MSEC.
+ */
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ if (cqe->user_data == 2) {
+ if (cqe->res != -ENOENT) {
+ fprintf(stderr, "%s: modify ret %d, wanted ENOENT\n", __FUNCTION__, cqe->res);
+ break;
+ }
+ } else if (cqe->user_data == 1) {
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: timeout ret %d, wanted -ETIME\n", __FUNCTION__, cqe->res);
+ break;
+ }
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_single_timeout_remove(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ io_uring_prep_timeout_remove(sqe, 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ /*
+ * We should have two completions ready. One is for the original timeout
+ * request, user_data == 1, that should have a ret of -ECANCELED. The other
+ * is for our modify request, user_data == 2, that should have a ret of 0.
+ */
+ for (i = 0; i < 2; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ if (no_modify)
+ goto seen;
+ if (cqe->res == -EINVAL && cqe->user_data == 2) {
+ fprintf(stdout, "Timeout modify not supported, ignoring\n");
+ no_modify = 1;
+ goto seen;
+ }
+ if (cqe->user_data == 1) {
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "%s: timeout ret %d, wanted canceled\n", __FUNCTION__, cqe->res);
+ break;
+ }
+ } else if (cqe->user_data == 2) {
+ if (cqe->res) {
+ fprintf(stderr, "%s: modify ret %d, wanted 0\n", __FUNCTION__, cqe->res);
+ break;
+ }
+ }
+seen:
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test single absolute timeout waking us up
+ */
+static int test_single_timeout_abs(struct io_uring *ring)
+{
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ unsigned long long exp;
+ struct __kernel_timespec ts;
+ struct timespec abs_ts;
+ struct timeval tv;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ clock_gettime(CLOCK_MONOTONIC, &abs_ts);
+ ts.tv_sec = abs_ts.tv_sec + 1;
+ ts.tv_nsec = abs_ts.tv_nsec;
+ io_uring_prep_timeout(sqe, &ts, 0, IORING_TIMEOUT_ABS);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ gettimeofday(&tv, NULL);
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ if (ret == -EINVAL) {
+ fprintf(stdout, "Absolute timeouts not supported, ignored\n");
+ return 0;
+ } else if (ret != -ETIME) {
+ fprintf(stderr, "Timeout: %s\n", strerror(-ret));
+ goto err;
+ }
+
+ exp = mtime_since_now(&tv);
+ if (exp >= 1000 / 2 && exp <= (1000 * 3) / 2)
+ return 0;
+ fprintf(stderr, "%s: Timeout seems wonky (got %llu)\n", __FUNCTION__, exp);
+err:
+ return 1;
+}
+
+/*
+ * Test that timeout is canceled on exit
+ */
+static int test_single_timeout_exit(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+
+ msec_to_ts(&ts, 30000);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ io_uring_queue_exit(ring);
+ return 0;
+err:
+ io_uring_queue_exit(ring);
+ return 1;
+}
+
+/*
+ * Test multi timeouts waking us up
+ */
+static int test_multi_timeout(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts[2];
+ unsigned int timeout[2];
+ unsigned long long exp;
+ struct timeval tv;
+ int ret, i;
+
+ /* req_1: timeout req, count = 1, time = (TIMEOUT_MSEC * 2) */
+ timeout[0] = TIMEOUT_MSEC * 2;
+ msec_to_ts(&ts[0], timeout[0]);
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts[0], 1, 0);
+ sqe->user_data = 1;
+
+ /* req_2: timeout req, count = 1, time = TIMEOUT_MSEC */
+ timeout[1] = TIMEOUT_MSEC;
+ msec_to_ts(&ts[1], timeout[1]);
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts[1], 1, 0);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ gettimeofday(&tv, NULL);
+ for (i = 0; i < 2; i++) {
+ unsigned int time = 0;
+ __u64 user_data = 0;
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ /*
+ * Both of these two reqs should timeout, but req_2 should
+ * return before req_1.
+ */
+ switch (i) {
+ case 0:
+ user_data = 2;
+ time = timeout[1];
+ break;
+ case 1:
+ user_data = 1;
+ time = timeout[0];
+ break;
+ }
+
+ if (cqe->user_data != user_data) {
+ fprintf(stderr, "%s: unexpected timeout req %d sequence\n",
+ __FUNCTION__, i+1);
+ goto err;
+ }
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: Req %d timeout: %s\n",
+ __FUNCTION__, i+1, strerror(cqe->res));
+ goto err;
+ }
+ exp = mtime_since_now(&tv);
+ if (exp < time / 2 || exp > (time * 3) / 2) {
+ fprintf(stderr, "%s: Req %d timeout seems wonky (got %llu)\n",
+ __FUNCTION__, i+1, exp);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test multi timeout req with different count
+ */
+static int test_multi_timeout_nr(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+
+ /* req_1: timeout req, count = 2 */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 2, 0);
+ sqe->user_data = 1;
+
+ /* req_2: timeout req, count = 1 */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 1, 0);
+ sqe->user_data = 2;
+
+ /* req_3: nop req */
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ io_uring_sqe_set_data(sqe, (void *) 1);
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ /*
+ * req_2 (count=1) should return without error and req_1 (count=2)
+ * should timeout.
+ */
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ switch (i) {
+ case 0:
+ /* Should be nop req */
+ if (io_uring_cqe_get_data(cqe) != (void *) 1) {
+ fprintf(stderr, "%s: nop not seen as 1 or 2\n", __FUNCTION__);
+ goto err;
+ }
+ break;
+ case 1:
+ /* Should be timeout req_2 */
+ if (cqe->user_data != 2) {
+ fprintf(stderr, "%s: unexpected timeout req %d sequence\n",
+ __FUNCTION__, i+1);
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "%s: Req %d res %d\n",
+ __FUNCTION__, i+1, cqe->res);
+ goto err;
+ }
+ break;
+ case 2:
+ /* Should be timeout req_1 */
+ if (cqe->user_data != 1) {
+ fprintf(stderr, "%s: unexpected timeout req %d sequence\n",
+ __FUNCTION__, i+1);
+ goto err;
+ }
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: Req %d timeout: %s\n",
+ __FUNCTION__, i+1, strerror(cqe->res));
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test timeout <link> timeout <drain> timeout
+ */
+static int test_timeout_flags1(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 2;
+ sqe->flags |= IOSQE_IO_DRAIN;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (cqe->res == -EINVAL) {
+ if (!i)
+ fprintf(stdout, "%s: timeout flags not supported\n",
+ __FUNCTION__);
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -ETIME);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res,
+ -ECANCELED);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -ETIME);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test timeout <link> timeout <link> timeout
+ */
+static int test_timeout_flags2(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 2;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (cqe->res == -EINVAL) {
+ if (!i)
+ fprintf(stdout, "%s: timeout flags not supported\n",
+ __FUNCTION__);
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -ETIME);
+ goto err;
+ }
+ break;
+ case 2:
+ case 3:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res,
+ -ECANCELED);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+/*
+ * Test timeout <drain> timeout <link> timeout
+ */
+static int test_timeout_flags3(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret, i;
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_DRAIN;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 2;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 3;
+
+ ret = io_uring_submit(ring);
+ if (ret <= 0) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ for (i = 0; i < 3; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ if (cqe->res == -EINVAL) {
+ if (!i)
+ fprintf(stdout, "%s: timeout flags not supported\n",
+ __FUNCTION__);
+ io_uring_cqe_seen(ring, cqe);
+ continue;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ case 2:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -ETIME);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res,
+ -ECANCELED);
+ goto err;
+ }
+ break;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ return 0;
+err:
+ return 1;
+}
+
+static int test_update_timeout(struct io_uring *ring, unsigned long ms,
+ bool abs, bool async, bool linked)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts, ts_upd;
+ unsigned long long exp_ms, base_ms = 10000;
+ struct timeval tv;
+ int ret, i, nr = 2;
+ __u32 mode = abs ? IORING_TIMEOUT_ABS : 0;
+
+ msec_to_ts(&ts_upd, ms);
+ gettimeofday(&tv, NULL);
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ msec_to_ts(&ts, base_ms);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->user_data = 1;
+
+ if (linked) {
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 3;
+ sqe->flags = IOSQE_IO_LINK;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+ nr++;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout_update(sqe, &ts_upd, 1, mode);
+ sqe->user_data = 2;
+ if (async)
+ sqe->flags |= IOSQE_ASYNC;
+
+ ret = io_uring_submit(ring);
+ if (ret != nr) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ for (i = 0; i < nr; i++) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ switch (cqe->user_data) {
+ case 1:
+ if (cqe->res != -ETIME) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -ETIME);
+ goto err;
+ }
+ break;
+ case 2:
+ if (cqe->res != 0) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res,
+ 0);
+ goto err;
+ }
+ break;
+ case 3:
+ if (cqe->res != 0) {
+ fprintf(stderr, "nop failed\n");
+ goto err;
+ }
+ break;
+ default:
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+
+ exp_ms = mtime_since_now(&tv);
+ if (exp_ms >= base_ms / 2) {
+ fprintf(stderr, "too long, timeout wasn't updated\n");
+ goto err;
+ }
+ if (ms >= 1000 && !abs && exp_ms < ms / 2) {
+ fprintf(stderr, "fired too early, potentially updated to 0 ms"
+ "instead of %lu\n", ms);
+ goto err;
+ }
+ return 0;
+err:
+ return 1;
+}
+
+static int test_update_nonexistent_timeout(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ msec_to_ts(&ts, 0);
+ io_uring_prep_timeout_update(sqe, &ts, 42, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = cqe->res;
+ if (ret == -ENOENT)
+ ret = 0;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+err:
+ return 1;
+}
+
+static int test_update_invalid_flags(struct io_uring *ring)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ io_uring_prep_timeout_remove(sqe, 0, IORING_TIMEOUT_ABS);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -EINVAL);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ goto err;
+ }
+ msec_to_ts(&ts, 0);
+ io_uring_prep_timeout_update(sqe, &ts, 0, -1);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ goto err;
+ }
+ if (cqe->res != -EINVAL) {
+ fprintf(stderr, "%s: got %d, wanted %d\n",
+ __FUNCTION__, cqe->res, -EINVAL);
+ goto err;
+ }
+ io_uring_cqe_seen(ring, cqe);
+
+ return 0;
+err:
+ return 1;
+}
+
+static int fill_exec_target(char *dst, char *path)
+{
+ struct stat sb;
+
+ /*
+ * Should either be ./exec-target.t or test/exec-target.t
+ */
+ sprintf(dst, "%s", path);
+ return stat(dst, &sb);
+}
+
+static int test_timeout_link_cancel(void)
+{
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ char prog_path[PATH_MAX];
+ pid_t p;
+ int ret, i, wstatus;
+
+ if (fill_exec_target(prog_path, "./exec-target.t") &&
+ fill_exec_target(prog_path, "test/exec-target.t")) {
+ fprintf(stdout, "Can't find exec-target, skipping\n");
+ return 0;
+ }
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ p = fork();
+ if (p == -1) {
+ fprintf(stderr, "fork() failed\n");
+ return 1;
+ }
+
+ if (p == 0) {
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts;
+
+ msec_to_ts(&ts, 10000);
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts, 0, 0);
+ sqe->flags |= IOSQE_IO_LINK;
+ sqe->user_data = 0;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 1;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: got %d, wanted 1\n", __FUNCTION__, ret);
+ exit(1);
+ }
+
+ /* trigger full cancellation */
+ ret = execl(prog_path, prog_path, NULL);
+ if (ret) {
+ fprintf(stderr, "exec failed %i\n", errno);
+ exit(1);
+ }
+ exit(0);
+ }
+
+ if (waitpid(p, &wstatus, 0) == (pid_t)-1) {
+ perror("waitpid()");
+ return 1;
+ }
+ if (!WIFEXITED(wstatus) || WEXITSTATUS(wstatus)) {
+ fprintf(stderr, "child failed %i\n", WEXITSTATUS(wstatus));
+ return 1;
+ }
+
+ for (i = 0; i < 2; ++i) {
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ return 1;
+ }
+ if (cqe->res != -ECANCELED) {
+ fprintf(stderr, "invalid result, user_data: %i res: %i\n",
+ (int)cqe->user_data, cqe->res);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+
+static int test_not_failing_links(void)
+{
+ struct io_uring ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ struct __kernel_timespec ts;
+ int ret;
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring create failed: %d\n", ret);
+ return 1;
+ }
+
+ msec_to_ts(&ts, 1);
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_timeout(sqe, &ts, 0, IORING_TIMEOUT_ETIME_SUCCESS);
+ sqe->user_data = 1;
+ sqe->flags |= IOSQE_IO_LINK;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_nop(sqe);
+ sqe->user_data = 2;
+
+ ret = io_uring_submit(&ring);
+ if (ret != 2) {
+ fprintf(stderr, "%s: sqe submit failed: %d\n", __FUNCTION__, ret);
+ return 1;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ return 1;
+ } else if (cqe->user_data == 1 && cqe->res == -EINVAL) {
+ fprintf(stderr, "ETIME_SUCCESS is not supported, skip\n");
+ goto done;
+ } else if (cqe->res != -ETIME || cqe->user_data != 1) {
+ fprintf(stderr, "timeout failed %i %i\n", cqe->res,
+ (int)cqe->user_data);
+ return 1;
+ }
+ io_uring_cqe_seen(&ring, cqe);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "%s: wait completion %d\n", __FUNCTION__, ret);
+ return 1;
+ } else if (cqe->res || cqe->user_data != 2) {
+ fprintf(stderr, "nop failed %i %i\n", cqe->res,
+ (int)cqe->user_data);
+ return 1;
+ }
+done:
+ io_uring_cqe_seen(&ring, cqe);
+ io_uring_queue_exit(&ring);
+ return 0;
+}
+
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring, sqpoll_ring;
+ bool has_timeout_update, sqpoll;
+ struct io_uring_params p = { };
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = io_uring_queue_init(8, &sqpoll_ring, IORING_SETUP_SQPOLL);
+ sqpoll = !ret;
+
+ ret = test_single_timeout(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout failed\n");
+ return ret;
+ }
+ if (not_supported)
+ return 0;
+
+ ret = test_multi_timeout(&ring);
+ if (ret) {
+ fprintf(stderr, "test_multi_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_abs(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_abs failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_remove(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_remove failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_remove_notfound(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_remove_notfound failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_many(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_many failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_nr(&ring, 1);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_nr(1) failed\n");
+ return ret;
+ }
+ ret = test_single_timeout_nr(&ring, 2);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_nr(2) failed\n");
+ return ret;
+ }
+
+ ret = test_multi_timeout_nr(&ring);
+ if (ret) {
+ fprintf(stderr, "test_multi_timeout_nr failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_flags1(&ring);
+ if (ret) {
+ fprintf(stderr, "test_timeout_flags1 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_flags2(&ring);
+ if (ret) {
+ fprintf(stderr, "test_timeout_flags2 failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_flags3(&ring);
+ if (ret) {
+ fprintf(stderr, "test_timeout_flags3 failed\n");
+ return ret;
+ }
+
+ ret = test_single_timeout_wait(&ring, &p);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_wait failed\n");
+ return ret;
+ }
+
+ /* io_uring_wait_cqes() may have left a timeout, reinit ring */
+ io_uring_queue_exit(&ring);
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed\n");
+ return 1;
+ }
+
+ ret = test_update_nonexistent_timeout(&ring);
+ has_timeout_update = (ret != -EINVAL);
+ if (has_timeout_update) {
+ if (ret) {
+ fprintf(stderr, "test_update_nonexistent_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_update_invalid_flags(&ring);
+ if (ret) {
+ fprintf(stderr, "test_update_invalid_flags failed\n");
+ return ret;
+ }
+
+ ret = test_update_timeout(&ring, 0, false, false, false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout failed\n");
+ return ret;
+ }
+
+ ret = test_update_timeout(&ring, 1, false, false, false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout 1ms failed\n");
+ return ret;
+ }
+
+ ret = test_update_timeout(&ring, 1000, false, false, false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout 1s failed\n");
+ return ret;
+ }
+
+ ret = test_update_timeout(&ring, 0, true, true, false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout abs failed\n");
+ return ret;
+ }
+
+
+ ret = test_update_timeout(&ring, 0, false, true, false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout async failed\n");
+ return ret;
+ }
+
+ ret = test_update_timeout(&ring, 0, false, false, true);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout linked failed\n");
+ return ret;
+ }
+
+ if (sqpoll) {
+ ret = test_update_timeout(&sqpoll_ring, 0, false, false,
+ false);
+ if (ret) {
+ fprintf(stderr, "test_update_timeout sqpoll"
+ "failed\n");
+ return ret;
+ }
+ }
+ }
+
+ /*
+ * this test must go last, it kills the ring
+ */
+ ret = test_single_timeout_exit(&ring);
+ if (ret) {
+ fprintf(stderr, "test_single_timeout_exit failed\n");
+ return ret;
+ }
+
+ ret = test_timeout_link_cancel();
+ if (ret) {
+ fprintf(stderr, "test_timeout_link_cancel failed\n");
+ return ret;
+ }
+
+ ret = test_not_failing_links();
+ if (ret) {
+ fprintf(stderr, "test_not_failing_links failed\n");
+ return ret;
+ }
+
+ if (sqpoll)
+ io_uring_queue_exit(&sqpoll_ring);
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/tty-write-dpoll.c b/contrib/libs/liburing/test/tty-write-dpoll.c
new file mode 100644
index 00000000000..62a5f155a72
--- /dev/null
+++ b/contrib/libs/liburing/test/tty-write-dpoll.c
@@ -0,0 +1,61 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Test double poll tty write. A test case for the regression fixed by:
+ *
+ * commit 6e295a664efd083ac9a5c1a8130c45be1db0cde7
+ * Author: Jens Axboe <axboe@kernel.dk>
+ * Date: Tue Mar 22 13:11:28 2022 -0600
+ *
+ * io_uring: fix assuming triggered poll waitqueue is the single poll
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+#define SQES 128
+#define BUFSIZE 512
+
+int main(int argc, char *argv[])
+{
+ static char buf[BUFSIZE];
+ struct iovec vecs[SQES];
+ struct io_uring ring;
+ int ret, i, fd;
+
+ if (argc > 1)
+ return 0;
+
+ fd = open("/dev/ttyS0", O_RDWR | O_NONBLOCK);
+ if (fd < 0)
+ return 0;
+
+ ret = t_create_ring(SQES, &ring, 0);
+ if (ret == T_SETUP_SKIP)
+ return 0;
+ else if (ret < 0)
+ return 1;
+
+ for (i = 0; i < SQES; i++) {
+ struct io_uring_sqe *sqe;
+
+ sqe = io_uring_get_sqe(&ring);
+ vecs[i].iov_base = buf;
+ vecs[i].iov_len = sizeof(buf);
+ io_uring_prep_writev(sqe, fd, &vecs[i], 1, 0);
+ }
+
+ ret = io_uring_submit(&ring);
+ if (ret != SQES) {
+ fprintf(stderr, "submit: %d\n", ret);
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/unlink.c b/contrib/libs/liburing/test/unlink.c
new file mode 100644
index 00000000000..b5094ccb59e
--- /dev/null
+++ b/contrib/libs/liburing/test/unlink.c
@@ -0,0 +1,113 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+/*
+ * Description: run various nop tests
+ *
+ */
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/stat.h>
+
+#include "liburing.h"
+
+static int test_unlink(struct io_uring *ring, const char *old)
+{
+ 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;
+ }
+ io_uring_prep_unlink(sqe, old, 0);
+
+ 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 stat_file(const char *buf)
+{
+ struct stat sb;
+
+ if (!stat(buf, &sb))
+ return 0;
+
+ return errno;
+}
+
+int main(int argc, char *argv[])
+{
+ struct io_uring ring;
+ char buf[32] = "./XXXXXX";
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring setup failed: %d\n", ret);
+ return 1;
+ }
+
+ ret = mkstemp(buf);
+ if (ret < 0) {
+ perror("mkstemp");
+ return 1;
+ }
+ close(ret);
+
+ if (stat_file(buf) != 0) {
+ perror("stat");
+ return 1;
+ }
+
+ ret = test_unlink(&ring, buf);
+ if (ret < 0) {
+ if (ret == -EBADF || ret == -EINVAL) {
+ fprintf(stdout, "Unlink not supported, skipping\n");
+ unlink(buf);
+ return 0;
+ }
+ fprintf(stderr, "rename: %s\n", strerror(-ret));
+ goto err;
+ } else if (ret)
+ goto err;
+
+ ret = stat_file(buf);
+ if (ret != ENOENT) {
+ fprintf(stderr, "stat got %s\n", strerror(ret));
+ return 1;
+ }
+
+ ret = test_unlink(&ring, "/3/2/3/1/z/y");
+ if (ret != -ENOENT) {
+ fprintf(stderr, "invalid unlink got %s\n", strerror(-ret));
+ return 1;
+ }
+
+ return 0;
+err:
+ unlink(buf);
+ return 1;
+}
diff --git a/contrib/libs/liburing/test/wakeup-hang.c b/contrib/libs/liburing/test/wakeup-hang.c
new file mode 100644
index 00000000000..1d1140c15e6
--- /dev/null
+++ b/contrib/libs/liburing/test/wakeup-hang.c
@@ -0,0 +1,163 @@
+#include "../config-host.h"
+/* SPDX-License-Identifier: MIT */
+#include <sys/eventfd.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <liburing.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/time.h>
+
+struct thread_data {
+ struct io_uring *ring;
+ int write_fd;
+};
+
+static void error_exit(char *message)
+{
+ perror(message);
+ exit(1);
+}
+
+static void *listener_thread(void *data)
+{
+ struct thread_data *td = data;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ ret = io_uring_wait_cqe(td->ring, &cqe);
+ if (ret < 0) {
+ fprintf(stderr, "Error waiting for completion: %s\n",
+ strerror(-ret));
+ goto err;
+ }
+ if (cqe->res < 0) {
+ fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res));
+ goto err;
+ }
+ io_uring_cqe_seen(td->ring, cqe);
+ return NULL;
+err:
+ return (void *) 1;
+}
+
+static void *wakeup_io_uring(void *data)
+{
+ struct thread_data *td = data;
+ int res;
+
+ res = eventfd_write(td->write_fd, (eventfd_t) 1L);
+ if (res < 0) {
+ perror("eventfd_write");
+ return (void *) 1;
+ }
+ return NULL;
+}
+
+static int test_pipes(void)
+{
+ struct io_uring_sqe *sqe;
+ struct thread_data td;
+ struct io_uring ring;
+ pthread_t t1, t2;
+ int ret, fds[2];
+ void *pret;
+
+ if (pipe(fds) < 0)
+ error_exit("eventfd");
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "Unable to setup io_uring: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ td.write_fd = fds[1];
+ td.ring = &ring;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, fds[0], POLLIN);
+ sqe->user_data = 2;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "ring_submit=%d\n", ret);
+ return 1;
+ }
+
+ pthread_create(&t1, NULL, listener_thread, &td);
+
+ sleep(1);
+
+ pthread_create(&t2, NULL, wakeup_io_uring, &td);
+ pthread_join(t1, &pret);
+
+ io_uring_queue_exit(&ring);
+ return pret != NULL;
+}
+
+static int test_eventfd(void)
+{
+ struct io_uring_sqe *sqe;
+ struct thread_data td;
+ struct io_uring ring;
+ pthread_t t1, t2;
+ int efd, ret;
+ void *pret;
+
+ efd = eventfd(0, 0);
+ if (efd < 0)
+ error_exit("eventfd");
+
+ ret = io_uring_queue_init(8, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "Unable to setup io_uring: %s\n", strerror(-ret));
+ return 1;
+ }
+
+ td.write_fd = efd;
+ td.ring = &ring;
+
+ sqe = io_uring_get_sqe(&ring);
+ io_uring_prep_poll_add(sqe, efd, POLLIN);
+ sqe->user_data = 2;
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "ring_submit=%d\n", ret);
+ return 1;
+ }
+
+ pthread_create(&t1, NULL, listener_thread, &td);
+
+ sleep(1);
+
+ pthread_create(&t2, NULL, wakeup_io_uring, &td);
+ pthread_join(t1, &pret);
+
+ io_uring_queue_exit(&ring);
+ return pret != NULL;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return 0;
+
+ ret = test_pipes();
+ if (ret) {
+ fprintf(stderr, "test_pipe failed\n");
+ return ret;
+ }
+
+ ret = test_eventfd();
+ if (ret) {
+ fprintf(stderr, "test_eventfd failed\n");
+ return ret;
+ }
+
+ return 0;
+}
diff --git a/contrib/libs/liburing/test/xattr.c b/contrib/libs/liburing/test/xattr.c
new file mode 100644
index 00000000000..e1f97e58292
--- /dev/null
+++ b/contrib/libs/liburing/test/xattr.c
@@ -0,0 +1,426 @@
+#include "../config-host.h"
+#include <assert.h>
+#include <fcntl.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/xattr.h>
+#include <unistd.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static int no_xattr;
+
+/* Define constants. */
+#define XATTR_SIZE 255
+#define QUEUE_DEPTH 32
+
+#define FILENAME "xattr.test"
+#define KEY1 "user.val1"
+#define KEY2 "user.val2"
+#define VALUE1 "value1"
+#define VALUE2 "value2-a-lot-longer"
+
+
+/* Call fsetxattr. */
+static int io_uring_fsetxattr(struct io_uring *ring, int fd, const char *name,
+ const void *value, size_t size, int flags)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Error cannot get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_fsetxattr(sqe, fd, name, value, flags, size);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ if (ret == -EINVAL)
+ no_xattr = 1;
+ io_uring_cqe_seen(ring, cqe);
+
+ return ret;
+}
+
+/* Submit fgetxattr request. */
+static int io_uring_fgetxattr(struct io_uring *ring, int fd, const char *name,
+ void *value, size_t size)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Error cannot get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_fgetxattr(sqe, fd, name, value, size);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ if (ret == -1) {
+ fprintf(stderr, "Error couldn'tget value\n");
+ return -1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/* Call setxattr. */
+static int io_uring_setxattr(struct io_uring *ring, const char *path,
+ const char *name, const void *value, size_t size,
+ int flags)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Error cannot get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_setxattr(sqe, name, value, path, flags, size);
+
+ ret = io_uring_submit_and_wait(ring, 1);
+ if (ret != 1) {
+ fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+
+ return ret;
+}
+
+/* Submit getxattr request. */
+static int io_uring_getxattr(struct io_uring *ring, const char *path,
+ const char *name, void *value, size_t size)
+{
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "Error cannot get sqe\n");
+ return -1;
+ }
+
+ io_uring_prep_getxattr(sqe, name, value, path, size);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ if (ret == -1) {
+ fprintf(stderr, "Error couldn'tget value\n");
+ return -1;
+ }
+
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+/* Test driver for fsetxattr and fgetxattr. */
+static int test_fxattr(void)
+{
+ int rc = 0;
+ size_t value_len;
+ struct io_uring ring;
+ char value[XATTR_SIZE];
+
+ /* Init io-uring queue. */
+ int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Create the test file. */
+ int fd = open(FILENAME, O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Error: cannot open file: ret=%d\n", fd);
+ return -1;
+ }
+
+ /* Test writing attributes. */
+ if (io_uring_fsetxattr(&ring, fd, KEY1, VALUE1, strlen(VALUE1), 0) < 0) {
+ if (no_xattr) {
+ fprintf(stdout, "No xattr support, skipping\n");
+ goto Exit;
+ }
+ fprintf(stderr, "Error fsetxattr cannot write key1\n");
+ rc = -1;
+ goto Exit;
+ }
+
+ if (io_uring_fsetxattr(&ring, fd, KEY2, VALUE2, strlen(VALUE2), 0) < 0) {
+ fprintf(stderr, "Error fsetxattr cannot write key1\n");
+ rc = -1;
+ goto Exit;
+ }
+
+ /* Test reading attributes. */
+ value_len = io_uring_fgetxattr(&ring, fd, KEY1, value, XATTR_SIZE);
+ if (value_len != strlen(VALUE1) || strncmp(value, VALUE1, value_len)) {
+ fprintf(stderr, "Error: fgetxattr expected value: %s, returned value: %s\n", VALUE1, value);
+ rc = -1;
+ goto Exit;
+ }
+
+ value_len = io_uring_fgetxattr(&ring, fd, KEY2, value, XATTR_SIZE);
+ if (value_len != strlen(VALUE2) || strncmp(value, VALUE2, value_len)) {
+ fprintf(stderr, "Error: fgetxattr expected value: %s, returned value: %s\n", VALUE2, value);
+ rc = -1;
+ goto Exit;
+ }
+
+ /* Cleanup. */
+Exit:
+ close(fd);
+ unlink(FILENAME);
+
+ io_uring_queue_exit(&ring);
+
+ return rc;
+}
+
+/* Test driver for setxattr and getxattr. */
+static int test_xattr(void)
+{
+ int rc = 0;
+ int value_len;
+ struct io_uring ring;
+ char value[XATTR_SIZE];
+
+ /* Init io-uring queue. */
+ int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Create the test file. */
+ t_create_file(FILENAME, 0);
+
+ /* Test writing attributes. */
+ if (io_uring_setxattr(&ring, FILENAME, KEY1, VALUE1, strlen(VALUE1), 0) < 0) {
+ fprintf(stderr, "Error setxattr cannot write key1\n");
+ rc = -1;
+ goto Exit;
+ }
+
+ if (io_uring_setxattr(&ring, FILENAME, KEY2, VALUE2, strlen(VALUE2), 0) < 0) {
+ fprintf(stderr, "Error setxattr cannot write key1\n");
+ rc = -1;
+ goto Exit;
+ }
+
+ /* Test reading attributes. */
+ value_len = io_uring_getxattr(&ring, FILENAME, KEY1, value, XATTR_SIZE);
+ if (value_len != strlen(VALUE1) || strncmp(value, VALUE1, value_len)) {
+ fprintf(stderr, "Error: getxattr expected value: %s, returned value: %s\n", VALUE1, value);
+ rc = -1;
+ goto Exit;
+ }
+
+ value_len = io_uring_getxattr(&ring, FILENAME, KEY2, value, XATTR_SIZE);
+ if (value_len != strlen(VALUE2) || strncmp(value, VALUE2, value_len)) {
+ fprintf(stderr, "Error: getxattr expected value: %s, returned value: %s\n", VALUE2, value);
+ rc = -1;
+ goto Exit;
+ }
+
+ /* Cleanup. */
+Exit:
+ io_uring_queue_exit(&ring);
+ unlink(FILENAME);
+
+ return rc;
+}
+
+/* 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];
+
+ /* Init io-uring queue. */
+ int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Create the test file. */
+ int fd = open(FILENAME, O_CREAT | O_RDWR, 0644);
+ if (fd < 0) {
+ fprintf(stderr, "Error: cannot open file: ret=%d\n", fd);
+ return -1;
+ }
+
+ /* 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);
+
+ /* 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);
+
+ /* Cleanup. */
+ close(fd);
+ unlink(FILENAME);
+
+ io_uring_queue_exit(&ring);
+
+ return rc;
+}
+
+
+/* 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];
+
+ /* Init io-uring queue. */
+ int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Create the test file. */
+ 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);
+
+ /* 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);
+
+ /* Cleanup. */
+ io_uring_queue_exit(&ring);
+ unlink(FILENAME);
+
+ return rc;
+}
+
+/* Test for invalid SQE, this will cause a segmentation fault if enabled. */
+static int test_invalid_sqe(void)
+{
+#ifdef DESTRUCTIVE_TEST
+ struct io_uring_sqe *sqe = NULL;
+ struct io_uring_cqe *cqe = NULL;
+ struct io_uring ring;
+
+ /* Init io-uring queue. */
+ int ret = io_uring_queue_init(QUEUE_DEPTH, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "child: ring setup failed: %d\n", ret);
+ return -1;
+ }
+
+ /* Pass invalid SQE. */
+ io_uring_prep_setxattr(sqe, FILENAME, KEY1, VALUE1, strlen(VALUE1), 0);
+
+ ret = io_uring_submit(&ring);
+ if (ret != 1) {
+ fprintf(stderr, "Error io_uring_submit_and_wait: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "Error io_uring_wait_cqe: ret=%d\n", ret);
+ return -1;
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(&ring, cqe);
+
+ return ret;
+#else
+ return 0;
+#endif
+}
+
+/* Test driver. */
+int main(int argc, char *argv[])
+{
+ if (argc > 1)
+ return 0;
+
+ if (test_fxattr())
+ return EXIT_FAILURE;
+ if (no_xattr)
+ return EXIT_SUCCESS;
+ if (test_xattr() || test_failure_fxattr() || test_failure_xattr() ||
+ test_invalid_sqe())
+ return EXIT_FAILURE;
+
+ return EXIT_SUCCESS;
+}