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