* [PATCH liburing 3/4] tests/helpers: add t_create_socketpair_ip
2025-03-07 16:22 [PATCH liburing 0/4] vectored registered buffer support and tests Pavel Begunkov
2025-03-07 16:22 ` [PATCH liburing 1/4] Add vectored registered buffer req init helpers Pavel Begunkov
2025-03-07 16:22 ` [PATCH liburing 2/4] test/sendzc: test registered vectored buffers Pavel Begunkov
@ 2025-03-07 16:22 ` Pavel Begunkov
2025-03-07 16:22 ` [PATCH liburing 4/4] tests: targeted registered vector tests Pavel Begunkov
3 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-03-07 16:22 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
create_socketpair_ip() is useful for zerocopy tx testing as it needs TCP
sockets and not just AF_UNIX. There will be tx zc tests in more files,
so move the function to helpers.
Signed-off-by: Pavel Begunkov <[email protected]>
---
test/helpers.c | 111 ++++++++++++++++++++++++++++++++++++++
test/helpers.h | 5 ++
test/send-zerocopy.c | 123 ++++---------------------------------------
3 files changed, 127 insertions(+), 112 deletions(-)
diff --git a/test/helpers.c b/test/helpers.c
index 07186911..b2d386f4 100644
--- a/test/helpers.c
+++ b/test/helpers.c
@@ -373,3 +373,114 @@ void *aligned_alloc(size_t alignment, size_t size)
return ret;
}
+
+int t_create_socketpair_ip(struct sockaddr_storage *addr,
+ int *sock_client, int *sock_server,
+ bool ipv6, bool client_connect,
+ bool msg_zc, bool tcp, const char *name)
+{
+ socklen_t addr_size;
+ int family, sock, listen_sock = -1;
+ int ret;
+
+ memset(addr, 0, sizeof(*addr));
+ if (ipv6) {
+ struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
+
+ family = AF_INET6;
+ saddr->sin6_family = family;
+ saddr->sin6_port = htons(0);
+ addr_size = sizeof(*saddr);
+ } else {
+ struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
+
+ family = AF_INET;
+ saddr->sin_family = family;
+ saddr->sin_port = htons(0);
+ saddr->sin_addr.s_addr = htonl(INADDR_ANY);
+ addr_size = sizeof(*saddr);
+ }
+
+ /* server sock setup */
+ if (tcp) {
+ sock = listen_sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ } else {
+ sock = *sock_server = socket(family, SOCK_DGRAM, 0);
+ }
+ if (sock < 0) {
+ perror("socket");
+ return 1;
+ }
+
+ ret = bind(sock, (struct sockaddr *)addr, addr_size);
+ if (ret < 0) {
+ perror("bind");
+ return 1;
+ }
+
+ ret = getsockname(sock, (struct sockaddr *)addr, &addr_size);
+ if (ret < 0) {
+ fprintf(stderr, "getsockname failed %i\n", errno);
+ return 1;
+ }
+
+ if (tcp) {
+ ret = listen(sock, 128);
+ assert(ret != -1);
+ }
+
+ if (ipv6) {
+ struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
+
+ inet_pton(AF_INET6, name, &(saddr->sin6_addr));
+ } else {
+ struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
+
+ inet_pton(AF_INET, name, &saddr->sin_addr);
+ }
+
+ /* client sock setup */
+ if (tcp) {
+ *sock_client = socket(family, SOCK_STREAM, IPPROTO_TCP);
+ assert(client_connect);
+ } else {
+ *sock_client = socket(family, SOCK_DGRAM, 0);
+ }
+ if (*sock_client < 0) {
+ perror("socket");
+ return 1;
+ }
+ if (client_connect) {
+ ret = connect(*sock_client, (struct sockaddr *)addr, addr_size);
+ if (ret < 0) {
+ perror("connect");
+ return 1;
+ }
+ }
+ if (msg_zc) {
+#ifdef SO_ZEROCOPY
+ int val = 1;
+
+ /*
+ * NOTE: apps must not set SO_ZEROCOPY when using io_uring zc.
+ * It's only here to test interactions with MSG_ZEROCOPY.
+ */
+ if (setsockopt(*sock_client, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
+ perror("setsockopt zc");
+ return 1;
+ }
+#else
+ fprintf(stderr, "no SO_ZEROCOPY\n");
+ return 1;
+#endif
+ }
+ if (tcp) {
+ *sock_server = accept(listen_sock, NULL, NULL);
+ if (!*sock_server) {
+ fprintf(stderr, "can't accept\n");
+ return 1;
+ }
+ close(listen_sock);
+ }
+ return 0;
+}
diff --git a/test/helpers.h b/test/helpers.h
index d0294eba..f8a5c7f2 100644
--- a/test/helpers.h
+++ b/test/helpers.h
@@ -81,6 +81,11 @@ struct iovec *t_create_buffers(size_t buf_num, size_t buf_size);
*/
int t_create_socket_pair(int fd[2], bool stream);
+int t_create_socketpair_ip(struct sockaddr_storage *addr,
+ int *sock_client, int *sock_server,
+ bool ipv6, bool client_connect,
+ bool msg_zc, bool tcp, const char *name);
+
/*
* Helper for setting up a ring and checking for user privs
*/
diff --git a/test/send-zerocopy.c b/test/send-zerocopy.c
index 680481a0..b505e4d0 100644
--- a/test/send-zerocopy.c
+++ b/test/send-zerocopy.c
@@ -258,117 +258,6 @@ static int test_send_faults(int sock_tx, int sock_rx)
return T_EXIT_PASS;
}
-static int create_socketpair_ip(struct sockaddr_storage *addr,
- int *sock_client, int *sock_server,
- bool ipv6, bool client_connect,
- bool msg_zc, bool tcp)
-{
- socklen_t addr_size;
- int family, sock, listen_sock = -1;
- int ret;
-
- memset(addr, 0, sizeof(*addr));
- if (ipv6) {
- struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
-
- family = AF_INET6;
- saddr->sin6_family = family;
- saddr->sin6_port = htons(0);
- addr_size = sizeof(*saddr);
- } else {
- struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
-
- family = AF_INET;
- saddr->sin_family = family;
- saddr->sin_port = htons(0);
- saddr->sin_addr.s_addr = htonl(INADDR_ANY);
- addr_size = sizeof(*saddr);
- }
-
- /* server sock setup */
- if (tcp) {
- sock = listen_sock = socket(family, SOCK_STREAM, IPPROTO_TCP);
- } else {
- sock = *sock_server = socket(family, SOCK_DGRAM, 0);
- }
- if (sock < 0) {
- perror("socket");
- return 1;
- }
-
- ret = bind(sock, (struct sockaddr *)addr, addr_size);
- if (ret < 0) {
- perror("bind");
- return 1;
- }
-
- ret = getsockname(sock, (struct sockaddr *)addr, &addr_size);
- if (ret < 0) {
- fprintf(stderr, "getsockname failed %i\n", errno);
- return 1;
- }
-
- if (tcp) {
- ret = listen(sock, 128);
- assert(ret != -1);
- }
-
- if (ipv6) {
- struct sockaddr_in6 *saddr = (struct sockaddr_in6 *)addr;
-
- inet_pton(AF_INET6, HOSTV6, &(saddr->sin6_addr));
- } else {
- struct sockaddr_in *saddr = (struct sockaddr_in *)addr;
-
- inet_pton(AF_INET, HOST, &saddr->sin_addr);
- }
-
- /* client sock setup */
- if (tcp) {
- *sock_client = socket(family, SOCK_STREAM, IPPROTO_TCP);
- assert(client_connect);
- } else {
- *sock_client = socket(family, SOCK_DGRAM, 0);
- }
- if (*sock_client < 0) {
- perror("socket");
- return 1;
- }
- if (client_connect) {
- ret = connect(*sock_client, (struct sockaddr *)addr, addr_size);
- if (ret < 0) {
- perror("connect");
- return 1;
- }
- }
- if (msg_zc) {
-#ifdef SO_ZEROCOPY
- int val = 1;
-
- /*
- * NOTE: apps must not set SO_ZEROCOPY when using io_uring zc.
- * It's only here to test interactions with MSG_ZEROCOPY.
- */
- if (setsockopt(*sock_client, SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) {
- perror("setsockopt zc");
- return 1;
- }
-#else
- fprintf(stderr, "no SO_ZEROCOPY\n");
- return 1;
-#endif
- }
- if (tcp) {
- *sock_server = accept(listen_sock, NULL, NULL);
- if (!*sock_server) {
- fprintf(stderr, "can't accept\n");
- return 1;
- }
- close(listen_sock);
- }
- return 0;
-}
-
struct send_conf {
bool fixed_buf;
bool mix_register;
@@ -574,6 +463,16 @@ static int do_test_inet_send(struct io_uring *ring, int sock_client, int sock_se
return 0;
}
+static int create_socketpair_ip(struct sockaddr_storage *addr,
+ int *sock_client, int *sock_server,
+ bool ipv6, bool client_connect,
+ bool msg_zc, bool tcp)
+{
+ return t_create_socketpair_ip(addr, sock_client, sock_server, ipv6,
+ client_connect, msg_zc, tcp,
+ ipv6 ? HOSTV6 : HOST);
+}
+
static int test_inet_send(struct io_uring *ring)
{
struct send_conf conf;
@@ -598,7 +497,7 @@ static int test_inet_send(struct io_uring *ring)
continue;
#endif
ret = create_socketpair_ip(&addr, &sock_client, &sock_server, ipv6,
- client_connect, msg_zc_set, tcp);
+ client_connect, msg_zc_set, tcp);
if (ret) {
fprintf(stderr, "sock prep failed %d\n", ret);
return 1;
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread
* [PATCH liburing 4/4] tests: targeted registered vector tests
2025-03-07 16:22 [PATCH liburing 0/4] vectored registered buffer support and tests Pavel Begunkov
` (2 preceding siblings ...)
2025-03-07 16:22 ` [PATCH liburing 3/4] tests/helpers: add t_create_socketpair_ip Pavel Begunkov
@ 2025-03-07 16:22 ` Pavel Begunkov
3 siblings, 0 replies; 8+ messages in thread
From: Pavel Begunkov @ 2025-03-07 16:22 UTC (permalink / raw)
To: io-uring; +Cc: asml.silence
The main part here is to exercise various iovec->bvec reallocation
scenarios, but it also checks edge cases and validates final data.
Signed-off-by: Pavel Begunkov <[email protected]>
---
test/Makefile | 1 +
test/regvec.c | 604 ++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 605 insertions(+)
create mode 100644 test/regvec.c
diff --git a/test/Makefile b/test/Makefile
index 0367ef72..e937c852 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -241,6 +241,7 @@ test_srcs := \
wq-aff.c \
xattr.c \
zcrx.c \
+ regvec.c \
# EOL
# Please keep this list sorted alphabetically.
diff --git a/test/regvec.c b/test/regvec.c
new file mode 100644
index 00000000..3afa82b3
--- /dev/null
+++ b/test/regvec.c
@@ -0,0 +1,604 @@
+/* SPDX-License-Identifier: MIT */
+#include <sys/mman.h>
+#include <linux/mman.h>
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <poll.h>
+#include <pthread.h>
+#include <errno.h>
+
+#include "helpers.h"
+#include "liburing.h"
+
+static bool has_regvec;
+
+struct buf_desc {
+ char *buf_wr;
+ char *buf_rd;
+ size_t size;
+
+ struct io_uring ring;
+ bool ring_init;
+ bool fixed;
+ int buf_idx;
+ bool rw;
+};
+
+#define BUF_BASE_IDX 1
+static int page_sz;
+
+static void probe_support(void)
+{
+ struct io_uring_probe *p;
+ struct io_uring ring;
+ int ret = 0;
+
+ ret = io_uring_queue_init(1, &ring, 0);
+ if (ret) {
+ fprintf(stderr, "queue init failed: %d\n", ret);
+ exit(ret);
+ }
+
+ p = t_calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
+ ret = io_uring_register_probe(&ring, p, 256);
+
+ /* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
+ if (ret == -EINVAL)
+ goto out;
+ if (ret) {
+ fprintf(stderr, "register_probe: %d\n", ret);
+ goto out;
+ }
+
+ has_regvec = p->ops_len > IORING_OP_READV_FIXED &&
+ (p->ops[IORING_OP_READV_FIXED].flags & IO_URING_OP_SUPPORTED);
+out:
+ io_uring_queue_exit(&ring);
+ if (p)
+ free(p);
+}
+
+static void bind_ring(struct buf_desc *bd, struct io_uring *ring, unsigned buf_idx)
+{
+ size_t size = bd->size;
+ struct iovec iov;
+ int ret;
+
+ iov.iov_len = size;
+ iov.iov_base = bd->buf_wr;
+
+ ret = io_uring_register_buffers_update_tag(ring, buf_idx, &iov, NULL, 1);
+ if (ret != 1) {
+ if (geteuid()) {
+ fprintf(stderr, "Not root, skipping\n");
+ exit(0);
+ }
+ fprintf(stderr, "buf reg failed %i\n", ret);
+ exit(1);
+ }
+ bd->buf_idx = buf_idx;
+}
+
+static void reinit_ring(struct buf_desc *bd)
+{
+ struct io_uring *ring = &bd->ring;
+ int ret;
+
+ if (bd->ring_init) {
+ io_uring_queue_exit(ring);
+ bd->ring_init = false;
+ }
+
+ ret = io_uring_queue_init(32, ring, 0);
+ if (ret) {
+ fprintf(stderr, "ring init error %i\n", ret);
+ exit(1);
+ }
+
+ ret = io_uring_register_buffers_sparse(ring, 128);
+ if (ret) {
+ fprintf(stderr, "table reg error %i\n", ret);
+ exit(1);
+ }
+
+ bind_ring(bd, &bd->ring, BUF_BASE_IDX);
+ bd->ring_init = true;
+}
+
+static void init_buffers(struct buf_desc *bd, size_t size)
+{
+ void *start;
+ void *mem;
+
+ start = mmap(NULL, size + page_sz * 2, PROT_NONE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
+ if (start == MAP_FAILED) {
+ fprintf(stderr, "Unable to preserve the page mixture memory.\n");
+ exit(1);
+ }
+
+ mem = mmap(start + page_sz, size, PROT_READ | PROT_WRITE,
+ MAP_PRIVATE | MAP_ANONYMOUS | MAP_FIXED, -1, 0);
+ if (mem == MAP_FAILED) {
+ fprintf(stderr, "mmap fail\n");
+ exit(1);
+ }
+
+ memset(bd, 0, sizeof(*bd));
+ bd->size = size;
+ bd->buf_wr = mem;
+ bd->buf_rd = malloc(size);
+ if (!bd->buf_rd) {
+ fprintf(stderr, "malloc fail\n");
+ exit(1);
+ }
+}
+
+static int verify_data(struct buf_desc *bd, struct iovec *wr_vecs, int nr_iovec,
+ int fd)
+{
+ int iov_idx, ret;
+
+ for (iov_idx = 0; iov_idx < nr_iovec; iov_idx++) {
+ struct iovec *vec = &wr_vecs[iov_idx];
+ size_t seg_size = vec->iov_len;
+ size_t read_bytes = 0;
+
+ while (1) {
+ ret = read(fd, bd->buf_rd + read_bytes, seg_size - read_bytes);
+ if (ret < 0) {
+ fprintf(stderr, "read error %i", ret);
+ return 1;
+ }
+ read_bytes += ret;
+ if (read_bytes == seg_size)
+ break;
+ if (ret == 0) {
+ fprintf(stderr, "can't read %i", ret);
+ return 2;
+ }
+ }
+
+ ret = memcmp(bd->buf_rd, vec->iov_base, seg_size);
+ if (ret != 0) {
+ fprintf(stderr, "data mismatch %i\n", ret);
+ return 3;
+ }
+ }
+ return 0;
+}
+
+struct verify_data {
+ struct buf_desc *bd;
+ struct iovec *vecs;
+ int nr_vec;
+ int fd;
+};
+
+static void *verify_thread_cb(void *data)
+{
+ struct verify_data *vd = data;
+ int ret;
+
+ ret = verify_data(vd->bd, vd->vecs, vd->nr_vec, vd->fd);
+ return (void *)(unsigned long)ret;
+}
+
+static int test_rw(struct buf_desc *bd, struct iovec *vecs, int nr_vec, int fd_wr)
+{
+ unsigned buf_idx = bd->buf_idx;
+ struct io_uring *ring = &bd->ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret;
+
+ sqe = io_uring_get_sqe(ring);
+ io_uring_prep_writev(sqe, fd_wr, vecs, nr_vec, 0);
+ if (bd->fixed)
+ sqe->buf_index = buf_idx;
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed %i\n", ret);
+ exit(1);
+ }
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ exit(1);
+ }
+
+ ret = cqe->res;
+ io_uring_cqe_seen(ring, cqe);
+ return ret;
+}
+
+static int test_sendzc(struct buf_desc *bd, struct iovec *vecs, int nr_vec, int fd_wr)
+{
+ unsigned buf_idx = bd->buf_idx;
+ struct io_uring *ring = &bd->ring;
+ struct io_uring_sqe *sqe;
+ struct io_uring_cqe *cqe;
+ int ret, cqe_ret, more;
+ struct msghdr msghdr;
+
+ memset(&msghdr, 0, sizeof(msghdr));
+ msghdr.msg_iov = vecs;
+ msghdr.msg_iovlen = nr_vec;
+
+ sqe = io_uring_get_sqe(ring);
+ if (bd->fixed)
+ io_uring_prep_sendmsg_zc_fixed(sqe, fd_wr, &msghdr, 0, buf_idx);
+ else
+ io_uring_prep_sendmsg_zc(sqe, fd_wr, &msghdr, 0);
+
+ ret = io_uring_submit(ring);
+ if (ret != 1) {
+ fprintf(stderr, "submit failed %i\n", ret);
+ exit(1);
+ }
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ exit(1);
+ }
+
+ cqe_ret = cqe->res;
+ more = cqe->flags & IORING_CQE_F_MORE;
+ io_uring_cqe_seen(ring, cqe);
+
+ if (more) {
+ ret = io_uring_wait_cqe(ring, &cqe);
+ if (ret) {
+ fprintf(stderr, "wait_cqe=%d\n", ret);
+ exit(1);
+ }
+ io_uring_cqe_seen(ring, cqe);
+ }
+ return cqe_ret;
+}
+
+static int test_vec(struct buf_desc *bd, struct iovec *vecs, int nr_vec,
+ bool expect_fail, int *cqe_ret)
+{
+ struct sockaddr_storage addr;
+ int sock_server, sock_client;
+ struct verify_data vd;
+ size_t total_len = 0;
+ int i, ret;
+ void *verify_res;
+ pthread_t th;
+
+ ret = t_create_socketpair_ip(&addr, &sock_client, &sock_server,
+ true, true, false, true, "::1");
+ if (ret) {
+ fprintf(stderr, "sock prep failed %d\n", ret);
+ return 1;
+ }
+
+ for (i = 0; i < bd->size; i++)
+ bd->buf_wr[i] = i;
+ memset(bd->buf_rd, 0, bd->size);
+
+ for (i = 0; i < nr_vec; i++)
+ total_len += vecs[i].iov_len;
+
+ vd.bd = bd;
+ vd.vecs = vecs;
+ vd.nr_vec = nr_vec;
+ vd.fd = sock_server;
+
+ if (!expect_fail) {
+ ret = pthread_create(&th, NULL, verify_thread_cb, &vd);
+ if (ret) {
+ fprintf(stderr, "pthread_create failed %i\n", ret);
+ return ret;
+ }
+ }
+
+ if (bd->rw)
+ ret = test_rw(bd, vecs, nr_vec, sock_client);
+ else
+ ret = test_sendzc(bd, vecs, nr_vec, sock_client);
+
+ *cqe_ret = ret;
+
+ if (!expect_fail && ret != total_len) {
+ fprintf(stderr, "invalid cqe %i, expected %lu\n",
+ ret, (unsigned long)total_len);
+ return 1;
+ }
+
+ if (!expect_fail) {
+ pthread_join(th, &verify_res);
+ ret = (int)(unsigned long)verify_res;
+ if (ret) {
+ fprintf(stderr, "verify failed %i\n", ret);
+ return 1;
+ }
+ }
+ close(sock_client);
+ close(sock_server);
+ return 0;
+}
+
+struct work {
+ struct iovec *vecs;
+ unsigned nr_vecs;
+};
+
+static int test_sequence(struct buf_desc *bd, unsigned nr, struct work *ws)
+{
+ int i, ret;
+ int cqe_ret;
+
+ reinit_ring(bd);
+
+ for (i = 0; i < nr; i++) {
+ ret = test_vec(bd, ws[i].vecs, ws[i].nr_vecs, false, &cqe_ret);
+ if (ret) {
+ fprintf(stderr, "sequence failed, idx %i/%i\n", i, nr);
+ return ret;
+ }
+ }
+ return 0;
+}
+
+static void test_basic(struct buf_desc *bd)
+{
+ void *p = bd->buf_wr;
+ int ret;
+ struct iovec iov_page = { .iov_base = p,
+ .iov_len = page_sz, };
+ struct iovec iov_inner = { .iov_base = p + 1,
+ .iov_len = 3, };
+ struct iovec iov_maxbvec = { .iov_base = p + page_sz - 1,
+ .iov_len = page_sz + 2, };
+ struct iovec iov_big = { .iov_base = p,
+ .iov_len = page_sz * 12 + 33, };
+ struct iovec iov_big_unalign = { .iov_base = p + 10,
+ .iov_len = page_sz * 7 + 41, };
+ struct iovec iov_full = { .iov_base = p,
+ .iov_len = bd->size, };
+ struct iovec iov_right1 = { .iov_base = p + bd->size - page_sz + 5,
+ .iov_len = page_sz - 5 };
+ struct iovec iov_right2 = { .iov_base = p + bd->size - page_sz - 5,
+ .iov_len = page_sz + 5 };
+ struct iovec iov_full_unalign = { .iov_base = p + 1,
+ .iov_len = bd->size - 1, };
+ struct iovec vecs[] = {
+ iov_page,
+ iov_big,
+ iov_inner,
+ iov_big_unalign,
+ iov_big_unalign,
+ };
+ struct iovec vecs_basic[] = { iov_page, iov_page, iov_page };
+ struct iovec vecs_full[] = { iov_full, iov_full, iov_full };
+ struct iovec vecs_full_unalign[] = { iov_full_unalign, iov_full_unalign,
+ iov_full_unalign };
+ struct iovec vecs_maxsegs[] = { iov_maxbvec, iov_maxbvec, iov_maxbvec,
+ iov_maxbvec, iov_maxbvec, iov_maxbvec};
+
+ ret = test_sequence(bd, 1, (struct work[]) {
+ { &iov_page, 1 },
+ { vecs, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: basic aligned, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs, 1 },
+ { vecs, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: basic aligned, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs + 1, 1 },
+ { vecs + 1, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: multi page buffer, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs + 2, 1 },
+ { vecs + 2, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: misaligned buffer, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs + 3, 1 },
+ { vecs + 3, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: misaligned multipage buffer, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs, 1 },
+ { vecs + 3, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: realloc + increase bvec, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs + 3, 1 },
+ { vecs + 0, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: realloc + decrease bvec, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 2, (struct work[]) {
+ { vecs, 4 },
+ { vecs, 4 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: multisegment, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs, 2 },
+ { vecs, 3 },
+ { vecs, 4 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: multisegment 2, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_basic, 1 },
+ { vecs_basic, 2 },
+ { vecs_basic, 3 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: increase iovec, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_basic, 3 },
+ { vecs_basic, 2 },
+ { vecs_basic, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: decrease iovec, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { &iov_right1, 1 },
+ { &iov_right2, 1 },
+ { &iov_right1, 1 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: right aligned, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_full, 1 },
+ { vecs_full, 1 },
+ { vecs_full, 3 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: full size, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_full_unalign, 1 },
+ { vecs_full_unalign, 1 },
+ { vecs_full_unalign, 3 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: full size unsigned, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_maxsegs, 1 },
+ { vecs_maxsegs, 2 },
+ { vecs_maxsegs, 3 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: overestimated segments, %i\n", ret);
+ exit(1);
+ }
+
+ ret = test_sequence(bd, 3, (struct work[]) {
+ { vecs_maxsegs, 6 },
+ { vecs_maxsegs, 6 },
+ { vecs_maxsegs, 6 }});
+ if (ret) {
+ fprintf(stderr, "seq failure: overestimated segments 2, %i\n", ret);
+ exit(1);
+ }
+}
+
+static void test_fail(struct buf_desc *bd)
+{
+ int ret, cqe_ret;
+ void *p = bd->buf_wr;
+ struct iovec iov_0len = { .iov_base = p, .iov_len = 0 };
+ struct iovec iov_0buf = { .iov_base = 0, .iov_len = 1 };
+ struct iovec iov_inv = { .iov_base = (void *)-1U, .iov_len = 1 };
+ struct iovec iov_under = { .iov_base = p - 1, .iov_len = 1 };
+ struct iovec iov_over = { .iov_base = p + bd->size, .iov_len = 1 };
+ struct iovec vecs_0[] = { iov_0len, iov_0len, iov_0len, iov_0len,
+ iov_0len, iov_0len, iov_0len, iov_0len };
+
+ reinit_ring(bd);
+ ret = test_vec(bd, vecs_0, 8, true, &cqe_ret);
+ if (ret || cqe_ret > 0) {
+ fprintf(stderr, "0 length test failed %i, cqe %i\n",
+ ret, cqe_ret);
+ exit(1);
+ }
+
+ reinit_ring(bd);
+ ret = test_vec(bd, &iov_0buf, 1, true, &cqe_ret);
+ if (ret || cqe_ret >= 0) {
+ fprintf(stderr, "0 buf test failed %i, cqe %i\n",
+ ret, cqe_ret);
+ exit(1);
+ }
+
+ reinit_ring(bd);
+ ret = test_vec(bd, &iov_inv, 1, true, &cqe_ret);
+ if (ret || cqe_ret >= 0) {
+ fprintf(stderr, "inv buf test failed %i, cqe %i\n",
+ ret, cqe_ret);
+ exit(1);
+ }
+
+ reinit_ring(bd);
+ ret = test_vec(bd, &iov_under, 1, true, &cqe_ret);
+ if (ret || cqe_ret >= 0) {
+ fprintf(stderr, "inv buf underflow failed %i, cqe %i\n",
+ ret, cqe_ret);
+ exit(1);
+ }
+
+ reinit_ring(bd);
+ ret = test_vec(bd, &iov_over, 1, true, &cqe_ret);
+ if (ret || cqe_ret >= 0) {
+ fprintf(stderr, "inv buf overflow failed %i, cqe %i\n",
+ ret, cqe_ret);
+ exit(1);
+ }
+}
+
+int main(void)
+{
+ struct buf_desc bd = {};
+ int i = 0;
+
+ page_sz = sysconf(_SC_PAGESIZE);
+
+ probe_support();
+ if (!has_regvec) {
+ printf("doesn't support registered vector ops, skip\n");
+ return 0;
+ }
+
+ init_buffers(&bd, page_sz * 32);
+ bd.fixed = true;
+
+ for (i = 0; i < 2; i++) {
+ bool rw = i & 1;
+
+ bd.rw = rw;
+
+ test_basic(&bd);
+ test_fail(&bd);
+ }
+
+ io_uring_queue_exit(&bd.ring);
+ return 0;
+}
--
2.48.1
^ permalink raw reply related [flat|nested] 8+ messages in thread