From 0ba9503f793647bfd967da8083a2adf56621307c Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Mon, 13 May 2019 16:16:07 -0600 Subject: Add link SQE support Just a basic test case that does various forms of linked nops, and a sample bare bones copy program using linked reads and writes. Signed-off-by: Jens Axboe --- examples/Makefile | 6 +- examples/link-cp.c | 163 +++++++++++++++++++++++++++++++++++++++++ src/io_uring.h | 1 + test/Makefile | 6 +- test/link.c | 209 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 381 insertions(+), 4 deletions(-) create mode 100644 examples/link-cp.c create mode 100644 test/link.c diff --git a/examples/Makefile b/examples/Makefile index 067fb94..6fe9bb9 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -1,10 +1,10 @@ CFLAGS ?= -g -O2 -Wall -D_GNU_SOURCE -L../src/ -all_targets += io_uring-test io_uring-cp +all_targets += io_uring-test io_uring-cp link-cp all: $(all_targets) -test_srcs := io_uring-test.c io_uring-cp.c +test_srcs := io_uring-test.c io_uring-cp.c link-cp.c test_objs := $(patsubst %.c,%.ol,$(test_srcs)) @@ -12,6 +12,8 @@ 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 +link-cp: link-cp.c + $(CC) $(CFLAGS) -o $@ link-cp.c -luring clean: rm -f $(all_targets) $(test_objs) diff --git a/examples/link-cp.c b/examples/link-cp.c new file mode 100644 index 0000000..c747966 --- /dev/null +++ b/examples/link-cp.c @@ -0,0 +1,163 @@ +/* + * Very basic proof-of-concept for doing a copy with linked SQEs. Needs a + * bit of error handling and short read love. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "../src/liburing.h" + +#define QD 64 +#define BS (32*1024) + +struct io_data { + struct iovec iov; +}; + +static int infd, outfd; + +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 int queue_rw_pair(struct io_uring *ring, off_t size, off_t offset) +{ + struct io_uring_sqe *sqe; + struct io_data *data; + + data = malloc(size + sizeof(*data)); + data->iov.iov_base = data + 1; + data->iov.iov_len = size; + + sqe = io_uring_get_sqe(ring); + io_uring_prep_readv(sqe, infd, &data->iov, 1, offset); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + io_uring_prep_writev(sqe, outfd, &data->iov, 1, offset); + io_uring_sqe_set_data(sqe, data); + return 0; +} + +static void handle_cqe(struct io_uring_cqe *cqe) +{ + struct io_data *data; + + data = io_uring_cqe_get_data(cqe); + if (!data) + return; + + free(data); +} + +static int copy_file(struct io_uring *ring, off_t insize) +{ + struct io_uring_cqe *cqe; + size_t this_size; + off_t offset; + int inflight; + + offset = 0; + inflight = 0; + while (insize) { + int has_inflight = inflight; + + while (inflight < QD) { + this_size = BS; + if (this_size > insize) + this_size = insize; + queue_rw_pair(ring, this_size, offset); + offset += this_size; + insize -= this_size; + inflight += 2; + } + + if (has_inflight != inflight) + io_uring_submit(ring); + + while (inflight >= QD) { + int ret; + + ret = io_uring_wait_cqe(ring, &cqe); + assert(ret >= 0); + handle_cqe(cqe); + inflight--; + } + } + + 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/src/io_uring.h b/src/io_uring.h index 43ecbc1..4da0053 100644 --- a/src/io_uring.h +++ b/src/io_uring.h @@ -39,6 +39,7 @@ struct io_uring_sqe { */ #define IOSQE_FIXED_FILE (1U << 0) /* use fixed fileset */ #define IOSQE_IO_DRAIN (1U << 1) +#define IOSQE_IO_LINK (1U << 2) /* * io_uring_setup() flags diff --git a/test/Makefile b/test/Makefile index 7866047..e4fe57d 100644 --- a/test/Makefile +++ b/test/Makefile @@ -2,14 +2,14 @@ CFLAGS ?= -g -O2 -Wall -D_GNU_SOURCE -L../src/ all_targets += poll poll-cancel ring-leak fsync io_uring_setup io_uring_register \ io_uring_enter nop sq-full cq-full 35fa71a030ca-test \ - 917257daa0fe-test b19062a56726-test eeed8b54e0df-test + 917257daa0fe-test b19062a56726-test eeed8b54e0df-test link all: $(all_targets) test_srcs := poll.c poll-cancel.c ring-leak.c fsync.c io_uring_setup.c \ io_uring_register.c io_uring_enter.c nop.c sq-full.c cq-full.c \ 35fa71a030ca-test.c 917257daa0fe-test.c b19062a56726-test.c \ - eeed8b54e0df-test + eeed8b54e0df-test link test_objs := $(patsubst %.c,%.ol,$(test_srcs)) @@ -41,6 +41,8 @@ b19062a56726-test: b19062a56726-test.c $(CC) $(CFLAGS) -o $@ b19062a56726-test.c eeed8b54e0df-test: eeed8b54e0df-test.c $(CC) $(CFLAGS) -o $@ eeed8b54e0df-test.c -luring +link: link.c + $(CC) $(CFLAGS) -o $@ link.c -luring clean: rm -f $(all_targets) $(test_objs) diff --git a/test/link.c b/test/link.c new file mode 100644 index 0000000..b656bb2 --- /dev/null +++ b/test/link.c @@ -0,0 +1,209 @@ +/* + * Description: run various linked sqe tests + * + */ +#include +#include +#include +#include +#include +#include + +#include "../src/liburing.h" + +/* + * Test two independent chains + */ +static int test_double_chain(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret, i; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + + ret = io_uring_submit(ring); + if (ret <= 0) { + printf("sqe submit failed: %d\n", ret); + goto err; + } + + for (i = 0; i < 4; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + printf("wait completion %d\n", ret); + goto err; + } + io_uring_cqe_seen(ring, cqe); + } + + return 0; +err: + return 1; +} + +/* + * Test multiple dependents + */ +static int test_double_link(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret, i; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + + ret = io_uring_submit(ring); + if (ret <= 0) { + printf("sqe submit failed: %d\n", ret); + goto err; + } + + for (i = 0; i < 3; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + printf("wait completion %d\n", ret); + goto err; + } + io_uring_cqe_seen(ring, cqe); + } + + return 0; +err: + return 1; +} + +/* + * Test single dependency + */ +static int test_single_link(struct io_uring *ring) +{ + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + int ret, i; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + sqe->flags |= IOSQE_IO_LINK; + + sqe = io_uring_get_sqe(ring); + if (!sqe) { + printf("get sqe failed\n"); + goto err; + } + + io_uring_prep_nop(sqe); + + ret = io_uring_submit(ring); + if (ret <= 0) { + printf("sqe submit failed: %d\n", ret); + goto err; + } + + for (i = 0; i < 2; i++) { + ret = io_uring_wait_cqe(ring, &cqe); + if (ret < 0) { + printf("wait completion %d\n", ret); + goto err; + } + io_uring_cqe_seen(ring, cqe); + } + + return 0; +err: + return 1; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + int ret; + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + printf("ring setup failed\n"); + return 1; + + } + + ret = test_single_link(&ring); + if (ret) { + printf("test_single_link failed\n"); + return ret; + } + + ret = test_double_link(&ring); + if (ret) { + printf("test_double_link failed\n"); + return ret; + } + + ret = test_double_chain(&ring); + if (ret) { + printf("test_double_chain failed\n"); + return ret; + } + + return 0; +} -- cgit