1/* 2 * 3 * Copyright (c) International Business Machines Corp., 2001 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/******************************************************************************/ 21/* */ 22/* File: mmstress.c */ 23/* */ 24/* Description: This is a test program that performs general stress with */ 25/* memory race conditions. It contains seven testcases that */ 26/* will test race conditions between simultaneous read fault, */ 27/* write fault, copy on write (COW) fault e.t.c. */ 28/* This testcase is intended to execute on the Linux operating */ 29/* system and can be easily ported to work on other operating */ 30/* systems as well. */ 31/* */ 32/* Usage: mmstress -h -n TEST NUMBER -p NPAGES -t EXECUTION TIME -v -V */ 33/* -h - Help */ 34/* -n TEST NUMBER - Execute a particular testcase */ 35/* -p NPAGES - Use NPAGES pages for tests */ 36/* -t EXECUTION TIME - Execute test for a certain time */ 37/* -v - Verbose output */ 38/* -V - Version of this program */ 39/* */ 40/* Author: Manoj Iyer - manjo@mail.utexas.edu */ 41/* */ 42/******************************************************************************/ 43 44/******************************************************************************/ 45/* */ 46/* Apr-13-2001 Created: Manoj Iyer, IBM Austin. */ 47/* These tests are adapted from AIX vmm FVT tests. */ 48/* */ 49/* Oct-24-2001 Modified. */ 50/* - freed buffers that were allocated. */ 51/* - closed removed files. This will remove the disk full error */ 52/* - use pthread_exit in case of theads instead of return. This */ 53/* was really bad to use return! */ 54/* - created usage function. */ 55/* - pthread_join checks for thread exit status reported by */ 56/* pthread_exit() */ 57/* */ 58/* Oct-25-2001 Modified. */ 59/* - Fixed bug in usage() */ 60/* - malloc'ed pointer for pthread return value. */ 61/* - changed scheme. If no options are specified, all the tests */ 62/* will be run once. */ 63/* */ 64/* Nov-02-2001 Modified - Paul Larson */ 65/* - Added sched_yield to thread_fault to fix hang */ 66/* - Removed thread_mmap */ 67/* */ 68/* Nov-09-2001 Modified - Manoj Iyer */ 69/* - Removed compile warnings. */ 70/* - Added missing header file. #include <stdlib.h> */ 71/* */ 72/* Oct-28-2003 Modified - Manoj Iyer */ 73/* - missing parenthesis added. */ 74/* - formatting changes. */ 75/* - increased NUMPAGES to 9999. */ 76/* */ 77/* Jan-30-2003 Modified - Gary Williams */ 78/* - fixed a race condition between the two threads */ 79/* - made it so if any of the testcases fail the test will fail */ 80/* - fixed so status of child in test 6 is used to determine result */ 81/* - fixed the use of the remove_files function in a conditional */ 82/* */ 83/******************************************************************************/ 84 85#include <stdio.h> 86#include <sys/types.h> 87#include <sys/stat.h> 88#include <fcntl.h> 89#include <unistd.h> 90#include <sys/mman.h> 91#include <sys/wait.h> 92#include <sys/time.h> 93#include <pthread.h> 94#include <signal.h> 95#include <errno.h> 96#include <stdlib.h> 97#include <string.h> 98#include <sched.h> 99#include <stdint.h> 100#include <getopt.h> 101 102#include "test.h" 103 104/* GLOBAL DEFINES */ 105#define SIGENDSIG -1 /* end of signal marker */ 106#define THNUM 0 /* array element pointing to number of threads */ 107#define MAPADDR 1 /* array element pointing to map address */ 108#define PAGESIZ 2 /* array element pointing to page size */ 109#define FLTIPE 3 /* array element pointing to fault type */ 110#define READ_FAULT 0 /* instructs routine to simulate read fault */ 111#define WRITE_FAULT 1 /* instructs routine to simulate write fault */ 112#define COW_FAULT 2 /* instructs routine to simulate copy-on-write fault */ 113#define NUMTHREAD 32 /* number of threads to spawn default to 32 */ 114#define NUMPAGES 9999 /* default (random) value of number of pages */ 115#ifndef TRUE 116#define TRUE 1 117#endif 118#ifndef FALSE 119#define FALSE 0 120#endif 121#define FAILED (-1) /* return status for all funcs indicating failure */ 122#define SUCCESS 0 /* return status for all routines indicating success */ 123 124#define BRKSZ 512*1024 /* program data space allocation value */ 125 126static volatile int wait_thread; /* used to wake up sleeping threads */ 127static volatile int thread_begin; /* used to coordinate threads */ 128static int verbose_print = FALSE; /* print more test information */ 129 130static int pages_num = NUMPAGES; /* number of pages to use for tests */ 131static volatile int alarm_fired; 132 133char *TCID = "mmstress"; 134int TST_TOTAL = 6; 135 136static void sig_handler(int signal) 137{ 138 if (signal != SIGALRM) { 139 fprintf(stderr, 140 "sig_handlder(): unexpected signal caught [%d]\n", 141 signal); 142 exit(TBROK); 143 } 144 145 alarm_fired = 1; 146} 147 148static void usage(char *progname) 149{ 150 fprintf(stderr, "usage:%s -h -n test -t time -v [-V]\n", progname); 151 fprintf(stderr, "\t-h displays all options\n"); 152 fprintf(stderr, "\t-n test number, if no test number\n" 153 "\t is specified, all the tests will be run\n"); 154 fprintf(stderr, "\t-p specify the number of pages to\n" 155 "\t use for allocation\n"); 156 fprintf(stderr, "\t-t specify the time in hours\n"); 157 fprintf(stderr, "\t-v verbose output\n"); 158 fprintf(stderr, "\t-V program version\n"); 159 exit(1); 160} 161 162static void set_timer(int run_time) 163{ 164 struct itimerval timer; 165 166 memset(&timer, 0, sizeof(struct itimerval)); 167 timer.it_interval.tv_usec = 0; 168 timer.it_interval.tv_sec = 0; 169 timer.it_value.tv_usec = 0; 170 timer.it_value.tv_sec = (time_t) (run_time * 3600.0); 171 172 if (setitimer(ITIMER_REAL, &timer, NULL)) { 173 perror("set_timer(): setitimer()"); 174 exit(1); 175 } 176} 177 178/******************************************************************************/ 179/* */ 180/* Function: thread_fault */ 181/* */ 182/* Description: Executes as a thread function and accesses the memory pages */ 183/* depending on the fault_type to be generated. This function */ 184/* can cause READ fault, WRITE fault, COW fault. */ 185/* */ 186/* Input: void *args - argments passed to the exec routine by */ 187/* pthread_create() */ 188/* */ 189/******************************************************************************/ 190static void *thread_fault(void *args) 191{ 192 long *local_args = args; /* local pointer to list of arguments */ 193 /* local_args[THNUM] - the thread number */ 194 /* local_args[MAPADDR] - map address */ 195 /* local_args[PAGESIZ] - page size */ 196 /* local_args[FLTIPE] - fault type */ 197 int pgnum_ndx = 0; /* index to the number of pages */ 198 char *start_addr /* start address of the page */ 199 = (void *) (local_args[MAPADDR] 200 + (int)local_args[THNUM] 201 * (pages_num / NUMTHREAD) 202 * local_args[PAGESIZ]); 203 char read_from_addr = 0; /* address to which read from page is done */ 204 char write_to_addr[] = { 'a' }; /* character to be writen to the page */ 205 206 /*************************************************************/ 207 /* The way it was, args could be overwritten by subsequent uses 208 * of it before this routine had a chance to use the data. 209 * This flag stops the overwrite until this routine gets to 210 * here. At this point, it is done initializing and it is 211 * safe for the parent thread to continue (which will change 212 * args). 213 */ 214 thread_begin = FALSE; 215 216 while (wait_thread) 217 sched_yield(); 218 219 for (; pgnum_ndx < (pages_num / NUMTHREAD); pgnum_ndx++) { 220 /* if the fault to be generated is READ_FAULT, read from the page */ 221 /* else write a character to the page. */ 222 ((int)local_args[3] == READ_FAULT) ? (read_from_addr = 223 *start_addr) 224 : (*start_addr = write_to_addr[0]); 225 start_addr += local_args[PAGESIZ]; 226 if (verbose_print) 227 tst_resm(TINFO, 228 "thread_fault(): generating fault type %ld" 229 " @page address %p", local_args[3], 230 start_addr); 231 fflush(NULL); 232 } 233 pthread_exit(NULL); 234} 235 236/******************************************************************************/ 237/* */ 238/* Function: remove_tmpfiles */ 239/* */ 240/* Description: remove temporary files that were created by the tests. */ 241/* */ 242/******************************************************************************/ 243static int remove_files(char *filename, char *addr) 244{ 245 if (addr) 246 if (munmap(addr, sysconf(_SC_PAGESIZE) * pages_num) < 0) { 247 perror("map_and_thread(): munmap()"); 248 return FAILED; 249 } 250 if (strcmp(filename, "NULL") && strcmp(filename, "/dev/zero")) { 251 if (unlink(filename)) { 252 perror("map_and_thread(): ulink()"); 253 return FAILED; 254 } 255 } else { 256 if (verbose_print) 257 tst_resm(TINFO, "file %s removed", filename); 258 259 } 260 return SUCCESS; 261} 262 263/******************************************************************************/ 264/* */ 265/* Function: map_and_thread */ 266/* */ 267/* Description: Creates mappings with the required properties, of MAP_PRIVATE */ 268/* MAP_SHARED and of PROT_RED / PROT_READ|PROT_WRITE. */ 269/* Create threads and execute a routine that will generate the */ 270/* desired fault condition, viz, read, write and cow fault. */ 271/* */ 272/* Input: char *tmpfile - name of temporary file that is created */ 273/* int fault_type - type of fault that is to be generated. */ 274/* */ 275/******************************************************************************/ 276int map_and_thread(char *tmpfile, 277 void *(*exec_func) (void *), 278 int fault_type, 279 int num_thread) 280{ 281 int fd = 0; /* file descriptor of the file created */ 282 int thrd_ndx = 0; /* index to the number of threads created */ 283 int map_type = 0; /* specifies the type of the mapped object */ 284 void *th_status; /* status of the thread when it is finished */ 285 long th_args[5]; /* argument list passed to thread_fault() */ 286 char *empty_buf = NULL; /* empty buffer used to fill temp file */ 287 long pagesize /* contains page size at runtime */ 288 = sysconf(_SC_PAGESIZE); 289 static pthread_t pthread_ids[NUMTHREAD]; 290 /* contains ids of the threads created */ 291 void * map_addr = NULL; /* address where the file is mapped */ 292 ssize_t written = 0; 293 ssize_t bytes; 294 295 /* Create a file with permissions 0666, and open it with RDRW perms */ 296 /* if the name is not a NULL */ 297 298 if (strcmp(tmpfile, "NULL")) { 299 if ((fd = 300 open(tmpfile, O_RDWR | O_CREAT, 301 S_IRWXO | S_IRWXU | S_IRWXG)) 302 == -1) { 303 perror("map_and_thread(): open()"); 304 fflush(NULL); 305 return FAILED; 306 } 307 308 /* Write pagesize * pages_num bytes to the file */ 309 empty_buf = malloc(pagesize * pages_num); 310 if (!empty_buf) { 311 perror("map_and_thread(): malloc()"); 312 remove_files(tmpfile, NULL); 313 close(fd); 314 fflush(NULL); 315 return FAILED; 316 } 317 318 /* Writing fewer bytes than required is not an error so retry if 319 * fewer were written; if that happened due to some permanent 320 * error like ENOSPC the following retry will fail and a proper 321 * errno will be reported. 322 */ 323 do { 324 bytes = write(fd, empty_buf + written, 325 pagesize * pages_num - written); 326 if (bytes < 0) { 327 perror("map_and_thread(): write()"); 328 free(empty_buf); 329 fflush(NULL); 330 close(fd); 331 remove_files(tmpfile, NULL); 332 return FAILED; 333 } 334 written += bytes; 335 } while (written < pagesize * pages_num); 336 map_type = (fault_type == COW_FAULT) ? MAP_PRIVATE : MAP_SHARED; 337 338 /* Map the file, if the required fault type is COW_FAULT map the file */ 339 /* private, else map the file shared. if READ_FAULT is required to be */ 340 /* generated map the file with read protection else map with read - */ 341 /* write protection. */ 342 343 if ((map_addr = (void *) mmap(0, pagesize * pages_num, 344 ((fault_type == READ_FAULT) ? 345 PROT_READ : PROT_READ | 346 PROT_WRITE), map_type, fd, 0)) 347 == MAP_FAILED) { 348 perror("map_and_thread(): mmap()"); 349 free(empty_buf); 350 fflush(NULL); 351 remove_files(tmpfile, NULL); 352 close(fd); 353 return FAILED; 354 } else { 355 if (verbose_print) 356 tst_resm(TINFO, 357 "map_and_thread(): mmap success, address = %p", 358 map_addr); 359 fflush(NULL); 360 } 361 } 362 363 /* As long as wait is set to TRUE, the thread that will be created will */ 364 /* loop in its exec routine */ 365 366 wait_thread = TRUE; 367 368 /* Create a few threads, ideally number of threads equals number of CPU'S */ 369 /* so that we can assume that each thread will run on a single CPU in */ 370 /* of SMP machines. Currently we will create NR_CPUS number of threads. */ 371 372 th_args[1] = (long)map_addr; 373 th_args[2] = pagesize; 374 th_args[3] = fault_type; 375 do { 376 th_args[0] = thrd_ndx; 377 th_args[4] = (long)0; 378 379 /*************************************************************/ 380 /* The way it was, args could be overwritten by subsequent uses 381 * of it before the called routine had a chance to fully initialize. 382 * This flag stops the overwrite until that routine gets to 383 * begin. At that point, it is done initializing and it is 384 * safe for the this thread to continue (which will change 385 * args). 386 * A basic race condition. 387 */ 388 thread_begin = TRUE; 389 if (pthread_create(&pthread_ids[thrd_ndx++], NULL, exec_func, 390 (void *)&th_args)) { 391 perror("map_and_thread(): pthread_create()"); 392 thread_begin = FALSE; 393 free(empty_buf); 394 fflush(NULL); 395 remove_files(tmpfile, map_addr); 396 close(fd); 397 return FAILED; 398 } else { 399 /***************************************************/ 400 /* Yield until new thread is done with args. 401 */ 402 while (thread_begin) 403 sched_yield(); 404 } 405 } while (thrd_ndx < num_thread); 406 407 if (verbose_print) 408 tst_resm(TINFO, "map_and_thread(): pthread_create() success"); 409 wait_thread = FALSE; 410 411 /* suspend the execution of the calling thread till the execution of the */ 412 /* other thread has been terminated. */ 413 414 for (thrd_ndx = 0; thrd_ndx < NUMTHREAD; thrd_ndx++) { 415 if (pthread_join(pthread_ids[thrd_ndx], &th_status)) { 416 perror("map_and_thread(): pthread_join()"); 417 free(empty_buf); 418 fflush(NULL); 419 remove_files(tmpfile, map_addr); 420 close(fd); 421 return FAILED; 422 } else { 423 if ((long)th_status == 1) { 424 tst_resm(TINFO, 425 "thread [%ld] - process exited with errors", 426 (long)pthread_ids[thrd_ndx]); 427 free(empty_buf); 428 remove_files(tmpfile, map_addr); 429 close(fd); 430 exit(1); 431 } 432 } 433 } 434 435 /* remove the temporary file that was created. - clean up */ 436 /* but dont try to remove special files. */ 437 438 /***********************************************/ 439 /* Was if !(remove_files()) ... 440 * If that routine succeeds, it returns SUCCESS, which 441 * happens to be 0. So if the routine succeeded, the 442 * above condition would indicate failure. This change 443 * fixes that. 444 */ 445 if (remove_files(tmpfile, map_addr) == FAILED) { 446 free(empty_buf); 447 return FAILED; 448 } 449 450 free(empty_buf); 451 close(fd); 452 return SUCCESS; 453} 454 455/******************************************************************************/ 456/* */ 457/* Test: Test case tests the race condition between simultaneous read */ 458/* faults in the same address space. */ 459/* */ 460/* Description: map a file into memory, create threads and execute a thread */ 461/* function that will cause read faults by simultaneously reading*/ 462/* from this memory space. */ 463/******************************************************************************/ 464static int test1(void) 465{ 466 tst_resm(TINFO, "test1: Test case tests the race condition between " 467 "simultaneous read faults in the same address space."); 468 return map_and_thread("./tmp.file.1", thread_fault, READ_FAULT, NUMTHREAD); 469} 470 471/******************************************************************************/ 472/* */ 473/* Test: Test case tests the race condition between simultaneous write */ 474/* faults in the same address space. */ 475/* */ 476/* Description: map a file into memory, create threads and execute a thread */ 477/* function that will cause write faults by simultaneously */ 478/* writing to this memory space. */ 479/******************************************************************************/ 480static int test2(void) 481{ 482 tst_resm(TINFO, "test2: Test case tests the race condition between " 483 "simultaneous write faults in the same address space."); 484 return map_and_thread("./tmp.file.2", thread_fault, WRITE_FAULT, NUMTHREAD); 485} 486 487/******************************************************************************/ 488/* */ 489/* Test: Test case tests the race condition between simultaneous COW */ 490/* faults in the same address space. */ 491/* */ 492/* Description: map a file into memory, create threads and execute a thread */ 493/* function that will cause COW faults by simultaneously */ 494/* writing to this memory space. */ 495/* */ 496/******************************************************************************/ 497static int test3(void) 498{ 499 tst_resm(TINFO, "test3: Test case tests the race condition between " 500 "simultaneous COW faults in the same address space."); 501 return map_and_thread("./tmp.file.3", thread_fault, COW_FAULT, NUMTHREAD); 502} 503 504/******************************************************************************/ 505/* */ 506/* Test: Test case tests the race condition between simultaneous READ */ 507/* faults in the same address space. File mapped is /dev/zero */ 508/* */ 509/* Description: Map a file into memory, create threads and execute a thread */ 510/* function that will cause READ faults by simultaneously */ 511/* writing to this memory space. */ 512/* */ 513/******************************************************************************/ 514static int test4(void) 515{ 516 tst_resm(TINFO, "test4: Test case tests the race condition between " 517 "simultaneous READ faults in the same address space. " 518 "The file mapped is /dev/zero"); 519 return map_and_thread("/dev/zero", thread_fault, COW_FAULT, NUMTHREAD); 520} 521 522/******************************************************************************/ 523/* */ 524/* Test: Test case tests the race condition between simultaneous */ 525/* fork - exit faults in the same address space. */ 526/* */ 527/* Description: Initialize large data in the parent process, fork a child and */ 528/* and the parent waits for the child to complete execution. */ 529/* */ 530/******************************************************************************/ 531static int test5(void) 532{ 533 int fork_ndx = 0; 534 pid_t pid = 0; 535 int wait_status = 0; 536 537 tst_resm(TINFO, "test5: Test case tests the race condition between " 538 "simultaneous fork - exit faults in the same address space."); 539 540 /* increment the program's data space by 200*1024 (BRKSZ) bytes */ 541 542 if (sbrk(BRKSZ) == (void *) - 1) { 543 perror("test5(): sbrk()"); 544 fflush(NULL); 545 return FAILED; 546 } 547 548 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */ 549 /* a separate CPU if NRCPUS = NUMTHREAD. The child does nothing; exits */ 550 /* immediately, parent waits for child to complete execution. */ 551 do { 552 if (!(pid = fork())) 553 _exit(0); 554 else { 555 if (pid != -1) 556 wait(&wait_status); 557 } 558 559 } while (fork_ndx++ < NUMTHREAD); 560 561 if (sbrk(-BRKSZ) == (void *) - 1) { 562 tst_resm(TINFO, "test5(): rollback sbrk failed"); 563 fflush(NULL); 564 perror("test5(): sbrk()"); 565 fflush(NULL); 566 return FAILED; 567 } 568 return SUCCESS; 569} 570 571/******************************************************************************/ 572/* */ 573/* Test: Test case tests the race condition between simultaneous */ 574/* fork - exec - exit faults in the same address space. */ 575/* */ 576/* Description: Initialize large data in the parent process, fork a child and */ 577/* and the parent waits for the child to complete execution. The */ 578/* child program execs a dummy program. */ 579/* */ 580/******************************************************************************/ 581static int test6(void) 582{ 583 int res = SUCCESS; 584 int fork_ndx = 0; 585 pid_t pid = 0; 586 int wait_status; 587 char *argv_init[2] = { "arg1", NULL }; 588 589 tst_resm(TINFO, "test6: Test case tests the race condition between " 590 "simultaneous fork -exec - exit faults in the same address space."); 591 592 /* increment the program's data space by 200*1024 (BRKSZ) bytes */ 593 if (sbrk(BRKSZ) == (void *) - 1) { 594 perror("test6(): sbrk()"); 595 fflush(NULL); 596 return FAILED; 597 } 598 599 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */ 600 /* a separate CPU if NRCPUS = NUMTHREAD. The child execs a dummy program */ 601 /* and parent waits for child to complete execution. */ 602 do { 603 if (!(pid = fork())) { 604 if (execvp("mmstress_dummy", argv_init) == -1) { 605 if (execvp("./mmstress_dummy", argv_init) == -1) { 606 perror("test6(): execvp()"); 607 fflush(NULL); 608 exit(99); 609 } 610 } 611 } else { 612 if (pid != -1) 613 wait(&wait_status); 614 615 if (WEXITSTATUS(wait_status) != 0) 616 res = FAILED; 617 } 618 619 } while (fork_ndx++ < NUMTHREAD); 620 621 if (sbrk(-BRKSZ) == (void *) - 1) { 622 tst_resm(TINFO, "test6(): rollback sbrk failed"); 623 fflush(NULL); 624 perror("test6(): sbrk()"); 625 fflush(NULL); 626 return FAILED; 627 } 628 629 return res; 630} 631 632static int (*(test_ptr)[]) () = {test1, test2, test3, test4, test5, test6}; 633 634static void run_test(unsigned int i) 635{ 636 int rc; 637 638 rc = test_ptr[i](); 639 640 if (rc == SUCCESS) 641 tst_resm(TPASS, "TEST %d Passed", i + 1); 642 else 643 tst_resm(TFAIL, "TEST %d Failed", i + 1); 644 645 if (alarm_fired) 646 tst_exit(); 647} 648 649int main(int argc, char **argv) 650{ 651 static char *version_info = "mmstress V1.00 04/17/2001"; 652 int ch; 653 unsigned int i; 654 int test_num = 0; 655 int test_time = 0; 656 int run_once = TRUE; 657 658 static struct signal_info { 659 int signum; 660 char *signame; 661 } sig_info[] = { 662 {SIGHUP, "SIGHUP"}, 663 {SIGINT, "SIGINT"}, 664 {SIGQUIT, "SIGQUIT"}, 665 {SIGABRT, "SIGABRT"}, 666 {SIGBUS, "SIGBUS"}, 667 {SIGSEGV, "SIGSEGV"}, 668 {SIGALRM, "SIGALRM"}, 669 {SIGUSR1, "SIGUSR1"}, 670 {SIGUSR2, "SIGUSR2"}, 671 {SIGENDSIG, "ENDSIG"} 672 }; 673 674 setvbuf(stdout, NULL, _IONBF, 0); 675 setvbuf(stderr, NULL, _IONBF, 0); 676 677 if (argc < 2) 678 tst_resm(TINFO, "run %s -h for all options", argv[0]); 679 680 while ((ch = getopt(argc, argv, "hn:p:t:vV")) != -1) { 681 switch (ch) { 682 case 'h': 683 usage(argv[0]); 684 break; 685 case 'n': 686 test_num = atoi(optarg); 687 break; 688 case 'p': 689 pages_num = atoi(optarg); 690 break; 691 case 't': 692 tst_resm(TINFO, 693 "Test is scheduled to run for %d hours", 694 test_time = atoi(optarg)); 695 run_once = FALSE; 696 break; 697 case 'v': 698 verbose_print = TRUE; 699 break; 700 case 'V': 701 tst_resm(TINFO, "%s: %s", argv[0], version_info); 702 break; 703 case '?': 704 fprintf(stderr, 705 "%s: unknown option - %c ignored\n", 706 argv[0], optopt); 707 break; 708 default: 709 tst_brkm(TBROK, NULL, "%s: getopt() failed!!!", 710 argv[0]); 711 } 712 } 713 714 set_timer(test_time); 715 716 for (i = 0; sig_info[i].signum != -1; i++) { 717 if (signal(sig_info[i].signum, sig_handler) == SIG_ERR) { 718 tst_brkm(TBROK | TERRNO, NULL, "signal(%s) failed", 719 sig_info[i].signame); 720 } 721 } 722 723 tst_tmpdir(); 724 725 do { 726 if (!test_num) { 727 for (i = 0; i < ARRAY_SIZE(test_ptr); i++) 728 run_test(i); 729 } else { 730 if (test_num > (int)ARRAY_SIZE(test_ptr)) { 731 tst_brkm(TBROK, NULL, "Invalid test number %i", 732 test_num); 733 } 734 735 run_test(test_num-1); 736 } 737 } while (!run_once); 738 739 tst_rmdir(); 740 tst_exit(); 741} 742