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