From: David Wei <davidhwei@xxxxxxxx> Add a unit test for io_uring_register_iowait() by creating a thread that writes into a pipe after a delay, checking iowait before and after. Signed-off-by: David Wei <davidhwei@xxxxxxxx> --- test/Makefile | 1 + test/iowait.c | 157 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 158 insertions(+) create mode 100644 test/iowait.c diff --git a/test/Makefile b/test/Makefile index b09228f..779a7db 100644 --- a/test/Makefile +++ b/test/Makefile @@ -107,6 +107,7 @@ test_srcs := \ io_uring_passthrough.c \ io_uring_register.c \ io_uring_setup.c \ + iowait.c \ lfs-openat.c \ lfs-openat-write.c \ link.c \ diff --git a/test/iowait.c b/test/iowait.c new file mode 100644 index 0000000..fcd4004 --- /dev/null +++ b/test/iowait.c @@ -0,0 +1,157 @@ +/* SPDX-License-Identifier: MIT */ +/* + * Description: Test that waiting for CQ is accounted as iowait if enabled via + * io_uring_register_iowait(), and vice versa. + * + */ +#include <errno.h> +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <fcntl.h> +#include <sys/time.h> +#include <pthread.h> +#include <linux/kernel.h> + +#include "liburing.h" +#include "helpers.h" + +struct data { + pthread_barrier_t startup; + int out_fd; +}; + +static unsigned long long get_iowait() +{ + FILE *fp; + char buf[256]; + unsigned long long user, nice, system, idle, iowait; + + fp = fopen("/proc/stat", "r"); + if (!fp) { + perror("fopen"); + exit(T_EXIT_FAIL); + } + + if (fgets(buf, sizeof(buf), fp) == NULL) { + perror("fgets"); + fclose(fp); + exit(T_EXIT_FAIL); + } + fclose(fp); + + sscanf(buf, "cpu %llu %llu %llu %llu %llu", &user, &nice, &system, + &idle, &iowait); + + return iowait; +} + +static void *pipe_write(void *data) +{ + struct data *d = data; + char buf[32]; + int ret; + + memset(buf, 0x55, sizeof(buf)); + pthread_barrier_wait(&d->startup); + usleep(100000); + + ret = write(d->out_fd, buf, sizeof(buf)); + if (ret < 0) { + perror("write"); + return NULL; + } + + return NULL; +} + +static int test_iowait(struct io_uring *ring, bool enabled) +{ + unsigned long long iowait_pre, iowait_post, iowait; + double iowait_ms_max_diff; + struct io_uring_cqe *cqe; + struct io_uring_sqe *sqe; + pthread_t thread; + double iowait_ms; + int ret, fds[2]; + struct data d; + char buf[32]; + void *tret; + + if (pipe(fds) < 0) { + perror("pipe"); + return T_EXIT_FAIL; + } + d.out_fd = fds[1]; + + pthread_barrier_init(&d.startup, NULL, 2); + pthread_create(&thread, NULL, pipe_write, &d); + pthread_barrier_wait(&d.startup); + + io_uring_register_iowait(ring, enabled); + + sqe = io_uring_get_sqe(ring); + io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0); + + io_uring_submit(ring); + + iowait_pre = get_iowait(); + ret = io_uring_wait_cqe(ring, &cqe); + if (ret) { + fprintf(stderr, "wait_cqe: %d\n", ret); + return T_EXIT_FAIL; + } + io_uring_cq_advance(ring, 1); + + iowait_post = get_iowait(); + + /* + * writer sleeps for 100 ms, so max diff is 100 plus a tolerance of + * 10 ms + */ + iowait_ms_max_diff = (enabled ? 100.0 : 0.0) + 10.0; + + if (iowait_post > iowait_pre) + iowait = iowait_post - iowait_pre; + else + iowait = iowait_pre - iowait_post; + iowait_ms = ((double)iowait / sysconf(_SC_CLK_TCK)) * 1000; + + if (iowait_ms > iowait_ms_max_diff) + ret = T_EXIT_FAIL; + else + ret = T_EXIT_PASS; + + pthread_join(thread, &tret); + close(fds[0]); + close(fds[1]); + return ret; +} + +int main(int argc, char *argv[]) +{ + struct io_uring ring; + struct io_uring_params p = { }; + int ret; + + if (argc > 1) + return 0; + + ret = t_create_ring_params(8, &ring, &p); + if (ret == T_SETUP_SKIP) + return T_EXIT_SKIP; + else if (ret != T_SETUP_OK) + return ret; + + ret = test_iowait(&ring, false); + if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP) + return ret; + + ret = test_iowait(&ring, true); + if (ret == T_EXIT_FAIL || ret == T_EXIT_SKIP) + return ret; + + io_uring_queue_exit(&ring); + return T_EXIT_PASS; +} -- 2.43.0