• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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