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
126 static volatile int wait_thread; /* used to wake up sleeping threads */
127 static volatile int thread_begin; /* used to coordinate threads */
128 static int verbose_print = FALSE; /* print more test information */
129
130 static int pages_num = NUMPAGES; /* number of pages to use for tests */
131 static volatile int alarm_fired;
132
133 char *TCID = "mmstress";
134 int TST_TOTAL = 6;
135
sig_handler(int signal)136 static 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
usage(char * progname)148 static 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
set_timer(int run_time)162 static 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 /******************************************************************************/
thread_fault(void * args)190 static 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 /******************************************************************************/
remove_files(char * filename,char * addr)243 static 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 /******************************************************************************/
map_and_thread(char * tmpfile,void * (* exec_func)(void *),int fault_type,int num_thread)276 int 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
293 /* Create a file with permissions 0666, and open it with RDRW perms */
294 /* if the name is not a NULL */
295
296 if (strcmp(tmpfile, "NULL")) {
297 if ((fd =
298 open(tmpfile, O_RDWR | O_CREAT,
299 S_IRWXO | S_IRWXU | S_IRWXG))
300 == -1) {
301 perror("map_and_thread(): open()");
302 close(fd);
303 fflush(NULL);
304 return FAILED;
305 }
306
307 /* Write pagesize * pages_num bytes to the file */
308 empty_buf = malloc(pagesize * pages_num);
309 if (write(fd, empty_buf, pagesize * pages_num) !=
310 (pagesize * pages_num)) {
311 perror("map_and_thread(): write()");
312 free(empty_buf);
313 fflush(NULL);
314 remove_files(tmpfile, NULL);
315 close(fd);
316 return FAILED;
317 }
318 map_type = (fault_type == COW_FAULT) ? MAP_PRIVATE : MAP_SHARED;
319
320 /* Map the file, if the required fault type is COW_FAULT map the file */
321 /* private, else map the file shared. if READ_FAULT is required to be */
322 /* generated map the file with read protection else map with read - */
323 /* write protection. */
324
325 if ((map_addr = (void *) mmap(0, pagesize * pages_num,
326 ((fault_type == READ_FAULT) ?
327 PROT_READ : PROT_READ |
328 PROT_WRITE), map_type, fd, 0))
329 == MAP_FAILED) {
330 perror("map_and_thread(): mmap()");
331 free(empty_buf);
332 fflush(NULL);
333 remove_files(tmpfile, NULL);
334 close(fd);
335 return FAILED;
336 } else {
337 if (verbose_print)
338 tst_resm(TINFO,
339 "map_and_thread(): mmap success, address = %p",
340 map_addr);
341 fflush(NULL);
342 }
343 }
344
345 /* As long as wait is set to TRUE, the thread that will be created will */
346 /* loop in its exec routine */
347
348 wait_thread = TRUE;
349
350 /* Create a few threads, ideally number of threads equals number of CPU'S */
351 /* so that we can assume that each thread will run on a single CPU in */
352 /* of SMP machines. Currently we will create NR_CPUS number of threads. */
353
354 th_args[1] = (long)map_addr;
355 th_args[2] = pagesize;
356 th_args[3] = fault_type;
357 do {
358 th_args[0] = thrd_ndx;
359 th_args[4] = (long)0;
360
361 /*************************************************************/
362 /* The way it was, args could be overwritten by subsequent uses
363 * of it before the called routine had a chance to fully initialize.
364 * This flag stops the overwrite until that routine gets to
365 * begin. At that point, it is done initializing and it is
366 * safe for the this thread to continue (which will change
367 * args).
368 * A basic race condition.
369 */
370 thread_begin = TRUE;
371 if (pthread_create(&pthread_ids[thrd_ndx++], NULL, exec_func,
372 (void *)&th_args)) {
373 perror("map_and_thread(): pthread_create()");
374 thread_begin = FALSE;
375 free(empty_buf);
376 fflush(NULL);
377 remove_files(tmpfile, map_addr);
378 close(fd);
379 return FAILED;
380 } else {
381 /***************************************************/
382 /* Yield until new thread is done with args.
383 */
384 while (thread_begin)
385 sched_yield();
386 }
387 } while (thrd_ndx < num_thread);
388
389 if (verbose_print)
390 tst_resm(TINFO, "map_and_thread(): pthread_create() success");
391 wait_thread = FALSE;
392
393 /* suspend the execution of the calling thread till the execution of the */
394 /* other thread has been terminated. */
395
396 for (thrd_ndx = 0; thrd_ndx < NUMTHREAD; thrd_ndx++) {
397 if (pthread_join(pthread_ids[thrd_ndx], &th_status)) {
398 perror("map_and_thread(): pthread_join()");
399 free(empty_buf);
400 fflush(NULL);
401 remove_files(tmpfile, map_addr);
402 close(fd);
403 return FAILED;
404 } else {
405 if ((long)th_status == 1) {
406 tst_resm(TINFO,
407 "thread [%ld] - process exited with errors",
408 (long)pthread_ids[thrd_ndx]);
409 free(empty_buf);
410 remove_files(tmpfile, map_addr);
411 close(fd);
412 exit(1);
413 }
414 }
415 }
416
417 /* remove the temporary file that was created. - clean up */
418 /* but dont try to remove special files. */
419
420 /***********************************************/
421 /* Was if !(remove_files()) ...
422 * If that routine succeeds, it returns SUCCESS, which
423 * happens to be 0. So if the routine succeeded, the
424 * above condition would indicate failure. This change
425 * fixes that.
426 */
427 if (remove_files(tmpfile, map_addr) == FAILED) {
428 free(empty_buf);
429 return FAILED;
430 }
431
432 free(empty_buf);
433 close(fd);
434 return SUCCESS;
435 }
436
437 /******************************************************************************/
438 /* */
439 /* Test: Test case tests the race condition between simultaneous read */
440 /* faults in the same address space. */
441 /* */
442 /* Description: map a file into memory, create threads and execute a thread */
443 /* function that will cause read faults by simultaneously reading*/
444 /* from this memory space. */
445 /******************************************************************************/
test1(void)446 static int test1(void)
447 {
448 tst_resm(TINFO, "test1: Test case tests the race condition between "
449 "simultaneous read faults in the same address space.");
450 return map_and_thread("./tmp.file.1", thread_fault, READ_FAULT, NUMTHREAD);
451 }
452
453 /******************************************************************************/
454 /* */
455 /* Test: Test case tests the race condition between simultaneous write */
456 /* faults in the same address space. */
457 /* */
458 /* Description: map a file into memory, create threads and execute a thread */
459 /* function that will cause write faults by simultaneously */
460 /* writing to this memory space. */
461 /******************************************************************************/
test2(void)462 static int test2(void)
463 {
464 tst_resm(TINFO, "test2: Test case tests the race condition between "
465 "simultaneous write faults in the same address space.");
466 return map_and_thread("./tmp.file.2", thread_fault, WRITE_FAULT, NUMTHREAD);
467 }
468
469 /******************************************************************************/
470 /* */
471 /* Test: Test case tests the race condition between simultaneous COW */
472 /* faults in the same address space. */
473 /* */
474 /* Description: map a file into memory, create threads and execute a thread */
475 /* function that will cause COW faults by simultaneously */
476 /* writing to this memory space. */
477 /* */
478 /******************************************************************************/
test3(void)479 static int test3(void)
480 {
481 tst_resm(TINFO, "test3: Test case tests the race condition between "
482 "simultaneous COW faults in the same address space.");
483 return map_and_thread("./tmp.file.3", thread_fault, COW_FAULT, NUMTHREAD);
484 }
485
486 /******************************************************************************/
487 /* */
488 /* Test: Test case tests the race condition between simultaneous READ */
489 /* faults in the same address space. File mapped is /dev/zero */
490 /* */
491 /* Description: Map a file into memory, create threads and execute a thread */
492 /* function that will cause READ faults by simultaneously */
493 /* writing to this memory space. */
494 /* */
495 /******************************************************************************/
test4(void)496 static int test4(void)
497 {
498 tst_resm(TINFO, "test4: Test case tests the race condition between "
499 "simultaneous READ faults in the same address space. "
500 "The file mapped is /dev/zero");
501 return map_and_thread("/dev/zero", thread_fault, COW_FAULT, NUMTHREAD);
502 }
503
504 /******************************************************************************/
505 /* */
506 /* Test: Test case tests the race condition between simultaneous */
507 /* fork - exit faults in the same address space. */
508 /* */
509 /* Description: Initialize large data in the parent process, fork a child and */
510 /* and the parent waits for the child to complete execution. */
511 /* */
512 /******************************************************************************/
test5(void)513 static int test5(void)
514 {
515 int fork_ndx = 0;
516 pid_t pid = 0;
517 int wait_status = 0;
518
519 tst_resm(TINFO, "test5: Test case tests the race condition between "
520 "simultaneous fork - exit faults in the same address space.");
521
522 /* increment the program's data space by 200*1024 (BRKSZ) bytes */
523
524 if (sbrk(BRKSZ) == (void *) - 1) {
525 perror("test5(): sbrk()");
526 fflush(NULL);
527 return FAILED;
528 }
529
530 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */
531 /* a separate CPU if NRCPUS = NUMTHREAD. The child does nothing; exits */
532 /* immediately, parent waits for child to complete execution. */
533 do {
534 if (!(pid = fork()))
535 _exit(0);
536 else {
537 if (pid != -1)
538 wait(&wait_status);
539 }
540
541 } while (fork_ndx++ < NUMTHREAD);
542
543 if (sbrk(-BRKSZ) == (void *) - 1) {
544 tst_resm(TINFO, "test5(): rollback sbrk failed");
545 fflush(NULL);
546 perror("test5(): sbrk()");
547 fflush(NULL);
548 return FAILED;
549 }
550 return SUCCESS;
551 }
552
553 /******************************************************************************/
554 /* */
555 /* Test: Test case tests the race condition between simultaneous */
556 /* fork - exec - exit faults in the same address space. */
557 /* */
558 /* Description: Initialize large data in the parent process, fork a child and */
559 /* and the parent waits for the child to complete execution. The */
560 /* child program execs a dummy program. */
561 /* */
562 /******************************************************************************/
test6(void)563 static int test6(void)
564 {
565 int res = SUCCESS;
566 int fork_ndx = 0;
567 pid_t pid = 0;
568 int wait_status;
569 char *argv_init[2] = { "arg1", NULL };
570
571 tst_resm(TINFO, "test6: Test case tests the race condition between "
572 "simultaneous fork -exec - exit faults in the same address space.");
573
574 /* increment the program's data space by 200*1024 (BRKSZ) bytes */
575 if (sbrk(BRKSZ) == (void *) - 1) {
576 perror("test6(): sbrk()");
577 fflush(NULL);
578 return FAILED;
579 }
580
581 /* fork NUMTHREAD number of processes, assumption is on SMP each will get */
582 /* a separate CPU if NRCPUS = NUMTHREAD. The child execs a dummy program */
583 /* and parent waits for child to complete execution. */
584 do {
585 if (!(pid = fork())) {
586 if (execvp("mmstress_dummy", argv_init) == -1) {
587 if (execvp("./mmstress_dummy", argv_init) == -1) {
588 perror("test6(): execvp()");
589 fflush(NULL);
590 exit(99);
591 }
592 }
593 } else {
594 if (pid != -1)
595 wait(&wait_status);
596
597 if (WEXITSTATUS(wait_status) != 0)
598 res = FAILED;
599 }
600
601 } while (fork_ndx++ < NUMTHREAD);
602
603 if (sbrk(-BRKSZ) == (void *) - 1) {
604 tst_resm(TINFO, "test6(): rollback sbrk failed");
605 fflush(NULL);
606 perror("test6(): sbrk()");
607 fflush(NULL);
608 return FAILED;
609 }
610
611 return res;
612 }
613
614 static int (*(test_ptr)[]) () = {test1, test2, test3, test4, test5, test6};
615
run_test(unsigned int i)616 static void run_test(unsigned int i)
617 {
618 int rc;
619
620 rc = test_ptr[i]();
621
622 if (rc == SUCCESS)
623 tst_resm(TPASS, "TEST %d Passed", i + 1);
624 else
625 tst_resm(TFAIL, "TEST %d Failed", i + 1);
626
627 if (alarm_fired)
628 tst_exit();
629 }
630
main(int argc,char ** argv)631 int main(int argc, char **argv)
632 {
633 static char *version_info = "mmstress V1.00 04/17/2001";
634 int ch;
635 unsigned int 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 < ARRAY_SIZE(test_ptr); i++)
710 run_test(i);
711 } else {
712 if (test_num > (int)ARRAY_SIZE(test_ptr)) {
713 tst_brkm(TBROK, NULL, "Invalid test number %i",
714 test_num);
715 }
716
717 run_test(test_num-1);
718 }
719 } while (!run_once);
720
721 tst_rmdir();
722 tst_exit();
723 }
724