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