xref: /aosp_15_r20/external/liburing/test/submit-link-fail.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
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