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 /* 11/18/2002	Port to LTP	robbiew@us.ibm.com */
21 /* 06/30/2001	Port to Linux	nsharoff@us.ibm.com */
22 
23 /*
24  * NAME
25  *	data_space.c -- test data space
26  *
27  * CALLS
28  *	malloc (3)
29  *
30  * ALGORITHM
31  *	Test VM for set of data-space intensive programs
32  *
33  */
34 
35 #define _XOPEN_SOURCE 500
36 #include <stdio.h>
37 #include <signal.h>
38 #include <sys/types.h>
39 #include <errno.h>
40 #include <sys/wait.h>
41 #include <stdlib.h>
42 #include <unistd.h>
43 #include <string.h>
44 //void (*sigset(int, void(*)(int)))(int);
45 
46 /** LTP Port **/
47 #include "test.h"
48 
49 #define FAILED 0
50 #define PASSED 1
51 
52 int local_flag = PASSED;
53 int block_number;
54 
55 char *TCID = "data_space";	/* Test program identifier.    */
56 int TST_TOTAL = 1;		/* Total number of test cases. */
57 /**************/
58 
59 #define MAXCHILD	100	/* max number of children to allow */
60 int allchild[MAXCHILD + 1];
61 #define K_1		1024
62 #define K_2		2048
63 #define K_4		4096
64 
65 #define bd_arg(str) \
66 	tst_brkm(TCONF, NULL, \
67 	    "bad argument - %s - could not parse as number.", str)
68 
69 int nchild;			/* # kids */
70 int csize;			/* chunk size */
71 int iterations;			/* # total iterations */
72 int rep_freq;			/* report frequency */
73 int max_size;			/* max file size */
74 int parent_pid;
75 
76 int usage(char *);
77 int runtest();
78 int dotest(int, int);
79 void bfill(char *, char, int);
80 int dumpbuf(char *);
81 void dumpbits(char *, int);
82 int massmurder();
83 int okexit(int);
84 
85 char *prog;			/* invoked name */
86 int chld_flag = 0;
87 
cleanup(void)88 void cleanup(void)
89 {
90 	tst_rmdir();
91 }
92 
usage(prog)93 int usage(prog)
94 char *prog;
95 {
96 	tst_resm(TCONF, "Usage: %s <nchild> <size> <chunk_size> <iterations>",
97 		 prog);
98 	tst_brkm(TCONF, NULL, "DEFAULTS: 10 1024*1024 4096 25");
99 }
100 
main(argc,argv)101 int main(argc, argv)
102 int argc;
103 char *argv[];
104 {
105 	int i = 1;
106 	int term();
107 	int chld();
108 
109 	prog = argv[0];
110 
111 	if (argc == 1) {
112 		nchild = 10;
113 		max_size = K_1 * K_1;
114 		csize = K_4;
115 		iterations = 25;
116 	} else if (argc == 5) {
117 		if (sscanf(argv[i++], "%d", &nchild) != 1)
118 			bd_arg(argv[i - 1]);
119 		if (sscanf(argv[i++], "%d", &max_size) != 1)
120 			bd_arg(argv[i - 1]);
121 		if (sscanf(argv[i++], "%d", &csize) != 1)
122 			bd_arg(argv[i - 1]);
123 		if (sscanf(argv[i++], "%d", &iterations) != 1)
124 			bd_arg(argv[i - 1]);
125 		if (nchild > MAXCHILD) {
126 			tst_brkm(TBROK, NULL,
127 				 "FAILURE, %d children exceeded maximum allowed",
128 				 nchild);
129 		}
130 	} else
131 		usage(prog);
132 
133 	tst_tmpdir();
134 
135 	parent_pid = getpid();
136 
137 	if (sigset(SIGTERM, (void (*)())term) == SIG_ERR) {
138 		tst_brkm(TBROK, NULL, "first sigset failed");
139 	}
140 	if (sigset(SIGUSR1, (void (*)())chld) == SIG_ERR) {
141 		tst_brkm(TBROK, NULL, "sigset shichld");
142 	}
143 
144 	runtest();
145 	tst_exit();
146 }
147 
runtest()148 int runtest()
149 {
150 	register int i;
151 	int child;
152 	int status;
153 	int count;
154 
155 	for (i = 0; i < nchild; i++) {
156 		chld_flag = 0;
157 		switch (child = fork()) {
158 		case -1:
159 			tst_brkm(TBROK | TERRNO, cleanup, "fork failed");
160 		case 0:
161 			dotest(nchild, i);
162 			exit(0);
163 		}
164 		allchild[i] = child;
165 		while (!chld_flag)
166 			sleep(1);
167 	}
168 
169 	/*
170 	 * Wait for children to finish.
171 	 */
172 
173 	count = 0;
174 	while ((child = wait(&status)) > 0) {
175 #ifdef DEBUG
176 		tst_resm(TINFO, "\t%s[%d] exited status = 0x%x\n", prog, child,
177 			 status);
178 #endif
179 		if (status) {
180 			tst_resm(TFAIL, "\tTest failed, expected 0 exit.\n");
181 			local_flag = FAILED;
182 		}
183 		++count;
184 	}
185 
186 	/*
187 	 * Should have collected all children.
188 	 */
189 
190 	if (count != nchild) {
191 		tst_resm(TFAIL, "\tWrong # children waited on, count = %d\n",
192 			 count);
193 		local_flag = FAILED;
194 	}
195 
196 	if (local_flag == FAILED)
197 		tst_resm(TFAIL, "Test failed");
198 	else
199 		tst_resm(TPASS, "Test passed");
200 	sync();			/* safeness */
201 
202 	return 0;
203 }
204 
205 /*
206  * dotest()
207  *	Children execute this.
208  *
209  * Randomly read/mod/write chunks with known pattern and check.
210  * When fill sectors, iterate.
211  *
212  */
213 
214 int nchunks;
215 
216 #define	CHUNK(i)	((i) * csize)
217 
dotest(testers,me)218 int dotest(testers, me)
219 int testers;
220 int me;
221 {
222 	char *bits;
223 	char *mondobuf;
224 	char *val_buf;
225 	char *zero_buf;
226 	char *buf;
227 	int count;
228 	int collide;
229 	char val;
230 	int chunk;
231 
232 	/*
233 	 * Do the mondo-test.
234 	 *
235 	 * NOTE: If we run this with a lot of children, the last child
236 	 *       processes may not have enough swap space to do these
237 	 *       malloc's  (mainly mondobuf).  So if the malloc's don't
238 	 *       work we just exit with zero status as long as we are
239 	 *       not the first child.
240 	 */
241 
242 	nchunks = max_size / csize;
243 	bits = malloc((nchunks + 7) / 8);
244 	if (bits == 0)
245 		okexit(me);
246 	val_buf = (char *)(malloc(csize));
247 	if (val_buf == 0)
248 		okexit(me);
249 	zero_buf = (char *)(malloc(csize));
250 	if (zero_buf == 0)
251 		okexit(me);
252 	mondobuf = malloc(max_size);
253 	if (mondobuf == 0)
254 		okexit(me);
255 
256 	kill(parent_pid, SIGUSR1);
257 
258 	/*
259 	 * No init sectors; allow file to be sparse.
260 	 */
261 
262 	val = (64 / testers) * me + 1;
263 
264 	/*
265 	 * For each iteration:
266 	 *      zap bits array
267 	 *      loop:
268 	 *              pick random chunk.
269 	 *              if corresponding bit off {
270 	 *                      verify == 0. (sparse file)
271 	 *                      ++count;
272 	 *              } else
273 	 *                      verify == val.
274 	 *              write "val" on it.
275 	 *              repeat until count = nchunks.
276 	 *      ++val.
277 	 *      Fill-in those chunks not yet seen.
278 	 */
279 
280 	bfill(zero_buf, 0, csize);
281 	bfill(mondobuf, 0, max_size);
282 
283 	srand(getpid());
284 	while (iterations-- > 0) {
285 		bfill(bits, 0, (nchunks + 7) / 8);
286 		bfill(val_buf, val, csize);
287 		count = 0;
288 		collide = 0;
289 		while (count < nchunks) {
290 			chunk = rand() % nchunks;
291 			buf = mondobuf + CHUNK(chunk);
292 
293 			/*
294 			 * If bit off, haven't seen it yet.
295 			 * Else, have.  Verify values.
296 			 */
297 
298 			if ((bits[chunk / 8] & (1 << (chunk % 8))) == 0) {
299 				if (memcmp(buf, zero_buf, csize)) {
300 					tst_resm(TFAIL,
301 						 "\t%s[%d] bad verify @ %d (%p) for val %d count %d, should be 0x%x.\n",
302 						 prog, me, chunk, buf, val,
303 						 count, val - 1);
304 					tst_resm(TINFO, "\tPrev ");
305 					dumpbuf(buf - csize);
306 					dumpbuf(buf);
307 					tst_resm(TINFO, "\tNext ");
308 					dumpbuf(buf + csize);
309 					dumpbits(bits, (nchunks + 7) / 8);
310 					tst_exit();
311 				}
312 				bits[chunk / 8] |= (1 << (chunk % 8));
313 				++count;
314 			} else {
315 				++collide;
316 				if (memcmp(buf, val_buf, csize)) {
317 					tst_resm(TFAIL,
318 						 "\t%s[%d] bad verify @ %d (%p) for val %d count %d.\n",
319 						 prog, me, chunk, buf, val,
320 						 count);
321 					tst_resm(TINFO, "\tPrev ");
322 					dumpbuf(buf - csize);
323 					dumpbuf(buf);
324 					tst_resm(TINFO, "\tNext ");
325 					dumpbuf(buf + csize);
326 					dumpbits(bits, (nchunks + 7) / 8);
327 					tst_exit();
328 				}
329 			}
330 
331 			/*
332 			 * Write it.
333 			 */
334 
335 			bfill(buf, val, csize);
336 
337 			if (count + collide > 2 * nchunks)
338 				break;
339 		}
340 
341 		/*
342 		 * End of iteration, maybe before doing all chunks.
343 		 */
344 #ifdef DEBUG
345 		tst_resm(TINFO,
346 			 "\t%s[%d] val %d done, count = %d, collide = %d.\n",
347 			 prog, me, val, count, collide);
348 #endif
349 		for (chunk = 0; chunk < nchunks; chunk++) {
350 			if ((bits[chunk / 8] & (1 << (chunk % 8))) == 0)
351 				bfill(mondobuf + CHUNK(chunk), val, csize);
352 		}
353 		bfill(zero_buf, val, csize);
354 		++val;
355 	}
356 
357 	return 0;
358 }
359 
bfill(buf,val,size)360 void bfill(buf, val, size)
361 register char *buf;
362 char val;
363 register int size;
364 {
365 	register int i;
366 
367 	for (i = 0; i < size; i++)
368 		buf[i] = val;
369 }
370 
371 /*
372  * dumpbuf
373  *	Dump the buffer.
374  */
375 
dumpbuf(buf)376 int dumpbuf(buf)
377 register char *buf;
378 {
379 	register int i;
380 	char val;
381 	int idx;
382 	int nout;
383 
384 #ifdef DEBUG
385 	tst_resm(TINFO, "Buf: ... ");
386 	for (i = -10; i < 0; i++)
387 		tst_resm(TINFO, "%x, ", buf[i]);
388 	tst_resm(TINFO, "\n");
389 #endif
390 
391 	nout = 0;
392 	idx = 0;
393 	val = buf[0];
394 	for (i = 0; i < csize; i++) {
395 		if (buf[i] != val) {
396 #ifdef DEBUG
397 			if (i == idx + 1)
398 				tst_resm(TINFO, "%x, ", buf[idx] & 0xff);
399 			else
400 				tst_resm(TINFO, "%d*%x, ", i - idx,
401 					 buf[idx] & 0xff);
402 #endif
403 			idx = i;
404 			val = buf[i];
405 			++nout;
406 		}
407 		if (nout > 10) {
408 #ifdef DEBUG
409 			tst_resm(TINFO, " ... more\n");
410 #endif
411 			return 0;
412 		}
413 	}
414 #ifdef DEBUG
415 	if (i == idx + 1)
416 		tst_resm(TINFO, "%x\n", buf[idx] & 0xff);
417 	else
418 		tst_resm(TINFO, "%d*%x\n", i - idx, buf[idx]);
419 #endif
420 	return 0;
421 }
422 
423 /*
424  * dumpbits
425  *	Dump the bit-map.
426  */
427 
dumpbits(bits,size)428 void dumpbits(bits, size)
429 char *bits;
430 register int size;
431 {
432 #ifdef DEBUG
433 	register char *buf;
434 
435 	tst_resm(TINFO, "Bits array:");
436 	for (buf = bits; size > 0; --size, ++buf) {
437 		if ((buf - bits) % 16 == 0)
438 			tst_resm(TINFO, "\n%04x:\t", 8 * (buf - bits));
439 		tst_resm(TINFO, "%02x ", (int)*buf & 0xff);
440 	}
441 	tst_resm(TINFO, "\n");
442 #endif
443 }
444 
445 /* term()
446  *
447  *	Parent - kill kids and return when signal arrives.
448  *	Child - exit.
449  */
term()450 int term()
451 {
452 #ifdef DEBUG
453 	tst_resm(TINFO, "\tterm -[%d]- got sig term.\n", getpid());
454 #endif
455 
456 	if (parent_pid == getpid()) {
457 		massmurder();
458 		return 0;
459 	}
460 
461 	exit(0);
462 }
463 
chld()464 int chld()
465 {
466 	if (sigset(SIGUSR1, (void (*)())chld) == SIG_ERR) {
467 		tst_resm(TBROK, "sigset shichld");
468 		exit(1);
469 	}
470 	chld_flag++;
471 	return 0;
472 }
473 
massmurder()474 int massmurder()
475 {
476 	int i;
477 	for (i = 0; i < MAXCHILD; i++) {
478 		if (allchild[i]) {
479 			kill(allchild[i], SIGTERM);
480 		}
481 	}
482 	return 0;
483 }
484 
okexit(me)485 int okexit(me)
486 int me;
487 {
488 	kill(parent_pid, SIGUSR1);
489 	tst_resm(TINFO, "\tChild [%d] - cannot malloc buffer - exiting.\n", me);
490 	if (me) {
491 		tst_resm(TINFO, "\tThis is ok - probably swap space limit.\n");
492 		tst_exit();
493 	} else {
494 		tst_brkm(TBROK,
495 			 NULL,
496 			 "\tThis is not ok for first child - check parameters.\n");
497 	}
498 
499 	return 0;
500 }
501