blob: 73be6c82673dd8ad7a1ca95620d268dadd7a2415 [file] [log] [blame]
/*
* Description: run various CQ ring overflow tests
*
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include "liburing.h"
static int no_nodrop;
#define FILE_SIZE (256 * 1024)
#define BS 4096
#define BUFFERS (FILE_SIZE / BS)
static struct iovec *vecs;
static int create_buffers(void)
{
int i;
vecs = malloc(BUFFERS * sizeof(struct iovec));
for (i = 0; i < BUFFERS; i++) {
if (posix_memalign(&vecs[i].iov_base, BS, BS))
return 1;
vecs[i].iov_len = BS;
}
return 0;
}
static int create_file(const char *file)
{
ssize_t ret;
char *buf;
int fd;
buf = malloc(FILE_SIZE);
memset(buf, 0xaa, FILE_SIZE);
fd = open(file, O_WRONLY | O_CREAT, 0644);
if (fd < 0) {
perror("open file");
return 1;
}
ret = write(fd, buf, FILE_SIZE);
close(fd);
return ret != FILE_SIZE;
}
static int test_io(const char *file, int nodrop, unsigned long usecs,
unsigned *drops)
{
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
struct io_uring ring;
unsigned reaped, total;
int ring_flags;
int i, fd, ret;
if (no_nodrop && nodrop)
return 0;
fd = open(file, O_RDONLY | O_DIRECT);
if (fd < 0) {
perror("file open");
goto err;
}
ring_flags = 0;
if (nodrop)
ring_flags |= IORING_SETUP_CQ_NODROP;
ret = io_uring_queue_init(8, &ring, ring_flags);
if (ret) {
if (nodrop && ret == -EINVAL) {
printf("CQ_NODROP not supported, skipped\n");
goto out;
}
fprintf(stderr, "ring create failed: %d\n", ret);
goto err;
}
total = BUFFERS;
for (i = 0; i < BUFFERS; i++) {
off_t offset;
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "sqe get failed\n");
goto err;
}
offset = BS * (rand() % BUFFERS);
io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
ret = io_uring_submit(&ring);
if (ret != 1) {
fprintf(stderr, "submit got %d, wanted %d\n", ret, 1);
total = i;
break;
}
}
usleep(usecs);
reaped = 0;
do {
if (nodrop) {
/* nodrop should never lose events */
if (reaped == total)
break;
} else {
if (reaped + *ring.cq.koverflow == total)
break;
}
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret) {
fprintf(stderr, "wait_cqe=%d\n", ret);
goto err;
}
if (cqe->res != BS) {
fprintf(stderr, "cqe res %d, wanted %d\n", cqe->res, BS);
goto err;
}
io_uring_cqe_seen(&ring, cqe);
reaped++;
} while (1);
if (!io_uring_peek_cqe(&ring, &cqe)) {
fprintf(stderr, "found unexpected completion\n");
goto err;
}
*drops = *ring.cq.koverflow;
io_uring_queue_exit(&ring);
out:
close(fd);
return 0;
err:
if (fd != -1)
close(fd);
return 1;
}
static int reap_events(struct io_uring *ring, unsigned nr_events, int do_wait)
{
struct io_uring_cqe *cqe;
int i, ret = 0, seq = 0;
for (i = 0; i < nr_events; i++) {
if (do_wait)
ret = io_uring_wait_cqe(ring, &cqe);
else
ret = io_uring_peek_cqe(ring, &cqe);
if (ret) {
if (ret != -EAGAIN)
fprintf(stderr, "cqe peek failed: %d\n", ret);
break;
}
if (cqe->user_data != seq) {
fprintf(stderr, "cqe sequence out-of-order\n");
fprintf(stderr, "got %d, wanted %d\n", (int) cqe->user_data,
seq);
return -EINVAL;
}
seq++;
io_uring_cqe_seen(ring, cqe);
}
return i ? i : ret;
}
/*
* Setup ring with CQ_NODROP and check we get -EBUSY on trying to submit new IO
* on an overflown ring, and that we get all the events (even overflows) when
* we finally reap them.
*/
static int test_overflow_nodrop(void)
{
struct __kernel_timespec ts;
struct io_uring_sqe *sqe;
struct io_uring ring;
unsigned pending;
int ret, i, j;
ret = io_uring_queue_init(4, &ring, IORING_SETUP_CQ_NODROP);
if (ret) {
if (ret == -EINVAL) {
fprintf(stdout, "CQ_NODROP not supported, skipped\n");
no_nodrop = 1;
return 0;
}
fprintf(stderr, "io_uring_queue_init failed %d\n", ret);
return 1;
}
ts.tv_sec = 0;
ts.tv_nsec = 10000000;
/* submit 4x4 SQEs, should overflow the ring by 8 */
pending = 0;
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "get sqe failed\n");
goto err;
}
io_uring_prep_timeout(sqe, &ts, -1U, 0);
sqe->user_data = (i * 4) + j;
}
ret = io_uring_submit(&ring);
if (ret != 4) {
fprintf(stderr, "sqe submit failed: %d, %d\n", ret, pending);
goto err;
}
}
/* wait for timers to fire */
usleep(2 * 10000);
/*
* We should have 16 pending CQEs now, 8 of them in the overflow list. Any
* attempt to queue more IO should return -EBUSY
*/
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "get sqe failed\n");
goto err;
}
io_uring_prep_nop(sqe);
ret = io_uring_submit(&ring);
if (ret != -EBUSY) {
fprintf(stderr, "expected sqe submit busy: %d\n", ret);
goto err;
}
/* reap the 16 events we should have available */
ret = reap_events(&ring, 16, 1);
if (ret < 0) {
fprintf(stderr, "ret=%d\n", ret);
goto err;
}
if (*ring.cq.koverflow) {
fprintf(stderr, "cq ring overflow %d, expected 0\n",
*ring.cq.koverflow);
goto err;
}
io_uring_queue_exit(&ring);
return 0;
err:
io_uring_queue_exit(&ring);
return 1;
}
/*
* Submit some NOPs and watch if the overflow is correct
*/
static int test_overflow(void)
{
struct io_uring ring;
struct io_uring_sqe *sqe;
int ret, i, j;
ret = io_uring_queue_init(4, &ring, 0);
if (ret) {
fprintf(stderr, "io_uring_queue_init failed %d\n", ret);
return 1;
}
/* submit 4x4 SQEs, should overflow the ring by 8 */
for (i = 0; i < 4; i++) {
for (j = 0; j < 4; j++) {
sqe = io_uring_get_sqe(&ring);
if (!sqe) {
fprintf(stderr, "get sqe failed\n");
goto err;
}
io_uring_prep_nop(sqe);
sqe->user_data = (i * 4) + j;
}
ret = io_uring_submit(&ring);
if (ret != 4) {
fprintf(stderr, "sqe submit failed: %d\n", ret);
goto err;
}
}
/* we should now have 8 completions ready */
ret = reap_events(&ring, 8, 0);
if (ret < 0)
goto err;
if (*ring.cq.koverflow != 8) {
fprintf(stderr, "cq ring overflow %d, expected 8\n",
*ring.cq.koverflow);
goto err;
}
io_uring_queue_exit(&ring);
return 0;
err:
io_uring_queue_exit(&ring);
return 1;
}
int main(int argc, char *argv[])
{
unsigned iters, drops;
unsigned long usecs;
int ret;
ret = test_overflow();
if (ret) {
printf("test_overflow failed\n");
return ret;
}
ret = test_overflow_nodrop();
if (ret) {
printf("test_overflow_nodrop failed\n");
return ret;
}
if (create_file(".basic-rw")) {
fprintf(stderr, "file creation failed\n");
goto err;
}
if (create_buffers()) {
fprintf(stderr, "file creation failed\n");
goto err;
}
iters = 0;
usecs = 1000;
do {
drops = 0;
if (test_io(".basic-rw", 0, 1000, &drops)) {
fprintf(stderr, "test_io drop failed\n");
goto err;
}
if ((drops >= BUFFERS / 4) && (drops <= 3 * BUFFERS / 4))
break;
usecs = (usecs * 12) / 10;
iters++;
} while (iters < 40);
if (test_io(".basic-rw", 1, usecs, &drops)) {
fprintf(stderr, "test_io nodrop failed\n");
goto err;
}
unlink(".basic-rw");
return 0;
err:
unlink(".basic-rw");
return 1;
}