• 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 
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