summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJens Axboe <axboe@kernel.dk>2019-10-29 08:47:04 -0600
committerJens Axboe <axboe@kernel.dk>2019-10-29 08:49:23 -0600
commit8b68609d67f481c5df7bee990234bf87be382514 (patch)
treeb08d4f0c7f6918b5c3c8f9d65b489def2aebeedb
parentaa3c760ed21abb26777f5c438911de21b2d16ab5 (diff)
downloadliburing-8b68609d67f481c5df7bee990234bf87be382514.tar.gz
liburing-8b68609d67f481c5df7bee990234bf87be382514.tar.bz2
Add test/io-cancel
This tests normal reads and writes mixed with IO cancellations. Signed-off-by: Jens Axboe <axboe@kernel.dk>
-rw-r--r--test/Makefile4
-rw-r--r--test/io-cancel.c259
2 files changed, 261 insertions, 2 deletions
diff --git a/test/Makefile b/test/Makefile
index c60942e..25cf4d9 100644
--- a/test/Makefile
+++ b/test/Makefile
@@ -8,7 +8,7 @@ all_targets += poll poll-cancel ring-leak fsync io_uring_setup io_uring_register
send_recvmsg a4c0b3decb33-test 500f9fbadef8-test timeout \
sq-space_left stdout cq-ready cq-peek-batch file-register \
cq-size 8a9973408177-test a0908ae19763-test 232c93d07b74-test \
- socket-rw accept timeout-overflow defer read-write
+ socket-rw accept timeout-overflow defer read-write io-cancel
include ../Makefile.quiet
@@ -24,7 +24,7 @@ test_srcs := poll.c poll-cancel.c ring-leak.c fsync.c io_uring_setup.c \
500f9fbadef8-test.c timeout.c sq-space_left.c stdout.c cq-ready.c\
cq-peek-batch.c file-register.c cq-size.c 8a9973408177-test.c \
a0908ae19763-test.c 232c93d07b74-test.c socket-rw.c accept.c \
- timeout-overflow.c defer.c read-write.c
+ timeout-overflow.c defer.c read-write.c io-cancel.c
test_objs := $(patsubst %.c,%.ol,$(test_srcs))
diff --git a/test/io-cancel.c b/test/io-cancel.c
new file mode 100644
index 0000000..edbfa80
--- /dev/null
+++ b/test/io-cancel.c
@@ -0,0 +1,259 @@
+/*
+ * 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;
+}