1/*
2 * Copyright (c) International Business Machines  Corp., 2002
3 *  04/30/2002 Narasimha Sharoff nsharoff@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
51char *TCID = "diotest06";
52int 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
63static int iter = 100;
64static int bufsize = BUFSIZE;
65static off_t offset = 0;
66static int nvector = 20;
67static char filename[LEN];
68static int fd1 = -1;
69
70static void setup(void);
71static void cleanup(void);
72
73static 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*/
85int runtest(int fd_r, int fd_w, int childnum, int action)
86{
87	off_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
161err:
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*/
174int 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
252int 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	fflush(stdout);
324	total++;
325
326	/* Testblock-2: Write with Direct IO, Read without */
327	if (forkchldrn(&pidlst, numchild, WRITE_DIRECT, child_function) < 0) {
328		failed = TRUE;
329		fail_count++;
330		tst_resm(TFAIL, "Write with Direct IO, Read without");
331	} else {
332		if (waitchldrn(&pidlst, numchild) < 0) {
333			failed = TRUE;
334			fail_count++;
335			tst_resm(TFAIL, "Write with Direct IO, Read without");
336		} else
337			tst_resm(TPASS, "Write with Direct IO, Read without");
338	}
339	unlink(filename);
340	free(pidlst);
341	fflush(stdout);
342	total++;
343
344	/* Testblock-3: Read, Write with Direct IO. */
345	if (forkchldrn(&pidlst, numchild, RDWR_DIRECT, child_function) < 0) {
346		failed = TRUE;
347		fail_count++;
348		tst_resm(TFAIL, "Read, Write with Direct IO");
349	} else {
350		if (waitchldrn(&pidlst, numchild) < 0) {
351			failed = TRUE;
352			fail_count++;
353			tst_resm(TFAIL, "Read, Write with Direct IO");
354		} else
355			tst_resm(TPASS, "Read, Write with Direct IO");
356	}
357	unlink(filename);
358	free(pidlst);
359	total++;
360
361	if (failed)
362		tst_resm(TINFO, "%d/%d testblocks failed", fail_count, total);
363	else
364		tst_resm(TINFO,
365			 "%d testblocks %d iterations with %d children completed",
366			 total, iter, numchild);
367	cleanup();
368	tst_exit();
369}
370
371static void setup(void)
372{
373	tst_tmpdir();
374
375	if ((fd1 = open(filename, O_CREAT | O_EXCL, 0600)) < 0) {
376		tst_brkm(TBROK, cleanup, "Couldn't create test file %s: %s",
377			 filename, strerror(errno));
378	}
379	close(fd1);
380
381	/* Test for filesystem support of O_DIRECT */
382	if ((fd1 = open(filename, O_DIRECT, 0600)) < 0) {
383		tst_brkm(TCONF, cleanup,
384			 "O_DIRECT is not supported by this filesystem. %s",
385			 strerror(errno));
386	}
387	close(fd1);
388}
389
390static void cleanup(void)
391{
392	if (fd1 != -1)
393		unlink(filename);
394
395	tst_rmdir();
396}
397
398#else /* O_DIRECT */
399
400int main(void)
401{
402	tst_brkm(TCONF, NULL, "O_DIRECT is not defined.");
403}
404
405#endif /* O_DIRECT */
406