|
Packit |
d3489f |
/* SPDX-License-Identifier: MIT */
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* Description: basic read/write tests with buffered, O_DIRECT, and SQPOLL
|
|
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_read;
|
|
Packit |
d3489f |
static int no_buf_select;
|
|
Packit |
d3489f |
static int warned;
|
|
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 __test_io(const char *file, struct io_uring *ring, int write, int buffered,
|
|
Packit |
d3489f |
int sqthread, int fixed, int mixed_fixed, int nonvec,
|
|
Packit |
d3489f |
int buf_select, int seq, int exp_len)
|
|
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 |
#ifdef VERBOSE
|
|
Packit |
d3489f |
fprintf(stdout, "%s: start %d/%d/%d/%d/%d/%d: ", __FUNCTION__, write,
|
|
Packit |
d3489f |
buffered, sqthread,
|
|
Packit |
d3489f |
fixed, mixed_fixed,
|
|
Packit |
d3489f |
nonvec);
|
|
Packit |
d3489f |
#endif
|
|
Packit |
d3489f |
if (sqthread && geteuid()) {
|
|
Packit |
d3489f |
#ifdef VERBOSE
|
|
Packit |
d3489f |
fprintf(stdout, "SKIPPED (not root)\n");
|
|
Packit |
d3489f |
#endif
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (write)
|
|
Packit |
d3489f |
open_flags = O_WRONLY;
|
|
Packit |
d3489f |
else
|
|
Packit |
d3489f |
open_flags = O_RDONLY;
|
|
Packit |
d3489f |
if (!buffered)
|
|
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 |
if (!seq)
|
|
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 if (nonvec) {
|
|
Packit |
d3489f |
io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
|
|
Packit |
d3489f |
vecs[i].iov_len, offset);
|
|
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 if (nonvec) {
|
|
Packit |
d3489f |
io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
|
|
Packit |
d3489f |
vecs[i].iov_len, offset);
|
|
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 |
if (nonvec)
|
|
Packit |
d3489f |
sqe->addr = 0;
|
|
Packit |
d3489f |
sqe->flags |= IOSQE_BUFFER_SELECT;
|
|
Packit |
d3489f |
sqe->buf_group = buf_select;
|
|
Packit |
d3489f |
sqe->user_data = i;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (seq)
|
|
Packit |
d3489f |
offset += BS;
|
|
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 |
}
|
|
Packit |
d3489f |
if (cqe->res == -EINVAL && nonvec) {
|
|
Packit |
d3489f |
if (!warned) {
|
|
Packit |
d3489f |
fprintf(stdout, "Non-vectored IO not "
|
|
Packit |
d3489f |
"supported, skipping\n");
|
|
Packit |
d3489f |
warned = 1;
|
|
Packit |
d3489f |
no_read = 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
} else if (cqe->res != exp_len) {
|
|
Packit |
d3489f |
fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, exp_len);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
if (buf_select && exp_len == BS) {
|
|
Packit |
d3489f |
int bid = cqe->flags >> 16;
|
|
Packit |
d3489f |
unsigned char *ptr = vecs[bid].iov_base;
|
|
Packit |
d3489f |
int j;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (j = 0; j < BS; j++) {
|
|
Packit |
d3489f |
if (ptr[j] == cqe->user_data)
|
|
Packit |
d3489f |
continue;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fprintf(stderr, "Data mismatch! bid=%d, "
|
|
Packit |
d3489f |
"wanted=%d, got=%d\n", bid,
|
|
Packit |
d3489f |
(int)cqe->user_data, ptr[j]);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
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 |
static int test_io(const char *file, int write, int buffered, int sqthread,
|
|
Packit |
d3489f |
int fixed, int mixed_fixed, int nonvec)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int ret, ring_flags;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (sqthread) {
|
|
Packit |
d3489f |
if (geteuid()) {
|
|
Packit |
d3489f |
if (!warned) {
|
|
Packit |
d3489f |
fprintf(stderr, "SQPOLL requires root, skipping\n");
|
|
Packit |
d3489f |
warned = 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
ring_flags = IORING_SETUP_SQPOLL;
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
ring_flags = 0;
|
|
Packit |
d3489f |
}
|
|
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, buffered, sqthread, fixed,
|
|
Packit |
d3489f |
mixed_fixed, nonvec, 0, 0, BS);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int read_poll_link(const char *file)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct __kernel_timespec ts;
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int i, fd, ret, fds[2];
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(8, &ring, 0);
|
|
Packit |
d3489f |
if (ret)
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = open(file, O_WRONLY);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("open");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (pipe(fds)) {
|
|
Packit |
d3489f |
perror("pipe");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(&ring);
|
|
Packit |
d3489f |
io_uring_prep_writev(sqe, fd, &vecs[0], 1, 0);
|
|
Packit |
d3489f |
sqe->flags |= IOSQE_IO_LINK;
|
|
Packit |
d3489f |
sqe->user_data = 1;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(&ring);
|
|
Packit |
d3489f |
io_uring_prep_poll_add(sqe, fds[0], POLLIN);
|
|
Packit |
d3489f |
sqe->flags |= IOSQE_IO_LINK;
|
|
Packit |
d3489f |
sqe->user_data = 2;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ts.tv_sec = 1;
|
|
Packit |
d3489f |
ts.tv_nsec = 0;
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(&ring);
|
|
Packit |
d3489f |
io_uring_prep_link_timeout(sqe, &ts, 0);
|
|
Packit |
d3489f |
sqe->user_data = 3;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(&ring);
|
|
Packit |
d3489f |
if (ret != 3) {
|
|
Packit |
d3489f |
fprintf(stderr, "submitted %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < 3; 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 |
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 has_nonvec_read(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, "queue init failed: %d\n", ret);
|
|
Packit |
d3489f |
exit(ret);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
p = calloc(1, sizeof(*p) + 256 * sizeof(struct io_uring_probe_op));
|
|
Packit |
d3489f |
ret = io_uring_register_probe(&ring, p, 256);
|
|
Packit |
d3489f |
/* if we don't have PROBE_REGISTER, we don't have OP_READ/WRITE */
|
|
Packit |
d3489f |
if (ret == -EINVAL) {
|
|
Packit |
d3489f |
out:
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
} else if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "register_probe: %d\n", ret);
|
|
Packit |
d3489f |
goto out;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (p->ops_len <= IORING_OP_READ)
|
|
Packit |
d3489f |
goto out;
|
|
Packit |
d3489f |
if (!(p->ops[IORING_OP_READ].flags & IO_URING_OP_SUPPORTED))
|
|
Packit |
d3489f |
goto out;
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_eventfd_read(void)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int fd, ret;
|
|
Packit |
d3489f |
eventfd_t event;
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (no_read)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
ret = io_uring_queue_init(8, &ring, 0);
|
|
Packit |
d3489f |
if (ret)
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = eventfd(1, 0);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("eventfd");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
sqe = io_uring_get_sqe(&ring);
|
|
Packit |
d3489f |
io_uring_prep_read(sqe, fd, &event, sizeof(eventfd_t), 0);
|
|
Packit |
d3489f |
ret = io_uring_submit(&ring);
|
|
Packit |
d3489f |
if (ret != 1) {
|
|
Packit |
d3489f |
fprintf(stderr, "submitted %d\n", ret);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
eventfd_write(fd, 1);
|
|
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 |
if (cqe->res == -EINVAL) {
|
|
Packit |
d3489f |
fprintf(stdout, "eventfd IO not supported, skipping\n");
|
|
Packit |
d3489f |
} else if (cqe->res != sizeof(eventfd_t)) {
|
|
Packit |
d3489f |
fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res,
|
|
Packit |
d3489f |
(int) sizeof(eventfd_t));
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
io_uring_cqe_seen(&ring, cqe);
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_buf_select_short(const char *filename, int nonvec)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int ret, i, exp_len;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (no_buf_select)
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(64, &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 |
exp_len = 0;
|
|
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 / 2, 1, 1, i);
|
|
Packit |
d3489f |
if (!exp_len)
|
|
Packit |
d3489f |
exp_len = vecs[i].iov_len / 2;
|
|
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 |
ret = __test_io(filename, &ring, 0, 0, 0, 0, 0, nonvec, 1, 1, exp_len);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_buf_select(const char *filename, int nonvec)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct io_uring_probe *p;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int ret, i;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(64, &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 |
|
|
Packit |
d3489f |
/*
|
|
Packit |
d3489f |
* Write out data with known pattern
|
|
Packit |
d3489f |
*/
|
|
Packit |
d3489f |
for (i = 0; i < BUFFERS; i++)
|
|
Packit |
d3489f |
memset(vecs[i].iov_base, i, vecs[i].iov_len);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = __test_io(filename, &ring, 1, 0, 0, 0, 0, 0, 0, 1, BS);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "failed writing data\n");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < BUFFERS; i++)
|
|
Packit |
d3489f |
memset(vecs[i].iov_base, 0x55, vecs[i].iov_len);
|
|
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 |
ret = __test_io(filename, &ring, 0, 0, 0, 0, 0, nonvec, 1, 1, BS);
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
return ret;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_io_link(const char *file)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
const int nr_links = 100;
|
|
Packit |
d3489f |
const int link_len = 100;
|
|
Packit |
d3489f |
const int nr_sqes = nr_links * link_len;
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
int i, j, fd, ret;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = open(file, O_WRONLY);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("file open");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(nr_sqes, &ring, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "ring create failed: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr_links; ++i) {
|
|
Packit |
d3489f |
for (j = 0; j < link_len; ++j) {
|
|
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 |
io_uring_prep_writev(sqe, fd, &vecs[0], 1, 0);
|
|
Packit |
d3489f |
sqe->flags |= IOSQE_ASYNC;
|
|
Packit |
d3489f |
if (j != link_len - 1)
|
|
Packit |
d3489f |
sqe->flags |= IOSQE_IO_LINK;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(&ring);
|
|
Packit |
d3489f |
if (ret != nr_sqes) {
|
|
Packit |
d3489f |
ret = io_uring_peek_cqe(&ring, &cqe);
|
|
Packit |
d3489f |
if (!ret && cqe->res == -EINVAL) {
|
|
Packit |
d3489f |
fprintf(stdout, "IOSQE_ASYNC not supported, skipped\n");
|
|
Packit |
d3489f |
goto out;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
fprintf(stderr, "submit got %d, wanted %d\n", ret, nr_sqes);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr_sqes; 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 |
}
|
|
Packit |
d3489f |
if (cqe->res == -EINVAL) {
|
|
Packit |
d3489f |
if (!warned) {
|
|
Packit |
d3489f |
fprintf(stdout, "Non-vectored IO not "
|
|
Packit |
d3489f |
"supported, skipping\n");
|
|
Packit |
d3489f |
warned = 1;
|
|
Packit |
d3489f |
no_read = 1;
|
|
Packit |
d3489f |
}
|
|
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 |
out:
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
if (fd != -1)
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
static int test_write_efbig(void)
|
|
Packit |
d3489f |
{
|
|
Packit |
d3489f |
struct io_uring_sqe *sqe;
|
|
Packit |
d3489f |
struct io_uring_cqe *cqe;
|
|
Packit |
d3489f |
struct io_uring ring;
|
|
Packit |
d3489f |
struct rlimit rlim;
|
|
Packit |
d3489f |
int i, fd, ret;
|
|
Packit |
d3489f |
loff_t off;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
if (getrlimit(RLIMIT_FSIZE, &rlim) < 0) {
|
|
Packit |
d3489f |
perror("getrlimit");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
rlim.rlim_cur = 64 * 1024;
|
|
Packit |
d3489f |
rlim.rlim_max = 64 * 1024;
|
|
Packit |
d3489f |
if (setrlimit(RLIMIT_FSIZE, &rlim) < 0) {
|
|
Packit |
d3489f |
perror("setrlimit");
|
|
Packit |
d3489f |
return 1;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
fd = open(".efbig", O_WRONLY | O_CREAT, 0644);
|
|
Packit |
d3489f |
if (fd < 0) {
|
|
Packit |
d3489f |
perror("file open");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_queue_init(32, &ring, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "ring create failed: %d\n", ret);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
off = 0;
|
|
Packit |
d3489f |
for (i = 0; i < 32; 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 |
io_uring_prep_writev(sqe, fd, &vecs[i], 1, off);
|
|
Packit |
d3489f |
off += BS;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = io_uring_submit(&ring);
|
|
Packit |
d3489f |
if (ret != 32) {
|
|
Packit |
d3489f |
fprintf(stderr, "submit got %d, wanted %d\n", ret, 32);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < 32; 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 |
}
|
|
Packit |
d3489f |
if (i < 16) {
|
|
Packit |
d3489f |
if (cqe->res != BS) {
|
|
Packit |
d3489f |
fprintf(stderr, "bad write: %d\n", cqe->res);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
if (cqe->res != -EFBIG) {
|
|
Packit |
d3489f |
fprintf(stderr, "Expected -EFBIG: %d\n", cqe->res);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
io_uring_cqe_seen(&ring, cqe);
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
io_uring_queue_exit(&ring);
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
unlink(".efbig");
|
|
Packit |
d3489f |
return 0;
|
|
Packit |
d3489f |
err:
|
|
Packit |
d3489f |
if (fd != -1)
|
|
Packit |
d3489f |
close(fd);
|
|
Packit |
d3489f |
unlink(".efbig");
|
|
Packit |
d3489f |
return 1;
|
|
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 (argc > 1) {
|
|
Packit |
d3489f |
fname = argv[1];
|
|
Packit |
d3489f |
} else {
|
|
Packit |
d3489f |
fname = ".basic-rw";
|
|
Packit |
d3489f |
if (create_file(fname)) {
|
|
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 |
/* if we don't have nonvec read, skip testing that */
|
|
Packit |
d3489f |
if (has_nonvec_read())
|
|
Packit |
d3489f |
nr = 64;
|
|
Packit |
d3489f |
else
|
|
Packit |
d3489f |
nr = 32;
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
for (i = 0; i < nr; i++) {
|
|
Packit |
d3489f |
int v1, v2, v3, v4, v5, v6;
|
|
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 |
v5 = (i & 16) != 0;
|
|
Packit |
d3489f |
v6 = (i & 32) != 0;
|
|
Packit |
d3489f |
ret = test_io(fname, v1, v2, v3, v4, v5, v6);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_io failed %d/%d/%d/%d/%d/%d\n",
|
|
Packit |
d3489f |
v1, v2, v3, v4, v5, v6);
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_buf_select(fname, 1);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_buf_select nonvec failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_buf_select(fname, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_buf_select vec failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_buf_select_short(fname, 1);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_buf_select_short nonvec failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_buf_select_short(fname, 0);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_buf_select_short vec failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_eventfd_read();
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_eventfd_read failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = read_poll_link(fname);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "read_poll_link failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_io_link(fname);
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_io_link failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
Packit |
d3489f |
}
|
|
Packit |
d3489f |
|
|
Packit |
d3489f |
ret = test_write_efbig();
|
|
Packit |
d3489f |
if (ret) {
|
|
Packit |
d3489f |
fprintf(stderr, "test_write_efbig failed\n");
|
|
Packit |
d3489f |
goto err;
|
|
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 |
}
|