1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: test that io-wq affinity is correctly set for SQPOLL
4 */
5 #include <stdio.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdlib.h>
9 #include <string.h>
10
11 #include "liburing.h"
12 #include "helpers.h"
13
14 #define IOWQ_CPU 0
15 #define SQPOLL_CPU 1
16
verify_comm(pid_t pid,const char * name,int cpu)17 static int verify_comm(pid_t pid, const char *name, int cpu)
18 {
19 char comm[64], buf[64];
20 cpu_set_t set;
21 int fd, ret;
22
23 sprintf(comm, "/proc/%d/comm", pid);
24 fd = open(comm, O_RDONLY);
25 if (fd < 0) {
26 perror("open");
27 return T_EXIT_SKIP;
28 }
29
30 ret = read(fd, buf, sizeof(buf));
31 if (ret < 0) {
32 close(fd);
33 return T_EXIT_SKIP;
34 }
35
36 if (strncmp(buf, name, strlen(name) - 1)) {
37 close(fd);
38 return T_EXIT_SKIP;
39 }
40
41 close(fd);
42
43 ret = sched_getaffinity(pid, sizeof(set), &set);
44 if (ret < 0) {
45 perror("sched_getaffinity");
46 return T_EXIT_SKIP;
47 }
48
49 if (CPU_COUNT(&set) != 1) {
50 fprintf(stderr, "More than one CPU set in mask\n");
51 return T_EXIT_FAIL;
52 }
53 if (!CPU_ISSET(cpu, &set)) {
54 fprintf(stderr, "Wrong CPU set in mask\n");
55 return T_EXIT_FAIL;
56 }
57
58 return T_EXIT_PASS;
59 }
60
verify_affinity(pid_t pid,int sqpoll)61 static int verify_affinity(pid_t pid, int sqpoll)
62 {
63 pid_t wq_pid, sqpoll_pid = -1;
64 char name[64];
65 int ret;
66
67 wq_pid = pid + 2;
68 if (sqpoll)
69 sqpoll_pid = pid + 1;
70
71 /* verify we had the pids right */
72 sprintf(name, "iou-wrk-%d", pid);
73 ret = verify_comm(wq_pid, name, IOWQ_CPU);
74 if (ret != T_EXIT_PASS)
75 return ret;
76
77 if (sqpoll_pid != -1) {
78 sprintf(name, "iou-sqp-%d", pid);
79 ret = verify_comm(sqpoll_pid, name, SQPOLL_CPU);
80 if (ret != T_EXIT_PASS)
81 return ret;
82 }
83
84 return T_EXIT_PASS;
85 }
86
test(int sqpoll)87 static int test(int sqpoll)
88 {
89 struct io_uring_params p = { };
90 struct io_uring ring;
91 struct io_uring_sqe *sqe;
92 char buf[64];
93 int fds[2], ret;
94 cpu_set_t set;
95
96 if (sqpoll) {
97 p.flags = IORING_SETUP_SQPOLL | IORING_SETUP_SQ_AFF;
98 p.sq_thread_cpu = SQPOLL_CPU;
99 }
100
101 io_uring_queue_init_params(8, &ring, &p);
102
103 CPU_ZERO(&set);
104 CPU_SET(IOWQ_CPU, &set);
105
106 ret = io_uring_register_iowq_aff(&ring, sizeof(set), &set);
107 if (ret) {
108 fprintf(stderr, "register aff: %d\n", ret);
109 return T_EXIT_FAIL;
110 }
111
112 if (pipe(fds) < 0) {
113 perror("pipe");
114 return T_EXIT_FAIL;
115 }
116
117 sqe = io_uring_get_sqe(&ring);
118 io_uring_prep_read(sqe, fds[0], buf, sizeof(buf), 0);
119 sqe->flags |= IOSQE_ASYNC;
120
121 io_uring_submit(&ring);
122
123 usleep(10000);
124
125 ret = verify_affinity(getpid(), sqpoll);
126 io_uring_queue_exit(&ring);
127 return ret;
128 }
129
test_invalid_cpu(void)130 static int test_invalid_cpu(void)
131 {
132 struct io_uring_params p = { };
133 struct io_uring ring;
134 int ret, nr_cpus;
135
136 nr_cpus = sysconf(_SC_NPROCESSORS_ONLN);
137 if (nr_cpus < 0) {
138 perror("sysconf(_SC_NPROCESSORS_ONLN");
139 return T_EXIT_SKIP;
140 }
141
142 p.flags = IORING_SETUP_SQPOLL | IORING_SETUP_SQ_AFF;
143 p.sq_thread_cpu = 16 * nr_cpus;
144
145 ret = io_uring_queue_init_params(8, &ring, &p);
146 if (ret == -EPERM) {
147 return T_EXIT_SKIP;
148 } else if (ret != -EINVAL) {
149 fprintf(stderr, "Queue init: %d\n", ret);
150 return T_EXIT_FAIL;
151 }
152
153 io_uring_queue_exit(&ring);
154 return T_EXIT_PASS;
155 }
156
main(int argc,char * argv[])157 int main(int argc, char *argv[])
158 {
159 int ret;
160
161 if (argc > 1)
162 return T_EXIT_SKIP;
163
164 ret = test_invalid_cpu();
165 if (ret == T_EXIT_SKIP) {
166 return T_EXIT_SKIP;
167 } else if (ret != T_EXIT_PASS) {
168 fprintf(stderr, "test sqpoll cpu failed\n");
169 return T_EXIT_FAIL;
170 }
171
172 ret = test(1);
173 if (ret == T_EXIT_SKIP) {
174 return T_EXIT_SKIP;
175 } else if (ret != T_EXIT_PASS) {
176 fprintf(stderr, "test sqpoll failed\n");
177 return T_EXIT_FAIL;
178 }
179
180 return T_EXIT_PASS;
181 }
182