Home | History | Annotate | Download | only in direct_io
      1 /*
      2  * Copyright (c) International Business Machines  Corp., 2002
      3  *  04/30/2002 Narasimha Sharoff nsharoff (at) us.ibm.com
      4  *
      5  * This program is free software;  you can redistribute it and/or modify
      6  * it under the terms of the GNU General Public License as published by
      7  * the Free Software Foundation; either version 2 of the License, or
      8  * (at your option) any later version.
      9  *
     10  * This program is distributed in the hope that it will be useful,
     11  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
     12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
     13  * the GNU General Public License for more details.
     14  *
     15  * You should have received a copy of the GNU General Public License
     16  * along with this program;  if not, write to the Free Software
     17  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
     18  */
     19 
     20 /*
     21  * DESCRIPTION
     22  *	Fork given number of children. Each child opens the same file, but
     23  *	uses its own file descriptior. The child does writes and reads from
     24  *	its segment in the file. The segment to which the child writes is
     25  *	determined by childnumber * bufsize. There is no need to use any locks.
     26  *	Tests the combinations of buffered/direct readv(), writev() calls.
     27  *	Test program contains the following test blocks:
     28  *	[1] Direct Read, Buffered write
     29  *	[2] Direct Write, Buffered read
     30  *	[3] Direct Read, Direct Write
     31  *
     32  * USAGE
     33  *	diotest6 [-b bufsize] [-o offset] [-n numchild] [-i iterations]
     34  *			[-v nvector] [-f fileaname]
     35 */
     36 
     37 #include <stdio.h>
     38 #include <stdlib.h>
     39 #include <unistd.h>
     40 #include <string.h>
     41 #include <sys/file.h>
     42 #include <fcntl.h>
     43 #include <sys/syscall.h>
     44 #include <sys/uio.h>
     45 #include <errno.h>
     46 
     47 #include "diotest_routines.h"
     48 
     49 #include "test.h"
     50 
     51 char *TCID = "diotest06";
     52 int TST_TOTAL = 3;
     53 
     54 #ifdef O_DIRECT
     55 
     56 #define	BUFSIZE	4096
     57 #define TRUE 1
     58 #define LEN 30
     59 #define	READ_DIRECT 1
     60 #define	WRITE_DIRECT 2
     61 #define	RDWR_DIRECT 3
     62 
     63 static int iter = 100;
     64 static int bufsize = BUFSIZE;
     65 static off64_t offset = 0;
     66 static int nvector = 20;
     67 static char filename[LEN];
     68 static int fd1 = -1;
     69 
     70 static void setup(void);
     71 static void cleanup(void);
     72 
     73 static void prg_usage(void)
     74 {
     75 	fprintf(stderr,
     76 		"Usage: diotest6 [-b bufsize] [-o offset] [-n numchild] [-i iterations] [-v nvector] [-f filename]\n");
     77 	exit(1);
     78 }
     79 
     80 /*
     81  * runtest: write the data to the file. Read the data from the file and compare.
     82  *	For each iteration, write data starting at offse+iter*bufsize
     83  *	location in the file and read from there.
     84 */
     85 int runtest(int fd_r, int fd_w, int childnum, int action)
     86 {
     87 	off64_t seekoff;
     88 	int i, ret = -1;
     89 	ssize_t n = 0;
     90 	struct iovec *iov_r, *iov_w;
     91 
     92 	/* allocate read/write io vectors */
     93 	iov_r = calloc(nvector, sizeof(*iov_r));
     94 	iov_w = calloc(nvector, sizeof(*iov_w));
     95 	if (!iov_r || !iov_w) {
     96 		tst_resm(TBROK | TERRNO, "calloc failed for iovector array");
     97 		free(iov_r);
     98 		free(iov_w);
     99 		return ret;
    100 	}
    101 
    102 	/* allocate buffers and setup read/write io vectors */
    103 	for (i = 0; i < nvector; i++) {
    104 		iov_r[i].iov_base = valloc(bufsize);
    105 		if (!iov_r[i].iov_base) {
    106 			tst_resm(TBROK | TERRNO, "valloc error iov_r[%d]", i);
    107 			goto err;
    108 		}
    109 		iov_r[i].iov_len = bufsize;
    110 	}
    111 	for (i = 0; i < nvector; i++) {
    112 		iov_w[i].iov_base = valloc(bufsize);
    113 		if (!iov_r[i].iov_base) {
    114 			tst_resm(TBROK | TERRNO, "valloc error iov_w[%d]", i);
    115 			goto err;
    116 		}
    117 		iov_w[i].iov_len = bufsize;
    118 	}
    119 
    120 	/* seek, write, read and verify */
    121 	seekoff = offset + bufsize * childnum * nvector;
    122 	for (i = 0; i < iter; i++) {
    123 		vfillbuf(iov_w, nvector, childnum+i);
    124 
    125 		if (lseek(fd_w, seekoff, SEEK_SET) < 0) {
    126 			tst_resm(TFAIL, "lseek before write failed: %s",
    127 				 strerror(errno));
    128 			goto err;
    129 		}
    130 		n = writev(fd_w, iov_w, nvector);
    131 		if (n < (bufsize * nvector)) {
    132 			tst_resm(TFAIL | TERRNO, "writev failed, ret = %zd", n);
    133 			goto err;
    134 		}
    135 		if (action == READ_DIRECT) {
    136 			/* Make sure data is on to disk before read */
    137 			if (fsync(fd_w) < 0) {
    138 				tst_resm(TFAIL, "fsync failed: %s",
    139 					 strerror(errno));
    140 				goto err;
    141 			}
    142 		}
    143 		if (lseek(fd_r, seekoff, SEEK_SET) < 0) {
    144 			tst_resm(TFAIL, "lseek before read failed: %s",
    145 				 strerror(errno));
    146 			goto err;
    147 		}
    148 		n = readv(fd_r, iov_r, nvector);
    149 		if (n < (bufsize * nvector)) {
    150 			tst_resm(TFAIL | TERRNO, "readv failed, ret = %zd", n);
    151 			goto err;
    152 		}
    153 		if (vbufcmp(iov_w, iov_r, nvector) != 0) {
    154 			tst_resm(TFAIL, "comparsion failed. Child=%d offset=%d",
    155 				 childnum, (int)seekoff);
    156 			goto err;
    157 		}
    158 	}
    159 	ret = 0;
    160 
    161 err:
    162 	for (i = 0; i < nvector; i++)
    163 		free(iov_r[i].iov_base);
    164 	for (i = 0; i < nvector; i++)
    165 		free(iov_w[i].iov_base);
    166 	free(iov_r);
    167 	free(iov_w);
    168 	return ret;
    169 }
    170 
    171 /*
    172  * child_function: open the file for read and write. Call the runtest routine.
    173 */
    174 int child_function(int childnum, int action)
    175 {
    176 	int fd_w, fd_r;
    177 
    178 	switch (action) {
    179 	case READ_DIRECT:
    180 		if ((fd_w = open(filename, O_WRONLY | O_CREAT, 0666)) < 0) {
    181 			tst_resm(TFAIL, "fd_w open failed for %s: %s",
    182 				 filename, strerror(errno));
    183 			return (-1);
    184 		}
    185 		if ((fd_r = open(filename, O_DIRECT | O_RDONLY, 0666)) < 0) {
    186 			tst_resm(TFAIL, "fd_r open failed for %s: %s",
    187 				 filename, strerror(errno));
    188 			close(fd_w);
    189 			unlink(filename);
    190 			return (-1);
    191 		}
    192 		if (runtest(fd_r, fd_w, childnum, action) == -1) {
    193 			tst_resm(TFAIL, "Read Direct-child %d failed",
    194 				 childnum);
    195 			close(fd_w);
    196 			close(fd_r);
    197 			return (-1);
    198 		}
    199 		break;
    200 	case WRITE_DIRECT:
    201 		if ((fd_w =
    202 		     open(filename, O_DIRECT | O_WRONLY | O_CREAT, 0666)) < 0) {
    203 			tst_resm(TFAIL, "fd_w open failed for %s: %s", filename,
    204 				 strerror(errno));
    205 			return (-1);
    206 		}
    207 		if ((fd_r = open(filename, O_RDONLY, 0666)) < 0) {
    208 			tst_resm(TFAIL, "fd_r open failed for %s: %s",
    209 				 filename, strerror(errno));
    210 			close(fd_w);
    211 			unlink(filename);
    212 			return (-1);
    213 		}
    214 		if (runtest(fd_r, fd_w, childnum, action) == -1) {
    215 			tst_resm(TFAIL, "Write Direct-child %d failed",
    216 				 childnum);
    217 			close(fd_w);
    218 			close(fd_r);
    219 			return (-1);
    220 		}
    221 		break;
    222 	case RDWR_DIRECT:
    223 		if ((fd_w =
    224 		     open(filename, O_DIRECT | O_WRONLY | O_CREAT, 0666)) < 0) {
    225 			tst_resm(TFAIL, "fd_w open failed for %s: %s", filename,
    226 				 strerror(errno));
    227 			return (-1);
    228 		}
    229 		if ((fd_r = open(filename, O_DIRECT | O_RDONLY, 0666)) < 0) {
    230 			tst_resm(TFAIL, "fd_r open failed for %s: %s",
    231 				 filename, strerror(errno));
    232 			close(fd_w);
    233 			return (-1);
    234 		}
    235 		if (runtest(fd_r, fd_w, childnum, action) == -1) {
    236 			tst_resm(TFAIL, "RDWR Direct-child %d failed",
    237 				 childnum);
    238 			close(fd_w);
    239 			close(fd_r);
    240 			return (-1);
    241 		}
    242 		break;
    243 	default:
    244 		fprintf(stderr, "Invalid Action Value\n");
    245 		return (-1);
    246 	}
    247 	close(fd_w);
    248 	close(fd_r);
    249 	exit(0);
    250 }
    251 
    252 int main(int argc, char *argv[])
    253 {
    254 	int *pidlst;
    255 	int numchild = 1;
    256 	int i, fail_count = 0, failed = 0, total = 0;
    257 
    258 	/* Options */
    259 	sprintf(filename, "testdata-6.%ld", syscall(__NR_gettid));
    260 	while ((i = getopt(argc, argv, "b:o:i:n:v:f:")) != -1) {
    261 		switch (i) {
    262 		case 'b':
    263 			if ((bufsize = atoi(optarg)) <= 0) {
    264 				fprintf(stderr, "bufsize must be > 0\n");
    265 				prg_usage();
    266 			}
    267 			if (bufsize % 4096 != 0) {
    268 				fprintf(stderr,
    269 					"bufsize must be multiple of 4k\n");
    270 				prg_usage();
    271 			}
    272 			break;
    273 		case 'o':
    274 			if ((offset = atoi(optarg)) <= 0) {
    275 				fprintf(stderr, "offset must be > 0\n");
    276 				prg_usage();
    277 			}
    278 			break;
    279 		case 'i':
    280 			if ((iter = atoi(optarg)) <= 0) {
    281 				fprintf(stderr, "iterations must be > 0\n");
    282 				prg_usage();
    283 			}
    284 			break;
    285 		case 'n':
    286 			if ((numchild = atoi(optarg)) <= 0) {
    287 				fprintf(stderr, "no of children must be > 0\n");
    288 				prg_usage();
    289 			}
    290 			break;
    291 		case 'v':
    292 			if ((nvector = atoi(optarg)) <= 0) {
    293 				fprintf(stderr, "vectory array must be > 0\n");
    294 				prg_usage();
    295 			}
    296 			break;
    297 		case 'f':
    298 			strcpy(filename, optarg);
    299 			break;
    300 		default:
    301 			prg_usage();
    302 		}
    303 	}
    304 
    305 	setup();
    306 
    307 	/* Testblock-1: Read with Direct IO, Write without */
    308 	if (forkchldrn(&pidlst, numchild, READ_DIRECT, child_function) < 0) {
    309 		failed = TRUE;
    310 		fail_count++;
    311 		tst_resm(TFAIL, "Read with Direct IO, Write without");
    312 	} else {
    313 		if (waitchldrn(&pidlst, numchild) < 0) {
    314 			failed = TRUE;
    315 			fail_count++;
    316 			tst_resm(TFAIL, "Read with Direct IO, Write without");
    317 		} else
    318 			tst_resm(TPASS, "Read with Direct IO, Write without");
    319 
    320 	}
    321 	unlink(filename);
    322 	free(pidlst);
    323 	total++;
    324 
    325 	/* Testblock-2: Write with Direct IO, Read without */
    326 	if (forkchldrn(&pidlst, numchild, WRITE_DIRECT, child_function) < 0) {
    327 		failed = TRUE;
    328 		fail_count++;
    329 		tst_resm(TFAIL, "Write with Direct IO, Read without");
    330 	} else {
    331 		if (waitchldrn(&pidlst, numchild) < 0) {
    332 			failed = TRUE;
    333 			fail_count++;
    334 			tst_resm(TFAIL, "Write with Direct IO, Read without");
    335 		} else
    336 			tst_resm(TPASS, "Write with Direct IO, Read without");
    337 	}
    338 	unlink(filename);
    339 	free(pidlst);
    340 	total++;
    341 
    342 	/* Testblock-3: Read, Write with Direct IO. */
    343 	if (forkchldrn(&pidlst, numchild, RDWR_DIRECT, child_function) < 0) {
    344 		failed = TRUE;
    345 		fail_count++;
    346 		tst_resm(TFAIL, "Read, Write with Direct IO");
    347 	} else {
    348 		if (waitchldrn(&pidlst, numchild) < 0) {
    349 			failed = TRUE;
    350 			fail_count++;
    351 			tst_resm(TFAIL, "Read, Write with Direct IO");
    352 		} else
    353 			tst_resm(TPASS, "Read, Write with Direct IO");
    354 	}
    355 	unlink(filename);
    356 	free(pidlst);
    357 	total++;
    358 
    359 	if (failed)
    360 		tst_resm(TINFO, "%d/%d testblocks failed", fail_count, total);
    361 	else
    362 		tst_resm(TINFO,
    363 			 "%d testblocks %d iterations with %d children completed",
    364 			 total, iter, numchild);
    365 	cleanup();
    366 	tst_exit();
    367 }
    368 
    369 static void setup(void)
    370 {
    371 	tst_tmpdir();
    372 
    373 	if ((fd1 = open(filename, O_CREAT | O_EXCL, 0600)) < 0) {
    374 		tst_brkm(TBROK, cleanup, "Couldn't create test file %s: %s",
    375 			 filename, strerror(errno));
    376 	}
    377 	close(fd1);
    378 
    379 	/* Test for filesystem support of O_DIRECT */
    380 	if ((fd1 = open(filename, O_DIRECT, 0600)) < 0) {
    381 		tst_brkm(TCONF, cleanup,
    382 			 "O_DIRECT is not supported by this filesystem. %s",
    383 			 strerror(errno));
    384 	}
    385 	close(fd1);
    386 }
    387 
    388 static void cleanup(void)
    389 {
    390 	if (fd1 != -1)
    391 		unlink(filename);
    392 
    393 	tst_rmdir();
    394 }
    395 
    396 #else /* O_DIRECT */
    397 
    398 int main(void)
    399 {
    400 	tst_brkm(TCONF, NULL, "O_DIRECT is not defined.");
    401 }
    402 
    403 #endif /* O_DIRECT */
    404