• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
54 char *TCID = "ftest04";
55 int TST_TOTAL = 1;
56 
57 static void setup(void);
58 static void runtest(void);
59 static void dotest(int, int, int);
60 static void domisc(int, int);
61 static 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 
72 static int csize;		/* chunk size */
73 static int iterations;		/* # total iterations */
74 static int max_size;		/* max file size */
75 static int misc_intvl;		/* for doing misc things; 0 ==> no */
76 static int nchild;		/* number of child processes */
77 static int parent_pid;
78 static int pidlist[MAXCHILD];
79 
80 static char filename[MAXPATHLEN];
81 
82 static int local_flag;
83 
main(int ac,char * av[])84 int 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 
setup(void)109 static 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 
runtest(void)143 static 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
201 enum m_type { m_fsync, m_sync };
202 char *m_str[] = { "fsync", "sync" };
203 
204 int misc_cnt[NMISC];		/* counts # of each kind of misc */
205 int misc_flag;
206 int nchunks;
207 
208 #define	CHUNK(i)	(((i) * testers + me) * csize)
209 #define	NEXTMISC	((rand() % misc_intvl) + 5)
210 
dotest(int testers,int me,int fd)211 static 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  */
domisc(int me,int fd)469 static 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 
term(int sig LTP_ATTRIBUTE_UNUSED)479 static 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