Merge branch 'Pr1' of https://github.com/romange/liburing
* 'Pr1' of https://github.com/romange/liburing:
Add const modifier to functions that do not change the state of the ring
diff --git a/.gitignore b/.gitignore
index 8f7f369..d9fa2c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -80,11 +80,15 @@
/test/poll-cancel-ton
/test/poll-link
/test/poll-many
+/test/poll-ring
/test/poll-v-poll
/test/probe
/test/read-write
/test/register-restrictions
+/test/rename
/test/ring-leak
+/test/ring-leak2
+/test/self
/test/send_recv
/test/send_recvmsg
/test/shared-wq
@@ -96,15 +100,21 @@
/test/splice
/test/sq-full
/test/sq-full-cpp
+/test/sq-poll-dup
/test/sq-poll-kthread
/test/sq-poll-share
+/test/sqpoll-exit-hang
+/test/sqpoll-sleep
/test/sq-space_left
/test/statx
/test/stdout
/test/submit-reuse
/test/teardowns
+/test/thread-exit
/test/timeout
+/test/timeout-new
/test/timeout-overflow
+/test/unlink
/test/wakeup-hang
/test/*.dmesg
diff --git a/Makefile b/Makefile
index c761604..5d9c4dc 100644
--- a/Makefile
+++ b/Makefile
@@ -56,6 +56,8 @@
$(INSTALL) -m 644 man/*.2 $(DESTDIR)$(mandir)/man2
$(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man3
$(INSTALL) -m 644 man/*.3 $(DESTDIR)$(mandir)/man3
+ $(INSTALL) -m 755 -d $(DESTDIR)$(mandir)/man7
+ $(INSTALL) -m 644 man/*.7 $(DESTDIR)$(mandir)/man7
install-tests:
@$(MAKE) -C test install prefix=$(DESTDIR)$(prefix) datadir=$(DESTDIR)$(datadir)
diff --git a/configure b/configure
index 518a5b0..3b96cde 100755
--- a/configure
+++ b/configure
@@ -1,13 +1,4 @@
#!/bin/sh
-#
-# set temporary file name
-if test ! -z "$TMPDIR" ; then
- TMPDIR1="${TMPDIR}"
-elif test ! -z "$TEMPDIR" ; then
- TMPDIR1="${TEMPDIR}"
-else
- TMPDIR1="/tmp"
-fi
cc=${CC:-gcc}
cxx=${CXX:-g++}
@@ -82,10 +73,10 @@
exit 0
fi
-TMPC="${TMPDIR1}/fio-conf-${RANDOM}-$$-${RANDOM}.c"
-TMPC2="${TMPDIR1}/fio-conf-${RANDOM}-$$-${RANDOM}-2.c"
-TMPO="${TMPDIR1}/fio-conf-${RANDOM}-$$-${RANDOM}.o"
-TMPE="${TMPDIR1}/fio-conf-${RANDOM}-$$-${RANDOM}.exe"
+TMPC="$(mktemp --tmpdir fio-conf-XXXXXXXXXX.c)"
+TMPC2="$(mktemp --tmpdir fio-conf-XXXXXXXXXX-2.c)"
+TMPO="$(mktemp --tmpdir fio-conf-XXXXXXXXXX.o)"
+TMPE="$(mktemp --tmpdir fio-conf-XXXXXXXXXX.exe)"
# NB: do not call "exit" in the trap handler; this is buggy with some shells;
# see <1285349658-3122-1-git-send-email-loic.minier@linaro.org>
@@ -296,6 +287,24 @@
fi
print_config "C++" "$has_cxx"
+##########################################
+# check for ucontext support
+has_ucontext="no"
+cat > $TMPC << EOF
+#include <ucontext.h>
+int main(int argc, char **argv)
+{
+ ucontext_t ctx;
+ getcontext(&ctx);
+ return 0;
+}
+EOF
+if compile_prog "" "" "has_ucontext"; then
+ has_ucontext="yes"
+fi
+print_config "has_ucontext" "$has_ucontext"
+
+
#############################################################################
if test "$__kernel_rwf_t" = "yes"; then
@@ -313,6 +322,9 @@
if test "$has_cxx" = "yes"; then
output_sym "CONFIG_HAVE_CXX"
fi
+if test "$has_ucontext" = "yes"; then
+ output_sym "CONFIG_HAVE_UCONTEXT"
+fi
echo "CC=$cc" >> $config_host_mak
print_config "CC" "$cc"
diff --git a/debian/liburing-dev.manpages b/debian/liburing-dev.manpages
index a3d21be..fbbee23 100644
--- a/debian/liburing-dev.manpages
+++ b/debian/liburing-dev.manpages
@@ -3,3 +3,4 @@
man/io_uring_register.2
man/io_uring_queue_exit.3
man/io_uring_queue_init.3
+man/io_uring_get_sqe.3
diff --git a/examples/Makefile b/examples/Makefile
index 28456a9..60c1b71 100644
--- a/examples/Makefile
+++ b/examples/Makefile
@@ -1,6 +1,8 @@
+CPPFLAGS ?=
+override CPPFLAGS += -D_GNU_SOURCE -I../src/include/
CFLAGS ?= -g -O2
XCFLAGS =
-override CFLAGS += -Wall -D_GNU_SOURCE -L../src/ -I../src/include/
+override CFLAGS += -Wall -L../src/
include ../Makefile.quiet
@@ -8,7 +10,11 @@
include ../config-host.mak
endif
-all_targets += io_uring-test io_uring-cp link-cp ucontext-cp
+all_targets += io_uring-test io_uring-cp link-cp
+
+ifdef CONFIG_HAVE_UCONTEXT
+all_targets += ucontext-cp
+endif
all: $(all_targets)
@@ -17,7 +23,7 @@
test_objs := $(patsubst %.c,%.ol,$(test_srcs))
%: %.c
- $(QUIET_CC)$(CC) $(CFLAGS) -o $@ $< -luring $(XCFLAGS)
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -luring $(XCFLAGS)
clean:
@rm -f $(all_targets) $(test_objs)
diff --git a/liburing.spec b/liburing.spec
index 9cbbc61..fa4d970 100644
--- a/liburing.spec
+++ b/liburing.spec
@@ -45,6 +45,7 @@
%{_libdir}/pkgconfig/*
%{_mandir}/man2/*
%{_mandir}/man3/*
+%{_mandir}/man7/*
%changelog
* Thu Oct 31 2019 Jeff Moyer <jmoyer@redhat.com> - 0.2-1
diff --git a/man/io_uring.7 b/man/io_uring.7
index d371afb..a63b3e9 100644
--- a/man/io_uring.7
+++ b/man/io_uring.7
@@ -637,7 +637,7 @@
/* Get the entry */
cqe = &cqes[head & (*cring_mask)];
if (cqe->res < 0)
- fprintf(stderr, "Error: %s\n", strerror(abs(cqe->res)));
+ fprintf(stderr, "Error: %s\\n", strerror(abs(cqe->res)));
head++;
@@ -698,7 +698,7 @@
/* Setup io_uring for use */
if(app_setup_uring()) {
- fprintf(stderr, "Unable to setup uring!\n");
+ fprintf(stderr, "Unable to setup uring!\\n");
return 1;
}
@@ -721,7 +721,7 @@
}
else if (res < 0) {
/* Error reading file */
- fprintf(stderr, "Error: %s\n", strerror(abs(res)));
+ fprintf(stderr, "Error: %s\\n", strerror(abs(res)));
break;
}
offset += res;
diff --git a/man/io_uring_enter.2 b/man/io_uring_enter.2
index 15a5a4a..086207d 100644
--- a/man/io_uring_enter.2
+++ b/man/io_uring_enter.2
@@ -29,15 +29,35 @@
is the file descriptor returned by
.BR io_uring_setup (2).
.I to_submit
-specifies the number of I/Os to submit from the submission queue. If
-the
+specifies the number of I/Os to submit from the submission queue.
+.I flags
+is a bitmask of the following values:
+.TP
.B IORING_ENTER_GETEVENTS
-bit is set in
-.IR flags ,
-then the system call will attempt to wait for
+If this flag is set, then the system call will wait for the specificied
+number of events in
.I min_complete
-event completions before returning. If the io_uring instance was
-configured for polling, by specifying
+before returning. This flag can be set along with
+.I to_submit
+to both submit and complete events in a single system call.
+.TP
+.B IORING_ENTER_SQ_WAKEUP
+If the ring has been created with
+.B IORING_SETUP_SQPOLL,
+then this flag asks the kernel to wakeup the SQ kernel thread to submit IO.
+.TP
+.B IORING_ENTER_SQ_WAIT
+If the ring has been created with
+.B IORING_SETUP_SQPOLL,
+then the application has no real insight into when the SQ kernel thread has
+consumed entries from the SQ ring. This can lead to a situation where the
+application can no longer get a free SQE entry to submit, without knowing
+when it one becomes available as the SQ kernel thread consumes them. If
+the system call is used with this flag set, then it will wait until at least
+one entry is free in the SQ ring.
+.PP
+.PP
+If the io_uring instance was configured for polling, by specifying
.B IORING_SETUP_IOPOLL
in the call to
.BR io_uring_setup (2),
@@ -206,7 +226,10 @@
field. Unlike poll or epoll without
.BR EPOLLONESHOT ,
this interface always works in one shot mode. That is, once the poll
-operation is completed, it will have to be resubmitted.
+operation is completed, it will have to be resubmitted. This command works like
+an async
+.BR poll(2)
+and the completion event result is the returned mask of events.
.TP
.B IORING_OP_POLL_REMOVE
@@ -322,7 +345,9 @@
.TP
.B IORING_OP_TIMEOUT_REMOVE
-Attempt to remove an existing timeout operation.
+If
+.I timeout_flags are zero, then it attempts to remove an existing timeout
+operation.
.I addr
must contain the
.I user_data
@@ -338,6 +363,19 @@
.I -ENOENT
Available since 5.5.
+If
+.I timeout_flags
+contain
+.I IORING_TIMEOUT_UPDATE,
+instead of removing an existing operation it updates it.
+.I addr
+and return values are same as before.
+.I addr2
+field must contain a pointer to a struct timespec64 structure.
+.I timeout_flags
+may also contain IORING_TIMEOUT_ABS.
+Available since 5.11.
+
.TP
.B IORING_OP_ACCEPT
Issue the equivalent of an
@@ -415,9 +453,11 @@
system call.
.I fd
must be set to the file descriptor,
+.I len
+must contain the mode associated with the operation,
.I off
must contain the offset on which to operate, and
-.I len
+.I addr
must contain the length. See also
.BR fallocate(2)
for the general description of the related system call. Available since 5.6.
@@ -663,6 +703,62 @@
must contain the buffer group ID from which to remove the buffers. Available
since 5.7.
+.TP
+.B IORING_OP_SHUTDOWN
+Issue the equivalent of a
+.BR shutdown(2)
+system call.
+.I fd
+is the file descriptor to the socket being shutdown, no other fields should
+be set. Available since 5.11.
+
+.TP
+.B IORING_OP_RENAMEAT
+Issue the equivalent of a
+.BR renameat2(2)
+system call.
+.I fd
+should be set to the
+.I olddirfd,
+.I addr
+should be set to the
+.I oldpath,
+.I len
+should be set to the
+.I newdirfd,
+.I addr
+should be set to the
+.I oldpath,
+.I addr2
+should be set to the
+.I newpath,
+and finally
+.I rename_flags
+should be set to the
+.I flags
+passed in to
+.BR renameat2(2).
+Available since 5.11.
+
+.TP
+.B IORING_OP_UNLINKAT
+Issue the equivalent of a
+.BR unlinkat2(2)
+system call.
+.I fd
+should be set to the
+.I dirfd,
+.I addr
+should be set to the
+.I pathname,
+and
+.I unlink_flags
+should be set to the
+.I flags
+being passed in to
+.BR unlinkat(2).
+Available since 5.11.
+
.PP
The
.I flags
diff --git a/man/io_uring_get_sqe.3 b/man/io_uring_get_sqe.3
new file mode 100644
index 0000000..24834f3
--- /dev/null
+++ b/man/io_uring_get_sqe.3
@@ -0,0 +1,37 @@
+.\" Copyright (C) 2020 Jens Axboe <axboe@kernel.dk>
+.\" Copyright (C) 2020 Red Hat, Inc.
+.\"
+.\" SPDX-License-Identifier: LGPL-2.0-or-later
+.\"
+.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
+.SH SYNOPSIS
+.nf
+.BR "#include <liburing.h>"
+.PP
+.BI "struct io_uring_sqe *io_uring_get_sqe(struct io_uring " *ring );
+.fi
+.PP
+.SH DESCRIPTION
+.PP
+The io_uring_get_sqe() function gets the next vacant event 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 failure NULL is returned.
+
+If a submission queue event is returned, it should be filled out via one of the
+prep functions such as
+.BR io_uring_prep_read (3)
+and submitted via
+.BR io_uring_submit (3).
+
+.SH RETURN VALUE
+.BR io_uring_get_sqe (3)
+returns a pointer to the next submission queue event on success and NULL on
+failure.
+.SH SEE ALSO
+.BR io_uring_submit (3)
diff --git a/man/io_uring_queue_exit.3 b/man/io_uring_queue_exit.3
index 835eb5d..8bdda6a 100644
--- a/man/io_uring_queue_exit.3
+++ b/man/io_uring_queue_exit.3
@@ -3,8 +3,7 @@
.\"
.\" SPDX-License-Identifier: LGPL-2.0-or-later
.\"
-.\" TODO: Fix the version number/date here
-.TH io_uring_queue_exit 3 "September 11, 2020" "liburing 1.0.0" "liburing Manual"
+.TH io_uring_queue_exit 3 "July 10, 2020" "liburing-0.7" "liburing Manual"
.SH NAME
io_uring_queue_exit - tear down io_uring submission and completion queues
.SH SYNOPSIS
diff --git a/man/io_uring_queue_init.3 b/man/io_uring_queue_init.3
index 1a62899..1980fa4 100644
--- a/man/io_uring_queue_init.3
+++ b/man/io_uring_queue_init.3
@@ -3,8 +3,7 @@
.\"
.\" SPDX-License-Identifier: LGPL-2.0-or-later
.\"
-.\" TODO: Fix the version number/date here
-.TH io_uring_queue_init 3 "September 11, 2020" "liburing 1.0.0" "liburing Manual"
+.TH io_uring_queue_init 3 "July 10, 2020" "liburing-0.7" "liburing Manual"
.SH NAME
io_uring_queue_init - setup io_uring submission and completion queues
.SH SYNOPSIS
diff --git a/man/io_uring_register.2 b/man/io_uring_register.2
index 225e461..5326a87 100644
--- a/man/io_uring_register.2
+++ b/man/io_uring_register.2
@@ -144,6 +144,7 @@
ones, either turning a sparse entry (one where fd is equal to -1) into a
real one, removing an existing entry (new one is set to -1), or replacing
an existing entry with a new existing entry.
+
.I arg
must contain a pointer to a struct io_uring_files_update, which contains
an offset on which to start the update, and an array of file descriptors to
@@ -152,6 +153,12 @@
must contain the number of descriptors in the passed in array. Available
since 5.5.
+File descriptors can be skipped if they are set to
+.B IORING_REGISTER_FILES_SKIP.
+Skipping an fd will not touch the file associated with the previous
+fd at that index. Available since 5.12.
+
+
.TP
.B IORING_UNREGISTER_FILES
This operation requires no argument, and
diff --git a/man/io_uring_setup.2 b/man/io_uring_setup.2
index a903b04..3122313 100644
--- a/man/io_uring_setup.2
+++ b/man/io_uring_setup.2
@@ -116,7 +116,7 @@
described below.
.TP
.BR
-Before version 5.10 of the Linux kernel, to successfully use this feature, the
+Before version 5.11 of the Linux kernel, to successfully use this feature, the
application must register a set of files to be used for IO through
.BR io_uring_register (2)
using the
@@ -126,8 +126,8 @@
The presence of this feature can be detected by the
.B IORING_FEAT_SQPOLL_NONFIXED
feature flag.
-In version 5.10 and later, it is no longer necessary to register files to use
-this feature. 5.10 also allows using this as non-root, if the user has the
+In version 5.11 and later, it is no longer necessary to register files to use
+this feature. 5.11 also allows using this as non-root, if the user has the
.B CAP_SYS_NICE
capability.
.TP
@@ -256,7 +256,7 @@
If this flag is set, then io_uring supports using an internal poll mechanism
to drive data/space readiness. This means that requests that cannot read or
write data to a file no longer need to be punted to an async thread for
-handling, instead they will being operation when the file is ready. This is
+handling, instead they will begin operation when the file is ready. This is
similar to doing poll + read/write in userspace, but eliminates the need to do
so. If this flag is set, requests waiting on space/data consume a lot less
resources doing so as they are not blocking a thread.
diff --git a/src/Makefile b/src/Makefile
index 4a28d25..dfca826 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -3,9 +3,10 @@
libdir ?= $(prefix)/lib
libdevdir ?= $(prefix)/lib
+CPPFLAGS ?=
+override CPPFLAGS += -Iinclude/ -include ../config-host.h
CFLAGS ?= -g -fomit-frame-pointer -O2
-override CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare\
- -Iinclude/ -include ../config-host.h
+override CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare
SO_CFLAGS=-fPIC $(CFLAGS)
L_CFLAGS=$(CFLAGS)
LINK_FLAGS=
@@ -38,10 +39,10 @@
$(liburing_objs) $(liburing_sobjs): include/liburing/io_uring.h
%.os: %.c
- $(QUIET_CC)$(CC) $(SO_CFLAGS) -c -o $@ $<
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(SO_CFLAGS) -c -o $@ $<
%.ol: %.c
- $(QUIET_CC)$(CC) $(L_CFLAGS) -c -o $@ $<
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(L_CFLAGS) -c -o $@ $<
AR ?= ar
RANLIB ?= ranlib
diff --git a/src/include/liburing.h b/src/include/liburing.h
index 014d504..6b2cfa1 100644
--- a/src/include/liburing.h
+++ b/src/include/liburing.h
@@ -2,6 +2,10 @@
#ifndef LIB_URING_H
#define LIB_URING_H
+#ifndef _XOPEN_SOURCE
+#define _XOPEN_SOURCE 500 /* Required for glibc to expose sigset_t */
+#endif
+
#include <sys/socket.h>
#include <sys/uio.h>
#include <sys/stat.h>
@@ -15,6 +19,14 @@
#include "liburing/io_uring.h"
#include "liburing/barrier.h"
+#ifndef uring_unlikely
+# define uring_unlikely(cond) __builtin_expect(!!(cond), 0)
+#endif
+
+#ifndef uring_likely
+# define uring_likely(cond) __builtin_expect(!!(cond), 1)
+#endif
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -62,7 +74,8 @@
unsigned flags;
int ring_fd;
- unsigned pad[4];
+ unsigned features;
+ unsigned pad[3];
};
/*
@@ -214,6 +227,25 @@
sqe->__pad2[0] = sqe->__pad2[1] = sqe->__pad2[2] = 0;
}
+/**
+ * @pre Either fd_in or fd_out must be a pipe.
+ * @param off_in If fd_in refers to a pipe, off_in must be (int64_t) -1;
+ * If fd_in does not refer to a pipe and off_in is (int64_t) -1, then bytes are read
+ * from fd_in starting from the file offset and it is adjust appropriately;
+ * If fd_in does not refer to a pipe and off_in is not (int64_t) -1, then the
+ * starting offset of fd_in will be off_in.
+ * @param off_out The description of off_in also applied to off_out.
+ * @param splice_flags see man splice(2) for description of flags.
+ *
+ * This splice operation can be used to implement sendfile by splicing to an intermediate pipe
+ * first, then splice to the final destination.
+ * In fact, the implementation of sendfile in kernel uses splice internally.
+ *
+ * NOTE that even if fd_in or fd_out refers to a pipe, the splice operation can still failed with
+ * EINVAL if one of the fd doesn't explicitly support splice operation, e.g. reading from terminal
+ * is unsupported from kernel 5.7 to 5.11.
+ * Check issue #291 for more information.
+ */
static inline void io_uring_prep_splice(struct io_uring_sqe *sqe,
int fd_in, int64_t off_in,
int fd_out, int64_t off_out,
@@ -325,6 +357,16 @@
sqe->timeout_flags = flags;
}
+static inline void io_uring_prep_timeout_update(struct io_uring_sqe *sqe,
+ struct __kernel_timespec *ts,
+ __u64 user_data, unsigned flags)
+{
+ io_uring_prep_rw(IORING_OP_TIMEOUT_REMOVE, sqe, -1,
+ (void *)(unsigned long)user_data, 0,
+ (uintptr_t)ts);
+ sqe->timeout_flags = flags | IORING_TIMEOUT_UPDATE;
+}
+
static inline void io_uring_prep_accept(struct io_uring_sqe *sqe, int fd,
struct sockaddr *addr,
socklen_t *addrlen, int flags)
@@ -469,6 +511,22 @@
io_uring_prep_rw(IORING_OP_SHUTDOWN, sqe, fd, NULL, how, 0);
}
+static inline void io_uring_prep_unlinkat(struct io_uring_sqe *sqe, int dfd,
+ const char *path, int flags)
+{
+ io_uring_prep_rw(IORING_OP_UNLINKAT, sqe, dfd, path, 0, 0);
+ sqe->unlink_flags = flags;
+}
+
+static inline void io_uring_prep_renameat(struct io_uring_sqe *sqe, int olddfd,
+ const char *oldpath, int newdfd,
+ const char *newpath, int flags)
+{
+ io_uring_prep_rw(IORING_OP_RENAMEAT, sqe, olddfd, oldpath, newdfd,
+ (uint64_t) (uintptr_t) newpath);
+ sqe->rename_flags = flags;
+}
+
/*
* Returns number of unconsumed (if SQPOLL) or unsubmitted entries exist in
* the SQ ring
diff --git a/src/include/liburing/barrier.h b/src/include/liburing/barrier.h
index a4a59fb..89ac682 100644
--- a/src/include/liburing/barrier.h
+++ b/src/include/liburing/barrier.h
@@ -56,17 +56,17 @@
#include <stdatomic.h>
#define IO_URING_WRITE_ONCE(var, val) \
- atomic_store_explicit((_Atomic typeof(var) *)&(var), \
+ atomic_store_explicit((_Atomic __typeof__(var) *)&(var), \
(val), memory_order_relaxed)
#define IO_URING_READ_ONCE(var) \
- atomic_load_explicit((_Atomic typeof(var) *)&(var), \
+ atomic_load_explicit((_Atomic __typeof__(var) *)&(var), \
memory_order_relaxed)
#define io_uring_smp_store_release(p, v) \
- atomic_store_explicit((_Atomic typeof(*(p)) *)(p), (v), \
+ atomic_store_explicit((_Atomic __typeof__(*(p)) *)(p), (v), \
memory_order_release)
#define io_uring_smp_load_acquire(p) \
- atomic_load_explicit((_Atomic typeof(*(p)) *)(p), \
+ atomic_load_explicit((_Atomic __typeof__(*(p)) *)(p), \
memory_order_acquire)
#endif
diff --git a/src/include/liburing/io_uring.h b/src/include/liburing/io_uring.h
index d23718f..1d47389 100644
--- a/src/include/liburing/io_uring.h
+++ b/src/include/liburing/io_uring.h
@@ -46,6 +46,8 @@
__u32 statx_flags;
__u32 fadvise_advice;
__u32 splice_flags;
+ __u32 rename_flags;
+ __u32 unlink_flags;
};
__u64 user_data; /* data to be passed back at completion time */
union {
@@ -137,6 +139,9 @@
IORING_OP_REMOVE_BUFFERS,
IORING_OP_TEE,
IORING_OP_SHUTDOWN,
+ IORING_OP_RENAMEAT,
+ IORING_OP_UNLINKAT,
+ IORING_OP_MKDIRAT,
/* this goes last, obviously */
IORING_OP_LAST,
@@ -151,6 +156,7 @@
* sqe->timeout_flags
*/
#define IORING_TIMEOUT_ABS (1U << 0)
+#define IORING_TIMEOUT_UPDATE (1U << 1)
/*
* sqe->splice_flags
@@ -231,6 +237,7 @@
#define IORING_ENTER_GETEVENTS (1U << 0)
#define IORING_ENTER_SQ_WAKEUP (1U << 1)
#define IORING_ENTER_SQ_WAIT (1U << 2)
+#define IORING_ENTER_EXT_ARG (1U << 3)
/*
* Passed in for io_uring_setup(2). Copied back with updated info on success
@@ -259,6 +266,7 @@
#define IORING_FEAT_FAST_POLL (1U << 5)
#define IORING_FEAT_POLL_32BITS (1U << 6)
#define IORING_FEAT_SQPOLL_NONFIXED (1U << 7)
+#define IORING_FEAT_EXT_ARG (1U << 8)
/*
* io_uring_register(2) opcodes and arguments
@@ -288,6 +296,9 @@
__aligned_u64 /* __s32 * */ fds;
};
+/* Skip updating fd indexes set to this value in the fd table */
+#define IORING_REGISTER_FILES_SKIP (-2)
+
#define IO_URING_OP_SUPPORTED (1U << 0)
struct io_uring_probe_op {
@@ -302,7 +313,7 @@
__u8 ops_len; /* length of ops[] array below */
__u16 resv;
__u32 resv2[3];
- struct io_uring_probe_op ops[0];
+ struct io_uring_probe_op ops[];
};
struct io_uring_restriction {
@@ -335,6 +346,12 @@
IORING_RESTRICTION_LAST
};
+struct io_uring_getevents_arg {
+ __u64 sigmask;
+ __u32 sigmask_sz;
+ __u32 pad;
+ __u64 ts;
+};
#ifdef __cplusplus
}
diff --git a/src/queue.c b/src/queue.c
index 24fde2d..ce5d237 100644
--- a/src/queue.c
+++ b/src/queue.c
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: MIT */
+#define _POSIX_C_SOURCE 200112L
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -23,7 +25,9 @@
{
if (!(ring->flags & IORING_SETUP_SQPOLL))
return true;
- if (IO_URING_READ_ONCE(*ring->sq.kflags) & IORING_SQ_NEED_WAKEUP) {
+
+ if (uring_unlikely(IO_URING_READ_ONCE(*ring->sq.kflags) &
+ IORING_SQ_NEED_WAKEUP)) {
*flags |= IORING_ENTER_SQ_WAKEUP;
return true;
}
@@ -37,83 +41,113 @@
}
static int __io_uring_peek_cqe(struct io_uring *ring,
- struct io_uring_cqe **cqe_ptr)
+ struct io_uring_cqe **cqe_ptr,
+ unsigned *nr_available)
{
struct io_uring_cqe *cqe;
- unsigned head;
int err = 0;
+ unsigned available;
+ unsigned mask = *ring->cq.kring_mask;
do {
- io_uring_for_each_cqe(ring, head, cqe)
+ unsigned tail = io_uring_smp_load_acquire(ring->cq.ktail);
+ unsigned head = *ring->cq.khead;
+
+ cqe = NULL;
+ available = tail - head;
+ if (!available)
break;
- if (cqe) {
- if (cqe->user_data == LIBURING_UDATA_TIMEOUT) {
- if (cqe->res < 0)
- err = cqe->res;
- io_uring_cq_advance(ring, 1);
- if (!err)
- continue;
- cqe = NULL;
- }
+
+ cqe = &ring->cq.cqes[head & mask];
+ if (!(ring->features & IORING_FEAT_EXT_ARG) &&
+ cqe->user_data == LIBURING_UDATA_TIMEOUT) {
+ if (cqe->res < 0)
+ err = cqe->res;
+ io_uring_cq_advance(ring, 1);
+ if (!err)
+ continue;
+ cqe = NULL;
}
+
break;
} while (1);
*cqe_ptr = cqe;
+ *nr_available = available;
+ return err;
+}
+
+struct get_data {
+ unsigned submit;
+ unsigned wait_nr;
+ unsigned get_flags;
+ int sz;
+ void *arg;
+};
+
+static int _io_uring_get_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr,
+ struct get_data *data)
+{
+ struct io_uring_cqe *cqe = NULL;
+ int err;
+
+ do {
+ bool need_enter = false;
+ bool cq_overflow_flush = false;
+ unsigned flags = 0;
+ unsigned nr_available;
+ int ret;
+
+ err = __io_uring_peek_cqe(ring, &cqe, &nr_available);
+ if (err)
+ break;
+ if (!cqe && !data->wait_nr && !data->submit) {
+ if (!cq_ring_needs_flush(ring)) {
+ err = -EAGAIN;
+ break;
+ }
+ cq_overflow_flush = true;
+ }
+ if (data->wait_nr > nr_available || cq_overflow_flush) {
+ flags = IORING_ENTER_GETEVENTS | data->get_flags;
+ need_enter = true;
+ }
+ if (data->submit) {
+ sq_ring_needs_enter(ring, &flags);
+ need_enter = true;
+ }
+ if (!need_enter)
+ break;
+
+ ret = __sys_io_uring_enter2(ring->ring_fd, data->submit,
+ data->wait_nr, flags, data->arg,
+ data->sz);
+ if (ret < 0) {
+ err = -errno;
+ break;
+ }
+
+ data->submit -= ret;
+ if (cqe)
+ break;
+ } while (1);
+
+ *cqe_ptr = cqe;
return err;
}
int __io_uring_get_cqe(struct io_uring *ring, struct io_uring_cqe **cqe_ptr,
unsigned submit, unsigned wait_nr, sigset_t *sigmask)
{
- struct io_uring_cqe *cqe = NULL;
- const int to_wait = wait_nr;
- int ret = 0, err;
+ struct get_data data = {
+ .submit = submit,
+ .wait_nr = wait_nr,
+ .get_flags = 0,
+ .sz = _NSIG / 8,
+ .arg = sigmask,
+ };
- do {
- bool cq_overflow_flush = false;
- unsigned flags = 0;
-
- err = __io_uring_peek_cqe(ring, &cqe);
- if (err)
- break;
- if (!cqe && !to_wait && !submit) {
- if (!cq_ring_needs_flush(ring)) {
- err = -EAGAIN;
- break;
- }
- cq_overflow_flush = true;
- }
- if (wait_nr && cqe)
- wait_nr--;
- if (wait_nr || cq_overflow_flush)
- flags = IORING_ENTER_GETEVENTS;
- if (submit)
- sq_ring_needs_enter(ring, &flags);
- if (wait_nr || submit || cq_overflow_flush)
- ret = __sys_io_uring_enter(ring->ring_fd, submit,
- wait_nr, flags, sigmask);
- if (ret < 0) {
- err = -errno;
- } else if (ret == (int)submit) {
- submit = 0;
- /*
- * When SETUP_IOPOLL is set, __sys_io_uring enter()
- * must be called to reap new completions but the call
- * won't be made if both wait_nr and submit are zero
- * so preserve wait_nr.
- */
- if (!(ring->flags & IORING_SETUP_IOPOLL))
- wait_nr = 0;
- } else {
- submit -= ret;
- }
- if (cqe)
- break;
- } while (!err);
-
- *cqe_ptr = cqe;
- return err;
+ return _io_uring_get_cqe(ring, cqe_ptr, &data);
}
/*
@@ -188,19 +222,61 @@
*/
io_uring_smp_store_release(sq->ktail, ktail);
out:
+ /*
+ * This _may_ look problematic, as we're not supposed to be reading
+ * SQ->head without acquire semantics. When we're in SQPOLL mode, the
+ * kernel submitter could be updating this right now. For non-SQPOLL,
+ * task itself does it, and there's no potential race. But even for
+ * SQPOLL, the load is going to be potentially out-of-date the very
+ * instant it's done, regardless or whether or not it's done
+ * atomically. Worst case, we're going to be over-estimating what
+ * we can submit. The point is, we need to be able to deal with this
+ * situation regardless of any perceived atomicity.
+ */
return ktail - *sq->khead;
}
/*
+ * If we have kernel support for IORING_ENTER_EXT_ARG, then we can use that
+ * more efficiently than queueing an internal timeout command.
+ */
+static int io_uring_wait_cqes_new(struct io_uring *ring,
+ struct io_uring_cqe **cqe_ptr,
+ unsigned wait_nr, struct __kernel_timespec *ts,
+ sigset_t *sigmask)
+{
+ struct io_uring_getevents_arg arg = {
+ .sigmask = (unsigned long) sigmask,
+ .sigmask_sz = _NSIG / 8,
+ .ts = (unsigned long) ts
+ };
+ struct get_data data = {
+ .submit = __io_uring_flush_sq(ring),
+ .wait_nr = wait_nr,
+ .get_flags = IORING_ENTER_EXT_ARG,
+ .sz = sizeof(arg),
+ .arg = &arg
+ };
+
+ return _io_uring_get_cqe(ring, cqe_ptr, &data);
+}
+
+/*
* Like io_uring_wait_cqe(), except it accepts a timeout value as well. Note
- * that an sqe is used internally to handle the timeout. Applications using
- * this function must never set sqe->user_data to LIBURING_UDATA_TIMEOUT!
+ * that an sqe is used internally to handle the timeout. For kernel doesn't
+ * support IORING_FEAT_EXT_ARG, applications using this function must never
+ * set sqe->user_data to LIBURING_UDATA_TIMEOUT!
*
- * If 'ts' is specified, the application need not call io_uring_submit() before
+ * For kernels without IORING_FEAT_EXT_ARG (5.10 and older), if 'ts' is
+ * specified, the application need not call io_uring_submit() before
* calling this function, as we will do that on its behalf. From this it also
* follows that this function isn't safe to use for applications that split SQ
* and CQ handling between two threads and expect that to work without
* synchronization, as this function manipulates both the SQ and CQ side.
+ *
+ * For kernels with IORING_FEAT_EXT_ARG, no implicit submission is done and
+ * hence this function is safe to use for applications that split SQ and CQ
+ * handling between two threads.
*/
int io_uring_wait_cqes(struct io_uring *ring, struct io_uring_cqe **cqe_ptr,
unsigned wait_nr, struct __kernel_timespec *ts,
@@ -212,6 +288,10 @@
struct io_uring_sqe *sqe;
int ret;
+ if (ring->features & IORING_FEAT_EXT_ARG)
+ return io_uring_wait_cqes_new(ring, cqe_ptr, wait_nr,
+ ts, sigmask);
+
/*
* If the SQ ring is full, we may need to submit IO first
*/
@@ -294,19 +374,6 @@
return __io_uring_submit_and_wait(ring, wait_nr);
}
-static inline struct io_uring_sqe *
-__io_uring_get_sqe(struct io_uring_sq *sq, unsigned int __head)
-{
- unsigned int __next = (sq)->sqe_tail + 1;
- struct io_uring_sqe *__sqe = NULL;
-
- if (__next - __head <= *(sq)->kring_entries) {
- __sqe = &(sq)->sqes[(sq)->sqe_tail & *(sq)->kring_mask];
- (sq)->sqe_tail = __next;
- }
- return __sqe;
-}
-
/*
* Return an sqe to fill. Application must later call io_uring_submit()
* when it's ready to tell the kernel about it. The caller may call this
@@ -317,8 +384,15 @@
struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring)
{
struct io_uring_sq *sq = &ring->sq;
+ unsigned int head = io_uring_smp_load_acquire(sq->khead);
+ unsigned int next = sq->sqe_tail + 1;
+ struct io_uring_sqe *sqe = NULL;
- return __io_uring_get_sqe(sq, io_uring_smp_load_acquire(sq->khead));
+ if (next - head <= *sq->kring_entries) {
+ sqe = &sq->sqes[sq->sqe_tail & *sq->kring_mask];
+ sq->sqe_tail = next;
+ }
+ return sqe;
}
int __io_uring_sqring_wait(struct io_uring *ring)
diff --git a/src/register.c b/src/register.c
index f3787c0..994aaff 100644
--- a/src/register.c
+++ b/src/register.c
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: MIT */
+#define _POSIX_C_SOURCE 200112L
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
diff --git a/src/setup.c b/src/setup.c
index 8e14085..062eaa0 100644
--- a/src/setup.c
+++ b/src/setup.c
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: MIT */
+#define _DEFAULT_SOURCE
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/mman.h>
@@ -6,6 +8,7 @@
#include <errno.h>
#include <string.h>
#include <stdlib.h>
+#include <signal.h>
#include "liburing/compat.h"
#include "liburing/io_uring.h"
@@ -142,10 +145,13 @@
return -errno;
ret = io_uring_queue_mmap(fd, p, ring);
- if (ret)
+ if (ret) {
close(fd);
+ return ret;
+ }
- return ret;
+ ring->features = p->features;
+ return 0;
}
/*
diff --git a/src/syscall.c b/src/syscall.c
index 598b531..2fd3dd4 100644
--- a/src/syscall.c
+++ b/src/syscall.c
@@ -1,4 +1,6 @@
/* SPDX-License-Identifier: MIT */
+#define _DEFAULT_SOURCE
+
/*
* Will go away once libc support is there
*/
@@ -46,9 +48,16 @@
return syscall(__NR_io_uring_setup, entries, p);
}
+int __sys_io_uring_enter2(int fd, unsigned to_submit, unsigned min_complete,
+ unsigned flags, sigset_t *sig, int sz)
+{
+ return syscall(__NR_io_uring_enter, fd, to_submit, min_complete,
+ flags, sig, sz);
+}
+
int __sys_io_uring_enter(int fd, unsigned to_submit, unsigned min_complete,
unsigned flags, sigset_t *sig)
{
- return syscall(__NR_io_uring_enter, fd, to_submit, min_complete,
- flags, sig, _NSIG / 8);
+ return __sys_io_uring_enter2(fd, to_submit, min_complete, flags, sig,
+ _NSIG / 8);
}
diff --git a/src/syscall.h b/src/syscall.h
index 0b00f70..3b94efc 100644
--- a/src/syscall.h
+++ b/src/syscall.h
@@ -12,6 +12,8 @@
extern int __sys_io_uring_setup(unsigned entries, struct io_uring_params *p);
extern int __sys_io_uring_enter(int fd, unsigned to_submit,
unsigned min_complete, unsigned flags, sigset_t *sig);
+extern int __sys_io_uring_enter2(int fd, unsigned to_submit,
+ unsigned min_complete, unsigned flags, sigset_t *sig, int sz);
extern int __sys_io_uring_register(int fd, unsigned int opcode, const void *arg,
unsigned int nr_args);
diff --git a/test/232c93d07b74-test.c b/test/232c93d07b74-test.c
index a0da3fd..cd194cb 100644
--- a/test/232c93d07b74-test.c
+++ b/test/232c93d07b74-test.c
@@ -60,20 +60,25 @@
{
struct params *p = arg;
int s0;
+ int res;
if (p->tcp) {
int val = 1;
+
s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
+ 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_port = PORT;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(bind(s0, (struct sockaddr *) &addr, sizeof(addr)) != -1);
+ res = bind(s0, (struct sockaddr *) &addr, sizeof(addr));
+ assert(res != -1);
} else {
s0 = socket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0);
assert(s0 != -1);
@@ -83,10 +88,11 @@
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, "\0sock", 6);
- assert(bind(s0, (struct sockaddr *) &addr, sizeof(addr)) != -1);
+ res = bind(s0, (struct sockaddr *) &addr, sizeof(addr));
+ assert(res != -1);
}
-
- assert(listen(s0, 128) != -1);
+ res = listen(s0, 128);
+ assert(res != -1);
set_rcv_ready();
@@ -98,13 +104,15 @@
assert(flags != -1);
flags |= O_NONBLOCK;
- assert(fcntl(s1, F_SETFL, flags) != -1);
+ res = fcntl(s1, F_SETFL, flags);
+ assert(res != -1);
}
struct io_uring m_io_uring;
void *ret = NULL;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ res = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(res >= 0);
int bytes_read = 0;
int expected_byte = 0;
@@ -122,7 +130,8 @@
io_uring_prep_readv(sqe, s1, &iov, 1, 0);
- assert(io_uring_submit(&m_io_uring) != -1);
+ res = io_uring_submit(&m_io_uring);
+ assert(res != -1);
struct io_uring_cqe *cqe;
unsigned head;
@@ -167,6 +176,7 @@
{
struct params *p = arg;
int s0;
+ int ret;
wait_for_rcv_ready();
@@ -174,14 +184,16 @@
int val = 1;
s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
- assert(setsockopt(s0, IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != -1);
+ 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 = PORT;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(connect(s0, (struct sockaddr*) &addr, sizeof(addr)) != -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);
@@ -191,7 +203,8 @@
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, "\0sock", 6);
- assert(connect(s0, (struct sockaddr*) &addr, sizeof(addr)) != -1);
+ ret = connect(s0, (struct sockaddr*) &addr, sizeof(addr));
+ assert(ret != -1);
}
if (p->non_blocking) {
@@ -199,12 +212,14 @@
assert(flags != -1);
flags |= O_NONBLOCK;
- assert(fcntl(s0, F_SETFL, flags) != -1);
+ ret = fcntl(s0, F_SETFL, flags);
+ assert(ret != -1);
}
struct io_uring m_io_uring;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
int bytes_written = 0;
int done = 0;
@@ -226,7 +241,8 @@
io_uring_prep_writev(sqe, s0, &iov, 1, 0);
- assert(io_uring_submit(&m_io_uring) != -1);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret != -1);
struct io_uring_cqe *cqe;
unsigned head;
diff --git a/test/35fa71a030ca-test.c b/test/35fa71a030ca-test.c
index 4ecf211..7f2124b 100644
--- a/test/35fa71a030ca-test.c
+++ b/test/35fa71a030ca-test.c
@@ -24,6 +24,10 @@
#include <linux/futex.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);
diff --git a/test/Makefile b/test/Makefile
index 8d76c1b..7751eff 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -3,12 +3,15 @@
INSTALL=install
+CPPFLAGS ?=
+override CPPFLAGS += -D_GNU_SOURCE -D__SANE_USERSPACE_TYPES__ \
+ -I../src/include/ -include ../config-host.h
CFLAGS ?= -g -O2
XCFLAGS =
-override CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare\
- -D_GNU_SOURCE -D__SANE_USERSPACE_TYPES__ -L../src/ \
- -I../src/include/ -include ../config-host.h
-CXXFLAGS += $(CFLAGS) -std=c++11
+override CFLAGS += -Wall -Wextra -Wno-unused-parameter -Wno-sign-compare \
+ -L../src/
+CXXFLAGS ?=
+override CXXFLAGS += $(CFLAGS) -std=c++11
test_targets += \
232c93d07b74-test \
@@ -75,11 +78,15 @@
poll-cancel-ton \
poll-link \
poll-many \
+ poll-ring \
poll-v-poll \
probe \
read-write \
register-restrictions \
+ rename \
ring-leak \
+ ring-leak2 \
+ self \
send_recv \
send_recvmsg \
shared-wq \
@@ -92,13 +99,19 @@
sq-poll-dup \
sq-poll-kthread \
sq-poll-share \
+ sqpoll-exit-hang \
+ sqpoll-sleep \
sq-space_left \
stdout \
submit-reuse \
teardowns \
+ thread-exit \
timeout \
+ timeout-new \
timeout-overflow \
+ unlink \
wakeup-hang \
+ sendmsg_fs_cve \
# EOL
all_targets += $(test_targets)
@@ -122,10 +135,10 @@
all: $(test_targets)
%: %.c
- $(QUIET_CC)$(CC) $(CFLAGS) -o $@ $< -luring $(XCFLAGS)
+ $(QUIET_CC)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $< -luring $(XCFLAGS)
%: %.cc
- $(QUIET_CXX)$(CXX) $(CXXFLAGS) -o $@ $< -luring $(XCFLAGS)
+ $(QUIET_CXX)$(CXX) $(CPPFLAGS) $(CXXFLAGS) -o $@ $< -luring $(XCFLAGS)
test_srcs := \
232c93d07b74-test.c \
@@ -190,13 +203,16 @@
poll-cancel-ton.c \
poll-cancel.c \
poll-link.c \
- poll-many.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 \
+ self.c \
send_recvmsg.c \
shared-wq.c \
short-read.c \
@@ -210,14 +226,20 @@
sq-poll-dup.c \
sq-poll-kthread.c \
sq-poll-share.c \
+ sqpoll-exit-hang.c \
+ sqpoll-sleep.c \
sq-space_left.c \
statx.c \
stdout.c \
submit-reuse.c \
teardowns.c \
+ thread-exit.c \
+ timeout-new.c \
timeout-overflow.c \
timeout.c \
+ unlink.c \
wakeup-hang.c \
+ sendmsg_fs_cve.c \
# EOL
test_objs := $(patsubst %.c,%.ol,$(patsubst %.cc,%.ol,$(test_srcs)))
@@ -234,6 +256,9 @@
ce593a6c480a-test: XCFLAGS = -lpthread
wakeup-hang: XCFLAGS = -lpthread
pipe-eof: XCFLAGS = -lpthread
+timeout-new: XCFLAGS = -lpthread
+thread-exit: XCFLAGS = -lpthread
+ring-leak2: XCFLAGS = -lpthread
install: $(test_targets) runtests.sh runtests-loop.sh
$(INSTALL) -D -d -m 755 $(datadir)/liburing-test/
diff --git a/test/accept-link.c b/test/accept-link.c
index 7e4df48..605e0ec 100644
--- a/test/accept-link.c
+++ b/test/accept-link.c
@@ -49,6 +49,7 @@
static void *send_thread(void *arg)
{
struct data *data = arg;
+ int ret;
wait_for_var(&recv_thread_ready);
@@ -64,7 +65,8 @@
addr.sin_port = data->port;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(connect(s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
+ ret = connect(s0, (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret != -1);
wait_for_var(&recv_thread_done);
@@ -76,21 +78,24 @@
{
struct data *data = arg;
struct io_uring ring;
- int i;
+ int i, ret;
- assert(io_uring_queue_init(8, &ring, 0) == 0);
+ 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;
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(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;
- addr.sin_addr.s_addr = 0x0100007fU;
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = 0x0100007fU;
i = 0;
do {
@@ -108,7 +113,8 @@
goto out;
}
- assert(listen(s0, 128) != -1);
+ ret = listen(s0, 128);
+ assert(ret != -1);
signal_var(&recv_thread_ready);
@@ -130,7 +136,8 @@
io_uring_prep_link_timeout(sqe, &ts, 0);
sqe->user_data = 2;
- assert(io_uring_submit(&ring) == 2);
+ ret = io_uring_submit(&ring);
+ assert(ret == 2);
for (i = 0; i < 2; i++) {
struct io_uring_cqe *cqe;
@@ -144,8 +151,13 @@
if (cqe->res != data->expected[idx]) {
if (cqe->res > 0 && data->just_positive[idx])
goto ok;
- fprintf(stderr, "cqe %llu got %d, wanted %d\n",
- cqe->user_data, cqe->res,
+ 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;
}
diff --git a/test/accept-reuse.c b/test/accept-reuse.c
index 59a2f79..0062729 100644
--- a/test/accept-reuse.c
+++ b/test/accept-reuse.c
@@ -1,5 +1,4 @@
/* SPDX-License-Identifier: MIT */
-#include <assert.h>
#include <liburing.h>
#include <netdb.h>
#include <string.h>
@@ -48,7 +47,7 @@
return 0;
memset(¶ms, 0, sizeof(params));
- ret = io_uring_queue_init_params(1024, &io_uring, ¶ms);
+ ret = io_uring_queue_init_params(4, &io_uring, ¶ms);
if (ret) {
fprintf(stderr, "io_uring_init_failed: %d\n", ret);
return 1;
diff --git a/test/accept-test.c b/test/accept-test.c
index 52b4395..71d9d80 100644
--- a/test/accept-test.c
+++ b/test/accept-test.c
@@ -37,8 +37,10 @@
addr.sun_family = AF_UNIX;
memcpy(addr.sun_path, "\0sock", 6);
- assert(bind(fd, (struct sockaddr *)&addr, addrlen) != -1);
- assert(listen(fd, 128) != -1);
+ 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) {
@@ -55,11 +57,23 @@
}
ret = io_uring_wait_cqe_timeout(&ring, &cqe, &ts);
- if (ret != -ETIME) {
+ if (!ret) {
+ if (cqe->res == -EBADF || cqe->res == -EINVAL) {
+ fprintf(stdout, "Accept not supported, skipping\n");
+ goto out;
+ } 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 1;
}
+out:
io_uring_queue_exit(&ring);
return 0;
+err:
+ io_uring_queue_exit(&ring);
+ return 1;
}
diff --git a/test/accept.c b/test/accept.c
index faf81d6..89e4c59 100644
--- a/test/accept.c
+++ b/test/accept.c
@@ -62,9 +62,11 @@
sqe = io_uring_get_sqe(ring);
io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
- assert(io_uring_submit(ring) != -1);
+ ret = io_uring_submit(ring);
+ assert(ret != -1);
- assert(!io_uring_wait_cqe(ring, &cqe));
+ ret = io_uring_wait_cqe(ring, &cqe);
+ assert(!ret);
ret = cqe->res;
io_uring_cqe_seen(ring, cqe);
return ret;
@@ -72,13 +74,15 @@
static int start_accept_listen(struct sockaddr_in *addr, int port_off)
{
- int fd;
+ int fd, ret;
fd = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
int32_t val = 1;
- assert(setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(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;
@@ -89,8 +93,10 @@
addr->sin_port = 0x1235 + port_off;
addr->sin_addr.s_addr = 0x0100007fU;
- assert(bind(fd, (struct sockaddr*)addr, sizeof(*addr)) != -1);
- assert(listen(fd, 128) != -1);
+ ret = bind(fd, (struct sockaddr*)addr, sizeof(*addr));
+ assert(ret != -1);
+ ret = listen(fd, 128);
+ assert(ret != -1);
return fd;
}
@@ -103,27 +109,32 @@
uint32_t count = 0;
int done = 0;
int p_fd[2];
+ int ret;
int32_t val, recv_s0 = start_accept_listen(&addr, 0);
p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
- assert(setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
- assert(connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr)) == -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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
p_fd[0] = accept_conn(ring, recv_s0);
if (p_fd[0] == -EINVAL) {
@@ -143,7 +154,8 @@
queue_send(ring, p_fd[1]);
queue_recv(ring, p_fd[0]);
- assert(io_uring_submit_and_wait(ring, 2) != -1);
+ ret = io_uring_submit_and_wait(ring, 2);
+ assert(ret != -1);
while (count < 2) {
io_uring_for_each_cqe(ring, head, cqe) {
@@ -184,19 +196,22 @@
struct io_uring m_io_uring;
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
- int fd;
+ int fd, ret;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
fd = start_accept_listen(NULL, 0);
sqe = io_uring_get_sqe(&m_io_uring);
io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
- assert(io_uring_submit(&m_io_uring) != -1);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret != -1);
signal(SIGALRM, sig_alrm);
alarm(1);
- assert(!io_uring_wait_cqe(&m_io_uring, &cqe));
+ 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);
@@ -213,7 +228,7 @@
struct io_uring_sqe *sqe;
unsigned long cur_lim;
struct rlimit rlim;
- int *fds, i, ret = 0;
+ int *fds, i, ret;
if (getrlimit(RLIMIT_NPROC, &rlim) < 0) {
perror("getrlimit");
@@ -228,7 +243,8 @@
return 1;
}
- assert(io_uring_queue_init(2 * nr, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(2 * nr, &m_io_uring, 0);
+ assert(ret >= 0);
fds = calloc(nr, sizeof(int));
@@ -239,7 +255,8 @@
sqe = io_uring_get_sqe(&m_io_uring);
io_uring_prep_accept(sqe, fds[i], NULL, NULL, 0);
sqe->user_data = 1 + i;
- assert(io_uring_submit(&m_io_uring) == 1);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
}
if (usecs)
@@ -263,7 +280,7 @@
free(fds);
io_uring_queue_exit(&m_io_uring);
- return ret;
+ return 0;
err:
ret = 1;
goto out;
@@ -274,16 +291,18 @@
struct io_uring m_io_uring;
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
- int fd, i;
+ int fd, i, ret;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
fd = start_accept_listen(NULL, 0);
sqe = io_uring_get_sqe(&m_io_uring);
io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
sqe->user_data = 1;
- assert(io_uring_submit(&m_io_uring) == 1);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
if (usecs)
usleep(usecs);
@@ -291,10 +310,12 @@
sqe = io_uring_get_sqe(&m_io_uring);
io_uring_prep_cancel(sqe, (void *) 1, 0);
sqe->user_data = 2;
- assert(io_uring_submit(&m_io_uring) == 1);
+ ret = io_uring_submit(&m_io_uring);
+ assert(ret == 1);
for (i = 0; i < 2; i++) {
- assert(!io_uring_wait_cqe(&m_io_uring, &cqe));
+ ret = io_uring_wait_cqe(&m_io_uring, &cqe);
+ assert(!ret);
/*
* Two cases here:
*
@@ -331,7 +352,8 @@
struct io_uring m_io_uring;
int ret;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
ret = test(&m_io_uring, 0);
io_uring_queue_exit(&m_io_uring);
return ret;
diff --git a/test/close-opath.c b/test/close-opath.c
index 884bda9..f267dad 100644
--- a/test/close-opath.c
+++ b/test/close-opath.c
@@ -58,7 +58,7 @@
ret = cqe->res;
io_uring_cqe_seen(ring, cqe);
- if (ret < 0 && ret != -EOPNOTSUPP) {
+ if (ret < 0 && ret != -EOPNOTSUPP && ret != -EINVAL && ret != -EBADF) {
fprintf(stderr, "io_uring close() failed, errno %d: %s\n",
-ret, strerror(-ret));
return ret;
diff --git a/test/config b/test/config
index cab2703..6c0925a 100644
--- a/test/config
+++ b/test/config
@@ -4,4 +4,7 @@
# TEST_EXCLUDE=""
#
# Define raw test devices (or files) for test cases, if any
-# TEST_FILES="/dev/nvme0n1p2 /data/file"
+# declare -A TEST_MAP=()
+#
+# If no TEST_MAP entry exists for a test, use the ones given in TEST_FILES
+# TEST_FILES="/dev/somedevice /data/somefile"
diff --git a/test/connect.c b/test/connect.c
index 668997f..34e3954 100644
--- a/test/connect.c
+++ b/test/connect.c
@@ -17,6 +17,8 @@
#include "liburing.h"
+static int no_connect;
+
static int create_socket(void)
{
int fd;
@@ -103,12 +105,9 @@
return 0;
}
-static int connect_socket(struct io_uring *ring, int fd, int *code)
+static int configure_connect(int fd, struct sockaddr_in* addr)
{
- struct io_uring_sqe *sqe;
- struct sockaddr_in addr;
- int ret, res, val = 1;
- socklen_t code_len = sizeof(*code);
+ int ret, val = 1;
ret = setsockopt(fd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val));
if (ret == -1) {
@@ -122,10 +121,23 @@
return -1;
}
- memset(&addr, 0, sizeof(addr));
- addr.sin_family = AF_INET;
- addr.sin_port = 0x1234;
- addr.sin_addr.s_addr = 0x0100007fU;
+ memset(addr, 0, sizeof(*addr));
+ addr->sin_family = AF_INET;
+ addr->sin_port = 0x1234;
+ addr->sin_addr.s_addr = 0x0100007fU;
+
+ return 0;
+}
+
+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) {
@@ -175,10 +187,16 @@
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;
@@ -227,6 +245,102 @@
return -1;
}
+static int test_connect_timeout(struct io_uring *ring)
+{
+ int fd = -1, connect_fd = -1, accept_fd = -1;
+ int ret;
+ struct sockaddr_in addr;
+ struct io_uring_sqe *sqe;
+ struct __kernel_timespec ts = {.tv_sec = 0, .tv_nsec = 100000};
+
+ connect_fd = create_socket();
+ if (connect_fd == -1)
+ return -1;
+
+ accept_fd = create_socket();
+ if (accept_fd == -1)
+ goto err;
+
+ if (configure_connect(connect_fd, &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
+ if (ret == -1) {
+ perror("listen()");
+ goto err;
+ }
+
+ // Fill up available place in the accept queue (backlog)
+ fd = create_socket();
+ if (connect(fd, &addr, sizeof(addr)) == -1) {
+ fprintf(stderr, "unable to connect %d\n", errno);
+ goto err;
+ }
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "unable to get sqe\n");
+ goto err;
+ }
+
+ io_uring_prep_connect(sqe, connect_fd, (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;
+ }
+ sqe->user_data = 2;
+
+ io_uring_prep_link_timeout(sqe, &ts, 0);
+ 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);
+ close(accept_fd);
+ close(fd);
+
+ return 0;
+
+err:
+ close(connect_fd);
+ if (accept_fd != -1)
+ close(accept_fd);
+ if (fd != -1)
+ close(fd);
+ return -1;
+}
+
int main(int argc, char *argv[])
{
struct io_uring ring;
@@ -246,6 +360,8 @@
fprintf(stderr, "test_connect_with_no_peer(): failed\n");
return 1;
}
+ if (no_connect)
+ return 0;
ret = test_connect(&ring);
if (ret == -1) {
@@ -253,6 +369,12 @@
return 1;
}
+ ret = test_connect_timeout(&ring);
+ if (ret == -1) {
+ fprintf(stderr, "test_connect_timeout(): failed\n");
+ return 1;
+ }
+
io_uring_queue_exit(&ring);
return 0;
}
diff --git a/test/cq-overflow-peek.c b/test/cq-overflow-peek.c
index 72b6768..353c6f3 100644
--- a/test/cq-overflow-peek.c
+++ b/test/cq-overflow-peek.c
@@ -62,16 +62,22 @@
{
int ret;
struct io_uring ring;
+ struct io_uring_params p = { };
if (argc > 1)
return 0;
- ret = io_uring_queue_init(16, &ring, 0);
+ ret = io_uring_queue_init_params(16, &ring, &p);
if (ret) {
fprintf(stderr, "ring setup failed: %d\n", ret);
return 1;
}
+ if (!(p.features & IORING_FEAT_NODROP)) {
+ fprintf(stdout, "No overflow protection, skipped\n");
+ return 0;
+ }
+
ret = test_cq_overflow(&ring);
if (ret) {
fprintf(stderr, "test_cq_overflow failed\n");
diff --git a/test/cq-peek-batch.c b/test/cq-peek-batch.c
index ee7537c..6c47bec 100644
--- a/test/cq-peek-batch.c
+++ b/test/cq-peek-batch.c
@@ -74,7 +74,8 @@
CHECK_BATCH(&ring, got, cqes, 4, 4);
for (i=0;i<4;i++) {
if (i != cqes[i]->user_data) {
- printf("Got user_data %lld, expected %d\n", cqes[i]->user_data, i);
+ printf("Got user_data %" PRIu64 ", expected %d\n",
+ (uint64_t) cqes[i]->user_data, i);
goto err;
}
}
@@ -86,7 +87,8 @@
CHECK_BATCH(&ring, got, cqes, 4, 4);
for (i=0;i<4;i++) {
if (i + 4 != cqes[i]->user_data) {
- printf("Got user_data %lld, expected %d\n", cqes[i]->user_data, i + 4);
+ printf("Got user_data %" PRIu64 ", expected %d\n",
+ (uint64_t) cqes[i]->user_data, i + 4);
goto err;
}
}
diff --git a/test/defer.c b/test/defer.c
index 05833d4..98abfba 100644
--- a/test/defer.c
+++ b/test/defer.c
@@ -148,6 +148,38 @@
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))
+ 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;
@@ -269,5 +301,11 @@
return ret;
}
+ ret = test_drain_with_linked_timeout(&ring);
+ if (ret) {
+ printf("test_drain_with_linked_timeout failed\n");
+ return ret;
+ }
+
return 0;
}
diff --git a/test/fallocate.c b/test/fallocate.c
index e662a6a..da90be8 100644
--- a/test/fallocate.c
+++ b/test/fallocate.c
@@ -191,8 +191,8 @@
goto err;
}
if (cqe->res) {
- fprintf(stderr, "cqe->res=%d,data=%llu\n", cqe->res,
- cqe->user_data);
+ fprintf(stderr, "cqe->res=%d,data=%" PRIu64 "\n", cqe->res,
+ (uint64_t) cqe->user_data);
goto err;
}
io_uring_cqe_seen(ring, cqe);
diff --git a/test/file-register.c b/test/file-register.c
index 7400b3a..b86ee59 100644
--- a/test/file-register.c
+++ b/test/file-register.c
@@ -346,17 +346,25 @@
return 1;
}
-static int test_basic(struct io_uring *ring)
+static int test_basic(struct io_uring *ring, int fail)
{
int *files;
int ret;
- files = open_files(100, 0, 0);
+ files = open_files(fail ? 10 : 100, 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);
@@ -542,6 +550,47 @@
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;
@@ -596,6 +645,91 @@
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;
+}
+
+
+
int main(int argc, char *argv[])
{
struct io_uring ring;
@@ -610,7 +744,13 @@
return 1;
}
- ret = test_basic(&ring);
+ ret = test_basic(&ring, 0);
+ if (ret) {
+ printf("test_basic failed\n");
+ return ret;
+ }
+
+ ret = test_basic(&ring, 1);
if (ret) {
printf("test_basic failed\n");
return ret;
@@ -679,11 +819,23 @@
return ret;
}
+ ret = test_skip(&ring);
+ if (ret) {
+ printf("test_skip failed\n");
+ return 1;
+ }
+
ret = test_sparse_updates();
if (ret) {
printf("test_sparse_updates failed\n");
return ret;
}
+ ret = test_fixed_removal_ordering();
+ if (ret) {
+ printf("test_fixed_removal_ordering failed\n");
+ return 1;
+ }
+
return 0;
}
diff --git a/test/fixed-link.c b/test/fixed-link.c
index 14def83..be8e9c8 100644
--- a/test/fixed-link.c
+++ b/test/fixed-link.c
@@ -4,7 +4,6 @@
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
-#include <assert.h>
#include <fcntl.h>
#include <sys/types.h>
diff --git a/test/lfs-openat.c b/test/lfs-openat.c
index 921e2a1..b14238a 100644
--- a/test/lfs-openat.c
+++ b/test/lfs-openat.c
@@ -133,10 +133,9 @@
{
struct io_uring ring;
struct io_uring_sqe *sqe;
- struct io_uring_cqe *cqe;
char buffer[128];
struct iovec iov = {.iov_base = buffer, .iov_len = sizeof(buffer), };
- int i, ret, fd, fds[2], to_cancel = 0;
+ int ret, fd, fds[2], to_cancel = 0;
ret = io_uring_queue_init(10, &ring, 0);
if (ret < 0)
@@ -202,18 +201,11 @@
return 1;
}
- /* io_uring->flush() */
+ /*
+ * close(), which triggers ->flush(), and io_uring_queue_exit()
+ * should successfully return and not hang.
+ */
close(fd);
-
- for (i = 0; i < to_cancel; i++) {
- ret = io_uring_wait_cqe(&ring, &cqe);
- if (cqe->res != -ECANCELED) {
- fprintf(stderr, "fail cqe->res=%d\n", cqe->res);
- return 1;
- }
- io_uring_cqe_seen(&ring, cqe);
- }
-
io_uring_queue_exit(&ring);
return 0;
}
diff --git a/test/link-timeout.c b/test/link-timeout.c
index c9aff11..5d8417f 100644
--- a/test/link-timeout.c
+++ b/test/link-timeout.c
@@ -528,6 +528,8 @@
io_uring_cqe_seen(ring, cqe);
}
+ close(fds[0]);
+ close(fds[1]);
return 0;
err:
return 1;
@@ -592,7 +594,7 @@
switch (cqe->user_data) {
case 1:
if (cqe->res != -EINTR && cqe->res != -ECANCELED) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -600,14 +602,14 @@
case 2:
/* FASTPOLL kernels can cancel successfully */
if (cqe->res != -EALREADY && cqe->res != -ETIME) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ 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 %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -687,14 +689,14 @@
/* poll cancel really should return -ECANCEL... */
case 1:
if (cqe->res != -ECANCELED) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ 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 %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -702,7 +704,7 @@
case 3:
case 4:
if (cqe->res != -ECANCELED) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -805,7 +807,7 @@
switch (cqe->user_data) {
case 2:
if (cqe->res != -ETIME) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -815,14 +817,14 @@
case 4:
case 5:
if (cqe->res != -ECANCELED) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ 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 %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
@@ -892,21 +894,21 @@
/* poll cancel really should return -ECANCEL... */
case 1:
if (cqe->res) {
- fprintf(stderr, "Req %llu got %d\n", cqe->user_data,
+ 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 %llu got %d\n", cqe->user_data,
+ 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 %llu got %d\n", cqe->user_data,
+ fprintf(stderr, "Req %" PRIu64 " got %d\n", (uint64_t) cqe->user_data,
cqe->res);
goto err;
}
diff --git a/test/pipe-reuse.c b/test/pipe-reuse.c
index 1d5200a..9dc60ee 100644
--- a/test/pipe-reuse.c
+++ b/test/pipe-reuse.c
@@ -17,6 +17,7 @@
{
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;
@@ -37,7 +38,15 @@
ptr += bsize;
}
- io_uring_queue_init(8, &ring, 0);
+ 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);
diff --git a/test/poll-cancel-ton.c b/test/poll-cancel-ton.c
index 1a75463..e9d612e 100644
--- a/test/poll-cancel-ton.c
+++ b/test/poll-cancel-ton.c
@@ -102,6 +102,7 @@
int main(int argc, char *argv[])
{
struct io_uring ring;
+ struct io_uring_params p = { };
int pipe1[2];
int ret;
@@ -113,10 +114,18 @@
return 1;
}
- ret = io_uring_queue_init(1024, &ring, 0);
+ p.flags = IORING_SETUP_CQSIZE;
+ p.cq_entries = 16384;
+ ret = io_uring_queue_init_params(1024, &ring, &p);
if (ret) {
- fprintf(stderr, "ring setup failed: %d\n", ret);
- return 1;
+ 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);
diff --git a/test/poll-link.c b/test/poll-link.c
index d0786d4..4b4f9aa 100644
--- a/test/poll-link.c
+++ b/test/poll-link.c
@@ -73,16 +73,19 @@
struct data *data = arg;
struct io_uring_sqe *sqe;
struct io_uring ring;
- int i;
+ int i, ret;
- assert(io_uring_queue_init(8, &ring, 0) == 0);
+ 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;
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(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;
@@ -105,7 +108,8 @@
goto out;
}
- assert(listen(s0, 128) != -1);
+ ret = listen(s0, 128);
+ assert(ret != -1);
signal_var(&recv_thread_ready);
@@ -125,7 +129,8 @@
io_uring_prep_link_timeout(sqe, &ts, 0);
sqe->user_data = 2;
- assert(io_uring_submit(&ring) == 2);
+ ret = io_uring_submit(&ring);
+ assert(ret == 2);
for (i = 0; i < 2; i++) {
struct io_uring_cqe *cqe;
@@ -137,13 +142,13 @@
}
idx = cqe->user_data - 1;
if (data->is_mask[idx] && !(data->expected[idx] & cqe->res)) {
- fprintf(stderr, "cqe %llu got %x, wanted mask %x\n",
- cqe->user_data, 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 %llu got %d, wanted %d\n",
- cqe->user_data, cqe->res,
+ fprintf(stderr, "cqe %" PRIu64 " got %d, wanted %d\n",
+ (uint64_t) cqe->user_data, cqe->res,
data->expected[idx]);
goto err;
}
diff --git a/test/poll-many.c b/test/poll-many.c
index 723a353..3f8d08d 100644
--- a/test/poll-many.c
+++ b/test/poll-many.c
@@ -140,6 +140,7 @@
int main(int argc, char *argv[])
{
struct io_uring ring;
+ struct io_uring_params params = { };
struct rlimit rlim;
int i, ret;
@@ -169,9 +170,18 @@
}
}
- if (io_uring_queue_init(RING_SIZE, &ring, 0)) {
- fprintf(stderr, "failed ring init\n");
- goto err_noring;
+ params.flags = IORING_SETUP_CQSIZE;
+ params.cq_entries = 4096;
+ ret = io_uring_queue_init_params(RING_SIZE, &ring, ¶ms);
+ 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))
diff --git a/test/poll-ring.c b/test/poll-ring.c
new file mode 100644
index 0000000..1f69e20
--- /dev/null
+++ b/test/poll-ring.c
@@ -0,0 +1,48 @@
+/* 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 <sys/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/test/read-write.c b/test/read-write.c
index 3bea26f..7f33ad4 100644
--- a/test/read-write.c
+++ b/test/read-write.c
@@ -37,6 +37,23 @@
return 0;
}
+static int create_nonaligned_buffers(void)
+{
+ int i;
+
+ vecs = malloc(BUFFERS * sizeof(struct iovec));
+ for (i = 0; i < BUFFERS; i++) {
+ char *p = 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 create_file(const char *file)
{
ssize_t ret;
@@ -56,8 +73,8 @@
return ret != FILE_SIZE;
}
-static int __test_io(const char *file, struct io_uring *ring, int write, int buffered,
- int sqthread, int fixed, int mixed_fixed, int nonvec,
+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;
@@ -67,10 +84,9 @@
off_t offset;
#ifdef VERBOSE
- fprintf(stdout, "%s: start %d/%d/%d/%d/%d/%d: ", __FUNCTION__, write,
+ fprintf(stdout, "%s: start %d/%d/%d/%d/%d: ", __FUNCTION__, write,
buffered, sqthread,
- fixed, mixed_fixed,
- nonvec);
+ fixed, nonvec);
#endif
if (sqthread && geteuid()) {
#ifdef VERBOSE
@@ -156,6 +172,7 @@
}
}
+ sqe->user_data = i;
if (sqthread)
sqe->flags |= IOSQE_FIXED_FILE;
if (buf_select) {
@@ -163,7 +180,6 @@
sqe->addr = 0;
sqe->flags |= IOSQE_BUFFER_SELECT;
sqe->buf_group = buf_select;
- sqe->user_data = i;
}
if (seq)
offset += BS;
@@ -188,6 +204,14 @@
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;
@@ -239,7 +263,7 @@
return 1;
}
static int test_io(const char *file, int write, int buffered, int sqthread,
- int fixed, int mixed_fixed, int nonvec)
+ int fixed, int nonvec, int exp_len)
{
struct io_uring ring;
int ret, ring_flags;
@@ -263,8 +287,8 @@
return 1;
}
- ret = __test_io(file, &ring, write, buffered, sqthread, fixed,
- mixed_fixed, nonvec, 0, 0, BS);
+ ret = __test_io(file, &ring, write, buffered, sqthread, fixed, nonvec,
+ 0, 0, exp_len);
io_uring_queue_exit(&ring);
return ret;
@@ -442,16 +466,48 @@
io_uring_cqe_seen(&ring, cqe);
}
- ret = __test_io(filename, &ring, 0, 0, 0, 0, 0, nonvec, 1, 1, exp_len);
+ ret = __test_io(filename, &ring, 0, 0, 0, 0, nonvec, 1, 1, exp_len);
io_uring_queue_exit(&ring);
return ret;
}
-static int test_buf_select(const char *filename, int nonvec)
+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(const char *filename, int nonvec)
+{
struct io_uring_probe *p;
struct io_uring ring;
int ret, i;
@@ -476,7 +532,7 @@
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, 0, 1, BS);
+ ret = __test_io(filename, &ring, 1, 0, 0, 0, 0, 0, 1, BS);
if (ret) {
fprintf(stderr, "failed writing data\n");
return 1;
@@ -485,29 +541,67 @@
for (i = 0; i < BUFFERS; i++)
memset(vecs[i].iov_base, 0x55, vecs[i].iov_len);
- for (i = 0; i < BUFFERS; i++) {
+ 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_provide_buffers(sqe, vecs[i].iov_base,
- vecs[i].iov_len, 1, 1, i);
+ 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 != BUFFERS) {
+ if (ret != nr) {
fprintf(stderr, "submit: %d\n", ret);
return -1;
}
- for (i = 0; i < BUFFERS; i++) {
+ for (; nr > 0; nr--) {
ret = io_uring_wait_cqe(&ring, &cqe);
- if (cqe->res < 0) {
+ 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);
}
- ret = __test_io(filename, &ring, 0, 0, 0, 0, 0, nonvec, 1, 1, BS);
-
io_uring_queue_exit(&ring);
return ret;
}
@@ -594,14 +688,15 @@
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
struct io_uring ring;
- struct rlimit rlim;
+ struct rlimit rlim, old_rlim;
int i, fd, ret;
loff_t off;
- if (getrlimit(RLIMIT_FSIZE, &rlim) < 0) {
+ if (getrlimit(RLIMIT_FSIZE, &old_rlim) < 0) {
perror("getrlimit");
return 1;
}
+ rlim = old_rlim;
rlim.rlim_cur = 64 * 1024;
rlim.rlim_max = 64 * 1024;
if (setrlimit(RLIMIT_FSIZE, &rlim) < 0) {
@@ -661,6 +756,11 @@
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)
@@ -690,24 +790,20 @@
}
/* if we don't have nonvec read, skip testing that */
- if (has_nonvec_read())
- nr = 64;
- else
- nr = 32;
+ nr = has_nonvec_read() ? 32 : 16;
for (i = 0; i < nr; i++) {
- int v1, v2, v3, v4, v5, v6;
+ 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;
- v1 = (i & 1) != 0;
- v2 = (i & 2) != 0;
- v3 = (i & 4) != 0;
- v4 = (i & 8) != 0;
- v5 = (i & 16) != 0;
- v6 = (i & 32) != 0;
- ret = test_io(fname, v1, v2, v3, v4, v5, v6);
+ ret = test_io(fname, write, buffered, sqthread, fixed, nonvec,
+ BS);
if (ret) {
- fprintf(stderr, "test_io failed %d/%d/%d/%d/%d/%d\n",
- v1, v2, v3, v4, v5, v6);
+ fprintf(stderr, "test_io failed %d/%d/%d/%d/%d\n",
+ write, buffered, sqthread, fixed, nonvec);
goto err;
}
}
@@ -760,6 +856,57 @@
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;
diff --git a/test/register-restrictions.c b/test/register-restrictions.c
index 4f64c41..04a0ed9 100644
--- a/test/register-restrictions.c
+++ b/test/register-restrictions.c
@@ -406,8 +406,8 @@
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 %lld \n",
- cqe->res, cqe->user_data);
+ fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
+ cqe->res, (uint64_t) cqe->user_data);
return TEST_FAILED;
}
@@ -417,8 +417,8 @@
case 6: /* writev - flags = IOSQE_ASYNC */
case 7: /* writev - flags = 0 */
if (cqe->res != -EACCES) {
- fprintf(stderr, "write res: %d user_data %lld \n",
- cqe->res, cqe->user_data);
+ fprintf(stderr, "write res: %d user_data %" PRIu64 "\n",
+ cqe->res, (uint64_t) cqe->user_data);
return TEST_FAILED;
}
break;
diff --git a/test/rename.c b/test/rename.c
new file mode 100644
index 0000000..af09d65
--- /dev/null
+++ b/test/rename.c
@@ -0,0 +1,134 @@
+/* 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"
+
+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));
+ sqe->opcode = IORING_OP_RENAMEAT;
+ sqe->fd = AT_FDCWD;
+ sqe->addr2 = (unsigned long) new;
+ sqe->addr = (unsigned long) old;
+ sqe->len = AT_FDCWD;
+
+ 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/test/ring-leak2.c b/test/ring-leak2.c
new file mode 100644
index 0000000..d9bfe0f
--- /dev/null
+++ b/test/ring-leak2.c
@@ -0,0 +1,248 @@
+/* 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 <sys/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 = ˚
+
+ 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 {
+ 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/test/runtests.sh b/test/runtests.sh
index fa240f2..e8f4ae5 100755
--- a/test/runtests.sh
+++ b/test/runtests.sh
@@ -5,10 +5,11 @@
TIMEOUT=60
DMESG_FILTER="cat"
TEST_DIR=$(dirname $0)
-TEST_FILES=""
FAILED=""
SKIPPED=""
MAYBE_FAILED=""
+TEST_FILES=""
+declare -A TEST_MAP
# Only use /dev/kmsg if running as root
DO_KMSG="1"
@@ -23,6 +24,12 @@
exit 1
fi
done
+ for dev in ${TEST_MAP[@]}; do
+ if [ ! -e "$dev" ]; then
+ echo "Test file in map $dev not valid"
+ exit 1
+ fi
+ done
fi
_check_dmesg()
@@ -84,7 +91,7 @@
fi
# Run the test
- timeout --preserve-status -s INT -k $TIMEOUT $TIMEOUT ./$test_name $dev
+ timeout -s INT -k $TIMEOUT $TIMEOUT ./$test_name $dev
local status=$?
# Check test status
@@ -109,11 +116,15 @@
# Run all specified tests
for tst in $TESTS; do
- run_test $tst
- if [ ! -z "$TEST_FILES" ]; then
- for dev in $TEST_FILES; do
- run_test $tst $dev
- done
+ if [ ! -n "${TEST_MAP[$tst]}" ]; then
+ run_test $tst
+ if [ ! -z "$TEST_FILES" ]; then
+ for dev in $TEST_FILES; do
+ run_test $tst $dev
+ done
+ fi
+ else
+ run_test $tst ${TEST_MAP[$tst]}
fi
done
diff --git a/test/self.c b/test/self.c
new file mode 100644
index 0000000..422c9e3
--- /dev/null
+++ b/test/self.c
@@ -0,0 +1,91 @@
+/* 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/test/send_recvmsg.c b/test/send_recvmsg.c
index 50c8e94..6b513bc 100644
--- a/test/send_recvmsg.c
+++ b/test/send_recvmsg.c
@@ -11,6 +11,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <pthread.h>
+#include <assert.h>
#include "liburing.h"
@@ -24,7 +25,10 @@
#define BUF_BGID 10
#define BUF_BID 89
-static int recv_prep(struct io_uring *ring, struct iovec *iov, int bgid)
+#define MAX_IOV_COUNT 10
+
+static int recv_prep(struct io_uring *ring, struct iovec iov[], int iov_count,
+ int bgid)
{
struct sockaddr_in saddr;
struct msghdr msg;
@@ -53,11 +57,6 @@
goto err;
}
- memset(&msg, 0, sizeof(msg));
- msg.msg_namelen = sizeof(struct sockaddr_in);
- msg.msg_iov = iov;
- msg.msg_iovlen = 1;
-
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "io_uring_get_sqe failed\n");
@@ -66,11 +65,15 @@
io_uring_prep_recvmsg(sqe, sockfd, &msg, 0);
if (bgid) {
- sqe->user_data = (unsigned long) iov->iov_base;
iov->iov_base = NULL;
sqe->flags |= IOSQE_BUFFER_SELECT;
sqe->buf_group = bgid;
+ iov_count = 1;
}
+ 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) {
@@ -89,9 +92,10 @@
pthread_mutex_t *mutex;
int buf_select;
int no_buf_add;
+ int iov_count;
};
-static int do_recvmsg(struct io_uring *ring, struct iovec *iov,
+static int do_recvmsg(struct io_uring *ring, char buf[MAX_MSG + 1],
struct recv_data *rd)
{
struct io_uring_cqe *cqe;
@@ -112,8 +116,6 @@
int bid = cqe->flags >> 16;
if (bid != BUF_BID)
fprintf(stderr, "Buffer ID mismatch %d\n", bid);
- /* just for passing the pointer to str */
- iov->iov_base = (void *) (uintptr_t) cqe->user_data;
}
if (rd->no_buf_add && rd->buf_select) {
@@ -127,7 +129,7 @@
goto err;
}
- if (strcmp(str, iov->iov_base)) {
+ if (strncmp(str, buf, MAX_MSG + 1)) {
fprintf(stderr, "string mismatch\n");
goto err;
}
@@ -137,20 +139,34 @@
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;
char buf[MAX_MSG + 1];
- struct iovec iov = {
- .iov_base = buf,
- .iov_len = sizeof(buf) - 1,
- };
+ struct iovec iov[MAX_IOV_COUNT];
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
struct io_uring ring;
int ret;
+ 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);
@@ -184,14 +200,14 @@
}
}
- ret = recv_prep(&ring, &iov, rd->buf_select ? BUF_BGID : 0);
+ ret = recv_prep(&ring, iov, rd->iov_count, rd->buf_select ? BUF_BGID : 0);
if (ret) {
fprintf(stderr, "recv_prep failed: %d\n", ret);
goto err;
}
pthread_mutex_unlock(mutex);
- ret = do_recvmsg(&ring, &iov, rd);
+ ret = do_recvmsg(&ring, buf, rd);
io_uring_queue_exit(&ring);
@@ -261,7 +277,7 @@
return 1;
}
-static int test(int buf_select, int no_buf_add)
+static int test(int buf_select, int no_buf_add, int iov_count)
{
struct recv_data rd;
pthread_mutexattr_t attr;
@@ -278,6 +294,7 @@
rd.mutex = &mutex;
rd.buf_select = buf_select;
rd.no_buf_add = no_buf_add;
+ rd.iov_count = iov_count;
ret = pthread_create(&recv_thread, NULL, recv_fn, &rd);
if (ret) {
fprintf(stderr, "Thread create failed\n");
@@ -299,19 +316,25 @@
if (argc > 1)
return 0;
- ret = test(0, 0);
+ ret = test(0, 0, 1);
if (ret) {
fprintf(stderr, "send_recvmsg 0 failed\n");
return 1;
}
- ret = test(1, 0);
+ ret = test(0, 0, 10);
+ if (ret) {
+ fprintf(stderr, "send_recvmsg multi iov failed\n");
+ return 1;
+ }
+
+ ret = test(1, 0, 1);
if (ret) {
fprintf(stderr, "send_recvmsg 1 0 failed\n");
return 1;
}
- ret = test(1, 1);
+ ret = test(1, 1, 1);
if (ret) {
fprintf(stderr, "send_recvmsg 1 1 failed\n");
return 1;
diff --git a/test/sendmsg_fs_cve.c b/test/sendmsg_fs_cve.c
new file mode 100644
index 0000000..85f271b
--- /dev/null
+++ b/test/sendmsg_fs_cve.c
@@ -0,0 +1,193 @@
+/*
+ * 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 <syscall.h>
+#include <stdio.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);
+
+ if (chroot(tmpdir)) {
+ 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/test/shutdown.c b/test/shutdown.c
index eb66ded..5f4e9cc 100644
--- a/test/shutdown.c
+++ b/test/shutdown.c
@@ -24,7 +24,7 @@
int main(int argc, char *argv[])
{
- int p_fd[2];
+ int p_fd[2], ret;
int32_t recv_s0;
int32_t val = 1;
struct sockaddr_in addr;
@@ -34,34 +34,42 @@
recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
+ 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_port = 0x1235;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
- assert(listen(recv_s0, 128) != -1);
+ ret = bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret != -1);
+ ret = listen(recv_s0, 128);
+ assert(ret != -1);
p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
- assert(setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
- assert(connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr)) == -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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
p_fd[0] = accept(recv_s0, NULL, NULL);
assert(p_fd[0] != -1);
@@ -72,7 +80,8 @@
int32_t code;
socklen_t code_len = sizeof(code);
- assert(getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len) != -1);
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
if (!code)
break;
@@ -80,21 +89,23 @@
struct io_uring m_io_uring;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
- int ret;
+ int res;
sqe = io_uring_get_sqe(&m_io_uring);
io_uring_prep_shutdown(sqe, p_fd[1], SHUT_WR);
sqe->user_data = 1;
- assert(io_uring_submit_and_wait(&m_io_uring, 1) != -1);
+ res = io_uring_submit_and_wait(&m_io_uring, 1);
+ assert(res != -1);
- ret = io_uring_wait_cqe(&m_io_uring, &cqe);
- if (ret < 0) {
+ res = io_uring_wait_cqe(&m_io_uring, &cqe);
+ if (res < 0) {
fprintf(stderr, "wait: %s\n", strerror(-ret));
goto err;
}
@@ -116,7 +127,7 @@
struct io_uring_sqe *sqe;
struct iovec iov[1];
char send_buff[128];
- int ret;
+ int res;
iov[0].iov_base = send_buff;
iov[0].iov_len = sizeof(send_buff);
@@ -125,10 +136,11 @@
assert(sqe != NULL);
io_uring_prep_writev(sqe, p_fd[1], iov, 1, 0);
- assert(io_uring_submit_and_wait(&m_io_uring, 1) != -1);
+ res = io_uring_submit_and_wait(&m_io_uring, 1);
+ assert(res != -1);
- ret = io_uring_wait_cqe(&m_io_uring, &cqe);
- if (ret < 0) {
+ res = io_uring_wait_cqe(&m_io_uring, &cqe);
+ if (res < 0) {
fprintf(stderr, "wait: %s\n", strerror(-ret));
goto err;
}
diff --git a/test/socket-rw-eagain.c b/test/socket-rw-eagain.c
index a6782cc..e08f984 100644
--- a/test/socket-rw-eagain.c
+++ b/test/socket-rw-eagain.c
@@ -20,7 +20,7 @@
int main(int argc, char *argv[])
{
- int p_fd[2];
+ int p_fd[2], ret;
int32_t recv_s0;
int32_t val = 1;
struct sockaddr_in addr;
@@ -30,28 +30,35 @@
recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
+ 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_port = 0x1235;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
- assert(listen(recv_s0, 128) != -1);
+ ret = bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret != -1);
+ ret = listen(recv_s0, 128);
+ assert(ret != -1);
p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
- assert(setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
- assert(connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr)) == -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);
@@ -60,13 +67,15 @@
assert(flags != -1);
flags |= O_NONBLOCK;
- assert(fcntl(p_fd[0], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[0], F_SETFL, flags);
+ assert(ret != -1);
while (1) {
int32_t code;
socklen_t code_len = sizeof(code);
- assert(getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len) != -1);
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
if (!code)
break;
@@ -74,7 +83,8 @@
struct io_uring m_io_uring;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
char recv_buff[128];
char send_buff[128];
@@ -105,7 +115,8 @@
sqe->user_data = 2;
}
- assert(io_uring_submit_and_wait(&m_io_uring, 2) != -1);
+ ret = io_uring_submit_and_wait(&m_io_uring, 2);
+ assert(ret != -1);
struct io_uring_cqe* cqe;
uint32_t head;
diff --git a/test/socket-rw.c b/test/socket-rw.c
index 45daf57..77fae59 100644
--- a/test/socket-rw.c
+++ b/test/socket-rw.c
@@ -22,7 +22,7 @@
int main(int argc, char *argv[])
{
- int p_fd[2];
+ int p_fd[2], ret;
int32_t recv_s0;
int32_t val = 1;
struct sockaddr_in addr;
@@ -32,35 +32,43 @@
recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
- assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
+ 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_port = 0x1235;
addr.sin_addr.s_addr = 0x0100007fU;
- assert(bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
- assert(listen(recv_s0, 128) != -1);
+ ret = bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr));
+ assert(ret != -1);
+ ret = listen(recv_s0, 128);
+ assert(ret != -1);
p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
- assert(setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
- assert(connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr)) == -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;
- assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
+ ret = fcntl(p_fd[1], F_SETFL, flags);
+ assert(ret != -1);
p_fd[0] = accept(recv_s0, NULL, NULL);
assert(p_fd[0] != -1);
@@ -69,7 +77,8 @@
int32_t code;
socklen_t code_len = sizeof(code);
- assert(getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len) != -1);
+ ret = getsockopt(p_fd[1], SOL_SOCKET, SO_ERROR, &code, &code_len);
+ assert(ret != -1);
if (!code)
break;
@@ -77,7 +86,8 @@
struct io_uring m_io_uring;
- assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
+ ret = io_uring_queue_init(32, &m_io_uring, 0);
+ assert(ret >= 0);
char recv_buff[128];
char send_buff[128];
@@ -106,7 +116,8 @@
io_uring_prep_writev(sqe, p_fd[1], iov, 1, 0);
}
- assert(io_uring_submit_and_wait(&m_io_uring, 2) != -1);
+ ret = io_uring_submit_and_wait(&m_io_uring, 2);
+ assert(ret != -1);
struct io_uring_cqe* cqe;
uint32_t head;
diff --git a/test/splice.c b/test/splice.c
index e67bb10..6442caf 100644
--- a/test/splice.c
+++ b/test/splice.c
@@ -446,6 +446,7 @@
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];
@@ -453,11 +454,15 @@
if (argc > 1)
return 0;
- ret = io_uring_queue_init(8, &ring, 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) {
diff --git a/test/sq-poll-share.c b/test/sq-poll-share.c
index 0f25389..02b008e 100644
--- a/test/sq-poll-share.c
+++ b/test/sq-poll-share.c
@@ -60,7 +60,14 @@
struct io_uring_cqe *cqe;
while (nr_ios) {
- io_uring_wait_cqe(ring, &cqe);
+ 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;
diff --git a/test/sqpoll-exit-hang.c b/test/sqpoll-exit-hang.c
new file mode 100644
index 0000000..43385ce
--- /dev/null
+++ b/test/sqpoll-exit-hang.c
@@ -0,0 +1,77 @@
+/*
+ * 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 <sys/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/test/sqpoll-sleep.c b/test/sqpoll-sleep.c
new file mode 100644
index 0000000..7ffd0e5
--- /dev/null
+++ b/test/sqpoll-sleep.c
@@ -0,0 +1,68 @@
+/*
+ * 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/test/submit-reuse.c b/test/submit-reuse.c
index 5491253..f14cc22 100644
--- a/test/submit-reuse.c
+++ b/test/submit-reuse.c
@@ -62,6 +62,8 @@
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;
@@ -149,6 +151,7 @@
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;
@@ -161,12 +164,18 @@
do_unlink = 0;
}
- ret = io_uring_queue_init(32, &ring, 0);
+ 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");
+ no_stable = 1;
+ return 0;
+ }
+
if (do_unlink && create_file(fname1)) {
fprintf(stderr, "file creation failed\n");
goto err;
@@ -177,7 +186,15 @@
}
fd1 = open(fname1, O_RDONLY);
+ if (fd1 < 0) {
+ perror("open fname1");
+ goto err;
+ }
fd2 = open(".reuse.2", O_RDONLY);
+ if (fd2 < 0) {
+ perror("open .reuse.2");
+ goto err;
+ }
data.fd1 = fd1;
data.fd2 = fd2;
@@ -240,6 +257,8 @@
fprintf(stderr, "test_reuse %d %d failed\n", split, async);
return ret;
}
+ if (no_stable)
+ break;
}
return 0;
diff --git a/test/thread-exit.c b/test/thread-exit.c
new file mode 100644
index 0000000..722edbc
--- /dev/null
+++ b/test/thread-exit.c
@@ -0,0 +1,156 @@
+/* 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 <sys/poll.h>
+#include <pthread.h>
+
+#include "liburing.h"
+
+#define NR_IOS 8
+#define WSIZE 512
+
+static int create_file(const char *file, size_t size)
+{
+ ssize_t ret;
+ char *buf;
+ int fd;
+
+ buf = malloc(size);
+ memset(buf, 0xaa, size);
+
+ fd = open(file, O_WRONLY | O_CREAT, 0644);
+ if (fd < 0) {
+ perror("open file");
+ return 1;
+ }
+ ret = write(fd, buf, size);
+ close(fd);
+ free(buf);
+ return ret != size;
+}
+
+struct d {
+ int fd;
+ struct io_uring *ring;
+ unsigned long off;
+ int pipe_fd;
+ int err;
+};
+
+static void *do_io(void *data)
+{
+ struct d *d = data;
+ struct io_uring_sqe *sqe;
+ char *buffer;
+ int ret;
+
+ buffer = malloc(WSIZE);
+ 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++;
+
+ free(buffer);
+ 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;
+ }
+
+ if (do_unlink && create_file(fname, 4096)) {
+ fprintf(stderr, "file create failed\n");
+ return 1;
+ }
+
+ fd = open(fname, O_WRONLY);
+ if (fd < 0) {
+ perror("open");
+ return 1;
+ }
+
+ d.fd = fd;
+ d.ring = ˚
+ d.off = 0;
+ d.pipe_fd = fds[0];
+ d.err = 0;
+ for (i = 0; i < NR_IOS; 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);
+ }
+
+ if (do_unlink)
+ unlink(fname);
+ return d.err;
+err:
+ if (do_unlink)
+ unlink(fname);
+ return 1;
+}
diff --git a/test/timeout-new.c b/test/timeout-new.c
new file mode 100644
index 0000000..45b9a14
--- /dev/null
+++ b/test/timeout-new.c
@@ -0,0 +1,246 @@
+/* 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;
+ struct __kernel_timespec ts;
+
+ sqe = io_uring_get_sqe(ring);
+ if (!sqe) {
+ fprintf(stderr, "%s: get sqe failed\n", __FUNCTION__);
+ return 1;
+ }
+
+ 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;
+ }
+
+ msec_to_ts(&ts, TIMEOUT_MSEC);
+ ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts);
+ 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.\n");
+ return 1;
+ }
+
+ 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/test/timeout.c b/test/timeout.c
index 7e9f11d..a28d599 100644
--- a/test/timeout.c
+++ b/test/timeout.c
@@ -112,7 +112,7 @@
/*
* Test numbered trigger of timeout
*/
-static int test_single_timeout_nr(struct io_uring *ring)
+static int test_single_timeout_nr(struct io_uring *ring, int nr)
{
struct io_uring_cqe *cqe;
struct io_uring_sqe *sqe;
@@ -126,7 +126,7 @@
}
msec_to_ts(&ts, TIMEOUT_MSEC);
- io_uring_prep_timeout(sqe, &ts, 2, 0);
+ io_uring_prep_timeout(sqe, &ts, nr, 0);
sqe = io_uring_get_sqe(ring);
io_uring_prep_nop(sqe);
@@ -149,33 +149,26 @@
goto err;
}
+ ret = cqe->res;
+
/*
* NOP commands have user_data as 1. Check that we get the
- * two NOPs first, then the successfully removed timout as
- * the last one.
+ * at least 'nr' NOPs first, then the successfully removed timout.
*/
- switch (i) {
- case 0:
- case 1:
- if (io_uring_cqe_get_data(cqe) != (void *) 1) {
- fprintf(stderr, "%s: nop not seen as 1 or 2\n", __FUNCTION__);
+ if (io_uring_cqe_get_data(cqe) == NULL) {
+ if (i < nr) {
+ fprintf(stderr, "%s: timeout received too early\n", __FUNCTION__);
goto err;
}
- break;
- case 2:
- if (io_uring_cqe_get_data(cqe) != NULL) {
- fprintf(stderr, "%s: timeout not last\n", __FUNCTION__);
+ if (ret) {
+ fprintf(stderr, "%s: timeout triggered by passage of"
+ " time, not by events completed\n", __FUNCTION__);
goto err;
}
- break;
}
- ret = cqe->res;
io_uring_cqe_seen(ring, cqe);
- if (ret < 0) {
- fprintf(stderr, "Timeout: %s\n", strerror(-ret));
- goto err;
- } else if (ret) {
+ if (ret) {
fprintf(stderr, "res: %d\n", ret);
goto err;
}
@@ -965,10 +958,213 @@
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;
+}
int main(int argc, char *argv[])
{
- struct io_uring ring;
+ struct io_uring ring, sqpoll_ring;
+ bool has_timeout_update, sqpoll;
int ret;
if (argc > 1)
@@ -980,6 +1176,9 @@
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");
@@ -1018,9 +1217,14 @@
return ret;
}
- ret = test_single_timeout_nr(&ring);
+ ret = test_single_timeout_nr(&ring, 1);
if (ret) {
- fprintf(stderr, "test_single_timeout_nr failed\n");
+ 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;
}
@@ -1054,6 +1258,76 @@
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
*/
@@ -1063,5 +1337,7 @@
return ret;
}
+ if (sqpoll)
+ io_uring_queue_exit(&sqpoll_ring);
return 0;
}
diff --git a/test/unlink.c b/test/unlink.c
new file mode 100644
index 0000000..f8c7639
--- /dev/null
+++ b/test/unlink.c
@@ -0,0 +1,111 @@
+/* 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"
+
+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_unlinkat(sqe, AT_FDCWD, 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;
+}