#include "../config-host.h" /* SPDX-License-Identifier: MIT */ /* * Description: test that importing a buffer node ties the lifetime to the * notification io_kiocb, not the normal request. * * Based on a test case from Google Big Sleep. * */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "liburing.h" #include "helpers.h" #define BUF_SIZE (1024 * 1024) static uint8_t read_buffer[BUF_SIZE] = { 0 }; /* Creates a pair of connected TCP sockets. */ static void tcp_socketpair(int sv[2]) { struct sockaddr_in addr_in = { .sin_family = AF_INET, .sin_addr.s_addr = htonl(INADDR_LOOPBACK), .sin_port = 0, }; struct sockaddr *addr = (struct sockaddr *) &addr_in; socklen_t addr_len = sizeof(addr_in); int listener_fd; listener_fd = socket(AF_INET, SOCK_STREAM, 0); if (listener_fd < 0) err(EXIT_FAILURE, "listener socket"); if (bind(listener_fd, addr, addr_len) < 0) err(EXIT_FAILURE, "bind"); if (listen(listener_fd, 1) < 0) err(EXIT_FAILURE, "listen"); if (getsockname(listener_fd, addr, &addr_len) < 0) err(EXIT_FAILURE, "getsockname"); sv[0] = socket(AF_INET, SOCK_STREAM, 0); if (sv[0] < 0) err(EXIT_FAILURE, "socket(AF_INET, SOCK_STREAM, 0)"); if (connect(sv[0], addr, addr_len) < 0) err(EXIT_FAILURE, "connect"); sv[1] = accept(listener_fd, NULL, NULL); if (sv[1] == -1) err(EXIT_FAILURE, "accept"); close(listener_fd); } int main(int argc, char *argv[]) { struct io_uring_sqe *sqe; struct io_uring_cqe* cqe; struct iovec send_iov; struct io_uring ring; struct iovec iov_reg; struct msghdr msg = { .msg_iov = &send_iov, .msg_iovlen = 1, }; void *send_buffer; int sockets[2]; int val, ret; if (argc > 1) return T_EXIT_SKIP; tcp_socketpair(sockets); val = 1; if (setsockopt(sockets[0], SOL_SOCKET, SO_ZEROCOPY, &val, sizeof(val))) err(EXIT_FAILURE, "setsockopt(SO_ZEROCOPY)"); ret = io_uring_queue_init(1, &ring, 0); if (ret < 0) errx(EXIT_FAILURE, "io_uring_queue_init: %s", strerror(-ret)); /* mmap send_buffer so we can unmap it later. */ send_buffer = mmap(NULL, BUF_SIZE, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); if (send_buffer == MAP_FAILED) err(EXIT_FAILURE, "mapping iouring buffer"); iov_reg.iov_base = send_buffer; iov_reg.iov_len = BUF_SIZE; ret = io_uring_register_buffers(&ring, &iov_reg, 1); if (ret < 0) errx(EXIT_FAILURE, "io_uring_register_buffers: %s", strerror(-ret)); sqe = io_uring_get_sqe(&ring); send_iov.iov_base = send_buffer; send_iov.iov_len = BUF_SIZE; io_uring_prep_sendmsg_zc(sqe, sockets[0], &msg, 0); sqe->ioprio = IORING_RECVSEND_FIXED_BUF; sqe->buf_index = 0; memset(send_buffer, 'B', BUF_SIZE); io_uring_submit(&ring); ret = io_uring_wait_cqe(&ring, &cqe); if (ret < 0) errx(EXIT_FAILURE, "io_uring_wait_cqe: %s", strerror(-ret)); if (cqe->res < 0) { if (cqe->res == -EINVAL) return T_EXIT_SKIP; errx(EXIT_FAILURE, "readv failed: %s", strerror(-cqe->res)); } io_uring_cqe_seen(&ring, cqe); io_uring_unregister_buffers(&ring); munmap(send_buffer, BUF_SIZE); ret = read(sockets[1], read_buffer, BUF_SIZE); if (ret < 0) err(EXIT_FAILURE, "read"); io_uring_queue_exit(&ring); return T_EXIT_PASS; }