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
52int local_flag = PASSED;
53int block_number;
54
55char *TCID = "data_space";	/* Test program identifier.    */
56int TST_TOTAL = 1;		/* Total number of test cases. */
57/**************/
58
59#define MAXCHILD	100	/* max number of children to allow */
60int 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
69int nchild;			/* # kids */
70int csize;			/* chunk size */
71int iterations;			/* # total iterations */
72int rep_freq;			/* report frequency */
73int max_size;			/* max file size */
74int parent_pid;
75
76int usage(char *);
77int runtest();
78int dotest(int, int);
79void bfill(char *, char, int);
80int dumpbuf(char *);
81void dumpbits(char *, int);
82int massmurder();
83int okexit(int);
84
85char *prog;			/* invoked name */
86int chld_flag = 0;
87
88void cleanup(void)
89{
90	tst_rmdir();
91}
92
93int usage(prog)
94char *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
101int main(argc, argv)
102int argc;
103char *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
148int 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
214int nchunks;
215
216#define	CHUNK(i)	((i) * csize)
217
218int dotest(testers, me)
219int testers;
220int 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
360void bfill(buf, val, size)
361register char *buf;
362char val;
363register 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
376int dumpbuf(buf)
377register 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
428void dumpbits(bits, size)
429char *bits;
430register 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 */
450int 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
464int 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
474int 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
485int okexit(me)
486int 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