• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 	int i = 0;
30 
31 	while (!data->do_exit) {
32 		posix_fadvise(data->fd1, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
33 		posix_fadvise(data->fd2, 0, FILE_SIZE, POSIX_FADV_DONTNEED);
34 		usleep(10);
35 		i++;
36 	}
37 
38 	return NULL;
39 }
40 
41 static char str1[STR_SIZE];
42 static char str2[STR_SIZE];
43 
44 static struct io_uring ring;
45 
46 static int no_stable;
47 
prep(int fd,char * str,int split,int async)48 static int prep(int fd, char *str, int split, int async)
49 {
50 	struct io_uring_sqe *sqe;
51 	struct iovec iovs[16];
52 	int ret, i;
53 
54 	if (split) {
55 		int vsize = STR_SIZE / 16;
56 		void *ptr = str;
57 
58 		for (i = 0; i < 16; i++) {
59 			iovs[i].iov_base = ptr;
60 			iovs[i].iov_len = vsize;
61 			ptr += vsize;
62 		}
63 	} else {
64 		iovs[0].iov_base = str;
65 		iovs[0].iov_len = STR_SIZE;
66 	}
67 
68 	sqe = io_uring_get_sqe(&ring);
69 	io_uring_prep_readv(sqe, fd, iovs, split ? 16 : 1, 0);
70 	sqe->user_data = fd;
71 	if (async)
72 		sqe->flags = IOSQE_ASYNC;
73 	ret = io_uring_submit(&ring);
74 	if (ret != 1) {
75 		fprintf(stderr, "submit got %d\n", ret);
76 		return 1;
77 	}
78 	if (split) {
79 		for (i = 0; i < 16; i++)
80 			iovs[i].iov_base = NULL;
81 	} else {
82 		iovs[0].iov_base = NULL;
83 	}
84 	return 0;
85 }
86 
wait_nr(int nr)87 static int wait_nr(int nr)
88 {
89 	int i, ret;
90 
91 	for (i = 0; i < nr; i++) {
92 		struct io_uring_cqe *cqe;
93 
94 		ret = io_uring_wait_cqe(&ring, &cqe);
95 		if (ret)
96 			return ret;
97 		if (cqe->res < 0) {
98 			fprintf(stderr, "cqe->res=%d\n", cqe->res);
99 			return 1;
100 		}
101 		io_uring_cqe_seen(&ring, cqe);
102 	}
103 
104 	return 0;
105 }
106 
mtime_since(const struct timeval * s,const struct timeval * e)107 static unsigned long long mtime_since(const struct timeval *s,
108 				      const struct timeval *e)
109 {
110 	long long sec, usec;
111 
112 	sec = e->tv_sec - s->tv_sec;
113 	usec = (e->tv_usec - s->tv_usec);
114 	if (sec > 0 && usec < 0) {
115 		sec--;
116 		usec += 1000000;
117 	}
118 
119 	sec *= 1000;
120 	usec /= 1000;
121 	return sec + usec;
122 }
123 
mtime_since_now(struct timeval * tv)124 static unsigned long long mtime_since_now(struct timeval *tv)
125 {
126 	struct timeval end;
127 
128 	gettimeofday(&end, NULL);
129 	return mtime_since(tv, &end);
130 }
131 
test_reuse(int argc,char * argv[],int split,int async)132 static int test_reuse(int argc, char *argv[], int split, int async)
133 {
134 	struct thread_data data;
135 	struct io_uring_params p = { };
136 	int fd1, fd2, ret, i;
137 	struct timeval tv;
138 	pthread_t thread;
139 	char *fname1 = ".reuse.1";
140 	int do_unlink = 1;
141 	void *tret;
142 
143 	if (argc > 1) {
144 		fname1 = argv[1];
145 		do_unlink = 0;
146 	}
147 
148 	ret = io_uring_queue_init_params(32, &ring, &p);
149 	if (ret) {
150 		fprintf(stderr, "io_uring_queue_init: %d\n", ret);
151 		return 1;
152 	}
153 
154 	if (!(p.features & IORING_FEAT_SUBMIT_STABLE)) {
155 		fprintf(stdout, "FEAT_SUBMIT_STABLE not there, skipping\n");
156 		no_stable = 1;
157 		return 0;
158 	}
159 
160 	if (do_unlink)
161 		t_create_file(fname1, FILE_SIZE);
162 
163 	t_create_file(".reuse.2", FILE_SIZE);
164 
165 	fd1 = open(fname1, O_RDONLY);
166 	if (fd1 < 0) {
167 		perror("open fname1");
168 		goto err;
169 	}
170 	fd2 = open(".reuse.2", O_RDONLY);
171 	if (fd2 < 0) {
172 		perror("open .reuse.2");
173 		goto err;
174 	}
175 
176 	data.fd1 = fd1;
177 	data.fd2 = fd2;
178 	data.do_exit = 0;
179 	pthread_create(&thread, NULL, flusher, &data);
180 	usleep(10000);
181 
182 	gettimeofday(&tv, NULL);
183 	for (i = 0; i < 1000; i++) {
184 		ret = prep(fd1, str1, split, async);
185 		if (ret) {
186 			fprintf(stderr, "prep1 failed: %d\n", ret);
187 			goto err;
188 		}
189 		ret = prep(fd2, str2, split, async);
190 		if (ret) {
191 			fprintf(stderr, "prep1 failed: %d\n", ret);
192 			goto err;
193 		}
194 		ret = wait_nr(2);
195 		if (ret) {
196 			fprintf(stderr, "wait_nr: %d\n", ret);
197 			goto err;
198 		}
199 		if (mtime_since_now(&tv) > 5000)
200 			break;
201 	}
202 
203 	data.do_exit = 1;
204 	pthread_join(thread, &tret);
205 
206 	close(fd2);
207 	close(fd1);
208 	io_uring_queue_exit(&ring);
209 	if (do_unlink)
210 		unlink(fname1);
211 	unlink(".reuse.2");
212 	return 0;
213 err:
214 	io_uring_queue_exit(&ring);
215 	if (do_unlink)
216 		unlink(fname1);
217 	unlink(".reuse.2");
218 	return 1;
219 
220 }
221 
main(int argc,char * argv[])222 int main(int argc, char *argv[])
223 {
224 	int ret, i;
225 
226 	for (i = 0; i < 4; i++) {
227 		int split, async;
228 
229 		split = (i & 1) != 0;
230 		async = (i & 2) != 0;
231 
232 		ret = test_reuse(argc, argv, split, async);
233 		if (ret) {
234 			fprintf(stderr, "test_reuse %d %d failed\n", split, async);
235 			return ret;
236 		}
237 		if (no_stable)
238 			break;
239 	}
240 
241 	return 0;
242 }
243