From 4916320ec374cc5d2b4062c48d2e4c576472b739 Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Wed, 17 Apr 2019 17:38:14 +0000 Subject: Separate test cases from examples Also adds a runtests makefile target. --- examples/Makefile | 17 ++++ examples/io_uring-cp.c | 249 +++++++++++++++++++++++++++++++++++++++++++++++ examples/io_uring-test.c | 91 +++++++++++++++++ 3 files changed, 357 insertions(+) create mode 100644 examples/Makefile create mode 100644 examples/io_uring-cp.c create mode 100644 examples/io_uring-test.c (limited to 'examples') diff --git a/examples/Makefile b/examples/Makefile new file mode 100644 index 0000000..067fb94 --- /dev/null +++ b/examples/Makefile @@ -0,0 +1,17 @@ +CFLAGS ?= -g -O2 -Wall -D_GNU_SOURCE -L../src/ + +all_targets += io_uring-test io_uring-cp + +all: $(all_targets) + +test_srcs := io_uring-test.c io_uring-cp.c + +test_objs := $(patsubst %.c,%.ol,$(test_srcs)) + +io_uring-test: io_uring-test.c + $(CC) $(CFLAGS) -o $@ io_uring-test.c -luring +io_uring-cp: io_uring-cp.c + $(CC) $(CFLAGS) -o $@ io_uring-cp.c -luring + +clean: + rm -f $(all_targets) $(test_objs) diff --git a/examples/io_uring-cp.c b/examples/io_uring-cp.c new file mode 100644 index 0000000..f704ff6 --- /dev/null +++ b/examples/io_uring-cp.c @@ -0,0 +1,249 @@ +/* + * gcc -Wall -O2 -D_GNU_SOURCE -o io_uring-cp io_uring-cp.c -luring + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/liburing.h" + +#define QD 64 +#define BS (32*1024) + +static int infd, outfd; + +struct io_data { + int read; + off_t first_offset, offset; + size_t first_len; + struct iovec iov; +}; + +static int setup_context(unsigned entries, struct io_uring *ring) +{ + int ret; + + ret = io_uring_queue_init(entries, ring, 0); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", strerror(-ret)); + return -1; + } + + return 0; +} + +static int get_file_size(int fd, off_t *size) +{ + struct stat st; + + if (fstat(fd, &st) < 0) + return -1; + if (S_ISREG(st.st_mode)) { + *size = st.st_size; + return 0; + } else if (S_ISBLK(st.st_mode)) { + unsigned long long bytes; + + if (ioctl(fd, BLKGETSIZE64, &bytes) != 0) + return -1; + + *size = bytes; + return 0; + } + + return -1; +} + +static void queue_prepped(struct io_uring *ring, struct io_data *data) +{ + struct io_uring_sqe *sqe; + + sqe = io_uring_get_sqe(ring); + assert(sqe); + + if (data->read) + io_uring_prep_readv(sqe, infd, &data->iov, 1, data->offset); + else + io_uring_prep_writev(sqe, outfd, &data->iov, 1, data->offset); + + io_uring_sqe_set_data(sqe, data); +} + +static int queue_read(struct io_uring *ring, off_t size, off_t offset) +{ + struct io_uring_sqe *sqe; + struct io_data *data; + + sqe = io_uring_get_sqe(ring); + if (!sqe) + return 1; + + data = malloc(size + sizeof(*data)); + data->read = 1; + data->offset = data->first_offset = offset; + + data->iov.iov_base = data + 1; + data->iov.iov_len = size; + data->first_len = size; + + io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); + io_uring_sqe_set_data(sqe, data); + return 0; +} + +static void queue_write(struct io_uring *ring, struct io_data *data) +{ + data->read = 0; + data->offset = data->first_offset; + + data->iov.iov_base = data + 1; + data->iov.iov_len = data->first_len; + + queue_prepped(ring, data); + io_uring_submit(ring); +} + +static int copy_file(struct io_uring *ring, off_t insize) +{ + unsigned long reads, writes; + struct io_uring_cqe *cqe; + off_t write_left, offset; + int ret; + + write_left = insize; + writes = reads = offset = 0; + + while (insize || write_left) { + int had_reads, got_comp; + + /* + * Queue up as many reads as we can + */ + had_reads = reads; + while (insize) { + off_t this_size = insize; + + if (reads + writes >= QD) + break; + if (this_size > BS) + this_size = BS; + else if (!this_size) + break; + + if (queue_read(ring, this_size, offset)) + break; + + insize -= this_size; + offset += this_size; + reads++; + } + + if (had_reads != reads) { + ret = io_uring_submit(ring); + if (ret < 0) { + fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); + break; + } + } + + /* + * Queue is full at this point. Find at least one completion. + */ + got_comp = 0; + while (write_left) { + struct io_data *data; + + if (!got_comp) { + ret = io_uring_wait_completion(ring, &cqe); + got_comp = 1; + } else + ret = io_uring_get_completion(ring, &cqe); + if (ret < 0) { + fprintf(stderr, "io_uring_get_completion: %s\n", + strerror(-ret)); + return 1; + } + if (!cqe) + break; + + data = io_uring_cqe_get_data(cqe); + if (cqe->res < 0) { + if (cqe->res == -EAGAIN) { + queue_prepped(ring, data); + io_uring_cqe_seen(ring, cqe); + continue; + } + fprintf(stderr, "cqe failed: %s\n", + strerror(-cqe->res)); + return 1; + } else if (cqe->res != data->iov.iov_len) { + /* Short read/write, adjust and requeue */ + data->iov.iov_base += cqe->res; + data->iov.iov_len -= cqe->res; + data->offset += cqe->res; + queue_prepped(ring, data); + io_uring_cqe_seen(ring, cqe); + continue; + } + + /* + * All done. if write, nothing else to do. if read, + * queue up corresponding write. + */ + if (data->read) { + queue_write(ring, data); + write_left -= data->first_len; + reads--; + writes++; + } else { + free(data); + writes--; + } + io_uring_cqe_seen(ring, cqe); + } + } + + return 0; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + off_t insize; + int ret; + + if (argc < 3) { + printf("%s: infile outfile\n", argv[0]); + return 1; + } + + infd = open(argv[1], O_RDONLY); + if (infd < 0) { + perror("open infile"); + return 1; + } + outfd = open(argv[2], O_WRONLY | O_CREAT | O_TRUNC, 0644); + if (outfd < 0) { + perror("open outfile"); + return 1; + } + + if (setup_context(QD, &ring)) + return 1; + if (get_file_size(infd, &insize)) + return 1; + + ret = copy_file(&ring, insize); + + close(infd); + close(outfd); + io_uring_queue_exit(&ring); + return ret; +} diff --git a/examples/io_uring-test.c b/examples/io_uring-test.c new file mode 100644 index 0000000..fe0098c --- /dev/null +++ b/examples/io_uring-test.c @@ -0,0 +1,91 @@ +/* + * Simple app that demonstrates how to setup an io_uring interface, + * submit and complete IO against it, and then tear it down. + * + * gcc -Wall -O2 -D_GNU_SOURCE -o io_uring-test io_uring-test.c -luring + */ +#include +#include +#include +#include +#include +#include "../src/liburing.h" + +#define QD 4 + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int i, fd, ret, pending, done; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe; + struct iovec *iovecs; + off_t offset; + void *buf; + + if (argc < 2) { + printf("%s: file\n", argv[0]); + return 1; + } + + ret = io_uring_queue_init(QD, &ring, 0); + if (ret < 0) { + fprintf(stderr, "queue_init: %s\n", strerror(-ret)); + return 1; + } + + fd = open(argv[1], O_RDONLY | O_DIRECT); + if (fd < 0) { + perror("open"); + return 1; + } + + iovecs = calloc(QD, sizeof(struct iovec)); + for (i = 0; i < QD; i++) { + if (posix_memalign(&buf, 4096, 4096)) + return 1; + iovecs[i].iov_base = buf; + iovecs[i].iov_len = 4096; + } + + offset = 0; + i = 0; + do { + sqe = io_uring_get_sqe(&ring); + if (!sqe) + break; + io_uring_prep_readv(sqe, fd, &iovecs[i], 1, offset); + offset += iovecs[i].iov_len; + } while (1); + + ret = io_uring_submit(&ring); + if (ret < 0) { + fprintf(stderr, "io_uring_submit: %s\n", strerror(-ret)); + return 1; + } + + done = 0; + pending = ret; + for (i = 0; i < pending; i++) { + ret = io_uring_wait_completion(&ring, &cqe); + if (ret < 0) { + fprintf(stderr, "io_uring_get_completion: %s\n", strerror(-ret)); + return 1; + } + + done++; + ret = 0; + if (cqe->res != 4096) { + fprintf(stderr, "ret=%d, wanted 4096\n", cqe->res); + ret = 1; + } + io_uring_cqe_seen(&ring, cqe); + if (ret) + break; + } + + printf("Submitted=%d, completed=%d\n", pending, done); + close(fd); + io_uring_queue_exit(&ring); + return 0; +} -- cgit