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 MAXTEST 6 /* total number of testcase in this program */
125 #define BRKSZ 512*1024 /* program data space allocation value */
126
127 static volatile int wait_thread; /* used to wake up sleeping threads */
128 static volatile int thread_begin; /* used to coordinate threads */
129 static int verbose_print = FALSE; /* print more test information */
130
131 static int pages_num = NUMPAGES; /* number of pages to use for tests */
132 static volatile int alarm_fired;
133
134 char *TCID = "mmstress";
135 int TST_TOTAL = 6;
136
sig_handler(int signal)137 static void sig_handler(int signal)
138 {
139 if (signal != SIGALRM) {
140 fprintf(stderr,
141 "sig_handlder(): unexpected signal caught [%d]\n",
142 signal);
143 exit(TBROK);
144 }
145
146 alarm_fired = 1;
147 }
148
usage(char * progname)149 static void usage(char *progname)
150 {
151 fprintf(stderr, "usage:%s -h -n test -t time -v [-V]\n", progname);
152 fprintf(stderr, "\t-h displays all options\n");
153 fprintf(stderr, "\t-n test number, if no test number\n"
154 "\t is specified, all the tests will be run\n");
155 fprintf(stderr, "\t-p specify the number of pages to\n"
156 "\t use for allocation\n");
157 fprintf(stderr, "\t-t specify the time in hours\n");
158 fprintf(stderr, "\t-v verbose output\n");
159 fprintf(stderr, "\t-V program version\n");
160 exit(1);
161 }
162
set_timer(int run_time)163 static void set_timer(int run_time)
164 {
165 struct itimerval timer;
166
167 memset(&timer, 0, sizeof(struct itimerval));
168 timer.it_interval.tv_usec = 0;
169 timer.it_interval.tv_sec = 0;
170 timer.it_value.tv_usec = 0;
171 timer.it_value.tv_sec = (time_t) (run_time * 3600.0);
172
173 if (setitimer(ITIMER_REAL, &timer, NULL)) {
174 perror("set_timer(): setitimer()");
175 exit(1);
176 }
177 }
178
179 /******************************************************************************/
180 /* */
181 /* Function: thread_fault */
182 /* */
183 /* Description: Executes as a thread function and accesses the memory pages */
184 /* depending on the fault_type to be generated. This function */
185 /* can cause READ fault, WRITE fault, COW fault. */
186 /* */
187 /* Input: void *args - argments passed to the exec routine by */
188 /* pthread_create() */
189 /* */
190 /******************************************************************************/
thread_fault(void * args)191 static void *thread_fault(void *args)
192 {
193 long *local_args = args; /* local pointer to list of arguments */
194 /* local_args[THNUM] - the thread number */
195 /* local_args[MAPADDR] - map address */
196 /* local_args[PAGESIZ] - page size */
197 /* local_args[FLTIPE] - fault type */
198 int pgnum_ndx = 0; /* index to the number of pages */
199 char *start_addr /* start address of the page */
200 = (void *) (local_args[MAPADDR]
201 + (int)local_args[THNUM]
202 * (pages_num / NUMTHREAD)
203 * local_args[PAGESIZ]);
204 char read_from_addr = 0; /* address to which read from page is done */
205 char write_to_addr[] = { 'a' }; /* character to be writen to the page */
206
207 /*************************************************************/
208 /* The way it was, args could be overwritten by subsequent uses
209 * of it before this routine had a chance to use the data.
210 * This flag stops the overwrite until this routine gets to
211 * here. At this point, it is done initializing and it is
212 * safe for the parent thread to continue (which will change
213 * args).
214 */
215 thread_begin = FALSE;
216
217 while (wait_thread)
218 sched_yield();
219
220 for (; pgnum_ndx < (pages_num / NUMTHREAD); pgnum_ndx++) {
221 /* if the fault to be generated is READ_FAULT, read from the page */
222 /* else write a character to the page. */
223 ((int)local_args[3] == READ_FAULT) ? (read_from_addr =
224 *start_addr)
225 : (*start_addr = write_to_addr[0]);
226 start_addr += local_args[PAGESIZ];
227 if (verbose_print)
228 tst_resm(TINFO,
229 "thread_fault(): generating fault type %ld"
230 " @page address %p", local_args[3],
231 start_addr);
232 fflush(NULL);
233 }
234 pthread_exit(NULL);
235 }
236
237 /******************************************************************************/
238 /* */
239 /* Function: remove_tmpfiles */
240 /* */
241 /* Description: remove temporary files that were created by the tests. */
242 /* */
243 /******************************************************************************/
remove_files(char * filename,char * addr)244 static int remove_files(char *filename, char *addr)
245 {
246 if (addr)
247 if (munmap(addr, sysconf(_SC_PAGESIZE) * pages_num) < 0) {
248 perror("map_and_thread(): munmap()");
249 return FAILED;
250 }
251 if (strcmp(filename, "NULL") && strcmp(filename, "/dev/zero")) {
252 if (unlink(filename)) {
253 perror("map_and_thread(): ulink()");
254 return FAILED;
255 }
256 } else {
257 if (verbose_print)
258 tst_resm(TINFO, "file %s removed", filename);
259
260 }
261 return SUCCESS;
262 }
263
264 /******************************************************************************/
265 /* */
266 /* Function: map_and_thread */
267 /* */
268 /* Description: Creates mappings with the required properties, of MAP_PRIVATE */
269 /* MAP_SHARED and of PROT_RED / PROT_READ|PROT_WRITE. */
270 /* Create threads and execute a routine that will generate the */
271 /* desired fault condition, viz, read, write and cow fault. */
272 /* */
273 /* Input: char *tmpfile - name of temporary file that is created */
274 /* int fault_type - type of fault that is to be generated. */
275 /* */
276 /******************************************************************************/
map_and_thread(char * tmpfile,void * (* exec_func)(void *),int fault_type,int num_thread)277 int map_and_thread(char *tmpfile,
278 void *(*exec_func) (void *),
279 int fault_type,
280 int num_thread)
281 {
282 int fd = 0; /* file descriptor of the file created */
283 int thrd_ndx = 0; /* index to the number of threads created */
284 int map_type = 0; /* specifies the type of the mapped object */
285 void *th_status; /* status of the thread when it is finished */
286 long th_args[5]; /* argument list passed to thread_fault() */
287 char *empty_buf = NULL; /* empty buffer used to fill temp file */
288 long pagesize /* contains page size at runtime */
289 = sysconf(_SC_PAGESIZE);
290 static pthread_t pthread_ids[NUMTHREAD];
291 /* contains ids of the threads created */
292 void * map_addr = NULL; /* address where the file is mapped */
293
294 /* Create a file with permissions 0666, and open it with RDRW perms */
295 /* if the name is not a NULL */
296
297 if (strcmp(tmpfile, "NULL")) {
298 if ((fd =
299 open(tmpfile, O_RDWR | O_CREAT,
300 S_IRWXO | S_IRWXU | S_IRWXG))
301 == -1) {
302 perror("map_and_thread(): open()");
303 close(fd);
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 (write(fd, empty_buf, pagesize * pages_num) !=
311 (pagesize * pages_num)) {
312 perror("map_and_thread(): write()");
313 free(empty_buf);
314 fflush(NULL);
315 remove_files(tmpfile, NULL);
316 close(fd);
317 return FAILED;
318 }
319 map_type = (fault_type == COW_FAULT) ? MAP_PRIVATE : MAP_SHARED;
320
321 /* Map the file, if the required fault type is COW_FAULT map the file */
322 /* private, else map the file shared. if READ_FAULT is required to be */
323 /* generated map the file with read protection else map with read - */
324 /* write protection. */
325
326 if ((map_addr = (void *) mmap(0, pagesize * pages_num,
327 ((fault_type == READ_FAULT) ?
328 PROT_READ : PROT_READ |
329 PROT_WRITE), map_type, fd, 0))
330 == MAP_FAILED) {
331 perror("map_and_thread(): mmap()");
332 free(empty_buf);
333 fflush(NULL);
334 remove_files(tmpfile, NULL);
335 close(fd);
336 return FAILED;
337 } else {
338 if (verbose_print)
339 tst_resm(TINFO,
340 "map_and_thread(): mmap success, address = %p",
341 map_addr);
342 fflush(NULL);
343 }
344 }
345
346 /* As long as wait is set to TRUE, the thread that will be created will */
347 /* loop in its exec routine */
348
349 wait_thread = TRUE;
350
351 /* Create a few threads, ideally number of threads equals number of CPU'S */
352 /* so that we can assume that each thread will run on a single CPU in */
353 /* of SMP machines. Currently we will create NR_CPUS number of threads. */
354
355 th_args[1] = (long)map_addr;
356 th_args[2] = pagesize;
357 th_args[3] = fault_type;
358 do {
359 th_args[0] = thrd_ndx;
360 th_args[4] = (long)0;
361
362 /*************************************************************/
363 /* The way it was, args could be overwritten by subsequent uses
364 * of it before the called routine had a chance to fully initialize.
365 * This flag stops the overwrite until that routine gets to
366 * begin. At that point, it is done initializing and it is
367 * safe for the this thread to continue (which will change
368 * args).
369 * A basic race condition.
370 */
371 thread_begin = TRUE;
372 if (pthread_create(&pthread_ids[thrd_ndx++], NULL, exec_func,
373 (void *)&th_args)) {
374 perror("map_and_thread(): pthread_create()");
375 thread_begin = FALSE;
376 free(empty_buf);
377 fflush(NULL);
378 remove_files(tmpfile, map_addr);
379 close(fd);
380 return FAILED;
381 } else {
382 /***************************************************/
383 /* Yield until new thread is done with args.
384 */
385 while (thread_begin)
386 sched_yield();
387 }
388 } while (thrd_ndx < num_thread);
389
390 if (verbose_print)
391 tst_resm(TINFO, "map_and_thread(): pthread_create() success");
392 wait_thread = FALSE;
393
394 /* suspend the execution of the calling thread till the execution of the */
395 /* other thread has been terminated. */
396
397 for (thrd_ndx = 0; thrd_ndx < NUMTHREAD; thrd_ndx++) {
398 if (pthread_join(pthread_ids[thrd_ndx], &th_status)) {
399 perror("map_and_thread(): pthread_join()");
400 free(empty_buf);
401 fflush(NULL);
402 remove_files(tmpfile, map_addr);
403 close(fd);
404 return FAILED;
405 } else {
406 if ((long)th_status == 1) {
407 tst_resm(TINFO,
408 "thread [%ld] - process exited with errors",
409 (long)pthread_ids[thrd_ndx]);
410 free(empty_buf);
411 remove_files(tmpfile, map_addr);
412 close(fd);
413 exit(1);
414 }
415 }
416 }
417
418 /* remove the temporary file that was created. - clean up */
419 /* but dont try to remove special files. */
420
421 /***********************************************/
422 /* Was if !(remove_files()) ...
423 * If that routine succeeds, it returns SUCCESS, which
424 * happens to be 0. So if the routine succeeded, the
425 * above condition would indicate failure. This change
426 * fixes that.
427 */
428 if (remove_files(tmpfile, map_addr) == FAILED) {
429 free(empty_buf);
430 return FAILED;
431 }
432
433 free(empty_buf);
434 close(fd);
435 return SUCCESS;
436 }
437
438 /******************************************************************************/
439 /* */
440 /* Test: Test case tests the race condition between simultaneous read */
441 /* faults in the same address space. */
442 /* */
443 /* Description: map a file into memory, create threads and execute a thread */
444 /* function that will cause read faults by simultaneously reading*/
445 /* from this memory space. */
446 /******************************************************************************/
test1(void)447 static int test1(void)
448 {
449 tst_resm(TINFO, "test1: Test case tests the race condition between "
450 "simultaneous read faults in the same address space.");
451 return map_and_thread("./tmp.file.1", thread_fault, READ_FAULT, NUMTHREAD);
452 }
453
454 /******************************************************************************/
455 /* */
456 /* Test: Test case tests the race condition between simultaneous write */
457 /* faults in the same address space. */
458 /* */
459 /* Description: map a file into memory, create threads and execute a thread */
460 /* function that will cause write faults by simultaneously */
461 /* writing to this memory space. */
462 /******************************************************************************/
test2(void)463 static int test2(void)
464 {
465 tst_resm(TINFO, "test2: Test case tests the race condition between "
466 "simultaneous write faults in the same address space.");
467 return map_and_thread("./tmp.file.2", thread_fault, WRITE_FAULT, NUMTHREAD);
468 }
469
470 /******************************************************************************/
471 /* */
472 /* Test: Test case tests the race condition between simultaneous COW */
473 /* faults in the same address space. */
474 /* */
475 /* Description: map a file into memory, create threads and execute a thread */
476 /* function that will cause COW faults by simultaneously */
477 /* writing to this memory space. */
478 /* */
479 /******************************************************************************/
test3(void)480 static int test3(void)
481 {
482 tst_resm(TINFO, "test3: Test case tests the race condition between "
483 "simultaneous COW faults in the same address space.");
484 return map_and_thread("./tmp.file.3", thread_fault, COW_FAULT, NUMTHREAD);
485 }
486
487 /******************************************************************************/
488 /* */
489 /* Test: Test case tests the race condition between simultaneous READ */
490 /* faults in the same address space. File mapped is /dev/zero */
491 /* */
492 /* Description: Map a file into memory, create threads and execute a thread */
493 /* function that will cause READ faults by simultaneously */
494 /* writing to this memory space. */
495 /* */
496 /******************************************************************************/
test4(void)497 static int test4(void)
498 {
499 tst_resm(TINFO, "test4: Test case tests the race condition between "
500 "simultaneous READ faults in the same address space. "
501 "The file mapped is /dev/zero");
502 return map_and_thread("/dev/zero", thread_fault, COW_FAULT, NUMTHREAD);
503 }
504
505 /******************************************************************************/
506 /* */
507 /* Test: Test case tests the race condition between simultaneous */
508 /* fork - exit faults in the same address space. */
509 /* */
510 /* Description: Initialize large data in the parent process, fork a child and */
511 /* and the parent waits for the child to complete execution. */
512 /* */
513 /******************************************************************************/
test5(void)514 static int test5(void)
515 {
516 int fork_ndx = 0;
517 pid_t pid = 0;
518 int wait_status = 0;
519
520 tst_resm(TINFO, "test5: Test case tests the race condition between "
521 "simultaneous fork - exit faults in the same address space.");
522
523 /* increment the program's data space by 200*1024 (BRKSZ) bytes */
524
525 if (sbrk(BRKSZ) == (void *) - 1) {
526 perror("test5(): sbrk()");
527 fflush(NULL);
528 return FAILED;
529 }
530
531 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */
532 /* a separate CPU if NRCPUS = NUMTHREAD. The child does nothing; exits */
533 /* immediately, parent waits for child to complete execution. */
534 do {
535 if (!(pid = fork()))
536 _exit(0);
537 else {
538 if (pid != -1)
539 wait(&wait_status);
540 }
541
542 } while (fork_ndx++ < NUMTHREAD);
543
544 if (sbrk(-BRKSZ) == (void *) - 1) {
545 tst_resm(TINFO, "test5(): rollback sbrk failed");
546 fflush(NULL);
547 perror("test5(): sbrk()");
548 fflush(NULL);
549 return FAILED;
550 }
551 return SUCCESS;
552 }
553
554 /******************************************************************************/
555 /* */
556 /* Test: Test case tests the race condition between simultaneous */
557 /* fork - exec - exit faults in the same address space. */
558 /* */
559 /* Description: Initialize large data in the parent process, fork a child and */
560 /* and the parent waits for the child to complete execution. The */
561 /* child program execs a dummy program. */
562 /* */
563 /******************************************************************************/
test6(void)564 static int test6(void)
565 {
566 int res = SUCCESS;
567 int fork_ndx = 0;
568 pid_t pid = 0;
569 int wait_status;
570 char *argv_init[2] = { "arg1", NULL };
571
572 tst_resm(TINFO, "test6: Test case tests the race condition between "
573 "simultaneous fork -exec - exit faults in the same address space.");
574
575 /* increment the program's data space by 200*1024 (BRKSZ) bytes */
576 if (sbrk(BRKSZ) == (void *) - 1) {
577 perror("test6(): sbrk()");
578 fflush(NULL);
579 return FAILED;
580 }
581
582 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */
583 /* a separate CPU if NRCPUS = NUMTHREAD. The child execs a dummy program */
584 /* and parent waits for child to complete execution. */
585 do {
586 if (!(pid = fork())) {
587 if (execvp("mmstress_dummy", argv_init) == -1) {
588 if (execvp("./mmstress_dummy", argv_init) == -1) {
589 perror("test6(): execvp()");
590 fflush(NULL);
591 exit(99);
592 }
593 }
594 } else {
595 if (pid != -1)
596 wait(&wait_status);
597
598 if (WEXITSTATUS(wait_status) != 0)
599 res = FAILED;
600 }
601
602 } while (fork_ndx++ < NUMTHREAD);
603
604 if (sbrk(-BRKSZ) == (void *) - 1) {
605 tst_resm(TINFO, "test6(): rollback sbrk failed");
606 fflush(NULL);
607 perror("test6(): sbrk()");
608 fflush(NULL);
609 return FAILED;
610 }
611
612 return res;
613 }
614
615 static int (*(test_ptr)[]) () = {test1, test2, test3, test4, test5, test6};
616
run_test(unsigned int i)617 static void run_test(unsigned int i)
618 {
619 int rc;
620
621 rc = test_ptr[i]();
622
623 if (rc == SUCCESS)
624 tst_resm(TPASS, "TEST %d Passed", i + 1);
625 else
626 tst_resm(TFAIL, "TEST %d Failed", i + 1);
627
628 if (alarm_fired)
629 tst_exit();
630 }
631
main(int argc,char ** argv)632 int main(int argc, char **argv)
633 {
634 static char *version_info = "mmstress V1.00 04/17/2001";
635 int ch, i;
636 int test_num = 0;
637 int test_time = 0;
638 int run_once = TRUE;
639
640 static struct signal_info {
641 int signum;
642 char *signame;
643 } sig_info[] = {
644 {SIGHUP, "SIGHUP"},
645 {SIGINT, "SIGINT"},
646 {SIGQUIT, "SIGQUIT"},
647 {SIGABRT, "SIGABRT"},
648 {SIGBUS, "SIGBUS"},
649 {SIGSEGV, "SIGSEGV"},
650 {SIGALRM, "SIGALRM"},
651 {SIGUSR1, "SIGUSR1"},
652 {SIGUSR2, "SIGUSR2"},
653 {SIGENDSIG, "ENDSIG"}
654 };
655
656 setvbuf(stdout, NULL, _IONBF, 0);
657 setvbuf(stderr, NULL, _IONBF, 0);
658
659 if (argc < 2)
660 tst_resm(TINFO, "run %s -h for all options", argv[0]);
661
662 while ((ch = getopt(argc, argv, "hn:p:t:vV")) != -1) {
663 switch (ch) {
664 case 'h':
665 usage(argv[0]);
666 break;
667 case 'n':
668 test_num = atoi(optarg);
669 break;
670 case 'p':
671 pages_num = atoi(optarg);
672 break;
673 case 't':
674 tst_resm(TINFO,
675 "Test is scheduled to run for %d hours",
676 test_time = atoi(optarg));
677 run_once = FALSE;
678 break;
679 case 'v':
680 verbose_print = TRUE;
681 break;
682 case 'V':
683 tst_resm(TINFO, "%s: %s", argv[0], version_info);
684 break;
685 case '?':
686 fprintf(stderr,
687 "%s: unknown option - %c ignored\n",
688 argv[0], optopt);
689 break;
690 default:
691 tst_brkm(TBROK, NULL, "%s: getopt() failed!!!\n",
692 argv[0]);
693 }
694 }
695
696 set_timer(test_time);
697
698 for (i = 0; sig_info[i].signum != -1; i++) {
699 if (signal(sig_info[i].signum, sig_handler) == SIG_ERR) {
700 tst_brkm(TBROK | TERRNO, NULL, "signal(%s) failed",
701 sig_info[i].signame);
702 }
703 }
704
705 tst_tmpdir();
706
707 do {
708 if (!test_num) {
709 for (i = 0; i < MAXTEST; i++)
710 run_test(i);
711 } else {
712 if (test_num >= MAXTEST) {
713 tst_brkm(TBROK, NULL, "Invalid test number %i",
714 test_num);
715 }
716
717 run_test(test_num);
718 }
719 } while (!run_once);
720
721 tst_rmdir();
722 tst_exit();
723 }
724