1 /* IBM Corporation */ 2 /* 01/02/2003 Port to LTP avenakt (at) us.ibm.com */ 3 /* 06/30/2001 Port to Linux nsharoff (at) us.ibm.com */ 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 #define _GNU_SOURCE 1 21 #include <sys/types.h> 22 #include <stdio.h> 23 #include <fcntl.h> 24 #include <signal.h> 25 #include <sys/mman.h> 26 #include <sys/wait.h> 27 #include <sys/stat.h> 28 #include <unistd.h> 29 #include <errno.h> 30 /***** LTP Port *****/ 31 #include "test.h" 32 #define FAILED 0 33 #define PASSED 1 34 35 int local_flag = PASSED; 36 char *TCID = "mmapstress09"; 37 FILE *temp; 38 int TST_TOTAL = 1; 39 40 int anyfail(); 41 void ok_exit(); 42 /***** ** ** *****/ 43 44 /* 45 * This test is mostly duplicated from the tmmap test, but tests 46 * stress tests anonymous maps. It forks a specified number of children, 47 * who inherit an anonymous shared map, and who, make a given number of 48 * accesses to random pages in the map (reading & writing and comparing data). 49 * Then the child exits and the parent forks another to take its place. 50 * The test fails if a child sees incorrect data. 51 * 52 * This program continues to run until it either receives a SIGINT, 53 * or times out (if a timeout value is specified). When either of 54 * these things happens, it cleans up its kids, then checks 55 * the map to make sure it has the correct data. 56 * 57 * usage: 58 * mmapstress09 -p nprocs [-t minutes -s mapsize -m msync -r -d] 59 * 60 * where: 61 * -p nprocs - specifies the number of mapping children 62 * to create. (nprocs + 1 children actually 63 * get created, since one is the writer child) 64 * -t minutes - specifies minutes to run. If not specified, 65 * default is to run forever until a SIGINT 66 * is received. 67 * -s mapsize - mapsize (defaults to MAPSIZE) 68 * -m - do msyncs 69 * -r - randomize number of pages map children check. 70 * (random % MAXLOOPS). If not specified, each 71 * child checks MAXLOOPS pages. 72 * -d - enable debug outputd 73 */ 74 75 #define MAXLOOPS 500 /* max pages for map children to write */ 76 #define MAPSIZE (64*1024) /* default mapsize set up by parent */ 77 #ifdef roundup 78 #undef roundup 79 #endif 80 #define roundup(x, y) ((((x)+((y)-1))/(y))*(y)) 81 #define min(x, y) (((x) < (y)) ? (x) : (y)) 82 83 extern time_t time(time_t *); 84 extern char *ctime(const time_t *); 85 extern void *malloc(size_t); 86 extern void exit(int); 87 extern long lrand48(void); 88 extern void srand(unsigned); 89 extern void srand48(long); 90 extern int rand(void); 91 extern int atoi(const char *); 92 93 typedef unsigned char uchar_t; 94 95 char *usage = "-p nprocs [-t minutes -s mapsize -m -r -d]"; 96 97 unsigned int initrand(void); 98 void finish(int sig); 99 void child_mapper(unsigned procno, unsigned nprocs); 100 int mapokay(uchar_t * expbuf); 101 102 int finished = 0; 103 int debug = 0; 104 int mapsize = MAPSIZE; 105 unsigned mappages; 106 int pagesize; 107 unsigned randloops = 0; 108 unsigned dosync = 0; 109 unsigned pattern = 0; 110 caddr_t mapaddr; 111 112 int main(int argc, char *argv[]) 113 { 114 char *progname; 115 unsigned c; 116 extern char *optarg; 117 unsigned nprocs = 0; 118 unsigned procno; 119 pid_t *pidarray = NULL; 120 pid_t pid; 121 uchar_t *buf, *ptr; 122 unsigned int seed; 123 float alarmtime = 0; 124 struct sigaction sa; 125 unsigned i, j; 126 uchar_t data; 127 int no_prob = 0; 128 time_t t; 129 int wait_stat; 130 131 progname = *argv; 132 pagesize = sysconf(_SC_PAGE_SIZE); 133 134 if (argc < 2) { 135 (void)fprintf(stderr, "usage: %s %s\n", progname, usage); 136 anyfail(); 137 } 138 139 while ((c = getopt(argc, argv, "mdrp:t:s:")) != -1) { 140 switch (c) { 141 case 'd': 142 debug = 1; 143 break; 144 case 't': 145 alarmtime = atof(optarg) * 60; 146 break; 147 case 'p': 148 nprocs = atoi(optarg); 149 break; 150 case 'm': 151 dosync = 1; 152 break; 153 case 's': 154 mapsize = atoi(optarg); 155 if (mapsize < 0) { 156 (void)fprintf(stderr, "error: negative " 157 "mapsize\n"); 158 anyfail(); 159 } 160 break; 161 case 'r': 162 randloops = 1; 163 break; 164 default: 165 (void)fprintf(stderr, "usage: %s %s\n", progname, 166 usage); 167 anyfail(); 168 } 169 } 170 171 /* nprocs is unsigned */ 172 if (nprocs > 255) { 173 (void)fprintf(stderr, "invalid nprocs %d - (range 0-255)\n", 174 nprocs); 175 anyfail(); 176 } 177 (void)time(&t); 178 // (void)printf("%s: Started %s", argv[0], ctime(&t)); LTP Port 179 180 seed = initrand(); 181 pattern = seed & 0xff; 182 183 if (debug) { 184 (void)printf("%s mapsize %d bytes, pattern %d\n", 185 progname, mapsize, pattern); 186 if (alarmtime) 187 (void)printf("running for %f minutes\n", 188 alarmtime / 60); 189 else 190 (void)printf("running with no time limit\n"); 191 } 192 193 if ((mapaddr = mmap(0, mapsize, PROT_READ | PROT_WRITE, 194 MAP_ANONYMOUS | MAP_SHARED, 0, 0)) 195 == (caddr_t) - 1) { 196 perror("mmap error"); 197 anyfail(); 198 } 199 200 if ((buf = malloc(pagesize)) == NULL 201 || (pidarray = malloc(nprocs * sizeof(pid_t))) == NULL) { 202 perror("malloc error"); 203 anyfail(); 204 } 205 206 for (i = 0; i < nprocs; i++) 207 *(pidarray + i) = 0; 208 209 /* 210 * Initialize page compare buffer, then initialize map. 211 */ 212 213 for (i = 0, data = 0; i < pagesize; i++) { 214 *(buf + i) = (data + pattern) & 0xff; 215 if (++data == nprocs) 216 data = 0; 217 } 218 219 mappages = roundup(mapsize, pagesize) / pagesize; 220 ptr = (uchar_t *) mapaddr; 221 222 for (i = 0; i < mappages; i++) { 223 for (j = 0; j < pagesize; j++) 224 *ptr++ = *(buf + j); 225 } 226 227 /* 228 * Fork off mmap children. 229 */ 230 for (procno = 0; procno < nprocs; procno++) { 231 switch (pid = fork()) { 232 233 case -1: 234 perror("fork error"); 235 goto cleanup; 236 237 case 0: 238 child_mapper(procno, nprocs); 239 exit(0); 240 241 default: 242 pidarray[procno] = pid; 243 } 244 } 245 246 /* 247 * Plan for death by signal. User may have specified 248 * a time limit, in which set an alarm and catch SIGALRM. 249 * Also catch and cleanup with SIGINT. 250 */ 251 sa.sa_handler = finish; 252 sa.sa_flags = 0; 253 if (sigemptyset(&sa.sa_mask)) { 254 perror("sigemptyset error"); 255 goto cleanup; 256 } 257 258 if (sigaction(SIGINT, &sa, 0) == -1) { 259 perror("sigaction error"); 260 goto cleanup; 261 } 262 263 if (alarmtime) { 264 if (sigaction(SIGALRM, &sa, 0) == -1) { 265 perror("sigaction error"); 266 goto cleanup; 267 } 268 (void)alarm(alarmtime); 269 } 270 271 /* 272 * Now wait for children and refork them as needed. 273 */ 274 275 while (!finished) { 276 do { 277 pid = wait(&wait_stat); 278 } while (pid == -1 && errno == EINTR); 279 /* 280 * Block signals while processing child exit. 281 */ 282 283 if (sighold(SIGALRM) || sighold(SIGINT)) { 284 perror("sighold error"); 285 goto cleanup; 286 } 287 288 if (pid != -1) { 289 /* 290 * Check exit status, then refork with the 291 * appropriate procno. 292 */ 293 if (!WIFEXITED(wait_stat) 294 || WEXITSTATUS(wait_stat) != 0) { 295 (void)fprintf(stderr, "child exit with err " 296 "<x%x>\n", wait_stat); 297 goto cleanup; 298 } 299 for (i = 0; i < nprocs; i++) 300 if (pid == pidarray[i]) 301 break; 302 if (i == nprocs) { 303 (void)fprintf(stderr, 304 "unknown child pid %d, <x%x>\n", 305 pid, wait_stat); 306 goto cleanup; 307 } 308 309 if ((pid = fork()) == -1) { 310 perror("fork error"); 311 pidarray[i] = 0; 312 goto cleanup; 313 } else if (pid == 0) { /* child */ 314 child_mapper(i, nprocs); 315 exit(0); 316 } else 317 pidarray[i] = pid; 318 } else { 319 /* 320 * wait returned an error. If EINTR, then 321 * normal finish, else it's an unexpected 322 * error... 323 */ 324 if (errno != EINTR || !finished) { 325 perror("unexpected wait error"); 326 goto cleanup; 327 } 328 } 329 if (sigrelse(SIGALRM) || sigrelse(SIGINT)) { 330 perror("sigrelse error"); 331 goto cleanup; 332 } 333 } 334 335 /* 336 * Finished! Check the map for sanity, then kill all 337 * the children and done!. 338 */ 339 340 if (sighold(SIGALRM)) { 341 perror("sighold error"); 342 goto cleanup; 343 } 344 (void)alarm(0); 345 no_prob = 1; 346 347 cleanup: 348 for (i = 0; i < nprocs; i++) 349 (void)kill(pidarray[i], SIGKILL); /* failure? oh well. */ 350 351 while (wait(&wait_stat) != -1 || errno != ECHILD) 352 continue; 353 354 if (no_prob) { /* only check file if no errors */ 355 if (!mapokay(buf)) { 356 (void)fprintf(stderr, "map data incorrect!\n"); 357 anyfail(); 358 } else 359 (void)printf("map data okay\n"); 360 } 361 362 (void)time(&t); 363 // (void)printf("%s: Finished %s", argv[0], ctime(&t)); LTP POrt 364 ok_exit(); 365 tst_exit(); 366 } 367 368 /* 369 * Child process that reads/writes map. The child reads/writes 370 * its own locations on random pages of the map (its locations being 371 * determined based on nprocs & procno). After a specific number of 372 * iterations, it exits. 373 */ 374 void child_mapper(unsigned procno, unsigned nprocs) 375 { 376 uchar_t *paddr; 377 unsigned randpage; 378 unsigned int seed; 379 unsigned loopcnt; 380 unsigned nloops; 381 unsigned i; 382 383 seed = initrand(); /* initialize random seed */ 384 385 nloops = (randloops) ? (lrand48() % MAXLOOPS) : MAXLOOPS; 386 387 if (debug) 388 (void)printf("child %d (pid %d): seed %d, loop %d\n", 389 procno, getpid(), seed, nloops); 390 391 /* 392 * Now loop read/writing random pages. 393 */ 394 395 for (loopcnt = 0; loopcnt < nloops; loopcnt++) { 396 randpage = lrand48() % mappages; 397 /* find the page address */ 398 paddr = (uchar_t *) (mapaddr + (randpage * pagesize)); 399 400 for (i = procno; i < pagesize; i += nprocs) { 401 if (*((unsigned char *)(paddr + i)) 402 != ((procno + pattern) & 0xff)) { 403 (void)fprintf(stderr, 404 "child %d: invalid data <x%x>", 405 procno, 406 *((unsigned char *)(paddr + i))); 407 (void)fprintf(stderr, 408 " at pg %d off %d, exp <x%x>\n", 409 randpage, i, 410 (procno + pattern) & 0xff); 411 anyfail(); 412 } 413 /* 414 * Now write it. 415 */ 416 417 *(paddr + i) = (procno + pattern) & 0xff; 418 } 419 } 420 421 if (dosync) { 422 randpage = (unsigned)lrand48() % mappages; 423 paddr = (uchar_t *) mapaddr + (randpage * pagesize); 424 if (msync((caddr_t) paddr, (mappages - randpage) * pagesize, 425 MS_SYNC) == -1) { 426 perror("msync error"); 427 anyfail(); 428 } 429 } 430 431 exit(0); 432 } 433 434 /* 435 * Make sure file has all the correct data. 436 */ 437 int mapokay(uchar_t * expbuf) 438 { 439 uchar_t *ptr; 440 unsigned i, j; 441 442 ptr = (uchar_t *) mapaddr; 443 for (i = 0; i < mappages; i++) { 444 /* 445 * Compare read bytes of data. 446 */ 447 for (j = 0; j < pagesize; j++) { 448 if (*ptr != expbuf[j]) { 449 (void)fprintf(stderr, 450 "bad map data: exp %c got %c)", 451 expbuf[j], *ptr); 452 (void)fprintf(stderr, ", pg %d off %d\n", i, j); 453 anyfail(); 454 } 455 ptr++; 456 } 457 } 458 459 return 1; 460 } 461 462 /*ARGSUSED*/ void finish(int sig) 463 { 464 finished++; 465 return; 466 } 467 468 unsigned int initrand(void) 469 { 470 unsigned int seed; 471 472 /* 473 * Initialize random seed... Got this from a test written 474 * by scooter: 475 * Use srand/rand to diffuse the information from the 476 * time and pid. If you start several processes, then 477 * the time and pid information don't provide much 478 * variation. 479 */ 480 srand((unsigned int)getpid()); 481 seed = rand(); 482 srand((unsigned int)time(NULL)); 483 seed = (seed ^ rand()) % 100000; 484 srand48((long int)seed); 485 return (seed); 486 } 487 488 /***** LTP Port *****/ 489 void ok_exit(void) 490 { 491 tst_resm(TPASS, "Test passed\n"); 492 tst_exit(); 493 } 494 495 int anyfail(void) 496 { 497 tst_brkm(TFAIL, NULL, "Test failed\n"); 498 } 499 500 /***** ** ** *****/ 501