blob: 6935b994230d99afb221b9c94c4b9cf13308fc64 [file] [log] [blame]
/*
* Check that IORING_OP_ACCEPT works, and send some data across to verify we
* didn't get a junk fd.
*/
#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/un.h>
#include <netinet/tcp.h>
#include <netinet/in.h>
#include <liburing.h>
static int no_accept;
static void queue_send(struct io_uring *ring, int fd)
{
struct io_uring_sqe *sqe;
char send_buff[128];
struct iovec iov;
iov.iov_base = send_buff;
iov.iov_len = sizeof(send_buff);
sqe = io_uring_get_sqe(ring);
io_uring_prep_writev(sqe, fd, &iov, 1, 0);
}
static void queue_recv(struct io_uring *ring, int fd)
{
struct io_uring_sqe *sqe;
char recv_buff[128];
struct iovec iov;
iov.iov_base = recv_buff;
iov.iov_len = sizeof(recv_buff);
sqe = io_uring_get_sqe(ring);
io_uring_prep_readv(sqe, fd, &iov, 1, 0);
}
static int accept_conn(struct io_uring *ring, int fd)
{
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int ret;
sqe = io_uring_get_sqe(ring);
io_uring_prep_accept(sqe, fd, NULL, NULL, 0);
assert(io_uring_submit(ring) != -1);
assert(!io_uring_wait_cqe(ring, &cqe));
ret = cqe->res;
io_uring_cqe_seen(ring, cqe);
return ret;
}
static int test(struct io_uring *ring, int accept_should_einval)
{
struct io_uring_cqe *cqe;
uint32_t head;
uint32_t count = 0;
int done = 0;
int p_fd[2];
int32_t recv_s0 = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
int32_t val = 1;
assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEPORT, &val, sizeof(val)) != -1);
assert(setsockopt(recv_s0, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) != -1);
struct sockaddr_in addr;
addr.sin_family = AF_INET;
addr.sin_port = 0x1235;
addr.sin_addr.s_addr = 0x0100007fU;
assert(bind(recv_s0, (struct sockaddr*)&addr, sizeof(addr)) != -1);
assert(listen(recv_s0, 128) != -1);
p_fd[1] = socket(AF_INET, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_TCP);
val = 1;
assert(setsockopt(p_fd[1], IPPROTO_TCP, TCP_NODELAY, &val, sizeof(val)) != -1);
int32_t flags = fcntl(p_fd[1], F_GETFL, 0);
assert(flags != -1);
flags |= O_NONBLOCK;
assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
assert(connect(p_fd[1], (struct sockaddr*)&addr, sizeof(addr)) == -1);
flags = fcntl(p_fd[1], F_GETFL, 0);
assert(flags != -1);
flags &= ~O_NONBLOCK;
assert(fcntl(p_fd[1], F_SETFL, flags) != -1);
assert(io_uring_queue_init(32, ring, 0) >= 0);
p_fd[0] = accept_conn(ring, recv_s0);
if (p_fd[0] == -EINVAL) {
if (accept_should_einval)
goto out;
fprintf(stdout, "Accept not supported, skipping\n");
no_accept = 1;
goto out;
}
assert(p_fd[0] >= 0);
queue_send(ring, p_fd[1]);
queue_recv(ring, p_fd[0]);
assert(io_uring_submit_and_wait(ring, 2) != -1);
while (count < 2) {
io_uring_for_each_cqe(ring, head, cqe) {
if (cqe->res < 0) {
fprintf(stderr, "Got cqe res %d\n", cqe->res);
done = 1;
break;
}
assert(cqe->res == 128);
count++;
}
assert(count <= 2);
io_uring_cq_advance(ring, count);
if (done)
goto err;
}
out:
close(p_fd[0]);
close(p_fd[1]);
return 0;
err:
close(p_fd[0]);
close(p_fd[1]);
return 1;
}
static int test_accept(void)
{
struct io_uring m_io_uring;
int ret;
assert(io_uring_queue_init(32, &m_io_uring, 0) >= 0);
ret = test(&m_io_uring, 0);
io_uring_queue_exit(&m_io_uring);
return ret;
}
static int test_accept_sqpoll(void)
{
struct io_uring m_io_uring;
int ret;
assert(io_uring_queue_init(32, &m_io_uring, IORING_SETUP_SQPOLL) >= 0);
ret = test(&m_io_uring, 1);
io_uring_queue_exit(&m_io_uring);
return ret;
}
int main(int argc, char *argv[])
{
int ret;
ret = test_accept();
if (ret) {
fprintf(stderr, "test_accept failed\n");
return ret;
}
if (no_accept)
return 0;
ret = test_accept_sqpoll();
if (ret) {
fprintf(stderr, "test_accept_sqpoll failed\n");
return ret;
}
return 0;
}