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
47 static void setup(void);
48 static void cleanup(void);
49 static void usage(void);
50
51 char *TCID = "aiodio_sparse";
52 int TST_TOTAL = 1;
53
54 #include "common_sparse.h"
55
56 /*
57 * do async DIO writes to a sparse file
58 */
aiodio_sparse(char * filename,int align,int writesize,int filesize,int num_aio)59 int aiodio_sparse(char *filename, int align, int writesize, int filesize,
60 int num_aio)
61 {
62 int fd;
63 int i, w;
64 struct iocb **iocbs;
65 off_t offset;
66 io_context_t myctx;
67 struct io_event event;
68 int aio_inflight;
69
70 if ((num_aio * writesize) > filesize)
71 num_aio = filesize / writesize;
72
73 memset(&myctx, 0, sizeof(myctx));
74 io_queue_init(num_aio, &myctx);
75
76 iocbs = malloc(sizeof(struct iocb *) * num_aio);
77 for (i = 0; i < num_aio; i++) {
78 if ((iocbs[i] = malloc(sizeof(struct iocb))) == 0) {
79 tst_resm(TBROK | TERRNO, "malloc()");
80 return 1;
81 }
82 }
83
84 fd = open(filename, O_DIRECT | O_WRONLY | O_CREAT | O_EXCL, 0600);
85
86 if (fd < 0) {
87 tst_resm(TBROK | TERRNO, "open()");
88 return 1;
89 }
90
91 SAFE_FTRUNCATE(cleanup, fd, filesize);
92
93 /*
94 * allocate the iocbs array and iocbs with buffers
95 */
96 offset = 0;
97 for (i = 0; i < num_aio; i++) {
98 void *bufptr;
99
100 if (posix_memalign(&bufptr, align, writesize)) {
101 tst_resm(TBROK | TERRNO, "posix_memalign()");
102 close(fd);
103 unlink(filename);
104 return 1;
105 }
106 memset(bufptr, 0, writesize);
107 io_prep_pwrite(iocbs[i], fd, bufptr, writesize, offset);
108 offset += writesize;
109 }
110
111 /*
112 * start the 1st num_aio write requests
113 */
114 if ((w = io_submit(myctx, num_aio, iocbs)) < 0) {
115 tst_resm(TBROK, "io_submit() returned %i", w);
116 close(fd);
117 unlink(filename);
118 return 1;
119 }
120
121 if (debug)
122 tst_resm(TINFO, "io_submit() returned %d", w);
123
124 /*
125 * As AIO requests finish, keep issuing more AIO until done.
126 */
127 aio_inflight = num_aio;
128
129 while (offset < filesize) {
130 int n;
131 struct iocb *iocbp;
132
133 if (debug)
134 tst_resm(TINFO,
135 "aiodio_sparse: offset %p filesize %d inflight %d",
136 &offset, filesize, aio_inflight);
137
138 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
139 if (-n != EINTR)
140 tst_resm(TBROK, "io_getevents() returned %d",
141 n);
142 break;
143 }
144
145 if (debug)
146 tst_resm(TINFO,
147 "aiodio_sparse: io_getevent() returned %d", n);
148
149 aio_inflight--;
150
151 /*
152 * check if write succeeded.
153 */
154 iocbp = (struct iocb *)event.obj;
155 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
156 tst_resm(TBROK,
157 "AIO write offset %lld expected %ld got %ld",
158 iocbp->u.c.offset, iocbp->u.c.nbytes,
159 event.res);
160 break;
161 }
162
163 if (debug)
164 tst_resm(TINFO,
165 "aiodio_sparse: io_getevent() res %ld res2 %ld",
166 event.res, event.res2);
167
168 /* start next write */
169 io_prep_pwrite(iocbp, fd, iocbp->u.c.buf, writesize, offset);
170 offset += writesize;
171 if ((w = io_submit(myctx, 1, &iocbp)) < 0) {
172 tst_resm(TBROK, "io_submit failed at offset %ld",
173 offset);
174 break;
175 }
176
177 if (debug)
178 tst_resm(TINFO, "io_submit() return %d", w);
179
180 aio_inflight++;
181 }
182
183 /*
184 * wait for AIO requests in flight.
185 */
186 while (aio_inflight > 0) {
187 int n;
188 struct iocb *iocbp;
189
190 if ((n = io_getevents(myctx, 1, 1, &event, 0)) != 1) {
191 tst_resm(TBROK, "io_getevents failed");
192 break;
193 }
194 aio_inflight--;
195 /*
196 * check if write succeeded.
197 */
198 iocbp = (struct iocb *)event.obj;
199 if (event.res2 != 0 || event.res != iocbp->u.c.nbytes) {
200 tst_resm(TBROK,
201 "AIO write offset %lld expected %ld got %ld",
202 iocbp->u.c.offset, iocbp->u.c.nbytes,
203 event.res);
204 }
205 }
206
207 close(fd);
208 unlink(filename);
209
210 return 0;
211 }
212
usage(void)213 static void usage(void)
214 {
215 fprintf(stderr, "usage: dio_sparse [-n children] [-s filesize]"
216 " [-w writesize]\n");
217 exit(1);
218 }
219
main(int argc,char ** argv)220 int main(int argc, char **argv)
221 {
222 char *filename = "aiodio_sparse";
223 int pid[NUM_CHILDREN];
224 int num_children = 1;
225 int i;
226 long alignment = 512;
227 int writesize = 65536;
228 int filesize = 100 * 1024 * 1024;
229 int num_aio = 16;
230 int children_errors = 0;
231 int c;
232 int ret;
233
234 while ((c = getopt(argc, argv, "dw:n:a:s:i:")) != -1) {
235 char *endp;
236 switch (c) {
237 case 'd':
238 debug++;
239 break;
240 case 'i':
241 num_aio = atoi(optarg);
242 break;
243 case 'a':
244 alignment = strtol(optarg, &endp, 0);
245 alignment = (int)scale_by_kmg((long long)alignment,
246 *endp);
247 break;
248 case 'w':
249 writesize = strtol(optarg, &endp, 0);
250 writesize =
251 (int)scale_by_kmg((long long)writesize, *endp);
252 break;
253 case 's':
254 filesize = strtol(optarg, &endp, 0);
255 filesize =
256 (int)scale_by_kmg((long long)filesize, *endp);
257 break;
258 case 'n':
259 num_children = atoi(optarg);
260 if (num_children > NUM_CHILDREN) {
261 fprintf(stderr,
262 "number of children limited to %d\n",
263 NUM_CHILDREN);
264 num_children = NUM_CHILDREN;
265 }
266 break;
267 case '?':
268 usage();
269 break;
270 }
271 }
272
273 setup();
274 tst_resm(TINFO, "Dirtying free blocks");
275 dirty_freeblocks(filesize);
276
277 tst_resm(TINFO, "Starting I/O tests");
278 signal(SIGTERM, SIG_DFL);
279 for (i = 0; i < num_children; i++) {
280 switch (pid[i] = fork()) {
281 case 0:
282 read_sparse(filename, filesize);
283 break;
284 case -1:
285 while (i-- > 0)
286 kill(pid[i], SIGTERM);
287
288 tst_brkm(TBROK | TERRNO, cleanup, "fork()");
289 default:
290 continue;
291 }
292 }
293 tst_sig(FORK, DEF_HANDLER, cleanup);
294
295 ret = aiodio_sparse(filename, alignment, writesize, filesize, num_aio);
296
297 tst_resm(TINFO, "Killing childrens(s)");
298
299 for (i = 0; i < num_children; i++)
300 kill(pid[i], SIGTERM);
301
302 for (i = 0; i < num_children; i++) {
303 int status;
304 pid_t p;
305
306 p = waitpid(pid[i], &status, 0);
307 if (p < 0) {
308 tst_resm(TBROK | TERRNO, "waitpid()");
309 } else {
310 if (WIFEXITED(status) && WEXITSTATUS(status) == 10)
311 children_errors++;
312 }
313 }
314
315 if (children_errors)
316 tst_resm(TFAIL, "%i children(s) exited abnormally",
317 children_errors);
318
319 if (!children_errors && !ret)
320 tst_resm(TPASS, "Test passed");
321
322 cleanup();
323 tst_exit();
324 }
325
setup(void)326 static void setup(void)
327 {
328 tst_sig(FORK, DEF_HANDLER, cleanup);
329 tst_tmpdir();
330 }
331
cleanup(void)332 static void cleanup(void)
333 {
334 tst_rmdir();
335 tst_exit();
336 }
337