static int test_io(const char *file, int write, int buffered, int sqthread,
int fixed, int mixed_fixed, int nonvec)
{
struct io_uring ring;
int ret, ring_flags;
if (sqthread) {
if (geteuid()) {
if (!warned) {
fprintf(stderr, "SQPOLL requires root, skipping\n");
warned = 1;
}
return 0;
}
ring_flags = IORING_SETUP_SQPOLL;
} else {
ring_flags = 0;
}
ret = io_uring_queue_init(64, &ring, ring_flags);
if (ret) {
fprintf(stderr, "ring create failed: %d\n", ret);
return 1;
}
ret = __test_io(file, &ring, write, buffered, sqthread, fixed,
mixed_fixed, nonvec, 0, 0, BS);
io_uring_queue_exit(&ring);
return ret;
}
static int __test_io(const char *file, struct io_uring *ring, int write, int buffered,
int sqthread, int fixed, int mixed_fixed, int nonvec,
int buf_select, int seq, int exp_len)
{
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int open_flags;
int i, fd, ret;
off_t offset;
#ifdef VERBOSE
fprintf(stdout, "%s: start %d/%d/%d/%d/%d/%d: ", __FUNCTION__, write,
buffered, sqthread,
fixed, mixed_fixed,
nonvec);
#endif
if (sqthread && geteuid()) {
#ifdef VERBOSE
fprintf(stdout, "SKIPPED (not root)\n");
#endif
return 0;
}
if (write)
open_flags = O_WRONLY;
else
open_flags = O_RDONLY;
if (!buffered)
open_flags |= O_DIRECT; //!无缓冲直接操作IO
fd = open(file, open_flags);
if (fd < 0) {
perror("file open");
goto err;
}
if (fixed) {
ret = io_uring_register_buffers(ring, vecs, BUFFERS);
if (ret) {
fprintf(stderr, "buffer reg failed: %d\n", ret);
goto err;
}
}
if (sqthread) {
ret = io_uring_register_files(ring, &fd, 1);
if (ret) {
fprintf(stderr, "file reg failed: %d\n", ret);
goto err;
}
}
offset = 0;
for (i = 0; i < BUFFERS; i++) {
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "sqe get failed\n");
goto err;
}
if (!seq)
offset = BS * (rand() % BUFFERS);
if (write) {
int do_fixed = fixed;
int use_fd = fd;
if (sqthread)
use_fd = 0;
if (fixed && (i & 1))
do_fixed = 0;
if (do_fixed) {
io_uring_prep_write_fixed(sqe, use_fd, vecs[i].iov_base,
vecs[i].iov_len,
offset, i);
} else if (nonvec) {
io_uring_prep_write(sqe, use_fd, vecs[i].iov_base,
vecs[i].iov_len, offset);
} else {
io_uring_prep_writev(sqe, use_fd, &vecs[i], 1,
offset);
}
} else {
int do_fixed = fixed;
int use_fd = fd;
if (sqthread)
use_fd = 0;
if (fixed && (i & 1))
do_fixed = 0;
if (do_fixed) {
io_uring_prep_read_fixed(sqe, use_fd, vecs[i].iov_base,
vecs[i].iov_len,
offset, i);
} else if (nonvec) {
io_uring_prep_read(sqe, use_fd, vecs[i].iov_base,
vecs[i].iov_len, offset);
} else {
io_uring_prep_readv(sqe, use_fd, &vecs[i], 1,
offset);
}
}
if (sqthread)
sqe->flags |= IOSQE_FIXED_FILE;
if (buf_select) {
if (nonvec)
sqe->addr = 0;
sqe->flags |= IOSQE_BUFFER_SELECT;
sqe->buf_group = buf_select;
sqe->user_data = i;
}
if (seq)
offset += BS;
}
ret = io_uring_submit(ring);
if (ret != BUFFERS) {
fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
goto err;
}
for (i = 0; i < BUFFERS; i++) {
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stderr, "wait_cqe=%d\n", ret);
goto err;
}
if (cqe->res == -EINVAL && nonvec) {
if (!warned) {
fprintf(stdout, "Non-vectored IO not "
"supported, skipping\n");
warned = 1;
no_read = 1;
}
} else if (cqe->res != exp_len) {
fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, exp_len);
goto err;
}
if (buf_select && exp_len == BS) {
int bid = cqe->flags >> 16;
unsigned char *ptr = vecs[bid].iov_base;
int j;
for (j = 0; j < BS; j++) {
if (ptr[j] == cqe->user_data)
continue;
fprintf(stderr, "Data mismatch! bid=%d, "
"wanted=%d, got=%d\n", bid,
(int)cqe->user_data, ptr[j]);
return 1;
}
}
io_uring_cqe_seen(ring, cqe);
}
if (fixed) {
ret = io_uring_unregister_buffers(ring);
if (ret) {
fprintf(stderr, "buffer unreg failed: %d\n", ret);
goto err;
}
}
if (sqthread) {
ret = io_uring_unregister_files(ring);
if (ret) {
fprintf(stderr, "file unreg failed: %d\n", ret);
goto err;
}
}
close(fd);
#ifdef VERBOSE
fprintf(stdout, "PASS\n");
#endif
return 0;
err:
#ifdef VERBOSE
fprintf(stderr, "FAILED\n");
#endif
if (fd != -1)
close(fd);
return 1;
}