1/*
2 *
3 *   Copyright (c) International Business Machines  Corp., 2002
4 *   Copyright (c) Cyril Hrubis chrubis@suse.cz 2009
5 *
6 *   This program is free software;  you can redistribute it and/or modify
7 *   it under the terms of the GNU General Public License as published by
8 *   the Free Software Foundation; either version 2 of the License, or
9 *   (at your option) any later version.
10 *
11 *   This program is distributed in the hope that it will be useful,
12 *   but WITHOUT ANY WARRANTY;  without even the implied warranty of
13 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
14 *   the GNU General Public License for more details.
15 *
16 *   You should have received a copy of the GNU General Public License
17 *   along with this program;  if not, write to the Free Software
18 *   Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21/*
22 * NAME
23 *	ftest04.c -- test single file io (tsfio.c by rbk) (ported from SPIE, section2/filesuite/ftest5.c, by Airong Zhang)
24 *
25 * CALLS
26 *	fsync, sync, lseek, read, write
27 *
28 *
29 * ALGORITHM
30 *	Several child processes doing random seeks, read/write
31 *	operations on the same file.
32 *
33 *
34 * RESTRICTIONS
35 *	Runs a long time with default args - can take others on input
36 *	line.  Use with "term mode".
37 *
38 */
39#define _XOPEN_SOURCE 500
40#include <stdio.h>
41#include <sys/types.h>
42#include <sys/param.h>
43#include <sys/wait.h>
44#include <sys/file.h>
45#include <fcntl.h>
46#include <sys/stat.h>
47#include <sys/uio.h>
48#include <errno.h>
49#include <signal.h>
50#include "test.h"
51#include "safe_macros.h"
52#include "libftest.h"
53
54char *TCID = "ftest04";
55int TST_TOTAL = 1;
56
57static void setup(void);
58static void runtest(void);
59static void dotest(int, int, int);
60static void domisc(int, int);
61static void term(int sig);
62
63#define PASSED 1
64#define FAILED 0
65
66#define MAXCHILD	25
67#define K_1		1024
68#define K_2		2048
69#define K_4		4096
70#define	MAXIOVCNT	16
71
72static int csize;		/* chunk size */
73static int iterations;		/* # total iterations */
74static int max_size;		/* max file size */
75static int misc_intvl;		/* for doing misc things; 0 ==> no */
76static int nchild;		/* number of child processes */
77static int parent_pid;
78static int pidlist[MAXCHILD];
79
80static char filename[MAXPATHLEN];
81
82static int local_flag;
83
84int main(int ac, char *av[])
85{
86	int lc;
87
88	tst_parse_opts(ac, av, NULL, NULL);
89
90	setup();
91
92	for (lc = 0; TEST_LOOPING(lc); lc++) {
93
94		runtest();
95
96		if (local_flag == PASSED)
97			tst_resm(TPASS, "Test passed.");
98		else
99			tst_resm(TFAIL, "Test failed.");
100
101		/* ??? only one loop ??? */
102		tst_rmdir();
103		tst_exit();
104	}
105
106	tst_exit();
107}
108
109static void setup(void)
110{
111	int fd;
112	char wdbuf[MAXPATHLEN];
113
114	parent_pid = getpid();
115
116	/*
117	 * Make a filename for the test.
118	 */
119	tst_tmpdir();
120	if (!filename[0])
121		sprintf(filename, "%s/ftest04.%d", getcwd(wdbuf, MAXPATHLEN),
122			getpid());
123
124	fd = SAFE_OPEN(NULL, filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
125	close(fd);
126
127	/*
128	 * Default values for run conditions.
129	 */
130	iterations = 10;
131	nchild = 5;
132	csize = K_2;		/* should run with 1, 2, and 4 K sizes */
133	max_size = K_1 * K_1;
134	misc_intvl = 10;
135
136	if (sigset(SIGTERM, term) == SIG_ERR) {
137		tst_brkm(TFAIL, NULL, "first sigset failed");
138	}
139
140	local_flag = PASSED;
141}
142
143static void runtest(void)
144{
145	int count, child, fd, i, nwait, status;
146
147	nwait = 0;
148
149	for (i = 0; i < nchild; i++) {
150		if ((child = fork()) == 0) {
151			fd = SAFE_OPEN(NULL, filename, O_RDWR);
152			dotest(nchild, i, fd);
153			close(fd);
154			tst_exit();
155		}
156		if (child < 0) {
157			tst_brkm(TBROK | TERRNO, NULL, "fork failed");
158		} else {
159			pidlist[i] = child;
160			nwait++;
161		}
162	}
163
164	/*
165	 * Wait for children to finish.
166	 */
167	count = 0;
168	while ((child = wait(&status)) != -1 || errno == EINTR) {
169		if (child > 0) {
170			//tst_resm(TINFO, "\tTest{%d} exited status = 0x%x", child, status);
171			if (status) {
172				tst_resm(TFAIL,
173					 "\tExpected 0 exit status - failed.");
174				local_flag = FAILED;
175			}
176			++count;
177		}
178	}
179
180	/*
181	 * Should have collected all children.
182	 */
183	if (count != nwait) {
184		tst_resm(TFAIL, "\tWrong # children waited on, count = %d",
185			 count);
186		local_flag = FAILED;
187	}
188
189	unlink(filename);
190	sync();
191}
192
193/*
194 * dotest()
195 *	Children execute this.
196 *
197 * Randomly read/mod/write chunks with known pattern and check.
198 * When fill sectors, iterate.
199 */
200#define	NMISC	2
201enum m_type { m_fsync, m_sync };
202char *m_str[] = { "fsync", "sync" };
203
204int misc_cnt[NMISC];		/* counts # of each kind of misc */
205int misc_flag;
206int nchunks;
207
208#define	CHUNK(i)	(((i) * testers + me) * csize)
209#define	NEXTMISC	((rand() % misc_intvl) + 5)
210
211static void dotest(int testers, int me, int fd)
212{
213	char *bits;
214	char val, val0;
215	int count, collide, chunk, whenmisc, xfr, i;
216
217	/* Stuff for the readv call */
218	struct iovec r_iovec[MAXIOVCNT];
219	int r_ioveclen;
220
221	/* Stuff for the writev call */
222	struct iovec val0_iovec[MAXIOVCNT];
223	struct iovec val_iovec[MAXIOVCNT];
224	int w_ioveclen;
225	struct stat stat;
226
227	nchunks = max_size / (testers * csize);
228	whenmisc = 0;
229
230	if ((bits = malloc((nchunks + 7) / 8)) == NULL) {
231		tst_brkm(TBROK, NULL, "\tmalloc failed(bits)");
232	}
233
234	/*Allocate memory for the iovec buffers and init the iovec arrays
235	 */
236	r_ioveclen = w_ioveclen = csize / MAXIOVCNT;
237
238	/* Please note that the above statement implies that csize
239	 * be evenly divisible by MAXIOVCNT.
240	 */
241
242	for (i = 0; i < MAXIOVCNT; i++) {
243		if ((r_iovec[i].iov_base = malloc(r_ioveclen)) == NULL) {
244			tst_brkm(TBROK, NULL, "\tmalloc failed(r_iovec[])");
245		}
246		r_iovec[i].iov_len = r_ioveclen;
247
248		/* Allocate unused memory areas between all the buffers to
249		 * make things more diffult for the OS.
250		 */
251		if (malloc((i + 1) * 8) == NULL) {
252			tst_brkm(TBROK, NULL, "\tmalloc failed");
253		}
254
255		if ((val0_iovec[i].iov_base = malloc(w_ioveclen)) == NULL) {
256			tst_brkm(TBROK, NULL, "\tmalloc failed(val0_iovec[])");
257		}
258
259		val0_iovec[i].iov_len = w_ioveclen;
260
261		if (malloc((i + 1) * 8) == NULL) {
262			tst_brkm(TBROK, NULL, "\tmalloc failed");
263		}
264
265		if ((val_iovec[i].iov_base = malloc(w_ioveclen)) == NULL) {
266			tst_brkm(TBROK, NULL, "\tmalloc failed(iov_base)");
267		}
268
269		val_iovec[i].iov_len = w_ioveclen;
270
271		if (malloc((i + 1) * 8) == NULL) {
272			tst_brkm(TBROK, NULL, "\tmalloc failed");
273		}
274	}
275
276	/*
277	 * No init sectors; file-sys makes 0 to start.
278	 */
279	val = (64 / testers) * me + 1;
280	val0 = 0;
281
282	/*
283	 * For each iteration:
284	 *      zap bits array
285	 *      loop:
286	 *              pick random chunk, read it.
287	 *              if corresponding bit off {
288	 *                      verify == 0. (sparse file)
289	 *                      ++count;
290	 *              } else
291	 *                      verify == val.
292	 *              write "val" on it.
293	 *              repeat until count = nchunks.
294	 *      ++val.
295	 */
296	srand(getpid());
297
298	if (misc_intvl)
299		whenmisc = NEXTMISC;
300
301	while (iterations-- > 0) {
302		for (i = 0; i < NMISC; i++)
303			misc_cnt[i] = 0;
304		memset(bits, 0, (nchunks + 7) / 8);
305		/* Have to fill the val0 and val iov buffers in a different manner */
306		for (i = 0; i < MAXIOVCNT; i++) {
307			memset(val0_iovec[i].iov_base, val0,
308			       val0_iovec[i].iov_len);
309			memset(val_iovec[i].iov_base, val,
310			       val_iovec[i].iov_len);
311
312		}
313		count = 0;
314		collide = 0;
315		while (count < nchunks) {
316			chunk = rand() % nchunks;
317			/*
318			 * Read it.
319			 */
320			if (lseek(fd, CHUNK(chunk), 0) < 0) {
321				tst_brkm(TFAIL,
322					 NULL,
323					 "\tTest[%d]: lseek(0) fail at %x, errno = %d.",
324					 me, CHUNK(chunk), errno);
325			}
326			if ((xfr = readv(fd, &r_iovec[0], MAXIOVCNT)) < 0) {
327				tst_brkm(TFAIL,
328					 NULL,
329					 "\tTest[%d]: readv fail at %x, errno = %d.",
330					 me, CHUNK(chunk), errno);
331			}
332			/*
333			 * If chunk beyond EOF just write on it.
334			 * Else if bit off, haven't seen it yet.
335			 * Else, have.  Verify values.
336			 */
337			if (xfr == 0) {
338				bits[chunk / 8] |= (1 << (chunk % 8));
339			} else if ((bits[chunk / 8] & (1 << (chunk % 8))) == 0) {
340				if (xfr != csize) {
341					tst_brkm(TFAIL,
342						 NULL,
343						 "\tTest[%d]: xfr=%d != %d, zero read.",
344						 me, xfr, csize);
345				}
346				for (i = 0; i < MAXIOVCNT; i++) {
347					if (memcmp
348					    (r_iovec[i].iov_base,
349					     val0_iovec[i].iov_base,
350					     r_iovec[i].iov_len)) {
351						tst_resm(TFAIL,
352							 "\tTest[%d] bad verify @ 0x%x for val %d count %d xfr %d.",
353							 me, CHUNK(chunk), val0,
354							 count, xfr);
355						fstat(fd, &stat);
356						tst_resm(TINFO,
357							 "\tStat: size=%llx, ino=%x",
358							 stat.st_size, (unsigned)stat.st_ino);
359						ft_dumpiov(&r_iovec[i]);
360						ft_dumpbits(bits,
361							    (nchunks + 7) / 8);
362						tst_exit();
363					}
364				}
365				bits[chunk / 8] |= (1 << (chunk % 8));
366				++count;
367			} else {
368				if (xfr != csize) {
369					tst_brkm(TFAIL,
370						 NULL,
371						 "\tTest[%d]: xfr=%d != %d, val read.",
372						 me, xfr, csize);
373				}
374				++collide;
375				for (i = 0; i < MAXIOVCNT; i++) {
376					if (memcmp
377					    (r_iovec[i].iov_base,
378					     val_iovec[i].iov_base,
379					     r_iovec[i].iov_len)) {
380						tst_resm(TFAIL,
381							 "\tTest[%d] bad verify @ 0x%x for val %d count %d xfr %d.",
382							 me, CHUNK(chunk), val,
383							 count, xfr);
384						fstat(fd, &stat);
385						tst_resm(TINFO,
386							 "\tStat: size=%llx, ino=%x",
387							 stat.st_size, (unsigned)stat.st_ino);
388						ft_dumpiov(&r_iovec[i]);
389						ft_dumpbits(bits,
390							    (nchunks + 7) / 8);
391						tst_exit();
392					}
393				}
394			}
395			/*
396			 * Write it.
397			 */
398			if (lseek(fd, -xfr, 1) < 0) {
399				tst_brkm(TFAIL,
400					 NULL,
401					 "\tTest[%d]: lseek(1) fail at %x, errno = %d.",
402					 me, CHUNK(chunk), errno);
403			}
404			if ((xfr =
405			     writev(fd, &val_iovec[0], MAXIOVCNT)) < csize) {
406				if (errno == ENOSPC) {
407					tst_resm(TFAIL,
408						 "\tTest[%d]: no space, exiting.",
409						 me);
410					fsync(fd);
411					tst_exit();
412				}
413				tst_brkm(TFAIL,
414					 NULL,
415					 "\tTest[%d]: writev fail at %x xfr %d, errno = %d.",
416					 me, CHUNK(chunk), xfr, errno);
417			}
418			/*
419			 * If hit "misc" interval, do it.
420			 */
421			if (misc_intvl && --whenmisc <= 0) {
422				domisc(me, fd);
423				whenmisc = NEXTMISC;
424			}
425			if (count + collide > 2 * nchunks)
426				break;
427		}
428
429		/*
430		 * End of iteration, maybe before doing all chunks.
431		 */
432
433		if (count < nchunks) {
434			//tst_resm(TINFO, "\tTest{%d} val %d stopping @ %d, collide = {%d}.",
435			//              me, val, count, collide);
436			for (i = 0; i < nchunks; i++) {
437				if ((bits[i / 8] & (1 << (i % 8))) == 0) {
438					if (lseek(fd, CHUNK(i), 0) < 0) {
439						tst_brkm(TFAIL,
440							 NULL,
441							 "\tTest[%d]: lseek fail at %x, errno = %d.",
442							 me, CHUNK(i), errno);
443					}
444					if (writev(fd, &val_iovec[0], MAXIOVCNT)
445					    != csize) {
446						tst_brkm(TFAIL,
447							 NULL,
448							 "\tTest[%d]: writev fail at %x, errno = %d.",
449							 me, CHUNK(i), errno);
450					}
451				}
452			}
453		}
454
455		fsync(fd);
456		++misc_cnt[m_fsync];
457		//tst_resm(TINFO, "\tTest[%d] val %d done, count = %d, collide = %d.",
458		//              me, val, count, collide);
459		//for (i = 0; i < NMISC; i++)
460		//      tst_resm(TINFO, "\t\tTest[%d]: %d %s's.", me, misc_cnt[i], m_str[i]);
461		val0 = val++;
462	}
463}
464
465/*
466 * domisc()
467 *	Inject misc syscalls into the thing.
468 */
469static void domisc(int me, int fd)
470{
471	if (fsync(fd) < 0) {
472		tst_brkm(TFAIL, NULL, "\tTest[%d]: fsync error %d.", me,
473			 errno);
474	}
475
476	++misc_cnt[1];
477}
478
479static void term(int sig LTP_ATTRIBUTE_UNUSED)
480{
481	int i;
482
483	tst_resm(TINFO, "\tterm -[%d]- got sig term.", getpid());
484
485	if (parent_pid == getpid()) {
486		for (i = 0; i < nchild; i++)
487			if (pidlist[i])
488				kill(pidlist[i], SIGTERM);
489	}
490
491	exit(0);
492}
493