|
Packit |
d3489f |
/* SPDX-License-Identifier: MIT */
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* Description: test io_uring poll handling
|
|
Packit |
d3489f |
*
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
#include <errno.h>
|
|
Packit |
d3489f |
#include <stdio.h>
|
|
Packit |
d3489f |
#include <unistd.h>
|
|
Packit |
d3489f |
#include <stdlib.h>
|
|
Packit |
d3489f |
#include <string.h>
|
|
Packit |
d3489f |
#include <signal.h>
|
|
Packit |
d3489f |
#include <fcntl.h>
|
|
Packit |
d3489f |
#include <sys/poll.h>
|
|
Packit |
d3489f |
#include <sys/wait.h>
|
|
Packit |
d3489f |
#include <sys/select.h>
|
|
Packit |
d3489f |
#include <pthread.h>
|
|
Packit |
d3489f |
#include <sys/epoll.h>
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
#include "liburing.h"
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
struct thread_data {
|
|
Packit |
d3489f |
struct io_uring *ring;
|
|
Packit |
d3489f |
int fd;
|
|
Packit |
d3489f |
int events;
|
|
Packit |
d3489f |
const char *test;
|
|
Packit |
d3489f |
int out[2];
|
|
Packit |
d3489f |
};
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static void *epoll_wait_fn(void *data)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data *td = data;
|
|
Packit |
d3489f |
struct epoll_event ev;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (epoll_wait(td->fd, &ev, 1, -1) < 0) {
|
|
Packit |
d3489f |
perror("epoll_wait");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
return (void *) 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static void *iou_poll(void *data)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data *td = data;
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(td->ring);
|
|
Packit |
d3489f |
io_uring_prep_poll_add(sqe, td->fd, td->events);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(td->ring);
|
|
Packit |
d3489f |
if (ret != 1) {
|
|
Packit |
d3489f |
fprintf(stderr, "submit got %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_wait_cqe(td->ring, &cqe);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "wait_cqe: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td->out[0] = cqe->res & 0x3f;
|
|
Packit |
d3489f |
io_uring_cqe_seen(td->ring, cqe);
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
return (void *) 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static void *poll_pipe(void *data)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data *td = data;
|
|
Packit |
d3489f |
struct pollfd pfd;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pfd.fd = td->fd;
|
|
Packit |
d3489f |
pfd.events = td->events;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = poll(&pfd, 1, -1);
|
|
Packit |
d3489f |
if (ret < 0)
|
|
Packit |
d3489f |
perror("poll");
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td->out[1] = pfd.revents;
|
|
Packit |
d3489f |
return NULL;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int do_pipe_pollin_test(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data td;
|
|
Packit |
d3489f |
pthread_t threads[2];
|
|
Packit |
d3489f |
int ret, pipe1[2];
|
|
Packit |
d3489f |
char buf;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (pipe(pipe1) < 0) {
|
|
Packit |
d3489f |
perror("pipe");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td.ring = ring;
|
|
Packit |
d3489f |
td.fd = pipe1[0];
|
|
Packit |
d3489f |
td.events = POLLIN;
|
|
Packit |
d3489f |
td.test = __FUNCTION__;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_create(&threads[1], NULL, iou_poll, &td);
|
|
Packit |
d3489f |
pthread_create(&threads[0], NULL, poll_pipe, &td);
|
|
Packit |
d3489f |
usleep(100000);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
buf = 0x89;
|
|
Packit |
d3489f |
ret = write(pipe1[1], &buf, sizeof(buf));
|
|
Packit |
d3489f |
if (ret != sizeof(buf)) {
|
|
Packit |
d3489f |
fprintf(stderr, "write failed: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_join(threads[0], NULL);
|
|
Packit |
d3489f |
pthread_join(threads[1], NULL);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (td.out[0] != td.out[1]) {
|
|
Packit |
d3489f |
fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
|
|
Packit |
d3489f |
td.out[0], td.out[1]);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int do_pipe_pollout_test(struct io_uring *ring)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data td;
|
|
Packit |
d3489f |
pthread_t threads[2];
|
|
Packit |
d3489f |
int ret, pipe1[2];
|
|
Packit |
d3489f |
char buf;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (pipe(pipe1) < 0) {
|
|
Packit |
d3489f |
perror("pipe");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td.ring = ring;
|
|
Packit |
d3489f |
td.fd = pipe1[1];
|
|
Packit |
d3489f |
td.events = POLLOUT;
|
|
Packit |
d3489f |
td.test = __FUNCTION__;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_create(&threads[0], NULL, poll_pipe, &td);
|
|
Packit |
d3489f |
pthread_create(&threads[1], NULL, iou_poll, &td);
|
|
Packit |
d3489f |
usleep(100000);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
buf = 0x89;
|
|
Packit |
d3489f |
ret = write(pipe1[1], &buf, sizeof(buf));
|
|
Packit |
d3489f |
if (ret != sizeof(buf)) {
|
|
Packit |
d3489f |
fprintf(stderr, "write failed: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_join(threads[0], NULL);
|
|
Packit |
d3489f |
pthread_join(threads[1], NULL);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (td.out[0] != td.out[1]) {
|
|
Packit |
d3489f |
fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
|
|
Packit |
d3489f |
td.out[0], td.out[1]);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int do_fd_test(struct io_uring *ring, const char *fname, int events)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct thread_data td;
|
|
Packit |
d3489f |
pthread_t threads[2];
|
|
Packit |
d3489f |
int fd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = open(fname, O_RDONLY);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("open");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td.ring = ring;
|
|
Packit |
d3489f |
td.fd = fd;
|
|
Packit |
d3489f |
td.events = events;
|
|
Packit |
d3489f |
td.test = __FUNCTION__;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_create(&threads[0], NULL, poll_pipe, &td);
|
|
Packit |
d3489f |
pthread_create(&threads[1], NULL, iou_poll, &td);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_join(threads[0], NULL);
|
|
Packit |
d3489f |
pthread_join(threads[1], NULL);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (td.out[0] != td.out[1]) {
|
|
Packit |
d3489f |
fprintf(stderr, "%s: res %x/%x differ\n", __FUNCTION__,
|
|
Packit |
d3489f |
td.out[0], td.out[1]);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int iou_epoll_ctl(struct io_uring *ring, int epfd, int fd,
|
|
Packit |
d3489f |
struct epoll_event *ev)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(ring);
|
|
Packit |
d3489f |
if (!sqe) {
|
|
Packit |
d3489f |
fprintf(stderr, "Failed to get sqe\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_prep_epoll_ctl(sqe, epfd, fd, EPOLL_CTL_ADD, ev);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(ring);
|
|
Packit |
d3489f |
if (ret != 1) {
|
|
Packit |
d3489f |
fprintf(stderr, "submit: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_wait_cqe(ring, &cqe);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "wait_cqe: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = cqe->res;
|
|
Packit |
d3489f |
io_uring_cqe_seen(ring, cqe);
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int do_test_epoll(struct io_uring *ring, int iou_epoll_add)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct epoll_event ev;
|
|
Packit |
d3489f |
struct thread_data td;
|
|
Packit |
d3489f |
pthread_t threads[2];
|
|
Packit |
d3489f |
int ret, pipe1[2];
|
|
Packit |
d3489f |
char buf;
|
|
Packit |
d3489f |
int fd;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = epoll_create1(0);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("epoll_create");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (pipe(pipe1) < 0) {
|
|
Packit |
d3489f |
perror("pipe");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ev.events = EPOLLIN;
|
|
Packit |
d3489f |
ev.data.fd = pipe1[0];
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (!iou_epoll_add) {
|
|
Packit |
d3489f |
if (epoll_ctl(fd, EPOLL_CTL_ADD, pipe1[0], &ev) < 0) {
|
|
Packit |
d3489f |
perror("epoll_ctrl");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
ret = iou_epoll_ctl(ring, fd, pipe1[0], &ev;;
|
|
Packit |
d3489f |
if (ret == -EINVAL) {
|
|
Packit |
d3489f |
fprintf(stdout, "epoll not supported, skipping\n");
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
} else if (ret < 0) {
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
td.ring = ring;
|
|
Packit |
d3489f |
td.fd = fd;
|
|
Packit |
d3489f |
td.events = POLLIN;
|
|
Packit |
d3489f |
td.test = __FUNCTION__;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_create(&threads[0], NULL, iou_poll, &td);
|
|
Packit |
d3489f |
pthread_create(&threads[1], NULL, epoll_wait_fn, &td);
|
|
Packit |
d3489f |
usleep(100000);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
buf = 0x89;
|
|
Packit |
d3489f |
ret = write(pipe1[1], &buf, sizeof(buf));
|
|
Packit |
d3489f |
if (ret != sizeof(buf)) {
|
|
Packit |
d3489f |
fprintf(stderr, "write failed: %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
pthread_join(threads[0], NULL);
|
|
Packit |
d3489f |
pthread_join(threads[1], NULL);
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
int main(int argc, char *argv[])
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
const char *fname;
|
|
Packit |
d3489f |
int ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(1, &ring, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "ring setup failed\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_pipe_pollin_test(&ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "pipe pollin test failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_pipe_pollout_test(&ring);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "pipe pollout test failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_test_epoll(&ring, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "epoll test 0 failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_test_epoll(&ring, 1);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "epoll test 1 failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (argc > 1)
|
|
Packit |
d3489f |
fname = argv[1];
|
|
Packit |
d3489f |
else
|
|
Packit |
d3489f |
fname = argv[0];
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_fd_test(&ring, fname, POLLIN);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "fd test IN failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_fd_test(&ring, fname, POLLOUT);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "fd test OUT failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = do_fd_test(&ring, fname, POLLOUT | POLLIN);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "fd test IN|OUT failed\n");
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
}
|