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 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 /******************************************************************************/
test1(void)464 static 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 /******************************************************************************/
test2(void)480 static 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 /******************************************************************************/
test3(void)497 static 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 /******************************************************************************/
test4(void)514 static 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 /******************************************************************************/
test5(void)531 static 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 /******************************************************************************/
test6(void)581 static 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
632 static int (*(test_ptr)[]) () = {test1, test2, test3, test4, test5, test6};
633
run_test(unsigned int i)634 static 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
main(int argc,char ** argv)649 int 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