1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2002
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 * NAME
22 *      diotest5.c
23 *
24 * DESCRIPTION
25 *	The programs test buffered and direct IO with vector arrays using
26 *	readv() and writev() calls.
27 *	Test blocks
28 *	[1] Direct readv, Buffered writev
29 *	[2] Direct writev, Buffered readv
30 *	[3] Direct readv, Direct writev
31 *	The bufsize should be in n*4k size for direct readv, writev. The offset
32 *	value marks the starting position in file from where to start the
33 *	write and read. (Using larger offset, larger files can be tested).
34 *	The nvector gives vector array size.  Test data file can be
35 * 	specified through commandline and is useful for running test with
36 * 	raw devices as a file.
37 *
38 * USAGE
39 *      diotest5 [-b bufsize] [-o offset] [-i iterations]
40 *			[-v nvector] [-f filename]
41 *
42 * History
43 *	04/29/2002	Narasimha Sharoff nsharoff@us.ibm.com
44 *
45 * RESTRICTIONS
46 *	None
47*/
48
49#include <stdio.h>
50#include <stdlib.h>
51#include <unistd.h>
52#include <string.h>
53#include <sys/file.h>
54#include <fcntl.h>
55#include <sys/syscall.h>
56#include <sys/uio.h>
57#include <errno.h>
58
59#include "diotest_routines.h"
60
61#include "test.h"
62
63char *TCID = "diotest05";	/* Test program identifier.    */
64int TST_TOTAL = 3;		/* Total number of test conditions */
65
66#ifdef O_DIRECT
67
68#define	BUFSIZE	4096
69#define TRUE 1
70#define LEN 30
71#define	READ_DIRECT 1
72#define	WRITE_DIRECT 2
73#define	RDWR_DIRECT 3
74
75static int bufsize = BUFSIZE;	/* Buffer size. Default 4k */
76static int iter = 20;		/* Iterations. Default 20 */
77static int nvector = 20;	/* Vector array. Default 20 */
78static off64_t offset = 0;	/* Start offset. Default 0 */
79static char filename[LEN];	/* Test data file */
80static int fd1 = -1;
81/*
82 * runtest: Write the data in vector array to the file. Read the data
83 *	from the file into another vectory array and verify. Repeat the test.
84*/
85int runtest(int fd_r, int fd_w, int iter, off64_t offset, int action)
86{
87	int i;
88	struct iovec *iov1, *iov2, *iovp;
89
90	/* Allocate for buffers and data pointers */
91	if ((iov1 =
92	     (struct iovec *)valloc(sizeof(struct iovec) * nvector)) == NULL) {
93		tst_resm(TFAIL, "valloc() buf1 failed: %s", strerror(errno));
94		return (-1);
95	}
96	if ((iov2 =
97	     (struct iovec *)valloc(sizeof(struct iovec) * nvector)) == NULL) {
98		tst_resm(TFAIL, "valloc buf2 failed: %s", strerror(errno));
99		return (-1);
100	}
101	for (i = 0, iovp = iov1; i < nvector; iovp++, i++) {
102		if ((iovp->iov_base = valloc(bufsize)) == NULL) {
103			tst_resm(TFAIL, "valloc for iovp->iov_base: %s",
104				 strerror(errno));
105			return (-1);
106		}
107		iovp->iov_len = bufsize;
108	}
109	for (i = 0, iovp = iov2; i < nvector; iovp++, i++) {
110		if ((iovp->iov_base = valloc(bufsize)) == NULL) {
111			tst_resm(TFAIL, "valloc, iov2 for iovp->iov_base: %s",
112				 strerror(errno));
113			return (-1);
114		}
115		iovp->iov_len = bufsize;
116	}
117
118	/* Test */
119	for (i = 0; i < iter; i++) {
120		vfillbuf(iov1, nvector, i);
121		vfillbuf(iov2, nvector, i + 1);
122		if (lseek(fd_w, offset, SEEK_SET) < 0) {
123			tst_resm(TFAIL, "lseek before writev failed: %s",
124				 strerror(errno));
125			return (-1);
126		}
127		if (writev(fd_w, iov1, nvector) < 0) {
128			tst_resm(TFAIL, "writev failed: %s", strerror(errno));
129			return (-1);
130		}
131		if (lseek(fd_r, offset, SEEK_SET) < 0) {
132			tst_resm(TFAIL, "lseek before readv failed: %s",
133				 strerror(errno));
134			return (-1);
135		}
136		if (readv(fd_r, iov2, nvector) < 0) {
137			tst_resm(TFAIL, "readv failed: %s", strerror(errno));
138			return (-1);
139		}
140		if (vbufcmp(iov1, iov2, nvector) != 0) {
141			tst_resm(TFAIL, "readv/writev comparision failed");
142			return (-1);
143		}
144	}
145
146	/* Cleanup */
147	for (i = 0, iovp = iov1; i < nvector; iovp++, i++) {
148		free(iovp->iov_base);
149	}
150	for (i = 0, iovp = iov2; i < nvector; iovp++, i++) {
151		free(iovp->iov_base);
152	}
153	free(iov1);
154	free(iov2);
155	return 0;
156}
157
158static void prg_usage(void)
159{
160	fprintf(stderr,
161		"Usage: diotest5 [-b bufsize] [-o offset] [ -i iteration] [ -v nvector] [-f filename]\n");
162	exit(1);
163}
164
165static void setup(void);
166static void cleanup(void);
167
168int main(int argc, char *argv[])
169{
170	int i, action, fd_r, fd_w;
171	int fail_count = 0, total = 0, failed = 0;
172
173	/* Options */
174	sprintf(filename, "testdata-5.%ld", syscall(__NR_gettid));
175	while ((i = getopt(argc, argv, "b:o:i:v:f:")) != -1) {
176		switch (i) {
177		case 'b':
178			if ((bufsize = atoi(optarg)) <= 0) {
179				fprintf(stderr, "bufsize must be > 0");
180				prg_usage();
181			}
182			if (bufsize % 4096 != 0) {
183				fprintf(stderr, "bufsize must be > 0");
184				prg_usage();
185			}
186			break;
187		case 'o':
188			if ((offset = atoll(optarg)) <= 0) {
189				fprintf(stderr, "offset must be > 0");
190				prg_usage();
191			}
192			break;
193		case 'i':
194			if ((iter = atoi(optarg)) <= 0) {
195				fprintf(stderr, "iterations must be > 0");
196				prg_usage();
197			}
198			break;
199		case 'v':
200			if ((nvector = atoi(optarg)) <= 0) {
201				fprintf(stderr, "vector array must be > 0");
202				prg_usage();
203			}
204			break;
205		case 'f':
206			strcpy(filename, optarg);
207			break;
208		default:
209			prg_usage();
210		}
211	}
212
213	setup();
214
215	/* Testblock-1: Read with Direct IO, Write without */
216	action = READ_DIRECT;
217	if ((fd_w = open(filename, O_WRONLY | O_CREAT, 0666)) < 0) {
218		tst_brkm(TBROK, cleanup, "fd_w open failed for %s: %s",
219			 filename, strerror(errno));
220	}
221	if ((fd_r = open64(filename, O_DIRECT | O_RDONLY | O_CREAT, 0666)) < 0) {
222		tst_brkm(TBROK, cleanup, "fd_r open failed for %s: %s",
223			 filename, strerror(errno));
224	}
225	if (runtest(fd_r, fd_w, iter, offset, action) < 0) {
226		failed = TRUE;
227		fail_count++;
228		tst_resm(TFAIL, "Read with Direct IO, Write without");
229	} else
230		tst_resm(TPASS, "Read with Direct IO, Write without");
231
232	unlink(filename);
233	close(fd_r);
234	close(fd_w);
235	total++;
236
237	/* Testblock-2: Write with Direct IO, Read without */
238	action = WRITE_DIRECT;
239	if ((fd_w = open(filename, O_DIRECT | O_WRONLY | O_CREAT, 0666)) < 0) {
240		tst_brkm(TBROK, cleanup, "fd_w open failed for %s: %s",
241			 filename, strerror(errno));
242	}
243	if ((fd_r = open64(filename, O_RDONLY | O_CREAT, 0666)) < 0) {
244		tst_brkm(TBROK, cleanup, "fd_r open failed for %s: %s",
245			 filename, strerror(errno));
246	}
247	if (runtest(fd_r, fd_w, iter, offset, action) < 0) {
248		failed = TRUE;
249		fail_count++;
250		tst_resm(TFAIL, "Write with Direct IO, Read without");
251	} else
252		tst_resm(TPASS, "Write with Direct IO, Read without");
253	unlink(filename);
254	close(fd_r);
255	close(fd_w);
256	total++;
257
258	/* Testblock-3: Read, Write with Direct IO */
259	action = RDWR_DIRECT;
260	if ((fd_w = open(filename, O_DIRECT | O_WRONLY | O_CREAT, 0666)) < 0) {
261		tst_brkm(TBROK, cleanup, "fd_w open failed for %s: %s",
262			 filename, strerror(errno));
263	}
264	if ((fd_r = open64(filename, O_DIRECT | O_RDONLY | O_CREAT, 0666)) < 0) {
265		tst_brkm(TBROK, cleanup, "fd_r open failed for %s: %s",
266			 filename, strerror(errno));
267	}
268	if (runtest(fd_r, fd_w, iter, offset, action) < 0) {
269		failed = TRUE;
270		fail_count++;
271		tst_resm(TFAIL, "Read, Write with Direct IO");
272	} else
273		tst_resm(TPASS, "Read, Write with Direct IO");
274	unlink(filename);
275	close(fd_r);
276	close(fd_w);
277	total++;
278
279	if (failed)
280		tst_resm(TINFO, "%d/%d testblocks failed", fail_count, total);
281	else
282		tst_resm(TINFO,
283			 "%d testblocks %d iterations with %d vector array completed",
284			 total, iter, nvector);
285
286	cleanup();
287
288	tst_exit();
289}
290
291static void setup(void)
292{
293	tst_tmpdir();
294
295	if ((fd1 = open(filename, O_CREAT | O_EXCL, 0600)) < 0) {
296		tst_brkm(TBROK, cleanup, "Couldn't create test file %s: %s",
297			 filename, strerror(errno));
298	}
299	close(fd1);
300
301	/* Test for filesystem support of O_DIRECT */
302	if ((fd1 = open(filename, O_DIRECT, 0600)) < 0) {
303		tst_brkm(TCONF, cleanup,
304			 "O_DIRECT is not supported by this filesystem. %s",
305			 strerror(errno));
306	}
307	close(fd1);
308}
309
310static void cleanup(void)
311{
312	if (fd1 != -1)
313		unlink(filename);
314
315	tst_rmdir();
316
317}
318#else /* O_DIRECT */
319
320int main()
321{
322
323	tst_resm(TCONF, "O_DIRECT is not defined.");
324	return 0;
325}
326#endif /* O_DIRECT */
327