| /* |
| * |
| * Copyright (c) International Business Machines Corp., 2002 |
| * |
| * This program is free software; you can redistribute it and/or modify |
| * it under the terms of the GNU General Public License as published by |
| * the Free Software Foundation; either version 2 of the License, or |
| * (at your option) any later version. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See |
| * the GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU General Public License |
| * along with this program; if not, write to the Free Software |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| */ |
| |
| /* 11/18/2002 Port to LTP robbiew@us.ibm.com */ |
| /* 06/30/2001 Port to Linux nsharoff@us.ibm.com */ |
| |
| /* |
| * NAME |
| * data_space.c -- test data space |
| * |
| * CALLS |
| * malloc (3) |
| * |
| * ALGORITHM |
| * Test VM for set of data-space intensive programs |
| * |
| */ |
| |
| #define _XOPEN_SOURCE 500 |
| #include <stdio.h> |
| #include <signal.h> |
| #include <sys/types.h> |
| #include <errno.h> |
| #include <sys/wait.h> |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| //void (*sigset(int, void(*)(int)))(int); |
| |
| /** LTP Port **/ |
| #include "test.h" |
| #include "usctest.h" |
| |
| #define FAILED 0 |
| #define PASSED 1 |
| |
| int local_flag = PASSED; |
| int block_number; |
| |
| char *TCID="data_space"; /* Test program identifier. */ |
| int TST_TOTAL=1; /* Total number of test cases. */ |
| extern int Tst_count; /* Test Case counter for tst_* routines */ |
| /**************/ |
| |
| |
| #define MAXCHILD 100 /* max number of children to allow */ |
| int allchild[MAXCHILD +1]; |
| #define K_1 1024 |
| #define K_2 2048 |
| #define K_4 4096 |
| |
| int nchild; /* # kids */ |
| int csize; /* chunk size */ |
| int iterations; /* # total iterations */ |
| int rep_freq; /* report frequency */ |
| int max_size; /* max file size */ |
| int parent_pid; |
| |
| int usage(char*); |
| int bd_arg(char *); |
| int runtest(); |
| int dotest(int,int); |
| void bfill(char*,char,int); |
| int dumpbuf(char*); |
| void dumpbits(char*,int); |
| int massmurder(); |
| int okexit(int); |
| |
| char *prog; /* invoked name */ |
| int chld_flag = 0; |
| |
| int usage(prog) |
| char *prog; |
| { |
| tst_resm(TCONF,"Usage: %s <nchild> <size> <chunk_size> <iterations>",prog); |
| tst_resm(TCONF,"DEFAULTS: 10 1024*1024 4096 25", prog); |
| tst_exit(); |
| return 0; |
| } |
| |
| int main(argc, argv) |
| int argc; |
| char *argv[]; |
| { |
| int i =1; |
| int term(); |
| int chld(); |
| |
| prog = argv[0]; |
| |
| if (argc == 1) |
| { |
| nchild = 10; |
| max_size = K_1 * K_1; |
| csize = K_4; |
| iterations = 25; |
| } else if (argc == 5) |
| { |
| if (sscanf(argv[i++], "%d", &nchild) != 1) |
| bd_arg(argv[i-1]); |
| if (sscanf(argv[i++], "%d", &max_size) != 1) |
| bd_arg(argv[i-1]); |
| if (sscanf(argv[i++], "%d", &csize) != 1) |
| bd_arg(argv[i-1]); |
| if (sscanf(argv[i++], "%d", &iterations) != 1) |
| bd_arg(argv[i-1]); |
| if( nchild > MAXCHILD ) |
| { |
| tst_resm(TBROK,"FAILURE, %d children exceeded maximum allowed\n", nchild); |
| tst_exit(); |
| } |
| } else |
| usage(prog); |
| |
| tst_tmpdir(); |
| |
| parent_pid = getpid(); |
| |
| if (sigset(SIGTERM, (void (*)())term) == SIG_ERR) |
| { |
| tst_resm(TBROK,"first sigset failed"); |
| tst_exit(); |
| } |
| if (sigset(SIGUSR1, (void (*)())chld) == SIG_ERR) |
| { |
| tst_resm(TBROK,"sigset shichld"); |
| tst_exit(); |
| } |
| |
| runtest(); |
| /**NOT REACHED**/ |
| return 0; |
| } |
| |
| int bd_arg(str) |
| char *str; |
| { |
| tst_resm(TCONF,"Bad argument - %s - could not parse as number.\n", str); |
| tst_exit(); |
| return 0; |
| } |
| |
| int runtest() |
| { |
| register int i; |
| int child; |
| int status; |
| int count; |
| |
| |
| for(i = 0; i < nchild; i++) |
| { |
| chld_flag = 0; |
| if ((child = fork()) == 0) /* child */ |
| { |
| dotest(nchild, i); /* do it! */ |
| exit(0); /* when done, exit */ |
| } |
| if (child < 0) |
| { |
| tst_resm(TBROK,"Fork failed (may be OK if under stress)"); |
| tst_resm(TINFO, "System resource may be too low.\n"); |
| tst_resm(TBROK, "Reason: %s\n", strerror(errno)); |
| tst_rmdir(); |
| tst_exit(); |
| } |
| allchild[i]=child; |
| while (!chld_flag) |
| sleep(1); |
| } |
| |
| /* |
| * Wait for children to finish. |
| */ |
| |
| count = 0; |
| while((child = wait(&status)) > 0) |
| { |
| #ifdef DEBUG |
| tst_resm(TINFO, "\t%s[%d] exited status = 0x%x\n", prog, child, status); |
| #endif |
| if (status) |
| { |
| tst_resm(TFAIL, "\tTest failed, expected 0 exit.\n"); |
| local_flag = FAILED; |
| } |
| ++count; |
| } |
| |
| /* |
| * Should have collected all children. |
| */ |
| |
| if (count != nchild) |
| { |
| tst_resm(TFAIL, "\tWrong # children waited on, count = %d\n", count); |
| local_flag = FAILED; |
| } |
| |
| (local_flag == FAILED) ? tst_resm(TFAIL, "Test failed") |
| : tst_resm(TPASS, "Test passed"); |
| sync(); /* safeness */ |
| tst_rmdir(); |
| tst_exit(); |
| |
| /**NOT REACHED**/ |
| return 0; |
| } |
| |
| /* |
| * dotest() |
| * Children execute this. |
| * |
| * Randomly read/mod/write chunks with known pattern and check. |
| * When fill sectors, iterate. |
| * |
| */ |
| |
| int nchunks; |
| |
| #define CHUNK(i) ((i) * csize) |
| |
| int dotest(testers, me) |
| int testers; |
| int me; |
| { |
| char *bits; |
| char *mondobuf; |
| char *val_buf; |
| char *zero_buf; |
| char *buf; |
| int count; |
| int collide; |
| char val; |
| int chunk; |
| |
| |
| /* |
| * Do the mondo-test. |
| * |
| * NOTE: If we run this with a lot of children, the last child |
| * processes may not have enough swap space to do these |
| * malloc's (mainly mondobuf). So if the malloc's don't |
| * work we just exit with zero status as long as we are |
| * not the first child. |
| */ |
| |
| nchunks = max_size / csize; |
| bits = (char*)malloc((nchunks+7)/8); |
| if (bits == 0) |
| okexit(me); |
| val_buf = (char*)(malloc(csize)); |
| if (val_buf == 0) |
| okexit(me); |
| zero_buf = (char*)(malloc(csize)); |
| if (zero_buf == 0) |
| okexit(me); |
| mondobuf = (char*)malloc(max_size); |
| if (mondobuf == 0) |
| okexit(me); |
| |
| kill(parent_pid, SIGUSR1); |
| |
| /* |
| * No init sectors; allow file to be sparse. |
| */ |
| |
| val = (64/testers) * me + 1; |
| |
| /* |
| * For each iteration: |
| * zap bits array |
| * loop: |
| * pick random chunk. |
| * if corresponding bit off { |
| * verify == 0. (sparse file) |
| * ++count; |
| * } else |
| * verify == val. |
| * write "val" on it. |
| * repeat until count = nchunks. |
| * ++val. |
| * Fill-in those chunks not yet seen. |
| */ |
| |
| bfill(zero_buf, 0, csize); |
| bfill(mondobuf, 0, max_size); |
| |
| srand(getpid()); |
| while(iterations-- > 0) |
| { |
| bfill(bits, 0, (nchunks+7)/8); |
| bfill(val_buf, val, csize); |
| count = 0; |
| collide = 0; |
| while(count < nchunks) |
| { |
| chunk = rand() % nchunks; |
| buf = mondobuf + CHUNK(chunk); |
| |
| /* |
| * If bit off, haven't seen it yet. |
| * Else, have. Verify values. |
| */ |
| |
| if ((bits[chunk/8] & (1<<(chunk%8))) == 0) |
| { |
| if (memcmp(buf, zero_buf, csize)) |
| { |
| tst_resm(TFAIL, "\t%s[%d] bad verify @ %d (0x%x) for val %d count %d, should be 0x%x.\n", |
| prog, me, chunk, buf, val, count, val - 1); |
| tst_resm(TINFO, "\tPrev "); dumpbuf(buf-csize); |
| dumpbuf(buf); |
| tst_resm(TINFO, "\tNext "); dumpbuf(buf+csize); |
| dumpbits(bits, (nchunks+7)/8); |
| tst_exit(); |
| } |
| bits[chunk/8] |= (1<<(chunk%8)); |
| ++count; |
| } else |
| { |
| ++collide; |
| if (memcmp(buf, val_buf, csize)) |
| { |
| tst_resm(TFAIL, "\t%s[%d] bad verify @ %d (0x%x) for val %d count %d.\n", |
| prog, me, chunk, buf, val, count); |
| tst_resm(TINFO, "\tPrev "); dumpbuf(buf-csize); |
| dumpbuf(buf); |
| tst_resm(TINFO, "\tNext "); dumpbuf(buf+csize); |
| dumpbits(bits, (nchunks+7)/8); |
| tst_exit(); |
| } |
| } |
| |
| /* |
| * Write it. |
| */ |
| |
| bfill(buf, val, csize); |
| |
| if (count + collide > 2 * nchunks) |
| break; |
| } |
| |
| /* |
| * End of iteration, maybe before doing all chunks. |
| */ |
| #ifdef DEBUG |
| tst_resm(TINFO, "\t%s[%d] val %d done, count = %d, collide = %d.\n", |
| prog, me, val, count, collide); |
| #endif |
| for(chunk = 0; chunk < nchunks; chunk++) |
| { |
| if ((bits[chunk/8] & (1<<(chunk%8))) == 0) |
| bfill(mondobuf+CHUNK(chunk), val, csize); |
| } |
| bfill(zero_buf, val, csize); |
| ++val; |
| } |
| |
| return 0; |
| } |
| |
| |
| |
| void bfill(buf, val, size) |
| register char *buf; |
| char val; |
| register int size; |
| { |
| register int i; |
| |
| for(i = 0; i < size; i++) |
| buf[i] = val; |
| } |
| |
| /* |
| * dumpbuf |
| * Dump the buffer. |
| */ |
| |
| int dumpbuf(buf) |
| register char *buf; |
| { |
| register int i; |
| char val; |
| int idx; |
| int nout; |
| |
| #ifdef DEBUG |
| tst_resm(TINFO, "Buf: ... "); |
| for(i = -10; i < 0; i++) tst_resm(TINFO, "%x, ", buf[i]); |
| tst_resm(TINFO, "\n"); |
| #endif |
| |
| nout = 0; |
| idx = 0; |
| val = buf[0]; |
| for(i = 0; i < csize; i++) |
| { |
| if (buf[i] != val) |
| { |
| #ifdef DEBUG |
| if (i == idx+1) |
| tst_resm(TINFO, "%x, ", buf[idx] & 0xff); |
| else |
| tst_resm(TINFO, "%d*%x, ", i-idx, buf[idx] & 0xff); |
| #endif |
| idx = i; |
| val = buf[i]; |
| ++nout; |
| } |
| if (nout > 10) |
| { |
| #ifdef DEBUG |
| tst_resm(TINFO, " ... more\n"); |
| #endif |
| return 0; |
| } |
| } |
| #ifdef DEBUG |
| if (i == idx+1) |
| tst_resm(TINFO, "%x\n", buf[idx] & 0xff); |
| else |
| tst_resm(TINFO, "%d*%x\n", i-idx, buf[idx]); |
| #endif |
| return 0; |
| } |
| |
| /* |
| * dumpbits |
| * Dump the bit-map. |
| */ |
| |
| void dumpbits(bits, size) |
| char *bits; |
| register int size; |
| { |
| #ifdef DEBUG |
| register char *buf; |
| |
| tst_resm(TINFO, "Bits array:"); |
| for(buf = bits; size > 0; --size, ++buf) |
| { |
| if ((buf-bits) % 16 == 0) |
| tst_resm(TINFO, "\n%04x:\t", 8*(buf-bits)); |
| tst_resm(TINFO, "%02x ", (int)*buf & 0xff); |
| } |
| tst_resm(TINFO, "\n"); |
| #endif |
| } |
| |
| |
| |
| /* term() |
| * |
| * Parent - kill kids and return when signal arrives. |
| * Child - exit. |
| */ |
| int term() |
| { |
| #ifdef DEBUG |
| tst_resm(TINFO, "\tterm -[%d]- got sig term.\n", getpid()); |
| #endif |
| |
| if (parent_pid == getpid()) |
| { |
| massmurder(); |
| return 0; |
| } |
| |
| exit(0); |
| } |
| |
| int chld() |
| { |
| if (sigset(SIGUSR1, (void (*)())chld) == SIG_ERR) |
| { |
| tst_resm(TBROK,"sigset shichld"); |
| exit(1); |
| } |
| chld_flag++; |
| return 0; |
| } |
| int massmurder() |
| { |
| int i; |
| for( i=0 ; i < MAXCHILD ; i++ ) |
| { |
| if( allchild[i] ) |
| { |
| kill(allchild[i], SIGTERM); |
| } |
| } |
| return 0; |
| } |
| |
| int okexit(me) |
| int me; |
| { |
| kill(parent_pid, SIGUSR1); |
| tst_resm(TINFO, "\tChild [%d] - cannot malloc buffer - exiting.\n", me); |
| if (me) |
| { |
| tst_resm(TINFO, "\tThis is ok - probably swap space limit.\n"); |
| tst_exit(); |
| } else |
| { |
| tst_resm(TBROK,"\tThis is not ok for first child - check parameters.\n"); |
| tst_exit(); |
| } |
| |
| return 0; |
| } |
| |