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
45int debug;
46int fd;
47
48static void setup(void);
49static void cleanup(void);
50static void usage(void);
51
52char *TCID = "aiodio_sparse";
53int TST_TOTAL = 1;
54
55#include "common_sparse.h"
56
57/*
58 * do async DIO writes to a sparse file
59 */
60int 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
197static void usage(void)
198{
199	fprintf(stderr, "usage: dio_sparse [-n children] [-s filesize]"
200		" [-w writesize]\n");
201	exit(1);
202}
203
204int 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
315static void setup(void)
316{
317	tst_sig(FORK, DEF_HANDLER, cleanup);
318	tst_tmpdir();
319}
320
321static 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