xref: /aosp_15_r20/external/liburing/test/submit-reuse.c (revision 25da2bea747f3a93b4c30fd9708b0618ef55a0e6)
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Test reads that will punt to blocking context, with immediate overwrite
4  * of iovec->iov_base to NULL. If the kernel doesn't properly handle
5  * reuse of the iovec, we should get -EFAULT.
6  */
7 #include <unistd.h>
8 #include <fcntl.h>
9 #include <stdio.h>
10 #include <string.h>
11 #include <stdlib.h>
12 #include <pthread.h>
13 #include <sys/time.h>
14 
15 #include "helpers.h"
16 #include "liburing.h"
17 
18 #define STR_SIZE	32768
19 #define FILE_SIZE	65536
20 
21 struct thread_data {
22 	int fd1, fd2;
23 	volatile int do_exit;
24 };
25 
flusher(void * __data)26 static void *flusher(void *__data)
27 {
28 	struct thread_data *data = __data;
29 
30 	while (!data->do_exit) {
31 		posix_fadvise(data->fd1, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
32 		posix_fadvise(data->fd2, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
33 		usleep(10);
34 	}
35 
36 	return NULL;
37 }
38 
39 static char str1[STR_SIZE];
40 static char str2[STR_SIZE];
41 
42 static struct io_uring ring;
43 
44 static int no_stable;
45 
prep(int fd,char * str,int split,int async)46 static int prep(int fd, char *str, int split, int async)
47 {
48 	struct io_uring_sqe *sqe;
49 	struct iovec iovs[16];
50 	int ret, i;
51 
52 	if (split) {
53 		int vsize = STR_SIZE / 16;
54 		void *ptr = str;
55 
56 		for (i = 0; i < 16; i++) {
57 			iovs[i].iov_base = ptr;
58 			iovs[i].iov_len = vsize;
59 			ptr += vsize;
60 		}
61 	} else {
62 		iovs[0].iov_base = str;
63 		iovs[0].iov_len = STR_SIZE;
64 	}
65 
66 	sqe = io_uring_get_sqe(&ring);
67 	io_uring_prep_readv(sqe, fd, iovs, split ? 16 : 1, 0);
68 	sqe->user_data = fd;
69 	if (async)
70 		sqe->flags = IOSQE_ASYNC;
71 	ret = io_uring_submit(&ring);
72 	if (ret != 1) {
73 		fprintf(stderr, "submit got %d\n", ret);
74 		return 1;
75 	}
76 	if (split) {
77 		for (i = 0; i < 16; i++)
78 			iovs[i].iov_base = NULL;
79 	} else {
80 		iovs[0].iov_base = NULL;
81 	}
82 	return 0;
83 }
84 
wait_nr(int nr)85 static int wait_nr(int nr)
86 {
87 	int i, ret;
88 
89 	for (i = 0; i < nr; i++) {
90 		struct io_uring_cqe *cqe;
91 
92 		ret = io_uring_wait_cqe(&ring, &cqe);
93 		if (ret)
94 			return ret;
95 		if (cqe->res < 0) {
96 			fprintf(stderr, "cqe->res=%d\n", cqe->res);
97 			return 1;
98 		}
99 		io_uring_cqe_seen(&ring, cqe);
100 	}
101 
102 	return 0;
103 }
104 
mtime_since(const struct timeval * s,const struct timeval * e)105 static unsigned long long mtime_since(const struct timeval *s,
106 				      const struct timeval *e)
107 {
108 	long long sec, usec;
109 
110 	sec = e->tv_sec - s->tv_sec;
111 	usec = (e->tv_usec - s->tv_usec);
112 	if (sec > 0 && usec < 0) {
113 		sec--;
114 		usec += 1000000;
115 	}
116 
117 	sec *= 1000;
118 	usec /= 1000;
119 	return sec + usec;
120 }
121 
mtime_since_now(struct timeval * tv)122 static unsigned long long mtime_since_now(struct timeval *tv)
123 {
124 	struct timeval end;
125 
126 	gettimeofday(&end, NULL);
127 	return mtime_since(tv, &end);
128 }
129 
test_reuse(int argc,char * argv[],int split,int async)130 static int test_reuse(int argc, char *argv[], int split, int async)
131 {
132 	struct thread_data data;
133 	struct io_uring_params p = { };
134 	int fd1, fd2, ret, i;
135 	struct timeval tv;
136 	pthread_t thread;
137 	char *fname1 = ".reuse.1";
138 	int do_unlink = 1;
139 	void *tret;
140 
141 	ret = io_uring_queue_init_params(32, &ring, &p);
142 	if (ret) {
143 		fprintf(stderr, "io_uring_queue_init: %d\n", ret);
144 		return 1;
145 	}
146 
147 	if (!(p.features & IORING_FEAT_SUBMIT_STABLE)) {
148 		fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
149 		io_uring_queue_exit(&ring);
150 		no_stable = 1;
151 		return 0;
152 	}
153 
154 	if (argc > 1) {
155 		fname1 = argv[1];
156 		do_unlink = 0;
157 	} else {
158 		t_create_file(fname1, FILE_SIZE);
159 	}
160 
161 	fd1 = open(fname1, O_RDONLY);
162 	if (do_unlink)
163 		unlink(fname1);
164 	if (fd1 < 0) {
165 		perror("open fname1");
166 		goto err;
167 	}
168 
169 	t_create_file(".reuse.2", FILE_SIZE);
170 	fd2 = open(".reuse.2", O_RDONLY);
171 	unlink(".reuse.2");
172 	if (fd2 < 0) {
173 		perror("open .reuse.2");
174 		goto err;
175 	}
176 
177 	data.fd1 = fd1;
178 	data.fd2 = fd2;
179 	data.do_exit = 0;
180 	pthread_create(&thread, NULL, flusher, &data);
181 	usleep(10000);
182 
183 	gettimeofday(&tv, NULL);
184 	for (i = 0; i < 1000; i++) {
185 		ret = prep(fd1, str1, split, async);
186 		if (ret) {
187 			fprintf(stderr, "prep1 failed: %d\n", ret);
188 			goto err;
189 		}
190 		ret = prep(fd2, str2, split, async);
191 		if (ret) {
192 			fprintf(stderr, "prep1 failed: %d\n", ret);
193 			goto err;
194 		}
195 		ret = wait_nr(2);
196 		if (ret) {
197 			fprintf(stderr, "wait_nr: %d\n", ret);
198 			goto err;
199 		}
200 		if (mtime_since_now(&tv) > 5000)
201 			break;
202 	}
203 
204 	data.do_exit = 1;
205 	pthread_join(thread, &tret);
206 
207 	close(fd2);
208 	close(fd1);
209 	io_uring_queue_exit(&ring);
210 	return 0;
211 err:
212 	io_uring_queue_exit(&ring);
213 	return 1;
214 
215 }
216 
main(int argc,char * argv[])217 int main(int argc, char *argv[])
218 {
219 	int ret, i;
220 
221 	for (i = 0; i < 4; i++) {
222 		int split, async;
223 
224 		split = (i & 1) != 0;
225 		async = (i & 2) != 0;
226 
227 		ret = test_reuse(argc, argv, split, async);
228 		if (ret) {
229 			fprintf(stderr, "test_reuse %d %d failed\n", split, async);
230 			return ret;
231 		}
232 		if (no_stable)
233 			break;
234 	}
235 
236 	return 0;
237 }
238