| /* SPDX-License-Identifier: MIT */ |
| /* |
| * Description: test toggling of iowait on kernels that support it |
| * |
| */ |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| |
| #include "liburing.h" |
| #include "helpers.h" |
| |
| static int get_iowait(int cpu) |
| { |
| char cpu_buf[32], this_cpu[32]; |
| int user, nice, system, idle, iowait, ret; |
| FILE *file; |
| |
| file = fopen("/proc/stat", "r"); |
| if (!file) { |
| perror("fopen stat"); |
| return 0; |
| } |
| |
| sprintf(this_cpu, "cpu%d", cpu); |
| ret = 0; |
| do { |
| int r; |
| r = fscanf(file, "%s %d %d %d %d %d", cpu_buf, &user, &nice, |
| &system, &idle, &iowait); |
| if (r != 6) |
| continue; |
| if (strncmp(cpu_buf, this_cpu, strlen(this_cpu))) |
| continue; |
| ret = iowait; |
| break; |
| } while (1); |
| |
| fclose(file); |
| return ret; |
| } |
| |
| static int test(struct io_uring *ring, int with_iowait, int cpu) |
| { |
| struct io_uring_sqe *sqe; |
| struct io_uring_cqe *cqe; |
| int iowait_pre, iowait_post; |
| struct __kernel_timespec ts; |
| int ret, fds[2], diff; |
| char buf[32]; |
| |
| if (!(ring->features & IORING_FEAT_NO_IOWAIT)) |
| return T_EXIT_SKIP; |
| |
| if (pipe(fds) < 0) { |
| perror("pipe"); |
| return T_EXIT_FAIL; |
| } |
| |
| ret = io_uring_set_iowait(ring, with_iowait); |
| if (ret) { |
| fprintf(stderr, "set_iowait=%d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| |
| sqe = io_uring_get_sqe(ring); |
| io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0); |
| io_uring_submit(ring); |
| |
| ts.tv_sec = 1; |
| ts.tv_nsec = 0; |
| iowait_pre = get_iowait(cpu); |
| ret = io_uring_wait_cqe_timeout(ring, &cqe, &ts); |
| if (ret != -ETIME) { |
| fprintf(stderr, "Unexpected wait ret: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| iowait_post = get_iowait(cpu); |
| diff = iowait_post - iowait_pre; |
| |
| close(fds[0]); |
| close(fds[1]); |
| |
| ret = io_uring_wait_cqe(ring, &cqe); |
| if (ret) { |
| fprintf(stderr, "Unexpected wait ret: %d\n", ret); |
| return T_EXIT_FAIL; |
| } |
| io_uring_cqe_seen(ring, cqe); |
| |
| if (with_iowait) { |
| if (diff < 50) { |
| fprintf(stderr, "iowait diff too small: %d\n", diff); |
| return T_EXIT_FAIL; |
| } |
| } else { |
| if (diff > 10) { |
| fprintf(stderr, "iowait diff too large: %d\n", diff); |
| return T_EXIT_FAIL; |
| } |
| } |
| return T_EXIT_PASS; |
| } |
| |
| int main(int argc, char *argv[]) |
| { |
| struct io_uring ring; |
| cpu_set_t cpuset; |
| int ret; |
| |
| CPU_ZERO(&cpuset); |
| CPU_SET(0, &cpuset); |
| if (sched_setaffinity(0, sizeof(cpuset), &cpuset)) |
| return T_EXIT_SKIP; |
| |
| ret = t_create_ring(64, &ring, 0); |
| if (ret == T_SETUP_SKIP) |
| return T_EXIT_SKIP; |
| else if (ret != T_SETUP_OK) |
| return T_EXIT_FAIL; |
| |
| ret = test(&ring, 0, 0); |
| if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } else if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "test 0 failed\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| ret = test(&ring, 1, 0); |
| if (ret == T_EXIT_SKIP) { |
| return T_EXIT_SKIP; |
| } else if (ret == T_EXIT_FAIL) { |
| fprintf(stderr, "test 1 failed\n"); |
| return T_EXIT_FAIL; |
| } |
| |
| io_uring_queue_exit(&ring); |
| return 0; |
| } |