From abe992c561780704219178d648d3a89a02567b7b Mon Sep 17 00:00:00 2001 From: Jens Axboe Date: Sat, 2 Nov 2024 16:13:26 -0600 Subject: [PATCH] examples/reg-wait: add registered wait example Sometimes a bit of code is more useful than a man page. Add a basic example of how to use registered waits, using two indices. One is just a basic timeout wait, the other one uses min_wait_usec as well. Signed-off-by: Jens Axboe --- examples/Makefile | 1 + examples/reg-wait.c | 139 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 140 insertions(+) create mode 100644 examples/reg-wait.c diff --git a/examples/Makefile b/examples/Makefile index 04528201f..7b740ca9e 100644 --- a/examples/Makefile +++ b/examples/Makefile @@ -28,6 +28,7 @@ example_srcs := \ napi-busy-poll-client.c \ napi-busy-poll-server.c \ poll-bench.c \ + reg-wait.c \ send-zerocopy.c \ rsrc-update-bench.c \ proxy.c \ diff --git a/examples/reg-wait.c b/examples/reg-wait.c new file mode 100644 index 000000000..96445cb3f --- /dev/null +++ b/examples/reg-wait.c @@ -0,0 +1,139 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Sample program that shows how to use registered waits. + * + * (C) 2024 Jens Axboe + */ +#include +#include +#include +#include +#include +#include +#include + +static unsigned long long mtime_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 *= 1000; + usec /= 1000; + return sec + usec; +} + +static unsigned long long mtime_since_now(struct timeval *tv) +{ + struct timeval end; + + gettimeofday(&end, NULL); + return mtime_since(tv, &end); +} + +int main(int argc, char *argv[]) +{ + struct io_uring_reg_wait *reg; + struct io_uring_sqe *sqe; + struct io_uring_cqe *cqe[2]; + struct io_uring ring; + char b1[8], b2[8]; + unsigned long msec; + struct timeval tv; + int ret, fds[2]; + + if (pipe(fds) < 0) { + perror("pipe"); + return 1; + } + + ret = io_uring_queue_init(8, &ring, 0); + if (ret) { + fprintf(stderr, "Queue init: %d\n", ret); + return 1; + } + + /* + * Setup wait region. We'll use 32 here, but 64 is probably a more + * logical value, as it'll pin a page regardless of size. 64 is the + * max value on a 4k page size architecture. + */ + reg = io_uring_setup_reg_wait(&ring, 32, &ret); + if (!reg) { + if (ret == -EINVAL) { + fprintf(stderr, "Kernel doesn't support registered waits\n"); + return 1; + } + fprintf(stderr, "Registered wait: %d\n", ret); + return 1; + } + + /* + * Setup two distinct wait regions. Index 0 will be a 1 second wait, + * and region 2 is a short wait using min_wait_usec as well. Neither + * of these use a signal mask, but sigmask/sigmask_sz can be set as + * well for that. + */ + reg[0].ts.tv_sec = 1; + reg[0].ts.tv_nsec = 0; + reg[0].flags = IORING_REG_WAIT_TS; + + reg[1].ts.tv_sec = 0; + reg[1].ts.tv_nsec = 100000000LL; + reg[1].min_wait_usec = 10000; + reg[1].flags = IORING_REG_WAIT_TS; + + /* + * No pending completions. Wait with region 0, which should time + * out after 1 second. + */ + gettimeofday(&tv, NULL); + ret = io_uring_submit_and_wait_reg(&ring, cqe, 1, 0); + if (ret == -EINVAL) { + fprintf(stderr, "Kernel doesn't support registered waits\n"); + return 1; + } else if (ret != -ETIME) { + fprintf(stderr, "Wait should've timed out... %d\n", ret); + return 1; + } + msec = mtime_since_now(&tv); + if (msec < 900 || msec > 1100) { + fprintf(stderr, "Wait took an unexpected amount of time: %lu\n", + msec); + return 1; + } + + /* + * Now prepare two pipe reads. We'll trigger one completion quickly, + * but the other one will never happen. Use min_wait_usec timeout + * to abort after 10 msec of time, where the overall timeout is + * otherwise 100 msec. Since we're waiting on two events, the min + * timeout ends up aborting us. + */ + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], b1, sizeof(b1), 0); + sqe = io_uring_get_sqe(&ring); + io_uring_prep_read(sqe, fds[0], b2, sizeof(b2), 0); + + /* trigger one read */ + write(fds[1], "Hello", 5); + + gettimeofday(&tv, NULL); + ret = io_uring_submit_and_wait_reg(&ring, cqe, 2, 1); + msec = mtime_since_now(&tv); + if (ret != 2) { + fprintf(stderr, "Should have submitted 2: %d\n", ret); + return 1; + } + if (msec < 8 || msec > 12) { + fprintf(stderr, "min_wait_usec should take ~10 msec: %lu\n", msec); + return 1; + } + + return 0; +}