#include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* * Description: Test non-immediate sendmsg completion with non-static iovec */ #include #include #include #include #include #include #include #include #include "liburing.h" #include "helpers.h" /* anything > 1 should be fine, do bigger than FAST_IOV to be sure */ #define IOVS 16 #define INFLIGHT 256 enum { IS_ACCEPT = 0x89, IS_SENDMSG = 0x91, }; struct thread_data { pthread_t thread; pthread_barrier_t barrier; int parent_pid; }; static void *thread_fn(void *__data) { struct thread_data *data = __data; struct sockaddr_in saddr; int sockfd, ret; char msg[64]; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_port = htons(9999); inet_pton(AF_INET, "127.0.0.1", &saddr.sin_addr); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); goto done; } ret = connect(sockfd, (struct sockaddr *) &saddr, sizeof(saddr)); if (ret < 0) { perror("connect"); close(sockfd); goto done; } pthread_barrier_wait(&data->barrier); do { usleep(100); memset(msg, 0, sizeof(msg)); ret = recv(sockfd, msg, sizeof(msg), 0); } while (ret > 0); close(sockfd); done: kill(data->parent_pid, SIGUSR1); return NULL; } static int queue_sends(struct io_uring *ring, int send_fd, struct thread_data *td, struct msghdr *msghdr) { struct io_uring_sqe *sqe; char buf[64]; int i, ret, sbuf; struct iovec *iovs = msghdr->msg_iov; sbuf = 8 * 1024; ret = setsockopt(send_fd, SOL_SOCKET, SO_SNDBUF, &sbuf, sizeof(sbuf)); if (ret < 0) { perror("setsockopt"); return 1; } memset(buf, 0xaa, sizeof(buf)); for (i = 0; i < IOVS; i++) { iovs[i].iov_base = buf; iovs[i].iov_len = sizeof(buf); } /* fill send buffer */ for (i = 0;; i++) { ret = sendmsg(send_fd, msghdr, MSG_DONTWAIT); if (ret == -1) { if (errno == EAGAIN) break; perror("sendmsg"); return 1; } } /* kick receiver, start sendmsg */ msghdr->msg_iovlen = IOVS; pthread_barrier_wait(&td->barrier); for (i = 0; i < INFLIGHT; i++) { sqe = io_uring_get_sqe(ring); io_uring_prep_sendmsg(sqe, send_fd, msghdr, 0); sqe->user_data = IS_SENDMSG; } return 0; } int main(int argc, char *argv[]) { struct io_uring ring; struct io_uring_sqe *sqe; struct io_uring_cqe *cqe; struct sockaddr_in saddr; int val, send_fd, ret, sockfd, seen_sends = 0; struct thread_data td; struct iovec iovs[IOVS]; struct msghdr msghdr = { .msg_iov = iovs, .msg_iovlen = 1, }; if (argc > 1) return T_EXIT_SKIP; memset(&saddr, 0, sizeof(saddr)); saddr.sin_family = AF_INET; saddr.sin_addr.s_addr = htonl(INADDR_ANY); saddr.sin_port = htons(9999); sockfd = socket(AF_INET, SOCK_STREAM, 0); if (sockfd < 0) { perror("socket"); return T_EXIT_FAIL; } val = 1; setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)); setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)); ret = bind(sockfd, (struct sockaddr *)&saddr, sizeof(saddr)); if (ret < 0) { perror("bind"); close(sockfd); return T_EXIT_FAIL; } ret = listen(sockfd, 1); if (ret < 0) { perror("listen"); close(sockfd); return T_EXIT_FAIL; } ret = io_uring_queue_init(INFLIGHT, &ring, IORING_SETUP_SINGLE_ISSUER | IORING_SETUP_DEFER_TASKRUN); if (ret == -EINVAL) { close(sockfd); return T_EXIT_SKIP; } sqe = io_uring_get_sqe(&ring); io_uring_prep_multishot_accept(sqe, sockfd, NULL, NULL, 0); sqe->user_data = IS_ACCEPT; io_uring_submit(&ring); /* check for no multishot accept */ ret = io_uring_peek_cqe(&ring, &cqe); if (!ret && cqe->res == -EINVAL) { close(sockfd); return T_EXIT_SKIP; } /* start receiver */ td.parent_pid = getpid(); pthread_barrier_init(&td.barrier, NULL, 2); pthread_create(&td.thread, NULL, thread_fn, &td); do { ret = io_uring_submit_and_wait(&ring, 1); if (ret < 0) { fprintf(stderr, "submit: %d\n", ret); break; } ret = io_uring_peek_cqe(&ring, &cqe); if (ret) { fprintf(stderr, "peek: %d\n", ret); break; } switch (cqe->user_data) { case IS_ACCEPT: send_fd = cqe->res; io_uring_cqe_seen(&ring, cqe); ret = queue_sends(&ring, send_fd, &td, &msghdr); if (ret) exit(T_EXIT_FAIL); break; case IS_SENDMSG: io_uring_cqe_seen(&ring, cqe); seen_sends++; if (seen_sends == INFLIGHT) exit(0); break; default: fprintf(stderr, "got unknown cqe\n"); return T_EXIT_FAIL; } } while (1); return T_EXIT_FAIL; }