blob: edbfa808d76b154588abc5c41d204419dee4d43d [file] [log] [blame]
/*
* Description: Basic IO cancel test
*/
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/time.h>
#include "liburing.h"
#define FILE_SIZE (128 * 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 unsigned long long utime_since(const struct timeval *s,
const struct timeval *e)
{
long long sec, usec;
sec = e->tv_sec - s->tv_sec;
usec = (e->tv_usec - s->tv_usec);
if (sec > 0 && usec < 0) {
sec--;
usec += 1000000;
}
sec *= 1000000;
return sec + usec;
}
static unsigned long long utime_since_now(struct timeval *tv)
{
struct timeval end;
gettimeofday(&end, NULL);
return utime_since(tv, &end);
}
static int start_io(struct io_uring *ring, int fd, int do_write)
{
struct io_uring_sqe *sqe;
int i, ret;
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);
if (do_write) {
io_uring_prep_writev(sqe, fd, &vecs[i], 1, offset);
} else {
io_uring_prep_readv(sqe, fd, &vecs[i], 1, offset);
}
sqe->user_data = i + 1;
}
ret = io_uring_submit(ring);
if (ret != BUFFERS) {
fprintf(stderr, "submit got %d, wanted %d\n", ret, BUFFERS);
goto err;
}
return 0;
err:
return 1;
}
static int wait_io(struct io_uring *ring, unsigned nr_io, int do_partial)
{
struct io_uring_cqe *cqe;
int i, ret;
for (i = 0; i < nr_io; i++) {
ret = io_uring_wait_cqe(ring, &cqe);
if (ret) {
fprintf(stderr, "wait_cqe=%d\n", ret);
goto err;
}
if (do_partial && cqe->user_data) {
if (!cqe->user_data & 1) {
if (cqe->res != BS) {
fprintf(stderr, "IO %d wasn't cancelled but got error %d\n", (unsigned) cqe->user_data, cqe->res);
goto err;
}
}
}
io_uring_cqe_seen(ring, cqe);
}
return 0;
err:
return 1;
}
static int do_io(struct io_uring *ring, int fd, int do_write)
{
if (start_io(ring, fd, do_write))
return 1;
if (wait_io(ring, BUFFERS, 0))
return 1;
return 0;
}
static int start_cancel(struct io_uring *ring, int do_partial)
{
struct io_uring_sqe *sqe;
int i, ret, submitted = 0;
for (i = 0; i < BUFFERS; i++) {
if (do_partial && (i & 1))
continue;
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "sqe get failed\n");
goto err;
}
io_uring_prep_cancel(sqe, (void *) (unsigned long) i + 1, 0);
sqe->user_data = 0;
submitted++;
}
ret = io_uring_submit(ring);
if (ret != submitted) {
fprintf(stderr, "submit got %d, wanted %d\n", ret, submitted);
goto err;
}
return 0;
err:
return 1;
}
/*
* Test cancels. If 'do_partial' is set, then we only attempt to cancel half of
* the submitted IO. This is done to verify that cancelling one piece of IO doesn't
* impact others.
*/
static int test_io_cancel(const char *file, int do_write, int do_partial)
{
struct io_uring ring;
struct timeval start_tv;
unsigned long usecs;
unsigned to_wait;
int fd, ret;
fd = open(file, O_RDONLY | O_DIRECT);
if (fd < 0) {
perror("file open");
goto err;
}
ret = io_uring_queue_init(4 * BUFFERS, &ring, 0);
if (ret) {
fprintf(stderr, "ring create failed: %d\n", ret);
goto err;
}
if (do_io(&ring, fd, do_write))
goto err;
gettimeofday(&start_tv, NULL);
if (do_io(&ring, fd, do_write))
goto err;
usecs = utime_since_now(&start_tv);
if (start_io(&ring, fd, do_write))
goto err;
/* sleep for 1/3 of the total time, to allow some to start/complete */
usleep(usecs / 3);
if (start_cancel(&ring, do_partial))
goto err;
to_wait = BUFFERS;
if (do_partial)
to_wait += BUFFERS / 2;
else
to_wait += BUFFERS;
if (wait_io(&ring, to_wait, do_partial))
goto err;
io_uring_queue_exit(&ring);
close(fd);
return 0;
err:
if (fd != -1)
close(fd);
return 1;
}
int main(int argc, char *argv[])
{
int i, 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;
}
for (i = 0; i < 4; i++) {
int v1 = (i & 1) != 0;
int v2 = (i & 2) != 0;
ret = test_io_cancel(".basic-rw", v1, v2);
if (ret) {
fprintf(stderr, "test_io_cancel %d %d failed\n", v1, v2);
goto err;
}
}
unlink(".basic-rw");
return 0;
err:
unlink(".basic-rw");
return 1;
}