Skip to content

Commit 4c288fe

Browse files
committed
Merge remote-tracking branch 'origin/master' into xbb-master
2 parents 10a6f6f + e3d35ea commit 4c288fe

5 files changed

Lines changed: 229 additions & 10 deletions

File tree

man/io_uring_cancelation.7

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,8 @@ io_uring_cancelation \- io_uring request cancelation overview
77
.SH DESCRIPTION
88
io_uring provides mechanisms to cancel in-flight requests before they
99
complete naturally. This is useful for implementing timeouts, handling
10-
connection drops, application shutdown, or when a request is no longer
11-
needed.
10+
connection drops, closing connections that go away, or when a request is no
11+
longer needed.
1212
.SS Why cancel requests?
1313
Common scenarios requiring cancelation:
1414
.IP \(bu 2
@@ -18,9 +18,6 @@ Cancel a read or accept that has been pending too long
1818
.B Connection management:
1919
Cancel pending operations when a connection is closed
2020
.IP \(bu
21-
.B Application shutdown:
22-
Clean up all pending I/O before exiting
23-
.IP \(bu
2421
.B Resource cleanup:
2522
Cancel operations on a file descriptor being closed
2623
.IP \(bu
@@ -49,7 +46,7 @@ io_uring_prep_cancel64(sqe, 1234, 0);
4946
io_uring_submit(ring);
5047
.EE
5148
.in
52-
.SS Cancellation results
49+
.SS Cancelation results
5350
When a cancelation is submitted, two CQEs are generated:
5451
.PP
5552
.B The canceled request's CQE:
@@ -86,7 +83,7 @@ if the request was found but already completing
8683
.PP
8784
The order of these CQEs is not guaranteed. The application may receive
8885
the cancel CQE before or after the canceled request's CQE.
89-
.SS Cancellation flags
86+
.SS Cancelation flags
9087
Various flags modify cancelation behavior:
9188
.PP
9289
.B IORING_ASYNC_CANCEL_ALL
@@ -139,7 +136,7 @@ io_uring_prep_cancel(sqe, NULL,
139136
.EE
140137
.in
141138
.SS Race conditions
142-
Cancellation is inherently racy. Between submitting the cancel request
139+
Cancelation is inherently racy. Between submitting the cancel request
143140
and the kernel processing it:
144141
.IP \(bu 2
145142
The target request may complete successfully

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/conn-unreach.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ static int check_cqe(struct io_uring *ring, struct io_uring_cqe *cqe)
3030

3131
switch (cqe->user_data) {
3232
case 1:
33-
if (cqe->res != -ECONNRESET) {
33+
if (cqe->res != -ECONNRESET && cqe->res != -ENETUNREACH) {
3434
fprintf(stderr, "Unexpected connect: %d\n", cqe->res);
3535
return T_EXIT_FAIL;
3636
}

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+
}

test/iowait.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,7 +90,7 @@ static int test(struct io_uring *ring, int with_iowait, int cpu)
9090
io_uring_cqe_seen(ring, cqe);
9191

9292
if (with_iowait) {
93-
if (diff < 90) {
93+
if (diff < 50) {
9494
fprintf(stderr, "iowait diff too small: %d\n", diff);
9595
return T_EXIT_FAIL;
9696
}

0 commit comments

Comments
 (0)