1 /*
2 * Copyright (c) 2004 Daniel McNeil <daniel@osdl.org>
3 * 2004 Open Source Development Lab
4 *
5 * Copyright (c) 2004 Marty Ridgeway <mridge@us.ibm.com>
6 *
7 * Copyright (c) 2011 Cyril Hrubis <chrubis@suse.cz>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
17 * the GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 */
23
24 #define _GNU_SOURCE
25
26 #include <stdlib.h>
27 #include <sys/types.h>
28 #include <errno.h>
29 #include <signal.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <unistd.h>
33 #include <sys/mman.h>
34 #include <sys/wait.h>
35 #include <limits.h>
36 #include <getopt.h>
37
38 #include <libaio.h>
39
40 #include "test.h"
41 #include "safe_macros.h"
42
43 #define NUM_CHILDREN 1000
44
45 int debug;
46 int fd;
47
48 static void setup(void);
49 static void cleanup(void);
50 static void usage(void);
51
52 char *TCID = "aiodio_sparse";
53 int TST_TOTAL = 1;
54
55 #include "common_sparse.h"
56
57 /*
58 * do async DIO writes to a sparse file
59 */
aiodio_sparse(int fd,int align,int writesize,int filesize,int num_aio)60 int aiodio_sparse(int fd, int align, int writesize, int filesize, int num_aio)
61 {
62 int i, w;
63 struct iocb **iocbs;
64 off_t offset;
65 io_context_t myctx;
66 struct io_event event;
67 int aio_inflight;
68
69 if ((num_aio * writesize) > filesize)
70 num_aio = filesize / writesize;
71
72 memset(&myctx, 0, sizeof(myctx));
73 io_queue_init(num_aio, &myctx);
74
75 iocbs = malloc(sizeof(struct iocb *) * num_aio);
76 for (i = 0; i < num_aio; i++) {
77 if ((iocbs[i] = malloc(sizeof(struct iocb))) == 0) {
78 tst_resm(TBROK | TERRNO, "malloc()");
79 return 1;
80 }
81 }
82
83 /*
84 * allocate the iocbs array and iocbs with buffers
85 */
86 offset = 0;
87 for (i = 0; i < num_aio; i++) {
88 void *bufptr;
89
90 TEST(posix_memalign(&bufptr, align, writesize));
91 if (TEST_RETURN) {
92 tst_resm(TBROK | TRERRNO, "cannot allocate aligned memory");
93 return 1;
94 }
95 memset(bufptr, 0, writesize);
96 io_prep_pwrite(iocbs[i], fd, bufptr, writesize, offset);
97 offset += writesize;
98 }
99
100 /*
101 * start the 1st num_aio write requests
102 */
103 if ((w = io_submit(myctx, num_aio, iocbs)) < 0) {
104 tst_resm(TBROK, "io_submit() returned %i", w);
105 return 1;
106 }
107
108 if (debug)
109 tst_resm(TINFO, "io_submit() returned %d", w);
110
111 /*
112 * As AIO requests finish, keep issuing more AIO until done.
113 */
114 aio_inflight = num_aio;
115
116 while (offset < filesize) {
117 int n;
118 struct iocb *iocbp;
119
120 if (debug)
121 tst_resm(TINFO,
122 "aiodio_sparse: offset %p filesize %d inflight %d",
123 &offset, filesize, aio_inflight);
124
125 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
126 if (-n != EINTR)
127 tst_resm(TBROK, "io_getevents() returned %d",
128 n);
129 break;
130 }
131
132 if (debug)
133 tst_resm(TINFO,
134 "aiodio_sparse: io_getevent() returned %d", n);
135
136 aio_inflight--;
137
138 /*
139 * check if write succeeded.
140 */
141 iocbp = (struct iocb *)event.obj;
142 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
143 tst_resm(TBROK,
144 "AIO write offset %lld expected %ld got %ld",
145 iocbp->u.c.offset, iocbp->u.c.nbytes,
146 event.res);
147 break;
148 }
149
150 if (debug)
151 tst_resm(TINFO,
152 "aiodio_sparse: io_getevent() res %ld res2 %ld",
153 event.res, event.res2);
154
155 /* start next write */
156 io_prep_pwrite(iocbp, fd, iocbp->u.c.buf, writesize, offset);
157 offset += writesize;
158 if ((w = io_submit(myctx, 1, &iocbp)) < 0) {
159 tst_resm(TBROK, "io_submit failed at offset %ld",
160 offset);
161 break;
162 }
163
164 if (debug)
165 tst_resm(TINFO, "io_submit() return %d", w);
166
167 aio_inflight++;
168 }
169
170 /*
171 * wait for AIO requests in flight.
172 */
173 while (aio_inflight > 0) {
174 int n;
175 struct iocb *iocbp;
176
177 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
178 tst_resm(TBROK, "io_getevents failed");
179 break;
180 }
181 aio_inflight--;
182 /*
183 * check if write succeeded.
184 */
185 iocbp = (struct iocb *)event.obj;
186 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
187 tst_resm(TBROK,
188 "AIO write offset %lld expected %ld got %ld",
189 iocbp->u.c.offset, iocbp->u.c.nbytes,
190 event.res);
191 }
192 }
193
194 return 0;
195 }
196
usage(void)197 static void usage(void)
198 {
199 fprintf(stderr, "usage: dio_sparse [-n children] [-s filesize]"
200 " [-w writesize]\n");
201 exit(1);
202 }
203
main(int argc,char ** argv)204 int main(int argc, char **argv)
205 {
206 char *filename = "aiodio_sparse";
207 int pid[NUM_CHILDREN];
208 int num_children = 1;
209 int i;
210 long alignment = 512;
211 int writesize = 65536;
212 int filesize = 100 * 1024 * 1024;
213 int num_aio = 16;
214 int children_errors = 0;
215 int c;
216 int ret;
217
218 while ((c = getopt(argc, argv, "dw:n:a:s:i:")) != -1) {
219 char *endp;
220 switch (c) {
221 case 'd':
222 debug++;
223 break;
224 case 'i':
225 num_aio = atoi(optarg);
226 break;
227 case 'a':
228 alignment = strtol(optarg, &endp, 0);
229 alignment = (int)scale_by_kmg((long long)alignment,
230 *endp);
231 break;
232 case 'w':
233 writesize = strtol(optarg, &endp, 0);
234 writesize =
235 (int)scale_by_kmg((long long)writesize, *endp);
236 break;
237 case 's':
238 filesize = strtol(optarg, &endp, 0);
239 filesize =
240 (int)scale_by_kmg((long long)filesize, *endp);
241 break;
242 case 'n':
243 num_children = atoi(optarg);
244 if (num_children > NUM_CHILDREN) {
245 fprintf(stderr,
246 "number of children limited to %d\n",
247 NUM_CHILDREN);
248 num_children = NUM_CHILDREN;
249 }
250 break;
251 case '?':
252 usage();
253 break;
254 }
255 }
256
257 setup();
258 tst_resm(TINFO, "Dirtying free blocks");
259 dirty_freeblocks(filesize);
260
261 fd = SAFE_OPEN(cleanup, filename,
262 O_DIRECT | O_WRONLY | O_CREAT | O_EXCL, 0600);
263 SAFE_FTRUNCATE(cleanup, fd, filesize);
264
265 tst_resm(TINFO, "Starting I/O tests");
266 signal(SIGTERM, SIG_DFL);
267 for (i = 0; i < num_children; i++) {
268 switch (pid[i] = fork()) {
269 case 0:
270 SAFE_CLOSE(NULL, fd);
271 read_sparse(filename, filesize);
272 break;
273 case -1:
274 while (i-- > 0)
275 kill(pid[i], SIGTERM);
276
277 tst_brkm(TBROK | TERRNO, cleanup, "fork()");
278 default:
279 continue;
280 }
281 }
282 tst_sig(FORK, DEF_HANDLER, cleanup);
283
284 ret = aiodio_sparse(fd, alignment, writesize, filesize, num_aio);
285
286 tst_resm(TINFO, "Killing childrens(s)");
287
288 for (i = 0; i < num_children; i++)
289 kill(pid[i], SIGTERM);
290
291 for (i = 0; i < num_children; i++) {
292 int status;
293 pid_t p;
294
295 p = waitpid(pid[i], &status, 0);
296 if (p < 0) {
297 tst_resm(TBROK | TERRNO, "waitpid()");
298 } else {
299 if (WIFEXITED(status) && WEXITSTATUS(status) == 10)
300 children_errors++;
301 }
302 }
303
304 if (children_errors)
305 tst_resm(TFAIL, "%i children(s) exited abnormally",
306 children_errors);
307
308 if (!children_errors && !ret)
309 tst_resm(TPASS, "Test passed");
310
311 cleanup();
312 tst_exit();
313 }
314
setup(void)315 static void setup(void)
316 {
317 tst_sig(FORK, DEF_HANDLER, cleanup);
318 tst_tmpdir();
319 }
320
cleanup(void)321 static void cleanup(void)
322 {
323 if (fd > 0 && close(fd))
324 tst_resm(TWARN | TERRNO, "Failed to close file");
325
326 tst_rmdir();
327 }
328