1 /* SPDX-License-Identifier: MIT */
2 /*
3 * Description: tests linked requests failing during submission
4 */
5 #include <errno.h>
6 #include <stdio.h>
7 #include <unistd.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <fcntl.h>
11 #include <assert.h>
12
13 #include "liburing.h"
14
15 #define DRAIN_USER_DATA 42
16
test_underprep_fail(bool hardlink,bool drain,bool link_last,int link_size,int fail_idx)17 static int test_underprep_fail(bool hardlink, bool drain, bool link_last,
18 int link_size, int fail_idx)
19 {
20 const int invalid_fd = 42;
21 int link_flags = IOSQE_IO_LINK;
22 int total_submit = link_size;
23 struct io_uring ring;
24 struct io_uring_sqe *sqe;
25 struct io_uring_cqe *cqe;
26 char buffer[1];
27 int i, ret, fds[2];
28
29 if (drain)
30 link_flags |= IOSQE_IO_DRAIN;
31 if (hardlink)
32 link_flags |= IOSQE_IO_HARDLINK;
33
34 assert(fail_idx < link_size);
35 assert(link_size < 40);
36
37 /* create a new ring as it leaves it dirty */
38 ret = io_uring_queue_init(8, &ring, 0);
39 if (ret) {
40 printf("ring setup failed\n");
41 return -1;
42 }
43 if (pipe(fds)) {
44 perror("pipe");
45 return -1;
46 }
47
48 if (drain) {
49 /* clog drain, so following reqs sent to draining */
50 sqe = io_uring_get_sqe(&ring);
51 io_uring_prep_read(sqe, fds[0], buffer, sizeof(buffer), 0);
52 sqe->user_data = DRAIN_USER_DATA;
53 sqe->flags |= IOSQE_IO_DRAIN;
54 total_submit++;
55 }
56
57 for (i = 0; i < link_size; i++) {
58 sqe = io_uring_get_sqe(&ring);
59 if (i == fail_idx) {
60 io_uring_prep_read(sqe, invalid_fd, buffer, 1, 0);
61 sqe->ioprio = (short) -1;
62 } else {
63 io_uring_prep_nop(sqe);
64 }
65
66 if (i != link_size - 1 || !link_last)
67 sqe->flags |= link_flags;
68 sqe->user_data = i;
69 }
70
71 ret = io_uring_submit(&ring);
72 if (ret != total_submit) {
73 /* Old behaviour, failed early and under-submitted */
74 if (ret == fail_idx + 1 + drain)
75 goto out;
76 fprintf(stderr, "submit failed: %d\n", ret);
77 return -1;
78 }
79
80 if (drain) {
81 /* unclog drain */
82 ret = write(fds[1], buffer, sizeof(buffer));
83 if (ret < 0) {
84 perror("write");
85 return 1;
86 }
87 }
88
89 for (i = 0; i < total_submit; i++) {
90 ret = io_uring_wait_cqe(&ring, &cqe);
91 if (ret) {
92 fprintf(stderr, "wait_cqe=%d\n", ret);
93 return 1;
94 }
95
96 ret = cqe->res;
97 if (cqe->user_data == DRAIN_USER_DATA) {
98 if (ret != 1) {
99 fprintf(stderr, "drain failed %d\n", ret);
100 return 1;
101 }
102 } else if (cqe->user_data == fail_idx) {
103 if (ret == 0 || ret == -ECANCELED) {
104 fprintf(stderr, "half-prep req unexpected return %d\n", ret);
105 return 1;
106 }
107 } else {
108 if (ret != -ECANCELED) {
109 fprintf(stderr, "cancel failed %d, ud %d\n", ret, (int)cqe->user_data);
110 return 1;
111 }
112 }
113 io_uring_cqe_seen(&ring, cqe);
114 }
115 out:
116 close(fds[0]);
117 close(fds[1]);
118 io_uring_queue_exit(&ring);
119 return 0;
120 }
121
main(int argc,char * argv[])122 int main(int argc, char *argv[])
123 {
124 int ret, link_size, fail_idx, i;
125
126 if (argc > 1)
127 return 0;
128
129 /*
130 * hardlink, size=3, fail_idx=1, drain=false -- kernel fault
131 * link, size=3, fail_idx=0, drain=true -- kernel fault
132 * link, size=3, fail_idx=1, drain=true -- invalid cqe->res
133 */
134 for (link_size = 0; link_size < 3; link_size++) {
135 for (fail_idx = 0; fail_idx < link_size; fail_idx++) {
136 for (i = 0; i < 8; i++) {
137 bool hardlink = (i & 1) != 0;
138 bool drain = (i & 2) != 0;
139 bool link_last = (i & 4) != 0;
140
141 ret = test_underprep_fail(hardlink, drain, link_last,
142 link_size, fail_idx);
143 if (!ret)
144 continue;
145
146 fprintf(stderr, "failed %d, hard %d, drain %d,"
147 "link_last %d, size %d, idx %d\n",
148 ret, hardlink, drain, link_last,
149 link_size, fail_idx);
150 return 1;
151 }
152 }
153 }
154
155 return 0;
156 }
157