• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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