Blame test/iopoll.c

Packit d3489f
/* SPDX-License-Identifier: MIT */
Packit d3489f
/*
Packit d3489f
 * Description: basic read/write tests with polled IO
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 <fcntl.h>
Packit d3489f
#include <sys/types.h>
Packit d3489f
#include <sys/poll.h>
Packit d3489f
#include <sys/eventfd.h>
Packit d3489f
#include <sys/resource.h>
Packit d3489f
#include "liburing.h"
Packit d3489f
Packit d3489f
#define FILE_SIZE	(128 * 1024)
Packit d3489f
#define BS		4096
Packit d3489f
#define BUFFERS		(FILE_SIZE / BS)
Packit d3489f
Packit d3489f
static struct iovec *vecs;
Packit d3489f
static int no_buf_select;
Packit d3489f
static int no_iopoll;
Packit d3489f
Packit d3489f
static int create_buffers(void)
Packit d3489f
{
Packit d3489f
	int i;
Packit d3489f
Packit d3489f
	vecs = malloc(BUFFERS * sizeof(struct iovec));
Packit d3489f
	for (i = 0; i < BUFFERS; i++) {
Packit d3489f
		if (posix_memalign(&vecs[i].iov_base, BS, BS))
Packit d3489f
			return 1;
Packit d3489f
		vecs[i].iov_len = BS;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	return 0;
Packit d3489f
}
Packit d3489f
Packit d3489f
static int create_file(const char *file)
Packit d3489f
{
Packit d3489f
	ssize_t ret;
Packit d3489f
	char *buf;
Packit d3489f
	int fd;
Packit d3489f
Packit d3489f
	buf = malloc(FILE_SIZE);
Packit d3489f
	memset(buf, 0xaa, FILE_SIZE);
Packit d3489f
Packit d3489f
	fd = open(file, O_WRONLY | O_CREAT, 0644);
Packit d3489f
	if (fd < 0) {
Packit d3489f
		perror("open file");
Packit d3489f
		return 1;
Packit d3489f
	}
Packit d3489f
	ret = write(fd, buf, FILE_SIZE);
Packit d3489f
	close(fd);
Packit d3489f
	return ret != FILE_SIZE;
Packit d3489f
}
Packit d3489f
Packit d3489f
static int provide_buffers(struct io_uring *ring)
Packit d3489f
{
Packit d3489f
	struct io_uring_sqe *sqe;
Packit d3489f
	struct io_uring_cqe *cqe;
Packit d3489f
	int ret, i;
Packit d3489f
Packit d3489f
	for (i = 0; i < BUFFERS; i++) {
Packit d3489f
		sqe = io_uring_get_sqe(ring);
Packit d3489f
		io_uring_prep_provide_buffers(sqe, vecs[i].iov_base,
Packit d3489f
						vecs[i].iov_len, 1, 1, i);
Packit d3489f
	}
Packit d3489f
Packit d3489f
	ret = io_uring_submit(ring);
Packit d3489f
	if (ret != BUFFERS) {
Packit d3489f
		fprintf(stderr, "submit: %d\n", ret);
Packit d3489f
		return 1;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	for (i = 0; i < BUFFERS; i++) {
Packit d3489f
		ret = io_uring_wait_cqe(ring, &cqe);
Packit d3489f
		if (cqe->res < 0) {
Packit d3489f
			fprintf(stderr, "cqe->res=%d\n", cqe->res);
Packit d3489f
			return 1;
Packit d3489f
		}
Packit d3489f
		io_uring_cqe_seen(ring, cqe);
Packit d3489f
	}
Packit d3489f
Packit d3489f
	return 0;
Packit d3489f
}
Packit d3489f
Packit d3489f
static int __test_io(const char *file, struct io_uring *ring, int write, int sqthread,
Packit d3489f
		     int fixed, int buf_select)
Packit d3489f
{
Packit d3489f
	struct io_uring_sqe *sqe;
Packit d3489f
	struct io_uring_cqe *cqe;
Packit d3489f
	int open_flags;
Packit d3489f
	int i, fd, ret;
Packit d3489f
	off_t offset;
Packit d3489f
Packit d3489f
	if (buf_select && write)
Packit d3489f
		write = 0;
Packit d3489f
	if (buf_select && fixed)
Packit d3489f
		fixed = 0;
Packit d3489f
Packit d3489f
	if (buf_select && provide_buffers(ring))
Packit d3489f
		return 1;
Packit d3489f
Packit d3489f
	if (write)
Packit d3489f
		open_flags = O_WRONLY;
Packit d3489f
	else
Packit d3489f
		open_flags = O_RDONLY;
Packit d3489f
	open_flags |= O_DIRECT;
Packit d3489f
Packit d3489f
	fd = open(file, open_flags);
Packit d3489f
	if (fd < 0) {
Packit d3489f
		perror("file open");
Packit d3489f
		goto err;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	if (fixed) {
Packit d3489f
		ret = io_uring_register_buffers(ring, vecs, BUFFERS);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "buffer reg failed: %d\n", ret);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
	if (sqthread) {
Packit d3489f
		ret = io_uring_register_files(ring, &fd, 1);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "file reg failed: %d\n", ret);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
Packit d3489f
	offset = 0;
Packit d3489f
	for (i = 0; i < BUFFERS; i++) {
Packit d3489f
		sqe = io_uring_get_sqe(ring);
Packit d3489f
		if (!sqe) {
Packit d3489f
			fprintf(stderr, "sqe get failed\n");
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
		offset = BS * (rand() % BUFFERS);
Packit d3489f
		if (write) {
Packit d3489f
			int do_fixed = fixed;
Packit d3489f
			int use_fd = fd;
Packit d3489f
Packit d3489f
			if (sqthread)
Packit d3489f
				use_fd = 0;
Packit d3489f
			if (fixed && (i & 1))
Packit d3489f
				do_fixed = 0;
Packit d3489f
			if (do_fixed) {
Packit d3489f
				io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
Packit d3489f
								vecs[i].iov_len,
Packit d3489f
								offset, i);
Packit d3489f
			} else {
Packit d3489f
				io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
Packit d3489f
								offset);
Packit d3489f
			}
Packit d3489f
		} else {
Packit d3489f
			int do_fixed = fixed;
Packit d3489f
			int use_fd = fd;
Packit d3489f
Packit d3489f
			if (sqthread)
Packit d3489f
				use_fd = 0;
Packit d3489f
			if (fixed && (i & 1))
Packit d3489f
				do_fixed = 0;
Packit d3489f
			if (do_fixed) {
Packit d3489f
				io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
Packit d3489f
								vecs[i].iov_len,
Packit d3489f
								offset, i);
Packit d3489f
			} else {
Packit d3489f
				io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
Packit d3489f
								offset);
Packit d3489f
			}
Packit d3489f
Packit d3489f
		}
Packit d3489f
		if (sqthread)
Packit d3489f
			sqe->flags |= IOSQE_FIXED_FILE;
Packit d3489f
		if (buf_select) {
Packit d3489f
			sqe->flags |= IOSQE_BUFFER_SELECT;
Packit d3489f
			sqe->buf_group = buf_select;
Packit d3489f
			sqe->user_data = i;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
Packit d3489f
	ret = io_uring_submit(ring);
Packit d3489f
	if (ret != BUFFERS) {
Packit d3489f
		fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
Packit d3489f
		goto err;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	for (i = 0; i < BUFFERS; i++) {
Packit d3489f
		ret = io_uring_wait_cqe(ring, &cqe);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "wait_cqe=%d\n", ret);
Packit d3489f
			goto err;
Packit d3489f
		} else if (cqe->res == -EOPNOTSUPP) {
Packit d3489f
			fprintf(stdout, "File/device/fs doesn't support polled IO\n");
Packit d3489f
			no_iopoll = 1;
Packit d3489f
			break;
Packit d3489f
		} else if (cqe->res != BS) {
Packit d3489f
			fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, BS);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
		io_uring_cqe_seen(ring, cqe);
Packit d3489f
	}
Packit d3489f
Packit d3489f
	if (fixed) {
Packit d3489f
		ret = io_uring_unregister_buffers(ring);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "buffer unreg failed: %d\n", ret);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
	if (sqthread) {
Packit d3489f
		ret = io_uring_unregister_files(ring);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "file unreg failed: %d\n", ret);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
Packit d3489f
	close(fd);
Packit d3489f
#ifdef VERBOSE
Packit d3489f
	fprintf(stdout, "PASS\n");
Packit d3489f
#endif
Packit d3489f
	return 0;
Packit d3489f
err:
Packit d3489f
#ifdef VERBOSE
Packit d3489f
	fprintf(stderr, "FAILED\n");
Packit d3489f
#endif
Packit d3489f
	if (fd != -1)
Packit d3489f
		close(fd);
Packit d3489f
	return 1;
Packit d3489f
}
Packit d3489f
Packit d3489f
static int test_io(const char *file, int write, int sqthread, int fixed,
Packit d3489f
		   int buf_select)
Packit d3489f
{
Packit d3489f
	struct io_uring ring;
Packit d3489f
	int ret, ring_flags;
Packit d3489f
Packit d3489f
	ring_flags = IORING_SETUP_IOPOLL;
Packit d3489f
	if (sqthread)
Packit d3489f
		ring_flags |= IORING_SETUP_SQPOLL;
Packit d3489f
Packit d3489f
	ret = io_uring_queue_init(64, &ring, ring_flags);
Packit d3489f
	if (ret) {
Packit d3489f
		fprintf(stderr, "ring create failed: %d\n", ret);
Packit d3489f
		return 1;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	ret = __test_io(file, &ring, write, sqthread, fixed, buf_select);
Packit d3489f
Packit d3489f
	io_uring_queue_exit(&ring);
Packit d3489f
	return ret;
Packit d3489f
}
Packit d3489f
Packit d3489f
static int probe_buf_select(void)
Packit d3489f
{
Packit d3489f
	struct io_uring_probe *p;
Packit d3489f
	struct io_uring ring;
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 create failed: %d\n", ret);
Packit d3489f
		return 1;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	p = io_uring_get_probe_ring(&ring);
Packit d3489f
	if (!p || !io_uring_opcode_supported(p, IORING_OP_PROVIDE_BUFFERS)) {
Packit d3489f
		no_buf_select = 1;
Packit d3489f
		fprintf(stdout, "Buffer select not supported, skipping\n");
Packit d3489f
		return 0;
Packit d3489f
	}
Packit d3489f
	free(p);
Packit d3489f
	return 0;
Packit d3489f
}
Packit d3489f
Packit d3489f
int main(int argc, char *argv[])
Packit d3489f
{
Packit d3489f
	int i, ret, nr;
Packit d3489f
	char *fname;
Packit d3489f
Packit d3489f
	if (geteuid()) {
Packit d3489f
		fprintf(stdout, "iopoll requires root, skipping\n");
Packit d3489f
		return 0;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	if (probe_buf_select())
Packit d3489f
		return 1;
Packit d3489f
Packit d3489f
	if (argc > 1) {
Packit d3489f
		fname = argv[1];
Packit d3489f
	} else {
Packit d3489f
		fname = ".iopoll-rw";
Packit d3489f
		if (create_file(".iopoll-rw")) {
Packit d3489f
			fprintf(stderr, "file creation failed\n");
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
	}
Packit d3489f
Packit d3489f
	if (create_buffers()) {
Packit d3489f
		fprintf(stderr, "file creation failed\n");
Packit d3489f
		goto err;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	nr = 16;
Packit d3489f
	if (no_buf_select)
Packit d3489f
		nr = 8;
Packit d3489f
	for (i = 0; i < nr; i++) {
Packit d3489f
		int v1, v2, v3, v4;
Packit d3489f
Packit d3489f
		v1 = (i & 1) != 0;
Packit d3489f
		v2 = (i & 2) != 0;
Packit d3489f
		v3 = (i & 4) != 0;
Packit d3489f
		v4 = (i & 8) != 0;
Packit d3489f
		ret = test_io(fname, v1, v2, v3, v4);
Packit d3489f
		if (ret) {
Packit d3489f
			fprintf(stderr, "test_io failed %d/%d/%d/%d\n", v1, v2, v3, v4);
Packit d3489f
			goto err;
Packit d3489f
		}
Packit d3489f
		if (no_iopoll)
Packit d3489f
			break;
Packit d3489f
	}
Packit d3489f
Packit d3489f
	if (fname != argv[1])
Packit d3489f
		unlink(fname);
Packit d3489f
	return 0;
Packit d3489f
err:
Packit d3489f
	if (fname != argv[1])
Packit d3489f
		unlink(fname);
Packit d3489f
	return 1;
Packit d3489f
}