blob: 26e8620f803519148f5787bde6401c9abaf934de [file] [log] [blame]
/* 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 <arpa/inet.h>
#include <err.h>
#include <fcntl.h>
#include <netinet/in.h>
#include <poll.h>
#include <pthread.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/socket.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#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;
}