• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* SPDX-License-Identifier: MIT */
2 /*
3  * Description: test io_uring fpos handling
4  *
5  */
6 #include <errno.h>
7 #include <stdio.h>
8 #include <unistd.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <fcntl.h>
12 #include <assert.h>
13 
14 #include "helpers.h"
15 #include "liburing.h"
16 
17 #define FILE_SIZE 5000
18 #define QUEUE_SIZE 2048
19 
create_file(const char * file,size_t size)20 static void create_file(const char *file, size_t size)
21 {
22 	ssize_t ret;
23 	char *buf;
24 	size_t idx;
25 	int fd;
26 
27 	buf = t_malloc(size);
28 	for (idx = 0; idx < size; ++idx) {
29 		/* write 0 or 1 */
30 		buf[idx] = (unsigned char)(idx & 0x01);
31 	}
32 
33 	fd = open(file, O_WRONLY | O_CREAT, 0644);
34 	assert(fd >= 0);
35 
36 	ret = write(fd, buf, size);
37 	fsync(fd);
38 	close(fd);
39 	free(buf);
40 	assert(ret == size);
41 }
42 
test_read(struct io_uring * ring,bool async,int blocksize)43 static int test_read(struct io_uring *ring, bool async, int blocksize)
44 {
45 	int ret, fd, i;
46 	bool done = false;
47 	struct io_uring_sqe *sqe;
48 	struct io_uring_cqe *cqe;
49 	loff_t current, expected = 0;
50 	int count_ok;
51 	int count_0 = 0, count_1 = 0;
52 	unsigned char buff[QUEUE_SIZE * blocksize];
53 	unsigned char reordered[QUEUE_SIZE * blocksize];
54 
55 	memset(buff, 0, QUEUE_SIZE * blocksize);
56 	memset(reordered, 0, QUEUE_SIZE * blocksize);
57 
58 	create_file(".test_fpos_read", FILE_SIZE);
59 	fd = open(".test_fpos_read", O_RDONLY);
60 	unlink(".test_fpos_read");
61 	assert(fd >= 0);
62 
63 	while (!done) {
64 		for (i = 0; i < QUEUE_SIZE; ++i) {
65 			sqe = io_uring_get_sqe(ring);
66 			if (!sqe) {
67 				fprintf(stderr, "no sqe\n");
68 				return -1;
69 			}
70 			io_uring_prep_read(sqe, fd,
71 					buff + i * blocksize,
72 					blocksize, -1);
73 			sqe->user_data = i;
74 			if (async)
75 				sqe->flags |= IOSQE_ASYNC;
76 			if (i != QUEUE_SIZE - 1)
77 				sqe->flags |= IOSQE_IO_LINK;
78 		}
79 		ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
80 		if (ret != QUEUE_SIZE) {
81 			fprintf(stderr, "submit failed: %d\n", ret);
82 			return 1;
83 		}
84 		count_ok  = 0;
85 		for (i = 0; i < QUEUE_SIZE; ++i) {
86 			int res;
87 
88 			ret = io_uring_peek_cqe(ring, &cqe);
89 			if (ret) {
90 				fprintf(stderr, "peek failed: %d\n", ret);
91 				return ret;
92 			}
93 			assert(cqe->user_data < QUEUE_SIZE);
94 			memcpy(reordered + count_ok,
95 				buff + cqe->user_data * blocksize, blocksize);
96 			res = cqe->res;
97 			io_uring_cqe_seen(ring, cqe);
98 			if (res == 0) {
99 				done = true;
100 			} else if (res == -ECANCELED) {
101 				/* canceled, probably ok */
102 			} else if (res < 0 || res > blocksize) {
103 				fprintf(stderr, "bad read: %d\n", res);
104 				return -1;
105 			} else {
106 				expected += res;
107 				count_ok += res;
108 			}
109 		}
110 		ret = 0;
111 		for (i = 0; i < count_ok; i++) {
112 			if (reordered[i] == 1) {
113 				count_1++;
114 			} else if (reordered[i] == 0) {
115 				count_0++;
116 			} else {
117 				fprintf(stderr, "odd read %d\n",
118 						(int)reordered[i]);
119 				ret = -1;
120 				break;
121 			}
122 		}
123 		if (labs(count_1 - count_0) > 1) {
124 			fprintf(stderr, "inconsistent reads, got 0s:%d 1s:%d\n",
125 					count_0, count_1);
126 			ret = -1;
127 		}
128 		current = lseek(fd, 0, SEEK_CUR);
129 		if (current != expected) {
130 			fprintf(stderr, "f_pos incorrect, expected %ld have %ld\n",
131 					(long) expected, (long) current);
132 			ret = -1;
133 		}
134 		if (ret)
135 			return ret;
136 	}
137 	return 0;
138 }
139 
140 
test_write(struct io_uring * ring,bool async,int blocksize)141 static int test_write(struct io_uring *ring, bool async, int blocksize)
142 {
143 	int ret, fd, i;
144 	struct io_uring_sqe *sqe;
145 	struct io_uring_cqe *cqe;
146 	bool fail = false;
147 	loff_t current;
148 	char data[blocksize+1];
149 	char readbuff[QUEUE_SIZE*blocksize+1];
150 
151 	fd = open(".test_fpos_write", O_RDWR | O_CREAT, 0644);
152 	unlink(".test_fpos_write");
153 	assert(fd >= 0);
154 
155 	for (i = 0; i < blocksize; i++)
156 		data[i] = 'A' + i;
157 
158 	data[blocksize] = '\0';
159 
160 	for (i = 0; i < QUEUE_SIZE; ++i) {
161 		sqe = io_uring_get_sqe(ring);
162 		if (!sqe) {
163 			fprintf(stderr, "no sqe\n");
164 			return -1;
165 		}
166 		io_uring_prep_write(sqe, fd, data + (i % blocksize), 1, -1);
167 		sqe->user_data = 1;
168 		if (async)
169 			sqe->flags |= IOSQE_ASYNC;
170 		if (i != QUEUE_SIZE - 1)
171 			sqe->flags |= IOSQE_IO_LINK;
172 	}
173 	ret = io_uring_submit_and_wait(ring, QUEUE_SIZE);
174 	if (ret != QUEUE_SIZE) {
175 		fprintf(stderr, "submit failed: %d\n", ret);
176 		return 1;
177 	}
178 	for (i = 0; i < QUEUE_SIZE; ++i) {
179 		int res;
180 
181 		ret = io_uring_peek_cqe(ring, &cqe);
182 		res = cqe->res;
183 		if (ret) {
184 			fprintf(stderr, "peek failed: %d\n", ret);
185 			return ret;
186 		}
187 		io_uring_cqe_seen(ring, cqe);
188 		if (!fail && res != 1) {
189 			fprintf(stderr, "bad result %d\n", res);
190 			fail = true;
191 		}
192 	}
193 	current = lseek(fd, 0, SEEK_CUR);
194 	if (current != QUEUE_SIZE) {
195 		fprintf(stderr, "f_pos incorrect, expected %ld have %d\n",
196 				(long) current, QUEUE_SIZE);
197 		fail = true;
198 	}
199 	current = lseek(fd, 0, SEEK_SET);
200 	if (current != 0) {
201 		perror("seek to start");
202 		return -1;
203 	}
204 	ret = read(fd, readbuff, QUEUE_SIZE);
205 	if (ret != QUEUE_SIZE) {
206 		fprintf(stderr, "did not write enough: %d\n", ret);
207 		return -1;
208 	}
209 	i = 0;
210 	while (i < QUEUE_SIZE - blocksize) {
211 		if (strncmp(readbuff + i, data, blocksize)) {
212 			char bad[QUEUE_SIZE+1];
213 
214 			memcpy(bad, readbuff + i, blocksize);
215 			bad[blocksize] = '\0';
216 			fprintf(stderr, "unexpected data %s\n", bad);
217 			fail = true;
218 		}
219 		i += blocksize;
220 	}
221 
222 	return fail ? -1 : 0;
223 }
224 
main(int argc,char * argv[])225 int main(int argc, char *argv[])
226 {
227 	struct io_uring ring;
228 	int ret;
229 
230 	if (argc > 1)
231 		return T_EXIT_SKIP;
232 
233 	ret = io_uring_queue_init(QUEUE_SIZE, &ring, 0);
234 	if (ret) {
235 		fprintf(stderr, "ring setup failed\n");
236 		return T_EXIT_FAIL;
237 	}
238 
239 	for (int test = 0; test < 8; test++) {
240 		int async = test & 0x01;
241 		int write = test & 0x02;
242 		int blocksize = test & 0x04 ? 1 : 7;
243 
244 		ret = write
245 			? test_write(&ring, !!async, blocksize)
246 			: test_read(&ring, !!async, blocksize);
247 		if (ret) {
248 			fprintf(stderr, "failed %s async=%d blocksize=%d\n",
249 					write ? "write" : "read",
250 					async, blocksize);
251 			return -1;
252 		}
253 	}
254 	return T_EXIT_PASS;
255 }
256