summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/pull_request_template.md86
-rw-r--r--.gitignore2
-rw-r--r--Makefile6
-rw-r--r--Makefile.common5
-rwxr-xr-xconfigure31
-rw-r--r--examples/Makefile21
-rw-r--r--examples/ucontext-cp.c9
-rw-r--r--liburing.spec2
-rw-r--r--man/io_uring.72
-rw-r--r--man/io_uring_cqe_get_data.334
-rw-r--r--man/io_uring_cqe_seen.332
-rw-r--r--man/io_uring_enter.222
-rw-r--r--man/io_uring_get_sqe.311
-rw-r--r--man/io_uring_prep_read.338
-rw-r--r--man/io_uring_prep_readv.337
-rw-r--r--man/io_uring_prep_readv2.360
-rw-r--r--man/io_uring_prep_write.338
-rw-r--r--man/io_uring_prep_writev.337
-rw-r--r--man/io_uring_prep_writev2.360
-rw-r--r--man/io_uring_register_buffers.334
-rw-r--r--man/io_uring_register_files.335
-rw-r--r--man/io_uring_setup.23
-rw-r--r--man/io_uring_sqe_set_data.330
-rw-r--r--man/io_uring_submit.329
-rw-r--r--man/io_uring_submit_and_wait.334
-rw-r--r--man/io_uring_submit_and_wait_timeout.349
-rw-r--r--man/io_uring_unregister_buffers.326
-rw-r--r--man/io_uring_unregister_files.326
-rw-r--r--man/io_uring_wait_cqe.333
-rw-r--r--man/io_uring_wait_cqe_nr.336
-rw-r--r--man/io_uring_wait_cqe_timeout.339
-rw-r--r--man/io_uring_wait_cqes.346
-rw-r--r--src/Makefile8
-rw-r--r--src/include/liburing.h17
-rw-r--r--src/include/liburing/io_uring.h4
-rw-r--r--src/register.c2
-rw-r--r--test/Makefile270
-rw-r--r--test/exit-no-cleanup.c114
-rw-r--r--test/file-verify.c1
-rw-r--r--test/io-cancel.c17
-rw-r--r--test/poll-cancel.c101
-rw-r--r--test/rename.c1
-rwxr-xr-xtest/runtests-loop.sh2
-rwxr-xr-xtest/runtests.sh2
-rw-r--r--test/skip-cqe.c425
-rw-r--r--test/timeout-overflow.c2
-rw-r--r--test/timeout.c4
-rw-r--r--test/unlink.c1
48 files changed, 1704 insertions, 220 deletions
diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md
new file mode 100644
index 0000000..ae9f4de
--- /dev/null
+++ b/.github/pull_request_template.md
@@ -0,0 +1,86 @@
+
+<!-- Explain your changes here... -->
+
+----
+## git request-pull output:
+```
+<!-- START REPLACE ME -->
+
+Generate your PR shortlog and diffstat with these commands:
+ git remote add axboe-tree https://github.com/axboe/liburing
+ git fetch axboe-tree
+ git request-pull axboe-tree/master your_fork_URL your_branch_name
+
+Then replace this with the output of `git request-pull` command.
+
+<!-- END REPLACE ME -->
+```
+----
+<details>
+<summary>Click to show/hide pull request guidelines</summary>
+
+## Pull Request Guidelines
+1. To make everyone easily filter pull request from the email
+notification, use `[GIT PULL]` as a prefix in your PR title.
+```
+[GIT PULL] Your Pull Request Title
+```
+2. Follow the commit message format rules below.
+3. Follow the Linux kernel coding style (see: https://github.com/torvalds/linux/blob/master/Documentation/process/coding-style.rst).
+
+### Commit message format rules:
+1. The first line is title (don't be more than 72 chars if possible).
+2. Then an empty line.
+3. Then a description (may be omitted for truly trivial changes).
+4. Then an empty line again (if it has a description).
+5. Then a `Signed-off-by` tag with your real name and email. For example:
+```
+Signed-off-by: Foo Bar <foo.bar@gmail.com>
+```
+
+The description should be word-wrapped at 72 chars. Some things should
+not be word-wrapped. They may be some kind of quoted text - long
+compiler error messages, oops reports, Link, etc. (things that have a
+certain specific format).
+
+Note that all of this goes in the commit message, not in the pull
+request text. The pull request text should introduce what this pull
+request does, and each commit message should explain the rationale for
+why that particular change was made. The git tree is canonical source
+of truth, not github.
+
+Each patch should do one thing, and one thing only. If you find yourself
+writing an explanation for why a patch is fixing multiple issues, that's
+a good indication that the change should be split into separate patches.
+
+If the commit is a fix for an issue, add a `Fixes` tag with the issue
+URL.
+
+Don't use GitHub anonymous email like this as the commit author:
+```
+123456789+username@users.noreply.github.com
+```
+
+Use a real email address!
+
+### Commit message example:
+```
+src/queue: don't flush SQ ring for new wait interface
+
+If we have IORING_FEAT_EXT_ARG, then timeouts are done through the
+syscall instead of by posting an internal timeout. This was done
+to be both more efficient, but also to enable multi-threaded use
+the wait side. If we touch the SQ state by flushing it, that isn't
+safe without synchronization.
+
+Fixes: https://github.com/axboe/liburing/issues/402
+Signed-off-by: Jens Axboe <axboe@kernel.dk>
+```
+
+</details>
+
+----
+## By submitting this pull request, I acknowledge that:
+1. I have followed the above pull request guidelines.
+2. I have the rights to submit this work under the same license.
+3. I agree to a Developer Certificate of Origin (see https://developercertificate.org for more information).
diff --git a/.gitignore b/.gitignore
index fb3a859..0a72f03 100644
--- a/.gitignore
+++ b/.gitignore
@@ -49,6 +49,7 @@
/test/eventfd
/test/eventfd-disable
/test/eventfd-ring
+/test/exit-no-cleanup
/test/fadvise
/test/fallocate
/test/fc2a85cb02ef-test
@@ -131,6 +132,7 @@
/test/testfile
/test/submit-link-fail
/test/exec-target
+/test/skip-cqe
/test/*.dmesg
/test/output/
diff --git a/Makefile b/Makefile
index 5d9c4dc..28c0fd8 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,5 @@
-NAME=liburing
-SPECFILE=$(NAME).spec
-VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECFILE))
-TAG = $(NAME)-$(VERSION)
+include Makefile.common
+
RPMBUILD=$(shell `which rpmbuild >&/dev/null` && echo "rpmbuild" || echo "rpm")
INSTALL=install
diff --git a/Makefile.common b/Makefile.common
new file mode 100644
index 0000000..e7c9412
--- /dev/null
+++ b/Makefile.common
@@ -0,0 +1,5 @@
+TOP := $(dir $(CURDIR)/$(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST)))
+NAME=liburing
+SPECFILE=$(TOP)/$(NAME).spec
+VERSION=$(shell awk '/Version:/ { print $$2 }' $(SPECFILE))
+TAG = $(NAME)-$(VERSION)
diff --git a/configure b/configure
index d2866b3..2061148 100755
--- a/configure
+++ b/configure
@@ -268,7 +268,6 @@ print_config "__kernel_timespec" "$__kernel_timespec"
open_how="no"
cat > $TMPC << EOF
#include <sys/types.h>
-#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <linux/openat2.h>
@@ -309,6 +308,27 @@ fi
print_config "statx" "$statx"
##########################################
+# check for glibc statx
+glibc_statx="no"
+cat > $TMPC << EOF
+#include <sys/types.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <string.h>
+#include <linux/stat.h>
+int main(int argc, char **argv)
+{
+ struct statx x;
+
+ return memset(&x, 0, sizeof(x)) != NULL;
+}
+EOF
+if compile_prog "" "" "glibc_statx"; then
+ glibc_statx="yes"
+fi
+print_config "glibc_statx" "$glibc_statx"
+
+##########################################
# check for C++
has_cxx="no"
cat > $TMPC << EOF
@@ -380,6 +400,9 @@ fi
if test "$statx" = "yes"; then
output_sym "CONFIG_HAVE_STATX"
fi
+if test "$glibc_statx" = "yes"; then
+ output_sym "CONFIG_HAVE_GLIBC_STATX"
+fi
if test "$has_cxx" = "yes"; then
output_sym "CONFIG_HAVE_CXX"
fi
@@ -448,6 +471,12 @@ else cat >> $compat_h << EOF
EOF
fi
+if test "$glibc_statx" = "no" && "$statx" = "yes"; then
+cat >> $compat_h << EOF
+#include <sys/stat.h>
+
+EOF
+fi
cat >> $compat_h << EOF
#endif
diff --git a/examples/Makefile b/examples/Makefile
index d3c5000..f966f94 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -10,20 +10,29 @@ ifneq ($(MAKECMDGOALS),clean)
include ../config-host.mak
endif
-all_targets += io_uring-test io_uring-cp link-cp
+example_srcs := \
+ io_uring-cp.c \
+ io_uring-test.c \
+ link-cp.c
+
+all_targets :=
+
ifdef CONFIG_HAVE_UCONTEXT
-all_targets += ucontext-cp
+ example_srcs += ucontext-cp.c
endif
+all_targets += ucontext-cp
-all: $(all_targets)
+example_targets := $(patsubst %.c,%,$(patsubst %.cc,%,$(example_srcs)))
+all_targets += $(example_targets)
-test_srcs := io_uring-test.c io_uring-cp.c link-cp.c
-test_objs := $(patsubst %.c,%.ol,$(test_srcs))
+all: $(example_targets)
%: %.c
$(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< $(LDFLAGS)
clean:
- @rm -f $(all_targets) $(test_objs)
+ @rm -f $(all_targets)
+
+.PHONY: all clean
diff --git a/examples/ucontext-cp.c b/examples/ucontext-cp.c
index b1369e2..ea0c934 100644
--- a/examples/ucontext-cp.c
+++ b/examples/ucontext-cp.c
@@ -3,7 +3,6 @@
* gcc -Wall -O2 -D_GNU_SOURCE -o ucontext-cp ucontext-cp.c -luring
*/
#define _POSIX_C_SOURCE 199309L
-#include <stddef.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
@@ -23,7 +22,9 @@
#define QD 64
#define BS 1024
-size_t sigstksz = (8 * 1024 + sizeof (max_align_t) - 1) / sizeof (max_align_t);
+#ifndef SIGSTKSZ
+#define SIGSTKSZ 8192
+#endif
typedef struct {
struct io_uring *ring;
@@ -114,13 +115,13 @@ static int setup_context(async_context *pctx, struct io_uring *ring)
perror("getcontext");
return -1;
}
- pctx->stack_buf = malloc(sigstksz);
+ pctx->stack_buf = malloc(SIGSTKSZ);
if (!pctx->stack_buf) {
perror("malloc");
return -1;
}
pctx->ctx_fnew.uc_stack.ss_sp = pctx->stack_buf;
- pctx->ctx_fnew.uc_stack.ss_size = sigstksz;
+ pctx->ctx_fnew.uc_stack.ss_size = SIGSTKSZ;
pctx->ctx_fnew.uc_link = &pctx->ctx_main;
return 0;
diff --git a/liburing.spec b/liburing.spec
index 7eb5731..df62d2f 100644
--- a/liburing.spec
+++ b/liburing.spec
@@ -1,5 +1,5 @@
Name: liburing
-Version: 2.1
+Version: 2.2
Release: 1%{?dist}
Summary: Linux-native io_uring I/O access library
License: (GPLv2 with exceptions and LGPLv2+) or MIT
diff --git a/man/io_uring.7 b/man/io_uring.7
index dc76f42..0a47831 100644
--- a/man/io_uring.7
+++ b/man/io_uring.7
@@ -641,7 +641,7 @@ int app_setup_uring(void) {
int read_from_cq() {
struct io_uring_cqe *cqe;
- unsigned head, reaped = 0;
+ unsigned head;
/* Read barrier */
head = io_uring_smp_load_acquire(cring_head);
diff --git a/man/io_uring_cqe_get_data.3 b/man/io_uring_cqe_get_data.3
new file mode 100644
index 0000000..0c576f9
--- /dev/null
+++ b/man/io_uring_cqe_get_data.3
@@ -0,0 +1,34 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_cqe_get_data 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_cqe_get_data - get user data for completion event
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void *io_uring_cqe_get_data(struct io_uring_cqe *cqe)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_cqe_get_data() function returns the user_data
+with the completion queue entry
+.I cqe.
+
+After the caller has received a completion queue entry (CQE) with io_uring_wait_cqe(),
+he can call he io_uring_cqe_get_data() function to retrieve the
+.I user_data
+value. This requires that
+.I user_data
+has been set earlier with the function io_uring_sqe_set_data().
+
+.SH RETURN VALUE
+If the
+.I user_data
+value has been set before submitting the request, it will be returned. Otherwise
+the functions returns NULL.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_sqe_set_data (3), io_uring_sqe_submit(3) \ No newline at end of file
diff --git a/man/io_uring_cqe_seen.3 b/man/io_uring_cqe_seen.3
new file mode 100644
index 0000000..6a8b151
--- /dev/null
+++ b/man/io_uring_cqe_seen.3
@@ -0,0 +1,32 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_cqe_seen 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_cqe_seen - Mark io_uring completion event as processed
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_cqe_seen(struct io_uring *ring,"
+.BI " struct io_uring_cqe *cqe)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_cqe_seen() function marks the IO completion
+.I cqe
+belonging to the
+.I ring
+param as processed.
+
+After the caller has submitted a request with io_uring_submit(), he can retrieve
+the completion with io_uring_wait_cqe() and mark it then as processed with
+io_uring_cqe_seen().
+
+Completions must be marked as completed, so their slot can get reused.
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_submit (3), io_uring_wait_cqe(3) \ No newline at end of file
diff --git a/man/io_uring_enter.2 b/man/io_uring_enter.2
index 93b97e6..b003e05 100644
--- a/man/io_uring_enter.2
+++ b/man/io_uring_enter.2
@@ -1094,6 +1094,28 @@ are available and this flag is set, then the request will fail with
as the error code. Once a buffer has been used, it is no longer available in
the kernel pool. The application must re-register the given buffer again when
it is ready to recycle it (eg has completed using it). Available since 5.7.
+.TP
+.B IOSQE_CQE_SKIP_SUCCESS
+Don't generate a CQE if the request completes successfully. If the request
+fails, an appropriate CQE will be posted as usual and if there is no
+.B IOSQE_IO_HARDLINK,
+CQEs for all linked requests will be omitted. The notion of failure/success is
+opcode specific and is the same as with breaking chains of
+.B IOSQE_IO_LINK.
+One special case is when the request has a linked timeout, then the CQE
+generation for the linked timeout is decided solely by whether it has
+.B IOSQE_CQE_SKIP_SUCCESS
+set, regardless whether it timed out or was cancelled. In other words, if a
+linked timeout has the flag set, it's guaranteed to not post a CQE.
+
+The semantics are chosen to accommodate several use cases. First, when all but
+the last request of a normal link without linked timeouts are marked with the
+flag, only one CQE per lin is posted. Additionally, it enables supression of
+CQEs in cases where the side effects of a successfully executed operation is
+enough for userspace to know the state of the system. One such example would
+be writing to a synchronisation file.
+
+Available since 5.17.
.PP
.I ioprio
diff --git a/man/io_uring_get_sqe.3 b/man/io_uring_get_sqe.3
index 24834f3..12ae545 100644
--- a/man/io_uring_get_sqe.3
+++ b/man/io_uring_get_sqe.3
@@ -5,7 +5,8 @@
.\"
.TH io_uring_get_sqe 3 "July 10, 2020" "liburing-0.7" "liburing Manual"
.SH NAME
-io_uring_get_sqe - get the next vacant event from the submission queue
+io_uring_get_sqe - get the next available submission queue entry from the
+submission queue
.SH SYNOPSIS
.nf
.BR "#include <liburing.h>"
@@ -15,15 +16,15 @@ io_uring_get_sqe - get the next vacant event from the submission queue
.PP
.SH DESCRIPTION
.PP
-The io_uring_get_sqe() function gets the next vacant event from the submission
-queue belonging to the
+The io_uring_get_sqe() function gets the next available submission queue entry
+from the submission queue belonging to the
.I ring
param.
-On success io_uring_get_sqe() returns a pointer to the submission queue event.
+On success io_uring_get_sqe() returns a pointer to the submission queue entry.
On failure NULL is returned.
-If a submission queue event is returned, it should be filled out via one of the
+If a submission queue entry is returned, it should be filled out via one of the
prep functions such as
.BR io_uring_prep_read (3)
and submitted via
diff --git a/man/io_uring_prep_read.3 b/man/io_uring_prep_read.3
new file mode 100644
index 0000000..e3feb1f
--- /dev/null
+++ b/man/io_uring_prep_read.3
@@ -0,0 +1,38 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_read 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_prep_read - prepare I/O read request
+
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_read(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " void *buf,"
+.BI " unsigned nbytes,"
+.BI " __u64 offset)"
+
+.SH DESCRIPTION
+.PP
+The io_uring_prep_read() prepares an IO read request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start reading
+.I nbytes
+into the buffer
+.I buf
+at the
+.I offset.
+
+After the read has been prepared it can be submitted with one of the submit
+functions.
+
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_prep_readv (3), io_uring_prep_readv2 (3), io_uring_submit(3)
diff --git a/man/io_uring_prep_readv.3 b/man/io_uring_prep_readv.3
new file mode 100644
index 0000000..31eebc1
--- /dev/null
+++ b/man/io_uring_prep_readv.3
@@ -0,0 +1,37 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_readv 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_prep_readv - prepare vector I/O read request
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_readv(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " const struct iovec *iovecs,"
+.BI " unsigned nr_vecs,"
+.BI " __u64 offset)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_prep_readv() prepares a vectored IO read request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start reading
+.I nr_vecs
+into the
+.I iovecs
+array at
+.I offset.
+
+After the write has been prepared it can be submitted with one of the submit
+functions.
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_prep_read (3), io_uring_prep_readv2 (3), io_uring_submit (3)
diff --git a/man/io_uring_prep_readv2.3 b/man/io_uring_prep_readv2.3
new file mode 100644
index 0000000..2755281
--- /dev/null
+++ b/man/io_uring_prep_readv2.3
@@ -0,0 +1,60 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_readv2 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+.fi
+io_uring_prep_readv2 - prepare vector I/O read request with flags
+
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_readv2(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " const struct iovec *iovecs,"
+.BI " unsigned nr_vecs,"
+.BI " __u64 offset,"
+.BI " int flags)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_prep_readv2() prepares a vectored IO read request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start reading
+.I nr_vecs
+into the
+.I iovecs
+array at
+.I offset.
+The behavior of the function can be controlled with the
+.I flags
+parameter.
+
+Supported values for flags are:
+.TP
+.B RWF_HIPRI
+High priority request, poll if possible
+.TP
+.B RWF_DSYNC
+per-IO O_DSYNC
+.TP
+.B RWF_SYNC
+per-IO O_SYNC
+.TP
+.B RWF_NOWAIT
+per-IO, return -EAGAIN if operation would block
+.TP
+.B RWF_APPEND
+per-IO O_APPEND
+.TP
+
+After the write has been prepared, it can be submitted with one of the submit functions.
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_prep_read (3), io_uring_prep_readv (3), io_uring_submit (3)
diff --git a/man/io_uring_prep_write.3 b/man/io_uring_prep_write.3
new file mode 100644
index 0000000..a03c1e7
--- /dev/null
+++ b/man/io_uring_prep_write.3
@@ -0,0 +1,38 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_write 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_prep_write - prepare I/O write request
+
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_write(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " void *buf,"
+.BI " unsigned nbytes,"
+.BI " __u64 offset)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_prep_write() prepares an IO write request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start writing
+.I nbytes
+from the buffer
+.I buf
+at file
+.I offset.
+
+After the write has been prepared, it can be submitted with one of the submit
+functions.
+
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_submit (3) \ No newline at end of file
diff --git a/man/io_uring_prep_writev.3 b/man/io_uring_prep_writev.3
new file mode 100644
index 0000000..8f68e29
--- /dev/null
+++ b/man/io_uring_prep_writev.3
@@ -0,0 +1,37 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_writev 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_prep_writev - prepare vector I/O write request
+
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_writev(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " const struct iovec *iovecs,"
+.BI " unsigned nr_vecs,"
+.BI " __u64 offset)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_prep_writev() prepares a vectored IO write request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start writing
+.I nr_vecs
+from the
+.I iovecs
+array at file
+.I offset.
+
+After the write has been prepared it can be submitted with one of the submit
+functions.
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_prep_write (3), io_uring_prep_writev2 (3), io_uring_submit (3) \ No newline at end of file
diff --git a/man/io_uring_prep_writev2.3 b/man/io_uring_prep_writev2.3
new file mode 100644
index 0000000..f1a4dcc
--- /dev/null
+++ b/man/io_uring_prep_writev2.3
@@ -0,0 +1,60 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_prep_writev2 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_prep_writev2 - prepare vector I/O write request with flags
+
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_prep_writev2(struct io_uring_sqe *sqe,"
+.BI " int fd,"
+.BI " const struct iovec *iovecs,"
+.BI " unsigned nr_vecs,"
+.BI " __u64 offset,"
+.BI " int flags)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_prep_writev2() prepares a vectored IO write request. The submission queue entry
+.I sqe
+is setup to use the file descriptor
+.I fd
+to start writing
+.I nr_vecs
+from the
+.I iovecs
+array at file
+.I offset.
+The behavior of the function can be controlled with the
+.I flags
+parameter.
+
+Supported values for flags are:
+.TP
+.B RWF_HIPRI
+High priority request, poll if possible
+.TP
+.B RWF_DSYNC
+per-IO O_DSYNC
+.TP
+.B RWF_SYNC
+per-IO O_SYNC
+.TP
+.B RWF_NOWAIT
+per-IO, return -EAGAIN if operation would block
+.TP
+.B RWF_APPEND
+per-IO O_APPEND
+
+.TP
+After the write has been prepared, it can be submitted with one of the submit functions.
+
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_prep_write (3), io_uring_prep_writev (3), io_uring_submit (3) \ No newline at end of file
diff --git a/man/io_uring_register_buffers.3 b/man/io_uring_register_buffers.3
new file mode 100644
index 0000000..48cc506
--- /dev/null
+++ b/man/io_uring_register_buffers.3
@@ -0,0 +1,34 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_register_buffers 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_register_buffers - register buffers for fixed buffer operations
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_register_buffers(struct io_uring *ring,"
+.BI " const struct iovec *iovecs,
+.BI " unsigned nr_iovecs)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_register_buffers() function registers
+.I nr_iovecs
+number of buffers defined by the array
+.I iovecs
+belonging to the
+.I ring.
+
+After the caller has registered the buffers, they can be used with one of the
+fixed buffers functions.
+
+.SH RETURN VALUE
+On success
+.BR io_uring_register_buffers (3)
+returns 0. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_unregister_buffers (3) \ No newline at end of file
diff --git a/man/io_uring_register_files.3 b/man/io_uring_register_files.3
new file mode 100644
index 0000000..9b259a2
--- /dev/null
+++ b/man/io_uring_register_files.3
@@ -0,0 +1,35 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_register_files 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_register_files - register file descriptors
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_register_files(struct io_uring *ring,"
+.BI " const int *files,"
+.BI " unsigned nr_files)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_register_files() function registers
+.I nr_files
+number of file descriptors defined by the array
+.I files
+belonging to the
+.I ring
+for subsequent operations.
+
+After the caller has registered the buffers, they can be used with the
+submission queue polling operations.
+
+.SH RETURN VALUE
+On success
+.BR io_uring_register_files (3)
+returns 0. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_unregister_files (3)
diff --git a/man/io_uring_setup.2 b/man/io_uring_setup.2
index 88ce1f9..d527a6a 100644
--- a/man/io_uring_setup.2
+++ b/man/io_uring_setup.2
@@ -37,7 +37,8 @@ struct io_uring_params {
__u32 sq_thread_cpu;
__u32 sq_thread_idle;
__u32 features;
- __u32 resv[4];
+ __u32 wq_fd;
+ __u32 resv[3];
struct io_sqring_offsets sq_off;
struct io_cqring_offsets cq_off;
};
diff --git a/man/io_uring_sqe_set_data.3 b/man/io_uring_sqe_set_data.3
new file mode 100644
index 0000000..b2dd333
--- /dev/null
+++ b/man/io_uring_sqe_set_data.3
@@ -0,0 +1,30 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_sqe_set_data 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_sqe_set_data - set user data for submission queue event
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "void io_uring_sqe_set_data(struct io_uring_sqe *sqe,"
+.BI " void *user_data)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_sqe_set_data() function stores a
+.I user_data
+pointer with the submission queue entry
+.I sqe.
+
+After the caller has requested an submission queue entry (SQE) with io_uring_get_sqe(),
+he can associate a data pointer with the SQE. Once the completion arrives, the
+function io_uring_cqe_get_data() can be called to identify the user request.
+
+.SH RETURN VALUE
+None
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_cqe_get_data (3) \ No newline at end of file
diff --git a/man/io_uring_submit.3 b/man/io_uring_submit.3
new file mode 100644
index 0000000..b2fe11f
--- /dev/null
+++ b/man/io_uring_submit.3
@@ -0,0 +1,29 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_submit 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_submit - submit requests to the submission queue
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_submit(struct io_uring *ring)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_submit() function submits the next events to the submission
+queue belonging to the
+.I ring.
+
+After the caller retrieves a submission queue entry (SQE) with io_uring_get_sqe(),
+prepares the SQE, it can be submitted with io_uring_submit().
+
+.SH RETURN VALUE
+On success
+.BR io_uring_submit (3)
+returns the number of submitted submission queue entries. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_submit_and_wait (3), io_uring_submit_and_wait_timeout (3) \ No newline at end of file
diff --git a/man/io_uring_submit_and_wait.3 b/man/io_uring_submit_and_wait.3
new file mode 100644
index 0000000..105cad2
--- /dev/null
+++ b/man/io_uring_submit_and_wait.3
@@ -0,0 +1,34 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_submit_and_wait 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_submit_and_wait - submit requests to the submission queue and wait for completion
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_submit_and_wait(struct io_uring *ring,"
+.fi
+.BI " unsigned wait_nr)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_submit_and_wait() function submits the next events to the submission
+queue belonging to the
+.I ring
+and waits for
+.I wait_nr
+completion events.
+
+After the caller retrieves a submission queue entry (SQE) with io_uring_get_sqe(),
+prepares the SQE, it can be submitted with io_uring_submit_and_wait().
+
+.SH RETURN VALUE
+On success
+.BR io_uring_submit_and_wait (3)
+returns the number of submitted submission queue entries. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_submit (3), io_uring_submit_and_wait_timeout (3) \ No newline at end of file
diff --git a/man/io_uring_submit_and_wait_timeout.3 b/man/io_uring_submit_and_wait_timeout.3
new file mode 100644
index 0000000..d509915
--- /dev/null
+++ b/man/io_uring_submit_and_wait_timeout.3
@@ -0,0 +1,49 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_submit_and_wait_timeout 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_submit_and_wait_timeout - submit requests to the submission queue and
+wait for the completion with timeout
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_submit_and_wait_timeout(struct io_uring *ring,"
+.fi
+.BI " struct io_uring_cqe **cqe_ptr,"
+.fi
+.BI " unsigned wait_nr,"
+.fi
+.BI " struct __kernel_timespec *ts,"
+.fi
+.BI " sigset_t *sigmask)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_submit_and_wait_timeout() function submits the next events to the submission
+queue belonging to the
+.I ring
+and waits for
+.I wait_nr
+completion events or until the timeout
+.I ts
+expires.The completion events are stored in the
+.I cqe_ptr array.
+The
+.I sigmask
+specifies the set of signals to block. The prevailing signal mask is restored
+before returning.
+
+After the caller retrieves a submission queue entry (SQE) with io_uring_get_sqe(),
+prepares the SQE, it can be submitted with io_uring_submit_and_wait_timeout().
+
+
+.SH RETURN VALUE
+On success
+.BR io_uring_submit_and_wait_timeout (3)
+returns the number of submitted submission queue entries. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_get_sqe (3), io_uring_submit (3), io_uring_submit_and_wait (3), io_uring_wait_cqe (3) \ No newline at end of file
diff --git a/man/io_uring_unregister_buffers.3 b/man/io_uring_unregister_buffers.3
new file mode 100644
index 0000000..4ec1048
--- /dev/null
+++ b/man/io_uring_unregister_buffers.3
@@ -0,0 +1,26 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_unregister_buffers 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_unregister_buffers - unregister buffers for fixed buffer operations
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_unregister_buffers(struct io_uring *ring)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_unregister_buffers() function unregisters
+the fixed buffers previously registered to the
+.I ring.
+
+.SH RETURN VALUE
+On success
+.BR io_uring_unregister_buffers (3)
+returns 0. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_register_buffers (3) \ No newline at end of file
diff --git a/man/io_uring_unregister_files.3 b/man/io_uring_unregister_files.3
new file mode 100644
index 0000000..0fadf5d
--- /dev/null
+++ b/man/io_uring_unregister_files.3
@@ -0,0 +1,26 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_unregister_files 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_unregister_files - unregister file descriptors
+.fi
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_unregister_files(struct io_uring *ring)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_unregister_files() function unregisters
+the file descriptors previously registered to the
+.I ring.
+
+.SH RETURN VALUE
+On success
+.BR io_uring_unregister_files (3)
+returns 0. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_register_files (3) \ No newline at end of file
diff --git a/man/io_uring_wait_cqe.3 b/man/io_uring_wait_cqe.3
new file mode 100644
index 0000000..85f6d78
--- /dev/null
+++ b/man/io_uring_wait_cqe.3
@@ -0,0 +1,33 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_wait_cqe 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_wait_cqe - wait for one io_uring completion event
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_wait_cqe(struct io_uring *ring,"
+.BI " struct io_uring_cqe **cqe_ptr);"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_wait_cqe() function returns an IO completion from the
+queue belonging to the
+.I ring
+param, waiting for it if necessary. The
+.I cqe_ptr
+param is filled in on success.
+
+After the caller has submitted a request with io_uring_submit(), he can retrieve
+the completion with io_uring_wait_cqe().
+
+.SH RETURN VALUE
+On success
+.BR io_uring_wait_cqe (3)
+returns 0 and the cqe_ptr parm is filled in. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_submit (3), io_uring_wait_cqes(3) \ No newline at end of file
diff --git a/man/io_uring_wait_cqe_nr.3 b/man/io_uring_wait_cqe_nr.3
new file mode 100644
index 0000000..a3d9b56
--- /dev/null
+++ b/man/io_uring_wait_cqe_nr.3
@@ -0,0 +1,36 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_wait_cqe_nr 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_wait_cqe_nr - wait for one or more io_uring completion events
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_wait_cqe_nr(struct io_uring *ring,"
+.BI " struct io_uring_cqe **cqe_ptr,"
+.BI " unsigned wait_nr)"
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_wait_cqe_nr() function returns
+.I wait_nr
+IO completion events from the
+queue belonging to the
+.I ring
+param, waiting for it if necessary. The
+.I cqe_ptr
+param is filled in on success.
+
+After the caller has submitted a request with io_uring_submit(), he can retrieve
+the completion with io_uring_wait_cqe_nr().
+
+.SH RETURN VALUE
+On success
+.BR io_uring_wait_cqe_nr (3)
+returns 0 and the cqe_ptr parm is filled in. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_submit (3), io_uring_wait_cqes (3) \ No newline at end of file
diff --git a/man/io_uring_wait_cqe_timeout.3 b/man/io_uring_wait_cqe_timeout.3
new file mode 100644
index 0000000..d836352
--- /dev/null
+++ b/man/io_uring_wait_cqe_timeout.3
@@ -0,0 +1,39 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_wait_cqe_timeout 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_wait_cqe_timeout - wait for one io_uring completion event with timeout
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_wait_cqe_timeout(struct io_uring *ring,"
+.BI " struct io_uring_cqe **cqe_ptr,"
+.BI " struct __kernel_timespec *ts)"
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_wait_cqe_timeout() function returns one IO completion from the
+queue belonging to the
+.I ring
+param, waiting for it if necessary or until the timeout
+.I ts
+expires.
+
+The
+.I cqe_ptr
+param is filled in on success.
+
+If
+.I ts
+is specified, the application does not need to call io_uring_submit (3) before
+calling io_uring_wait_cqes (3).
+
+.SH RETURN VALUE
+On success
+.BR io_uring_wait_cqes (3)
+returns 0 and the cqe_ptr parm is filled in. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_submit (3), io_uring_wait_cqe_timeout (3), io_uring_wait_cqe(3). \ No newline at end of file
diff --git a/man/io_uring_wait_cqes.3 b/man/io_uring_wait_cqes.3
new file mode 100644
index 0000000..5c732da
--- /dev/null
+++ b/man/io_uring_wait_cqes.3
@@ -0,0 +1,46 @@
+.\" Copyright (C) 2021 Stefan Roesch <shr@fb.com>
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.TH io_uring_wait_cqes 3 "November 15, 2021" "liburing-2.1" "liburing Manual"
+.SH NAME
+io_uring_wait_cqes - wait for one or more io_uring completion events
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "int io_uring_wait_cqes(struct io_uring *ring,"
+.BI " struct io_uring_cqe **cqe_ptr,"
+.BI " unsigned wait_nr,"
+.BI " struct __kernel_timespec *ts,"
+.BI " sigset_t *sigmask)
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_wait_cqes() function returns
+.I wait_nr
+IO completions from the queue belonging to the
+.I ring
+param, waiting for it if necessary or until the timeout
+.I ts
+expires. The
+.I sigmask
+specifies the set of signals to block. The prevailing signal mask is restored
+before returning.
+
+The
+.I cqe_ptr
+param is filled in on success.
+
+If
+.I ts
+is specified, the application does not need to call io_uring_submit (3) before
+calling io_uring_wait_cqes (3).
+
+.SH RETURN VALUE
+On success
+.BR io_uring_wait_cqes (3)
+returns 0 and the cqe_ptr parm is filled in. On failure it returns -errno.
+.SH SEE ALSO
+.BR io_uring_submit (3), io_uring_wait_cqe_timeout (3), io_uring_wait_cqe(3).
diff --git a/src/Makefile b/src/Makefile
index 09ff395..c29a80d 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -1,3 +1,5 @@
+include ../Makefile.common
+
prefix ?= /usr
includedir ?= $(prefix)/include
libdir ?= $(prefix)/lib
@@ -14,10 +16,8 @@ LINK_FLAGS=
LINK_FLAGS+=$(LDFLAGS)
ENABLE_SHARED ?= 1
-soname=liburing.so.2
-minor=1
-micro=0
-libname=$(soname).$(minor).$(micro)
+soname=liburing.so
+libname=$(soname).$(VERSION)
all_targets += liburing.a
ifeq ($(ENABLE_SHARED),1)
diff --git a/src/include/liburing.h b/src/include/liburing.h
index 99f4f37..169e098 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -8,7 +8,6 @@
#include <sys/socket.h>
#include <sys/uio.h>
-#include <sys/stat.h>
#include <errno.h>
#include <signal.h>
#include <stdbool.h>
@@ -312,6 +311,14 @@ static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd,
io_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset);
}
+static inline void io_uring_prep_readv2(struct io_uring_sqe *sqe, int fd,
+ const struct iovec *iovecs,
+ unsigned nr_vecs, __u64 offset, int flags)
+{
+ io_uring_prep_readv(sqe, fd, iovecs, nr_vecs, offset);
+ sqe->rw_flags = flags;
+}
+
static inline void io_uring_prep_read_fixed(struct io_uring_sqe *sqe, int fd,
void *buf, unsigned nbytes,
__u64 offset, int buf_index)
@@ -327,6 +334,14 @@ static inline void io_uring_prep_writev(struct io_uring_sqe *sqe, int fd,
io_uring_prep_rw(IORING_OP_WRITEV, sqe, fd, iovecs, nr_vecs, offset);
}
+static inline void io_uring_prep_writev2(struct io_uring_sqe *sqe, int fd,
+ const struct iovec *iovecs,
+ unsigned nr_vecs, __u64 offset, int flags)
+{
+ io_uring_prep_writev(sqe, fd, iovecs, nr_vecs, offset);
+ sqe->rw_flags = flags;
+}
+
static inline void io_uring_prep_write_fixed(struct io_uring_sqe *sqe, int fd,
const void *buf, unsigned nbytes,
__u64 offset, int buf_index)
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index 61683bd..a7d193d 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -74,6 +74,7 @@ enum {
IOSQE_IO_HARDLINK_BIT,
IOSQE_ASYNC_BIT,
IOSQE_BUFFER_SELECT_BIT,
+ IOSQE_CQE_SKIP_SUCCESS_BIT,
};
/*
@@ -91,6 +92,8 @@ enum {
#define IOSQE_ASYNC (1U << IOSQE_ASYNC_BIT)
/* select buffer from sqe->buf_group */
#define IOSQE_BUFFER_SELECT (1U << IOSQE_BUFFER_SELECT_BIT)
+/* don't post CQE if request succeeded */
+#define IOSQE_CQE_SKIP_SUCCESS (1U << IOSQE_CQE_SKIP_SUCCESS_BIT)
/*
* io_uring_setup() flags
@@ -293,6 +296,7 @@ struct io_uring_params {
#define IORING_FEAT_EXT_ARG (1U << 8)
#define IORING_FEAT_NATIVE_WORKERS (1U << 9)
#define IORING_FEAT_RSRC_TAGS (1U << 10)
+#define IORING_FEAT_CQE_SKIP (1U << 11)
/*
* io_uring_register(2) opcodes and arguments
diff --git a/src/register.c b/src/register.c
index 1f2c409..a1b1a22 100644
--- a/src/register.c
+++ b/src/register.c
@@ -248,7 +248,7 @@ int io_uring_register_iowq_aff(struct io_uring *ring, size_t cpusz,
int io_uring_unregister_iowq_aff(struct io_uring *ring)
{
return ____sys_io_uring_register(ring->ring_fd,
- IORING_REGISTER_IOWQ_AFF, NULL, 0);
+ IORING_UNREGISTER_IOWQ_AFF, NULL, 0);
}
int io_uring_register_iowq_max_workers(struct io_uring *ring, unsigned int *val)
diff --git a/test/Makefile b/test/Makefile
index 1a10a24..c70693c 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -8,177 +8,32 @@ include ../config-host.mak
endif
CPPFLAGS ?=
-override CPPFLAGS += -D_GNU_SOURCE -D__SANE_USERSPACE_TYPES__ \
- -I../src/include/ -include ../config-host.h
-CFLAGS ?= -g -O2 -Wall -Wextra
+override CPPFLAGS += \
+ -D_GNU_SOURCE \
+ -D__SANE_USERSPACE_TYPES__ \
+ -I../src/include/ \
+ -include ../config-host.h
+
+CFLAGS ?= -g -O2 -Wall -Wextra
XCFLAGS = -Wno-unused-parameter -Wno-sign-compare
+
ifdef CONFIG_HAVE_STRINGOP_OVERFLOW
- XCFLAGS += -Wstringop-overflow=0
+ XCFLAGS += -Wstringop-overflow=0
endif
+
ifdef CONFIG_HAVE_ARRAY_BOUNDS
- XCFLAGS += -Warray-bounds=0
+ XCFLAGS += -Warray-bounds=0
endif
CXXFLAGS ?= $(CFLAGS)
override CFLAGS += $(XCFLAGS) -DLIBURING_BUILD_TEST
override CXXFLAGS += $(XCFLAGS) -std=c++11 -DLIBURING_BUILD_TEST
+
LDFLAGS ?=
override LDFLAGS += -L../src/ -luring
-test_targets += \
- 232c93d07b74-test \
- 35fa71a030ca-test \
- 500f9fbadef8-test \
- 7ad0e4b2f83c-test \
- 8a9973408177-test \
- 917257daa0fe-test \
- a0908ae19763-test \
- a4c0b3decb33-test \
- accept \
- accept-link \
- accept-reuse \
- accept-test \
- across-fork splice \
- b19062a56726-test \
- b5837bd5311d-test \
- ce593a6c480a-test \
- close-opath \
- connect \
- cq-full \
- cq-overflow \
- cq-peek-batch \
- cq-ready \
- cq-size \
- d4ae271dfaae-test \
- d77a67ed5f27-test \
- defer \
- double-poll-crash \
- eeed8b54e0df-test \
- empty-eownerdead \
- eventfd \
- eventfd-disable \
- eventfd-ring \
- fadvise \
- fallocate \
- fc2a85cb02ef-test \
- file-register \
- file-verify \
- file-update \
- files-exit-hang-poll \
- files-exit-hang-timeout \
- fixed-link \
- fsync \
- hardlink \
- io-cancel \
- io_uring_enter \
- io_uring_register \
- io_uring_setup \
- iopoll \
- lfs-openat \
- lfs-openat-write \
- link \
- link-timeout \
- link_drain \
- madvise \
- mkdir \
- multicqes_drain \
- nop \
- nop-all-sizes \
- open-close \
- openat2 \
- personality \
- pipe-eof \
- pipe-reuse \
- poll \
- poll-cancel \
- poll-cancel-ton \
- poll-link \
- poll-many \
- poll-mshot-update \
- poll-ring \
- poll-v-poll \
- probe \
- read-write \
- register-restrictions \
- rename \
- ring-leak \
- ring-leak2 \
- rw_merge_test \
- self \
- send_recv \
- send_recvmsg \
- shared-wq \
- short-read \
- shutdown \
- sigfd-deadlock \
- socket-rw \
- socket-rw-eagain \
- sq-full \
- sq-poll-dup \
- sq-poll-kthread \
- sq-poll-share \
- sqpoll-disable-exit \
- sqpoll-exit-hang \
- sqpoll-cancel-hang \
- sqpoll-sleep \
- sq-space_left \
- stdout \
- submit-reuse \
- submit-link-fail \
- symlink \
- teardowns \
- thread-exit \
- timeout \
- timeout-new \
- timeout-overflow \
- unlink \
- wakeup-hang \
- sendmsg_fs_cve \
- rsrc_tags \
- exec-target \
- # EOL
-
-all_targets += $(test_targets)
-
-include ../Makefile.quiet
-
-ifdef CONFIG_HAVE_STATX
-test_targets += statx
-endif
-all_targets += statx
-
-ifdef CONFIG_HAVE_CXX
-test_targets += sq-full-cpp
-endif
-all_targets += sq-full-cpp
-
-#
-# Build ../src/syscall.c manually from test's Makefile to support
-# liburing nolibc.
-#
-# Functions in ../src/syscall.c require libc to work with, if we
-# build liburing without libc, we don't have those functions
-# in liburing.a. So build it manually here.
-#
-helpers = helpers.o ../src/syscall.o
-
-all: ${helpers} $(test_targets)
-
-../src/syscall.o: ../src/syscall.c
- $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
-
-helpers.o: helpers.c
- $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
-
-%: %.c ${helpers} helpers.h
- $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< ${helpers} $(LDFLAGS)
-
-%: %.cc ${helpers} helpers.h
- $(QUIET_CXX)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $< ${helpers} $(LDFLAGS)
-
test_srcs := \
- helpers.c \
232c93d07b74-test.c \
35fa71a030ca-test.c \
500f9fbadef8-test.c \
@@ -187,10 +42,10 @@ test_srcs := \
917257daa0fe-test.c \
a0908ae19763-test.c \
a4c0b3decb33-test.c \
+ accept.c \
accept-link.c \
accept-reuse.c \
accept-test.c \
- accept.c \
across-fork.c \
b19062a56726-test.c \
b5837bd5311d-test.c \
@@ -200,7 +55,7 @@ test_srcs := \
cq-full.c \
cq-overflow.c \
cq-peek-batch.c \
- cq-ready.c\
+ cq-ready.c \
cq-size.c \
d4ae271dfaae-test.c \
d77a67ed5f27-test.c \
@@ -208,56 +63,61 @@ test_srcs := \
double-poll-crash.c \
eeed8b54e0df-test.c \
empty-eownerdead.c \
+ eventfd.c \
eventfd-disable.c \
eventfd-ring.c \
- eventfd.c \
+ exec-target.c \
+ exit-no-cleanup.c \
fadvise.c \
fallocate.c \
fc2a85cb02ef-test.c \
file-register.c \
- file-verify.c \
- file-update.c \
files-exit-hang-poll.c \
files-exit-hang-timeout.c \
+ file-update.c \
+ file-verify.c \
fixed-link.c \
fsync.c \
hardlink.c \
io-cancel.c \
+ iopoll.c \
io_uring_enter.c \
io_uring_register.c \
io_uring_setup.c \
- iopoll.c \
- lfs-openat-write.c \
lfs-openat.c \
- link-timeout.c \
+ lfs-openat-write.c \
link.c \
link_drain.c \
+ link-timeout.c \
madvise.c \
mkdir.c \
multicqes_drain.c \
nop-all-sizes.c \
nop.c \
- open-close.c \
openat2.c \
+ open-close.c \
personality.c \
pipe-eof.c \
pipe-reuse.c \
- poll-cancel-ton.c \
+ poll.c \
poll-cancel.c \
+ poll-cancel-ton.c \
poll-link.c \
poll-many.c \
poll-mshot-update.c \
poll-ring.c \
poll-v-poll.c \
- poll.c \
probe.c \
read-write.c \
register-restrictions.c \
rename.c \
- ring-leak.c \
ring-leak2.c \
+ ring-leak.c \
+ rsrc_tags.c \
rw_merge_test.c \
self.c \
+ sendmsg_fs_cve.c \
+ send_recv.c \
send_recvmsg.c \
shared-wq.c \
short-read.c \
@@ -266,34 +126,79 @@ test_srcs := \
socket-rw.c \
socket-rw-eagain.c \
splice.c \
- sq-full-cpp.cc \
sq-full.c \
+ sq-full-cpp.cc \
+ sqpoll-cancel-hang.c \
+ sqpoll-disable-exit.c \
sq-poll-dup.c \
+ sqpoll-exit-hang.c \
sq-poll-kthread.c \
sq-poll-share.c \
- sqpoll-disable-exit.c \
- sqpoll-exit-hang.c \
- sqpoll-cancel-hang.c \
sqpoll-sleep.c \
sq-space_left.c \
statx.c \
stdout.c \
- submit-reuse.c \
submit-link-fail.c \
+ submit-reuse.c \
symlink.c \
teardowns.c \
thread-exit.c \
+ timeout.c \
timeout-new.c \
timeout-overflow.c \
- timeout.c \
unlink.c \
wakeup-hang.c \
- sendmsg_fs_cve.c \
- rsrc_tags.c \
- exec-target.c \
+ skip-cqe.c \
# EOL
-test_objs := $(patsubst %.c,%.ol,$(patsubst %.cc,%.ol,$(test_srcs)))
+
+all_targets :=
+include ../Makefile.quiet
+
+
+ifdef CONFIG_HAVE_STATX
+ test_srcs += statx.c
+endif
+
+ifdef CONFIG_HAVE_GLIBC_STATX
+ test_srcs += statx.c
+endif
+all_targets += statx
+
+
+ifdef CONFIG_HAVE_CXX
+ test_srcs += sq-full-cpp.cc
+endif
+all_targets += sq-full-cpp
+
+
+test_targets := $(patsubst %.c,%,$(patsubst %.cc,%,$(test_srcs)))
+all_targets += $(test_targets)
+
+#
+# Build ../src/syscall.c manually from test's Makefile to support
+# liburing nolibc.
+#
+# Functions in ../src/syscall.c require libc to work with, if we
+# build liburing without libc, we don't have those functions
+# in liburing.a. So build it manually here.
+#
+helpers = helpers.o ../src/syscall.o
+
+all: $(test_targets)
+
+../src/syscall.o: ../src/syscall.c
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+
+helpers.o: helpers.c
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ -c $<
+
+%: %.c $(helpers) helpers.h
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< $(helpers) $(LDFLAGS)
+
+%: %.cc $(helpers) helpers.h
+ $(QUIET_CXX)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $< $(helpers) $(LDFLAGS)
+
35fa71a030ca-test: override LDFLAGS += -lpthread
232c93d07b74-test: override LDFLAGS += -lpthread
@@ -311,17 +216,22 @@ timeout-new: override LDFLAGS += -lpthread
thread-exit: override LDFLAGS += -lpthread
ring-leak2: override LDFLAGS += -lpthread
poll-mshot-update: override LDFLAGS += -lpthread
+exit-no-cleanup: override LDFLAGS += -lpthread
install: $(test_targets) runtests.sh runtests-loop.sh
$(INSTALL) -D -d -m 755 $(datadir)/liburing-test/
$(INSTALL) -D -m 755 $(test_targets) $(datadir)/liburing-test/
$(INSTALL) -D -m 755 runtests.sh $(datadir)/liburing-test/
$(INSTALL) -D -m 755 runtests-loop.sh $(datadir)/liburing-test/
+
clean:
- @rm -f $(all_targets) $(test_objs) helpers.o output/*
+ @rm -f $(all_targets) helpers.o output/*
@rm -rf output/
runtests: all
@./runtests.sh $(test_targets)
+
runtests-loop: all
@./runtests-loop.sh $(test_targets)
+
+.PHONY: all install clean runtests runtests-loop
diff --git a/test/exit-no-cleanup.c b/test/exit-no-cleanup.c
new file mode 100644
index 0000000..90ef394
--- /dev/null
+++ b/test/exit-no-cleanup.c
@@ -0,0 +1,114 @@
+/* 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;
+
+ 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/test/file-verify.c b/test/file-verify.c
index 50cad45..327cb1d 100644
--- a/test/file-verify.c
+++ b/test/file-verify.c
@@ -12,6 +12,7 @@
#include <assert.h>
#include <string.h>
#include <sys/ioctl.h>
+#include <sys/stat.h>
#include <linux/fs.h>
#include "helpers.h"
diff --git a/test/io-cancel.c b/test/io-cancel.c
index b5b443d..703ffa7 100644
--- a/test/io-cancel.c
+++ b/test/io-cancel.c
@@ -341,8 +341,21 @@ static int test_cancel_req_across_fork(void)
fprintf(stderr, "wait_cqe=%d\n", ret);
return 1;
}
- if ((cqe->user_data == 1 && cqe->res != -EINTR) ||
- (cqe->user_data == 2 && cqe->res != -EALREADY && cqe->res)) {
+ 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);
}
diff --git a/test/poll-cancel.c b/test/poll-cancel.c
index a74e915..408159d 100644
--- a/test/poll-cancel.c
+++ b/test/poll-cancel.c
@@ -26,7 +26,7 @@ static void sig_alrm(int sig)
exit(1);
}
-int main(int argc, char *argv[])
+static int test_poll_cancel(void)
{
struct io_uring ring;
int pipe1[2];
@@ -36,9 +36,6 @@ int main(int argc, char *argv[])
struct sigaction act;
int ret;
- if (argc > 1)
- return 0;
-
if (pipe(pipe1) != 0) {
perror("pipe");
return 1;
@@ -130,6 +127,102 @@ int main(int argc, char *argv[])
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/test/rename.c b/test/rename.c
index af09d65..7798d43 100644
--- a/test/rename.c
+++ b/test/rename.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
+#include <sys/stat.h>
#include "liburing.h"
diff --git a/test/runtests-loop.sh b/test/runtests-loop.sh
index f56d26d..b80bc76 100755
--- a/test/runtests-loop.sh
+++ b/test/runtests-loop.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
TESTS=("$@")
ITER=0
diff --git a/test/runtests.sh b/test/runtests.sh
index c77df6a..122e482 100755
--- a/test/runtests.sh
+++ b/test/runtests.sh
@@ -1,4 +1,4 @@
-#!/bin/bash
+#!/usr/bin/env bash
TESTS=("$@")
RET=0
diff --git a/test/skip-cqe.c b/test/skip-cqe.c
new file mode 100644
index 0000000..ba84d92
--- /dev/null
+++ b/test/skip-cqe.c
@@ -0,0 +1,425 @@
+#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 (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/test/timeout-overflow.c b/test/timeout-overflow.c
index f952f80..671f171 100644
--- a/test/timeout-overflow.c
+++ b/test/timeout-overflow.c
@@ -101,7 +101,7 @@ static int test_timeout_overflow(void)
msec_to_ts(&ts, TIMEOUT_MSEC);
for (i = 0; i < 4; i++) {
- unsigned num;
+ unsigned num = 0;
sqe = io_uring_get_sqe(&ring);
switch (i) {
case 0:
diff --git a/test/timeout.c b/test/timeout.c
index f8ba973..8c35b00 100644
--- a/test/timeout.c
+++ b/test/timeout.c
@@ -563,8 +563,8 @@ static int test_multi_timeout(struct io_uring *ring)
gettimeofday(&tv, NULL);
for (i = 0; i < 2; i++) {
- unsigned int time;
- __u64 user_data;
+ unsigned int time = 0;
+ __u64 user_data = 0;
ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
diff --git a/test/unlink.c b/test/unlink.c
index f8c7639..e4c210b 100644
--- a/test/unlink.c
+++ b/test/unlink.c
@@ -9,6 +9,7 @@
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
+#include <sys/stat.h>
#include "liburing.h"