aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2024-05-14 06:36:45 -0600
committerJens Axboe <axboe@kernel.dk>2024-05-14 06:36:45 -0600
commit184e6ec53ef2a42b6deb1d6aabfa1c0f898fe4d8 (patch)
tree934d4c29f9bec7803e195b1d46b15d7ff2ce2961
parent0a0ceba677114a0ec95453aa35ae3752d93a1cde (diff)
downloadliburing-master.tar.gz
test/accept-non-empty: add accept IORING_CQE_F_SOCK_NONEMPTY testHEADmaster
This tests whether we correctly get IORING_CQE_F_SOCK_NONEMPTY set on an accept request, if there are more connections pending after the current one has been accepted. The test is currently gated on IORING_FEAT_RECVSEND_BUNDLE, as that got added to the kernel at the same time as this feature. Not the prettiest, but it's what we have for now... Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--test/Makefile1
-rw-r--r--test/accept-non-empty.c256
2 files changed, 257 insertions, 0 deletions
diff --git a/test/Makefile b/test/Makefile
index 2742ef47..03e328d7 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -46,6 +46,7 @@ test_srcs := \
a4c0b3decb33.c \
accept.c \
accept-link.c \
+ accept-non-empty.c \
accept-reuse.c \
accept-test.c \
across-fork.c \
diff --git a/test/accept-non-empty.c b/test/accept-non-empty.c
new file mode 100644
index 00000000..75a658ba
--- /dev/null
+++ b/test/accept-non-empty.c
@@ -0,0 +1,256 @@
+/* SPDX-License-Identifier: MIT */
+/*
+ * Check that kernels that support it will return IORING_CQE_F_SOCK_NONEMPTY
+ * on accepts requests where more connections are pending.
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <assert.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/socket.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <sys/un.h>
+#include <netinet/tcp.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <pthread.h>
+
+#include "liburing.h"
+#include "helpers.h"
+
+static int no_more_accept;
+
+#define MAX_ACCEPTS 8
+
+struct data {
+ pthread_t thread;
+ pthread_barrier_t barrier;
+ pthread_barrier_t conn_barrier;
+ int connects;
+};
+
+static int start_accept_listen(int port_off, int extra_flags)
+{
+ struct sockaddr_in addr;
+ int32_t val = 1;
+ int fd, ret;
+
+ fd = socket(AF_INET, SOCK_STREAM | extra_flags, IPPROTO_TCP);
+
+ 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);
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(0x1235 + port_off);
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ ret = bind(fd, (struct sockaddr *) &addr, sizeof(addr));
+ assert(ret != -1);
+ ret = listen(fd, 20000);
+ assert(ret != -1);
+
+ return fd;
+}
+
+static int test_maccept(struct data *d, int flags, int fixed)
+{
+ struct io_uring_params p = { };
+ struct io_uring ring;
+ struct io_uring_cqe *cqe;
+ struct io_uring_sqe *sqe;
+ int err = 0, fd, ret, i, *fds;
+
+ p.flags = flags;
+ ret = io_uring_queue_init_params(8, &ring, &p);
+ if (ret == -EINVAL) {
+ return T_EXIT_SKIP;
+ } else if (ret < 0) {
+ fprintf(stderr, "ring setup failure: %d\n", ret);
+ return T_EXIT_FAIL;
+ }
+
+ if (!(p.features & IORING_FEAT_RECVSEND_BUNDLE)) {
+ no_more_accept = 1;
+ return 0;
+ }
+
+ fds = malloc(MAX_ACCEPTS * sizeof(int));
+ memset(fds, -1, MAX_ACCEPTS * sizeof(int));
+
+ if (fixed) {
+ io_uring_register_ring_fd(&ring);
+
+ ret = io_uring_register_files(&ring, fds, MAX_ACCEPTS);
+ if (ret) {
+ fprintf(stderr, "file reg %d\n", ret);
+ return -1;
+ }
+ }
+
+ fd = start_accept_listen(0, 0);
+
+ pthread_barrier_wait(&d->barrier);
+
+ if (d->connects > 1)
+ pthread_barrier_wait(&d->conn_barrier);
+
+ for (i = 0; i < d->connects; i++) {
+ sqe = io_uring_get_sqe(&ring);
+ if (fixed)
+ io_uring_prep_accept_direct(sqe, fd, NULL, NULL, 0, i);
+ else
+ io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
+
+ ret = io_uring_submit_and_wait(&ring, 1);
+ assert(ret != -1);
+
+ ret = io_uring_wait_cqe(&ring, &cqe);
+ assert(!ret);
+ if (cqe->res < 0) {
+ fprintf(stderr, "res=%d\n", cqe->res);
+ break;
+ }
+ fds[i] = cqe->res;
+ if (d->connects == 1) {
+ if (cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
+ fprintf(stderr, "Non-empty sock on single?\n");
+ err = 1;
+ break;
+ }
+ } else {
+ int last = i + 1 == d->connects;
+
+ if (last && cqe->flags & IORING_CQE_F_SOCK_NONEMPTY) {
+ fprintf(stderr, "Non-empty sock on last?\n");
+ err = 1;
+ break;
+ } else if (!last && !(cqe->flags & IORING_CQE_F_SOCK_NONEMPTY)) {
+ fprintf(stderr, "Empty on multi connect?\n");
+ err = 1;
+ break;
+ }
+ }
+ io_uring_cqe_seen(&ring, cqe);
+ }
+
+ close(fd);
+ if (!fixed) {
+ for (i = 0; i < MAX_ACCEPTS; i++)
+ if (fds[i] != -1)
+ close(fds[i]);
+ }
+ free(fds);
+ io_uring_queue_exit(&ring);
+ return err;
+}
+
+static void *connect_fn(void *data)
+{
+ struct sockaddr_in addr = { };
+ struct data *d = data;
+ int i;
+
+ pthread_barrier_wait(&d->barrier);
+
+ addr.sin_family = AF_INET;
+ addr.sin_port = htons(0x1235);
+ addr.sin_addr.s_addr = inet_addr("127.0.0.1");
+
+ for (i = 0; i < d->connects; i++) {
+ int s;
+
+ s = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ if (s < 0) {
+ perror("socket");
+ break;
+ }
+ if (connect(s, (struct sockaddr *) &addr, sizeof(addr)) < 0) {
+ perror("connect");
+ break;
+ }
+ }
+
+ if (d->connects > 1)
+ pthread_barrier_wait(&d->conn_barrier);
+
+ return NULL;
+}
+
+static void setup_thread(struct data *d, int nconns)
+{
+ d->connects = nconns;
+ pthread_barrier_init(&d->barrier, NULL, 2);
+ pthread_barrier_init(&d->conn_barrier, NULL, 2);
+ pthread_create(&d->thread, NULL, connect_fn, d);
+}
+
+static int test(int flags, int fixed)
+{
+ struct data d;
+ void *tret;
+ int ret;
+
+ setup_thread(&d, 1);
+ ret = test_maccept(&d, flags, fixed);
+ if (ret) {
+ fprintf(stderr, "test conns=1 failed\n");
+ return ret;
+ }
+ if (no_more_accept)
+ return T_EXIT_SKIP;
+
+ pthread_join(d.thread, &tret);
+
+ setup_thread(&d, MAX_ACCEPTS);
+ ret = test_maccept(&d, flags, fixed);
+ if (ret) {
+ fprintf(stderr, "test conns=MAX failed\n");
+ return ret;
+ }
+
+ pthread_join(d.thread, &tret);
+ return 0;
+}
+
+int main(int argc, char *argv[])
+{
+ int ret;
+
+ if (argc > 1)
+ return T_EXIT_SKIP;
+
+ ret = test(0, 0);
+ if (no_more_accept)
+ return T_EXIT_SKIP;
+ if (ret) {
+ fprintf(stderr, "test 0 0 failed\n");
+ return ret;
+ }
+
+ ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN, 0);
+ if (ret) {
+ fprintf(stderr, "test DEFER 0 failed\n");
+ return ret;
+ }
+
+ ret = test(0, 1);
+ if (ret) {
+ fprintf(stderr, "test 0 1 failed\n");
+ return ret;
+ }
+
+ ret = test(IORING_SETUP_SINGLE_ISSUER|IORING_SETUP_DEFER_TASKRUN, 1);
+ if (ret) {
+ fprintf(stderr, "test DEFER 1 failed\n");
+ return ret;
+ }
+
+ return 0;
+}