Skip to content

Commit 3a59094

Browse files
committed
* 'idle' of https://github.com/FirstLoveLife/liburing: test/io-wq: verify unused workers exit
2 parents 14000b9 + 3f6d382 commit 3a59094

2 files changed

Lines changed: 222 additions & 0 deletions

File tree

test/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,7 @@ test_srcs := \
133133
io_uring_register.c \
134134
io_uring_setup.c \
135135
io-wq-exit.c \
136+
io-wq-unused-exit.c \
136137
iowait.c \
137138
kallsyms.c \
138139
lfs-openat.c \

test/io-wq-unused-exit.c

Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
/* SPDX-License-Identifier: MIT */
2+
/*
3+
* Description: Ensure that an io-wq worker created by io_uring doesn't linger
4+
* in the task thread group after the last ring is closed.
5+
*
6+
* This matters for checkpoint/restore of long-running processes which quiesce
7+
* and close their io_uring instance(s) before dumping process state.
8+
*/
9+
#include <dirent.h>
10+
#include <errno.h>
11+
#include <fcntl.h>
12+
#include <limits.h>
13+
#include <stdio.h>
14+
#include <stdlib.h>
15+
#include <string.h>
16+
#include <sys/time.h>
17+
#include <unistd.h>
18+
19+
#include "liburing.h"
20+
#include "helpers.h"
21+
22+
#define IOWQ_COMM_PREFIX "iou-wrk-"
23+
24+
static int count_iowq_workers(void)
25+
{
26+
DIR *dir;
27+
struct dirent *de;
28+
int nr = 0;
29+
30+
dir = opendir("/proc/self/task");
31+
if (!dir)
32+
return -errno;
33+
34+
while ((de = readdir(dir)) != NULL) {
35+
char path[PATH_MAX];
36+
char comm[64];
37+
int fd, ret;
38+
39+
if (de->d_name[0] == '.')
40+
continue;
41+
42+
snprintf(path, sizeof(path), "/proc/self/task/%s/comm", de->d_name);
43+
fd = open(path, O_RDONLY);
44+
if (fd < 0)
45+
continue;
46+
47+
ret = read(fd, comm, sizeof(comm) - 1);
48+
close(fd);
49+
if (ret <= 0)
50+
continue;
51+
52+
comm[ret] = '\0';
53+
if (!strncmp(comm, IOWQ_COMM_PREFIX, strlen(IOWQ_COMM_PREFIX)))
54+
nr++;
55+
}
56+
57+
closedir(dir);
58+
return nr;
59+
}
60+
61+
static int wait_for_iowq_workers(bool want_any, unsigned long timeout_ms)
62+
{
63+
struct timeval start;
64+
int nr = 0;
65+
66+
gettimeofday(&start, NULL);
67+
do {
68+
nr = count_iowq_workers();
69+
if (nr < 0) {
70+
fprintf(stderr, "count_iowq_workers: %s\n", strerror(-nr));
71+
return T_EXIT_SKIP;
72+
}
73+
74+
if (want_any) {
75+
if (nr > 0)
76+
return T_EXIT_PASS;
77+
} else {
78+
if (!nr)
79+
return T_EXIT_PASS;
80+
}
81+
82+
usleep(10 * 1000);
83+
} while (mtime_since_now(&start) < timeout_ms);
84+
85+
fprintf(stderr, "timeout waiting for iou-wrk threads (want_any=%d, nr=%d)\n",
86+
want_any, nr);
87+
return T_EXIT_FAIL;
88+
}
89+
90+
int main(int argc, char *argv[])
91+
{
92+
struct io_uring ring;
93+
struct io_uring_sqe *sqe;
94+
struct io_uring_cqe *cqe;
95+
bool ring_inited = false;
96+
bool ring_exited = false;
97+
char src_fname[PATH_MAX];
98+
char dst_fname[PATH_MAX];
99+
char buf[32];
100+
int pipe_fds[2] = { -1, -1 };
101+
int fd_src = -1, fd_dst = -1;
102+
int ret;
103+
104+
if (argc > 1)
105+
return T_EXIT_SKIP;
106+
107+
if (t_create_ring(4, &ring, 0) != T_SETUP_OK)
108+
return T_EXIT_SKIP;
109+
ring_inited = true;
110+
111+
snprintf(src_fname, sizeof(src_fname), ".iowq_unused_exit.%d.src", getpid());
112+
fd_src = open(src_fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
113+
if (fd_src < 0) {
114+
perror("open source");
115+
ret = T_EXIT_SKIP;
116+
goto out;
117+
}
118+
119+
memset(buf, 0xab, sizeof(buf));
120+
if (write(fd_src, buf, sizeof(buf)) != sizeof(buf)) {
121+
perror("write source");
122+
ret = T_EXIT_FAIL;
123+
goto out;
124+
}
125+
126+
snprintf(dst_fname, sizeof(dst_fname), ".iowq_unused_exit.%d.dst", getpid());
127+
fd_dst = open(dst_fname, O_RDWR | O_CREAT | O_TRUNC, 0644);
128+
if (fd_dst < 0) {
129+
perror("open destination");
130+
ret = T_EXIT_SKIP;
131+
goto out;
132+
}
133+
134+
if (pipe(pipe_fds) < 0) {
135+
perror("pipe");
136+
ret = T_EXIT_FAIL;
137+
goto out;
138+
}
139+
140+
sqe = io_uring_get_sqe(&ring);
141+
io_uring_prep_splice(sqe, fd_src, 0, pipe_fds[1], -1, sizeof(buf), 0);
142+
sqe->user_data = 1;
143+
144+
sqe = io_uring_get_sqe(&ring);
145+
io_uring_prep_splice(sqe, pipe_fds[0], -1, fd_dst, 0, sizeof(buf), 0);
146+
sqe->user_data = 2;
147+
148+
ret = io_uring_submit(&ring);
149+
if (ret < 0) {
150+
fprintf(stderr, "io_uring_submit: %d\n", ret);
151+
ret = T_EXIT_FAIL;
152+
goto out;
153+
}
154+
if (ret != 2) {
155+
fprintf(stderr, "io_uring_submit: submitted %d (expected 2)\n", ret);
156+
ret = T_EXIT_FAIL;
157+
goto out;
158+
}
159+
160+
ret = io_uring_wait_cqe(&ring, &cqe);
161+
if (ret) {
162+
fprintf(stderr, "wait cqe: %d\n", ret);
163+
ret = T_EXIT_FAIL;
164+
goto out;
165+
}
166+
if (cqe->res != sizeof(buf)) {
167+
fprintf(stderr, "splice cqe res %d (expected %zu)\n", cqe->res,
168+
sizeof(buf));
169+
ret = T_EXIT_FAIL;
170+
goto out;
171+
}
172+
io_uring_cqe_seen(&ring, cqe);
173+
174+
ret = io_uring_wait_cqe(&ring, &cqe);
175+
if (ret) {
176+
fprintf(stderr, "wait cqe: %d\n", ret);
177+
ret = T_EXIT_FAIL;
178+
goto out;
179+
}
180+
if (cqe->res != sizeof(buf)) {
181+
fprintf(stderr, "splice cqe res %d (expected %zu)\n", cqe->res,
182+
sizeof(buf));
183+
ret = T_EXIT_FAIL;
184+
goto out;
185+
}
186+
io_uring_cqe_seen(&ring, cqe);
187+
188+
/*
189+
* Ensure we actually created io-wq worker(s) before checking they exit
190+
* after closing the last ring.
191+
*/
192+
ret = wait_for_iowq_workers(true, 1000);
193+
if (ret != T_EXIT_PASS) {
194+
if (ret == T_EXIT_FAIL)
195+
ret = T_EXIT_SKIP;
196+
goto out;
197+
}
198+
199+
io_uring_queue_exit(&ring);
200+
ring_exited = true;
201+
202+
/* Now the worker(s) should exit quickly. */
203+
ret = wait_for_iowq_workers(false, 2000);
204+
205+
out:
206+
if (ring_inited && !ring_exited)
207+
io_uring_queue_exit(&ring);
208+
if (pipe_fds[0] != -1)
209+
close(pipe_fds[0]);
210+
if (pipe_fds[1] != -1)
211+
close(pipe_fds[1]);
212+
if (fd_src != -1)
213+
close(fd_src);
214+
if (fd_dst != -1)
215+
close(fd_dst);
216+
if (fd_src != -1)
217+
unlink(src_fname);
218+
if (fd_dst != -1)
219+
unlink(dst_fname);
220+
return ret;
221+
}

0 commit comments

Comments
 (0)