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 /* GLOBAL INCLUDE FILES                                                       */
86 #include <stdio.h>		/* standard C input/output routines                     */
87 #include <sys/types.h>		/* definitions for open()                               */
88 #include <sys/stat.h>		/* definitions for open()                               */
89 #include <fcntl.h>		/* definition of open()                                 */
90 #include <unistd.h>		/* declaration of close()                               */
91 #include <sys/mman.h>		/* declaration of mmap()                                */
92 #include <sys/wait.h>		/* declaration for wait routine                         */
93 #include <sys/time.h>		/* definitions for the interval timer                   */
94 #include <pthread.h>		/* declaration of pthread functions                     */
95 #include <signal.h>		/* definitions for signals - required for SIGALRM       */
96 #include <errno.h>		/* definitions for errors                               */
97 #include <stdlib.h>		/* declaration for malloc                               */
98 #include <string.h>		/* declaration for memset                               */
99 #include <sched.h>		/* declaration of sched_yield()                         */
100 #include <stdint.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 			  /* prints error if no argument passed               */
124 #define OPT_MISSING(prog, opt) do {                        \
125             fprintf(stderr, "%s: option -%c ", prog, opt); \
126             fprintf(stderr, "requires an argument\n");    \
127                 } while (0)
128 
129 			  /* exit thread macro                                */
130 #define PTHREAD_EXIT(val)      do {              \
131             exit_val = val;                      \
132                 pthread_exit((void *)exit_val); \
133                                   } while (0)
134 #define MAXTEST      6		/* total number of testcase in this program          */
135 #define BRKSZ        512*1024	/* program data space allocation value          */
136 
137 /* GLOBAL VARIABLES                                                           */
138 typedef struct {		/* structure returned by map_and_thread()               */
139 	int status;		/* status of the operation - FAILED or SUCCESS          */
140 	caddr_t mapaddr;	/* address at which the file is mapped                  */
141 } RETINFO_t;
142 
143 static volatile int wait_thread = 0;	/* used to wake up sleeping threads    */
144 static volatile int thread_begin = 0;	/* used to coordinate threads          */
145 static int verbose_print = FALSE;	/* print more test information           */
146 
147 static int pages_num = NUMPAGES;	/* number of pages to use for tests     */
148 
149 char *TCID = "mmstress";
150 int TST_TOTAL = 6;
151 
152 /******************************************************************************/
153 /*                                                                            */
154 /* Function:    sig_handler                                                   */
155 /*                                                                            */
156 /* Description: handle SIGALRM raised by set_timer(), SIGALRM is raised when  */
157 /*              the timer expires. If any other signal is received exit the   */
158 /*              test.                                                         */
159 /*                                                                            */
160 /* Input:       signal - signal number, intrested in SIGALRM!                 */
161 /*                                                                            */
162 /* Return:      exit -1 if unexpected signal is received                      */
163 /*              exit 0 if SIGALRM is received                                 */
164 /*                                                                            */
165 /* Synopsis:    void signal_handler(int signal);                              */
166 /*                                                                            */
167 /******************************************************************************/
sig_handler(int signal)168 static void sig_handler(int signal)
169 {				/* signal number, set to handle SIGALRM               */
170 	if (signal != SIGALRM) {
171 		fprintf(stderr,
172 			"sig_handlder(): unexpected signal caught [%d]\n",
173 			signal);
174 		exit(-1);
175 	} else
176 		tst_resm(TPASS, "Test ended, success");
177 	exit(0);
178 }
179 
180 /******************************************************************************/
181 /*                                                                            */
182 /* Function:    usage                                                         */
183 /*                                                                            */
184 /* Description: prints the usage function.                                    */
185 /*                                                                            */
186 /* Input:       char *progname - name of this executable.                     */
187 /*                                                                            */
188 /* Return:      exit 1                                                        */
189 /*                                                                            */
190 /* Synopsis:    void usage(char *progname);                                   */
191 /*                                                                            */
192 /******************************************************************************/
usage(char * progname)193 static void usage(char *progname)
194 {				/* name of this program                       */
195 	fprintf(stderr, "usage:%s -h -n test -t time -v [-V]\n", progname);
196 	fprintf(stderr, "\t-h displays all options\n");
197 	fprintf(stderr, "\t-n test number, if no test number\n"
198 		"\t   is specified, all the tests will be run\n");
199 	fprintf(stderr, "\t-p specify the number of pages to\n"
200 		"\t   use for allocation\n");
201 	fprintf(stderr, "\t-t specify the time in hours\n");
202 	fprintf(stderr, "\t-v verbose output\n");
203 	fprintf(stderr, "\t-V program version\n");
204 	exit(1);
205 }
206 
207 /******************************************************************************/
208 /*                                                                            */
209 /* Function:    set_timer                                                     */
210 /*                                                                            */
211 /* Description: set up a timer to user specified number of hours. SIGALRM is  */
212 /*              raised when the timer expires.                                */
213 /*                                                                            */
214 /* Input:       run_time - number of hours the test is intended to run.       */
215 /*                                                                            */
216 /* Return:      NONE                                                          */
217 /*                                                                            */
218 /* Synopsis:    void set_timer(int run_time);                                 */
219 /*                                                                            */
220 /******************************************************************************/
set_timer(int run_time)221 static void set_timer(int run_time)
222 {				/* period for which test is intended to run   */
223 	struct itimerval timer;	/* timer structure, tv_sec is set to run_time */
224 	memset(&timer, 0, sizeof(struct itimerval));
225 	timer.it_interval.tv_usec = 0;
226 	timer.it_interval.tv_sec = 0;
227 	timer.it_value.tv_usec = 0;
228 	timer.it_value.tv_sec = (time_t) (run_time * 3600.0);
229 
230 	if (setitimer(ITIMER_REAL, &timer, NULL)) {
231 		perror("set_timer(): setitimer()");
232 		exit(1);
233 	}
234 }
235 
236 /******************************************************************************/
237 /*                                                                            */
238 /* Function:    thread_fault                                                  */
239 /*                                                                            */
240 /* Description: Executes as a thread function and accesses the memory pages   */
241 /*              depending on the fault_type to be generated. This function    */
242 /*              can cause READ fault, WRITE fault, COW fault.                 */
243 /*                                                                            */
244 /* Input:       void *args - argments passed to the exec routine by           */
245 /*              pthread_create()                                              */
246 /*                                                                            */
247 /* Return:      NONE                                                          */
248 /*                                                                            */
249 /* Synopsis:    void *thread_fault(void *args);                               */
250 /*                                                                            */
251 /******************************************************************************/
thread_fault(void * args)252 static void *thread_fault(void *args)
253 {				/* pointer to the arguments passed to routine */
254 	long *local_args = args;	/* local pointer to list of arguments        */
255 	/* local_args[THNUM]   - the thread number   */
256 	/* local_args[MAPADDR] - map address         */
257 	/* local_args[PAGESIZ] - page size           */
258 	/* local_args[FLTIPE]  - fault type          */
259 	int pgnum_ndx = 0;	/* index to the number of pages              */
260 	caddr_t start_addr	/* start address of the page                 */
261 	    = (caddr_t) (local_args[MAPADDR]
262 			 + (int)local_args[THNUM]
263 			 * (pages_num / NUMTHREAD)
264 			 * local_args[PAGESIZ]);
265 	char read_from_addr = 0;	/* address to which read from page is done   */
266 	char write_to_addr[] = { 'a' };	/* character to be writen to the page    */
267 	uintptr_t exit_val = 0;	/* exit value of the pthread. 0 - success    */
268 
269     /*************************************************************/
270 	/*   The way it was, args could be overwritten by subsequent uses
271 	 *   of it before this routine had a chance to use the data.
272 	 *   This flag stops the overwrite until this routine gets to
273 	 *   here.  At this point, it is done initializing and it is
274 	 *   safe for the parent thread to continue (which will change
275 	 *   args).
276 	 */
277 	thread_begin = FALSE;
278 
279 	while (wait_thread)
280 		sched_yield();
281 
282 	for (; pgnum_ndx < (pages_num / NUMTHREAD); pgnum_ndx++) {
283 		/* if the fault to be generated is READ_FAULT, read from the page     */
284 		/* else write a character to the page.                                */
285 		((int)local_args[3] == READ_FAULT) ? (read_from_addr =
286 						      *start_addr)
287 		    : (*start_addr = write_to_addr[0]);
288 		start_addr += local_args[PAGESIZ];
289 		if (verbose_print)
290 			tst_resm(TINFO,
291 				 "thread_fault(): generating fault type %ld"
292 				 " @page address %p", local_args[3],
293 				 start_addr);
294 		fflush(NULL);
295 	}
296 	PTHREAD_EXIT(0);
297 }
298 
299 /******************************************************************************/
300 /*                                                                            */
301 /* Function:    remove_tmpfiles                                               */
302 /*                                                                            */
303 /* Description: remove temporary files that were created by the tests.        */
304 /*                                                                            */
305 /* Input:       char *filename - name of the file to be removed.              */
306 /*                                                                            */
307 /* Return:      FAILED  - on failure                                          */
308 /*              SUCCESS - on success                                          */
309 /*                                                                            */
310 /* Synopsis:    int remove_files(char *filename);                             */
311 /*                                                                            */
312 /******************************************************************************/
remove_files(char * filename,char * addr)313 static int remove_files(char *filename, char *addr)
314 {				/* name of the file that is to be removed     */
315 	if (addr)
316 		if (munmap(addr, sysconf(_SC_PAGESIZE) * pages_num) < 0) {
317 			perror("map_and_thread(): munmap()");
318 			return FAILED;
319 		}
320 	if (strcmp(filename, "NULL") && strcmp(filename, "/dev/zero")) {
321 		if (unlink(filename)) {
322 			perror("map_and_thread(): ulink()");
323 			return FAILED;
324 		}
325 	} else {
326 		if (verbose_print)
327 			tst_resm(TINFO, "file %s removed", filename);
328 
329 	}
330 	return SUCCESS;
331 }
332 
333 /******************************************************************************/
334 /*                                                                            */
335 /* Function:    map_and_thread                                                */
336 /*                                                                            */
337 /* Description: Creates mappings with the required properties, of MAP_PRIVATE */
338 /*              MAP_SHARED and of PROT_RED / PROT_READ|PROT_WRITE.            */
339 /*              Create threads and execute a routine that will generate the   */
340 /*              desired fault condition, viz, read, write and cow fault.      */
341 /*                                                                            */
342 /* Input:       char *tmpfile - name of temporary file that is created        */
343 /*              int   fault_type - type of fault that is to be generated.     */
344 /*                                                                            */
345 /* Return:    RETINFO_t * - pointer to RETINFO_t structure                    */
346 /*                                                                            */
347 /* Synopsis:  RETINFO_t *map_and_thread(                                      */
348 /*                        char  *tmpfile,                                     */
349 /*                        void  *(*exec_func)(void *),                        */
350 /*                        int    fault_type,                                  */
351 /*                        int    num_thread,                                  */
352 /*                        RETINFO_t *retinfo);                                */
353 /*                                                                            */
354 /******************************************************************************/
map_and_thread(char * tmpfile,void * (* exec_func)(void *),int fault_type,int num_thread,RETINFO_t * retinfo)355 RETINFO_t *map_and_thread(char *tmpfile,	/* name of temporary file to be created      */
356 			  void *(*exec_func) (void *),	/* thread function to execute                */
357 			  int fault_type,	/* type of fault to be generated             */
358 			  int num_thread,	/* number of threads to create               */
359 			  RETINFO_t * retinfo)
360 {				/* return map address and oper status        */
361 	int fd = 0;		/* file descriptor of the file created       */
362 	int thrd_ndx = 0;	/* index to the number of threads created    */
363 	int map_type = 0;	/* specifies the type of the mapped object   */
364 	void *th_status;	/* status of the thread when it is finished  */
365 	long th_args[5];	/* argument list passed to  thread_fault()   */
366 	char *empty_buf = NULL;	/* empty buffer used to fill temp file       */
367 	long pagesize		/* contains page size at runtime             */
368 	    = sysconf(_SC_PAGESIZE);
369 	static pthread_t pthread_ids[NUMTHREAD];
370 	/* contains ids of the threads created       */
371 	caddr_t map_addr = NULL;	/* address where the file is mapped          */
372 
373 	/* Create a file with permissions 0666, and open it with RDRW perms       */
374 	/* if the name is not a NULL                                              */
375 
376 	if (strcmp(tmpfile, "NULL")) {
377 		if ((fd =
378 		     open(tmpfile, O_RDWR | O_CREAT,
379 			  S_IRWXO | S_IRWXU | S_IRWXG))
380 		    == -1) {
381 			perror("map_and_thread(): open()");
382 			close(fd);
383 			fflush(NULL);
384 			retinfo->status = FAILED;
385 			return retinfo;
386 		}
387 
388 		/* Write pagesize * pages_num bytes to the file */
389 		empty_buf = malloc(pagesize * pages_num);
390 		if (write(fd, empty_buf, pagesize * pages_num) !=
391 		    (pagesize * pages_num)) {
392 			perror("map_and_thread(): write()");
393 			free(empty_buf);
394 			fflush(NULL);
395 			remove_files(tmpfile, NULL);
396 			close(fd);
397 			retinfo->status = FAILED;
398 			return retinfo;
399 		}
400 		map_type = (fault_type == COW_FAULT) ? MAP_PRIVATE : MAP_SHARED;
401 
402 		/* Map the file, if the required fault type is COW_FAULT map the file */
403 		/* private, else map the file shared. if READ_FAULT is required to be */
404 		/* generated map the file with read protection else map with read -   */
405 		/* write protection.                               */
406 
407 		if ((map_addr = (caddr_t) mmap(0, pagesize * pages_num,
408 					       ((fault_type == READ_FAULT) ?
409 						PROT_READ : PROT_READ |
410 						PROT_WRITE), map_type, fd, 0))
411 		    == MAP_FAILED) {
412 			perror("map_and_thread(): mmap()");
413 			free(empty_buf);
414 			fflush(NULL);
415 			remove_files(tmpfile, NULL);
416 			close(fd);
417 			retinfo->status = FAILED;
418 			return retinfo;
419 		} else {
420 			retinfo->mapaddr = map_addr;
421 			if (verbose_print)
422 				tst_resm(TINFO,
423 					 "map_and_thread(): mmap success, address = %p",
424 					 map_addr);
425 			fflush(NULL);
426 		}
427 	}
428 
429 	/* As long as wait is set to TRUE, the thread that will be created will */
430 	/* loop in its exec routine */
431 
432 	wait_thread = TRUE;
433 
434 	/* Create a few threads, ideally number of threads equals number of CPU'S */
435 	/* so that we can assume that each thread will run on a single CPU in     */
436 	/* of SMP machines. Currently we will create NR_CPUS number of threads.   */
437 
438 	th_args[1] = (long)map_addr;
439 	th_args[2] = pagesize;
440 	th_args[3] = fault_type;
441 	do {
442 		th_args[0] = thrd_ndx;
443 		th_args[4] = (long)0;
444 
445        /*************************************************************/
446 		/*   The way it was, args could be overwritten by subsequent uses
447 		 *   of it before the called routine had a chance to fully initialize.
448 		 *   This flag stops the overwrite until that routine gets to
449 		 *   begin.  At that point, it is done initializing and it is
450 		 *   safe for the this thread to continue (which will change
451 		 *   args).
452 		 *   A basic race condition.
453 		 */
454 		thread_begin = TRUE;
455 		if (pthread_create(&pthread_ids[thrd_ndx++], NULL, exec_func,
456 				   (void *)&th_args)) {
457 			perror("map_and_thread(): pthread_create()");
458 			thread_begin = FALSE;
459 			free(empty_buf);
460 			fflush(NULL);
461 			remove_files(tmpfile, map_addr);
462 			close(fd);
463 			retinfo->status = FAILED;
464 			return retinfo;
465 		} else {
466 	    /***************************************************/
467 			/*   Yield until new thread is done with args.
468 			 */
469 			while (thread_begin)
470 				sched_yield();
471 		}
472 	} while (thrd_ndx < num_thread);
473 
474 	if (verbose_print)
475 		tst_resm(TINFO, "map_and_thread(): pthread_create() success");
476 	wait_thread = FALSE;
477 
478 	/* suspend the execution of the calling thread till the execution of the  */
479 	/* other thread has been terminated.                                      */
480 
481 	for (thrd_ndx = 0; thrd_ndx < NUMTHREAD; thrd_ndx++) {
482 		if (pthread_join(pthread_ids[thrd_ndx], &th_status)) {
483 			perror("map_and_thread(): pthread_join()");
484 			free(empty_buf);
485 			fflush(NULL);
486 			remove_files(tmpfile, map_addr);
487 			close(fd);
488 			retinfo->status = FAILED;
489 			return retinfo;
490 		} else {
491 			if ((long)th_status == 1) {
492 				tst_resm(TINFO,
493 					 "thread [%ld] - process exited with errors",
494 					 (long)pthread_ids[thrd_ndx]);
495 				free(empty_buf);
496 				remove_files(tmpfile, map_addr);
497 				close(fd);
498 				exit(1);
499 			}
500 		}
501 	}
502 
503 	/* remove the temporary file that was created. - clean up                 */
504 	/* but dont try to remove special files.                                  */
505 
506     /***********************************************/
507 	/*   Was if !(remove_files()) ...
508 	 *   If that routine succeeds, it returns SUCCESS, which
509 	 *   happens to be 0.  So if the routine succeeded, the
510 	 *   above condition would indicate failure.  This change
511 	 *   fixes that.
512 	 */
513 	if (remove_files(tmpfile, map_addr) == FAILED) {
514 		free(empty_buf);
515 		retinfo->status = FAILED;
516 		return retinfo;
517 	}
518 
519 	free(empty_buf);
520 	close(fd);
521 	retinfo->status = SUCCESS;
522 	return retinfo;
523 }
524 
525 /******************************************************************************/
526 /*                                                                            */
527 /* Test:        Test case tests the race condition between simultaneous read  */
528 /*              faults in the same address space.                             */
529 /*                                                                            */
530 /* Description: map a file into memory, create threads and execute a thread   */
531 /*              function that will cause read faults by simultaneously reading*/
532 /*              from this memory space.                                       */
533 /*                                                                            */
534 /* Input:       NONE                                                          */
535 /*                                                                            */
536 /* Return:      int - status from map_and_thread()                            */
537 /*                                                                            */
538 /* Synopsis:    int test1();                                                  */
539 /*                                                                            */
540 /******************************************************************************/
test1()541 static int test1()
542 {
543 	RETINFO_t retval;	/* contains info relating to test status          */
544 
545 	tst_resm(TINFO, "test1: Test case tests the race condition between "
546 		 "simultaneous read faults in the same address space.");
547 	map_and_thread("./tmp.file.1", thread_fault, READ_FAULT, NUMTHREAD,
548 		       &retval);
549 	return (retval.status);
550 }
551 
552 /******************************************************************************/
553 /*                                                                            */
554 /* Test:        Test case tests the race condition between simultaneous write */
555 /*              faults in the same address space.                             */
556 /*                                                                            */
557 /* Description: map a file into memory, create threads and execute a thread   */
558 /*              function that will cause write faults by simultaneously       */
559 /*              writing to this memory space.                                 */
560 /*                                                                            */
561 /* Input:       NONE                                                          */
562 /*                                                                            */
563 /* Return:      int - status from map_and_thread()                            */
564 /*                                                                            */
565 /* Synopsis:    int test2();                                                  */
566 /*                                                                            */
567 /******************************************************************************/
test2()568 static int test2()
569 {
570 	RETINFO_t retval;	/* contains test stats information               */
571 
572 	tst_resm(TINFO, "test2: Test case tests the race condition between "
573 		 "simultaneous write faults in the same address space.");
574 	map_and_thread("./tmp.file.2", thread_fault, WRITE_FAULT, NUMTHREAD,
575 		       &retval);
576 	return (retval.status);
577 }
578 
579 /******************************************************************************/
580 /*                                                                            */
581 /* Test:        Test case tests the race condition between simultaneous COW   */
582 /*              faults in the same address space.                             */
583 /*                                                                            */
584 /* Description: map a file into memory, create threads and execute a thread   */
585 /*              function that will cause COW faults by simultaneously         */
586 /*              writing to this memory space.                                 */
587 /*                                                                            */
588 /* Input:       NONE                                                          */
589 /*                                                                            */
590 /* Return:      int - status from map_and_thread()                            */
591 /*                                                                            */
592 /* Synopsis:    int test3();                                                  */
593 /*                                                                            */
594 /******************************************************************************/
test3()595 static int test3()
596 {
597 	RETINFO_t retval;	/* contains test stats information                   */
598 
599 	tst_resm(TINFO, "test3: Test case tests the race condition between "
600 		 "simultaneous COW faults in the same address space.");
601 	map_and_thread("./tmp.file.3", thread_fault, COW_FAULT, NUMTHREAD,
602 		       &retval);
603 	return (retval.status);
604 }
605 
606 /******************************************************************************/
607 /*                                                                            */
608 /* Test:        Test case tests the race condition between simultaneous READ  */
609 /*              faults in the same address space. File mapped is /dev/zero    */
610 /*                                                                            */
611 /* Description: Map a file into memory, create threads and execute a thread   */
612 /*              function that will cause READ faults by simultaneously        */
613 /*              writing to this memory space.                                 */
614 /*                                                                            */
615 /* Input:       NONE                                                          */
616 /*                                                                            */
617 /* Return:      int - status from map_and_thread()                            */
618 /*                                                                            */
619 /* Synopsis:    int test4();                                                  */
620 /*                                                                            */
621 /******************************************************************************/
test4()622 static int test4()
623 {
624 	RETINFO_t retval;	/* contains test status information                 */
625 
626 	tst_resm(TINFO, "test4: Test case tests the race condition between "
627 		 "simultaneous READ faults in the same address space. "
628 		 "The file mapped is /dev/zero");
629 	map_and_thread("/dev/zero", thread_fault, COW_FAULT, NUMTHREAD,
630 		       &retval);
631 	return (retval.status);
632 }
633 
634 /******************************************************************************/
635 /*                                                                            */
636 /* Test:    Test case tests the race condition between simultaneous           */
637 /*         fork - exit faults in the same address space.                      */
638 /*                                                                            */
639 /* Description: Initialize large data in the parent process, fork a child and */
640 /*              and the parent waits for the child to complete execution.     */
641 /*                                                                            */
642 /* Input:       NONE                                                          */
643 /*                                                                            */
644 /* Return:      int - status from map_and_thread()                            */
645 /*                                                                            */
646 /* Synopsis:    int test5();                                                  */
647 /*                                                                            */
648 /******************************************************************************/
test5()649 static int test5()
650 {
651 	int fork_ndx = 0;	/* index to the number of processes forked        */
652 	pid_t pid = 0;		/* process id, returned by fork system call.      */
653 	int wait_status = 0;	/* if status is not NULL store status information */
654 
655 	tst_resm(TINFO, "test5: Test case tests the race condition between "
656 		 "simultaneous fork - exit faults in the same address space.");
657 
658 	/* increment the  program's  data  space  by 200*1024 (BRKSZ) bytes       */
659 
660 	if (sbrk(BRKSZ) == (caddr_t) - 1) {
661 		perror("test5(): sbrk()");
662 		fflush(NULL);
663 		return FAILED;
664 	}
665 
666 	/* fork NUMTHREAD number of processes, assumption is on SMP each will get */
667 	/* a separate CPU if NRCPUS = NUMTHREAD. The child does nothing; exits    */
668 	/* immediately, parent waits for child to complete execution.             */
669 	do {
670 		if (!(pid = fork()))
671 			_exit(0);
672 		else {
673 			if (pid != -1)
674 				wait(&wait_status);
675 		}
676 
677 	} while (fork_ndx++ < NUMTHREAD);
678 
679 	if (sbrk(-BRKSZ) == (caddr_t) - 1) {
680 		tst_resm(TINFO, "test5(): rollback sbrk failed");
681 		fflush(NULL);
682 		perror("test5(): sbrk()");
683 		fflush(NULL);
684 		return FAILED;
685 	}
686 	return SUCCESS;
687 }
688 
689 /******************************************************************************/
690 /*                                                                            */
691 /* Test:        Test case tests the race condition between simultaneous       */
692 /*              fork - exec - exit faults in the same address space.          */
693 /*                                                                            */
694 /* Description: Initialize large data in the parent process, fork a child and */
695 /*              and the parent waits for the child to complete execution. The */
696 /*              child program execs a dummy program.                          */
697 /*                                                                            */
698 /* Input:       NONE                                                          */
699 /*                                                                            */
700 /* Return:      int - status from map_and_thread()                            */
701 /*                                                                            */
702 /* Synopsis:    int test6();                                                  */
703 /*                                                                            */
704 /******************************************************************************/
test6()705 static int test6()
706 {
707 	int res = SUCCESS;
708 	int fork_ndx = 0;	/* index to the number of processes forked         */
709 	pid_t pid = 0;		/* process id, returned by fork system call.       */
710 	int wait_status;	/* if status is not NULL store status information  */
711 	char *argv_init[2] =	/* parameter required for dummy function to execvp */
712 	{ "arg1", NULL };
713 
714 	tst_resm(TINFO, "test6: Test case tests the race condition between "
715 		 "simultaneous fork -exec - exit faults in the same address space.");
716 
717 	/* increment the  program's  data  space  by 200*1024 (BRKSZ) bytes       */
718 
719 	if (sbrk(BRKSZ) == (caddr_t) - 1) {
720 		perror("test6(): sbrk()");
721 		fflush(NULL);
722 		return FAILED;
723 	}
724 
725 	/* fork NUMTHREAD number of processes, assumption is on SMP each will get */
726 	/* a separate CPU if NRCPUS = NUMTHREAD. The child execs a dummy program  */
727 	/*  and parent waits for child to complete execution.                     */
728 
729 	do {
730 		if (!(pid = fork())) {
731 			if (execvp("mmstress_dummy", argv_init) == -1) {
732 				if (execvp("./mmstress_dummy", argv_init) == -1) {
733 					perror("test6(): execvp()");
734 					fflush(NULL);
735 		   /*************************************************/
736 					/*   Dummy uses exit 0 so use something else for
737 					 *   error exit.
738 					 */
739 					exit(99);
740 				}
741 			}
742 		} else {
743 			if (pid != -1)
744 				wait(&wait_status);
745 	    /*************************************************/
746 			/*   Dummy uses exit 0.
747 			 *   Capture exit of child and set res accordingly.
748 			 *   It defaults to SUCCESS.  Only gets set if
749 			 *   child fails.
750 			 */
751 			if (WEXITSTATUS(wait_status) != 0)
752 				res = FAILED;
753 		}
754 
755 	} while (fork_ndx++ < NUMTHREAD);
756 
757 	if (sbrk(-BRKSZ) == (caddr_t) - 1) {
758 		tst_resm(TINFO, "test6(): rollback sbrk failed");
759 		fflush(NULL);
760 		perror("test6(): sbrk()");
761 		fflush(NULL);
762 		return FAILED;
763 	}
764     /*************************************/
765 	/*  This used to return SUCCESS, whether or not the
766 	 *  test actually worked.
767 	 */
768 	return res;
769 }
770 
771 /******************************************************************************/
772 /*                                                                            */
773 /* Function:    main                                                          */
774 /*                                                                            */
775 /* Desciption:  This is the main function of mmstress program. It processes   */
776 /*              all command line options and flags and runs the tests.        */
777 /*              If no specific tests are chosen, all tests will be run one    */
778 /*              time by default.                                              */
779 /*                                                                            */
780 /* Input:       argc - number of command line parameters                      */
781 /*              argv - pointer to the array of the command line parameters.   */
782 /*                                                                            */
783 /* Return:      EXIT_FAILURE - in case sigaction() fails                      */
784 /*              rc - return code from the testcase                            */
785 /*                   zero     - success                                       */
786 /*                   non zero - failure                                       */
787 /*                                                                            */
788 /******************************************************************************/
main(int argc,char ** argv)789 int main(int argc,		/* number of command line parameters                     */
790 	 char **argv)
791 {				/* pointer to the array of the command line parameters.  */
792 	extern char *optarg;	/* getopt() function global variables               */
793 	extern int optind;	/* index into argument                              */
794 	extern int opterr;	/* optarg error detection                           */
795 	extern int optopt;	/* stores bad option passed to the program          */
796 
797 	static char *version_info = "mmstress V1.00 04/17/2001";
798 	/* version of this program                          */
799 	int (*(test_ptr)[]) () = {
800 	NULL, test1, test2, test3, test4, test5, test6};
801 	/* pointer to the array of test names     */
802 	int ch;			/* command line flag character            */
803 	int test_num = 0;	/* test number that is to be run          */
804 	int test_time = 0;	/* duration for which test is to be run   */
805 	int sig_ndx = 0;	/* index into signal handler structure.   */
806 	int run_once = TRUE;	/* test/tests are run once by default.    */
807 	char *prog_name = argv[0];	/* name of this program                   */
808 	int rc = 0;		/* return value from tests.  0 - success  */
809 	int global_rc = 0;	/* return value from tests.  0 - success  */
810 	int version_flag = FALSE;	/* printf the program version             */
811 	struct sigaction sigptr;	/* set up signal, for interval timer      */
812 
813 	static struct signal_info {
814 		int signum;	/* signal number that hasto be handled                */
815 		char *signame;	/* name of the signal to be handled.                  */
816 	} sig_info[] = { {
817 	SIGHUP, "SIGHUP"}, {
818 	SIGINT, "SIGINT"}, {
819 	SIGQUIT, "SIGQUIT"}, {
820 	SIGABRT, "SIGABRT"}, {
821 	SIGBUS, "SIGBUS"}, {
822 	SIGSEGV, "SIGSEGV"}, {
823 	SIGALRM, "SIGALRM"}, {
824 	SIGUSR1, "SIGUSR1"}, {
825 	SIGUSR2, "SIGUSR2"}, {
826 	SIGENDSIG, "ENDSIG"}
827 	};
828 
829 	setvbuf(stdout, NULL, _IONBF, 0);
830 	setvbuf(stderr, NULL, _IONBF, 0);
831 
832 	optarg = NULL;
833 	opterr = 0;
834 
835 	if (argc < 2)
836 		tst_resm(TINFO, "run %s -h for all options", argv[0]);
837 
838 	while ((ch = getopt(argc, argv, "hn:p:t:vV")) != -1) {
839 		switch (ch) {
840 		case 'h':
841 			usage(argv[0]);
842 			break;
843 		case 'n':
844 			if (optarg)
845 				test_num = atoi(optarg);
846 			else
847 				OPT_MISSING(prog_name, optopt);
848 			break;
849 		case 'p':
850 			if (optarg)
851 				pages_num = atoi(optarg);
852 			else
853 				OPT_MISSING(prog_name, optopt);
854 			break;
855 		case 't':
856 			if (optarg) {
857 				tst_resm(TINFO,
858 					 "Test is scheduled to run for %d hours",
859 					 test_time = atoi(optarg));
860 				run_once = FALSE;
861 			} else
862 				OPT_MISSING(prog_name, optopt);
863 			break;
864 		case 'v':
865 			verbose_print = TRUE;
866 			break;
867 		case 'V':
868 			if (!version_flag) {
869 				version_flag = TRUE;
870 				tst_resm(TINFO, "%s: %s", prog_name,
871 					 version_info);
872 			}
873 			break;
874 		case '?':
875 			if (argc < optind)
876 				OPT_MISSING(prog_name, optopt);
877 			else
878 				fprintf(stderr,
879 					"%s: unknown option - %c ignored\n",
880 					prog_name, optopt);
881 			break;
882 		default:
883 			fprintf(stderr, "%s: getopt() failed!!!\n", prog_name);
884 			exit(2);
885 		}
886 	}
887 
888 	/* duration for which the tests have to be run. test_time is converted to */
889 	/* corresponding seconds and the tests are run as long as the current time */
890 	/* is less than the required time and test are successul (ie rc = 0)      */
891 
892 	set_timer(test_time);
893 
894 	/* set up signals */
895 	sigptr.sa_handler = (void (*)(int signal))sig_handler;
896 	sigfillset(&sigptr.sa_mask);
897 	sigptr.sa_flags = 0;
898 	for (sig_ndx = 0; sig_info[sig_ndx].signum != -1; sig_ndx++) {
899 		sigaddset(&sigptr.sa_mask, sig_info[sig_ndx].signum);
900 
901 		if (sigaction(sig_info[sig_ndx].signum, &sigptr,
902 			      (struct sigaction *)NULL) == SIGENDSIG) {
903 			perror("main(): sigaction()");
904 			fprintf(stderr,
905 				"could not set handler for SIGALRM, errno = %d\n",
906 				errno);
907 			exit(EXIT_FAILURE);
908 		}
909 	}
910 
911     /*************************************************/
912 	/*   The way this loop was, 5 of 6 tests could fail,
913 	 *   and as long test test 6 passed, the test passed.
914 	 *   The changes in this loop repair that problem.
915 	 */
916 	do {
917 		if (!test_num) {
918 			int test_ndx;	/* index into the array of tests               */
919 
920 			for (test_ndx = 1; test_ndx <= MAXTEST; test_ndx++) {
921 				rc = test_ptr[test_ndx] ();
922 				if (rc == SUCCESS) {
923 					tst_resm(TPASS, "TEST %d Passed",
924 						 test_ndx);
925 				} else {
926 					tst_resm(TFAIL, "TEST %d Failed",
927 						 test_ndx);
928 					global_rc = rc;
929 				}
930 			}
931 		} else {
932 			global_rc = (test_num > MAXTEST) ?
933 			    fprintf(stderr,
934 				    "Invalid test number, must be in range [1 - %d]\n",
935 				    MAXTEST) : test_ptr[test_num] ();
936 		}
937 
938 		if (global_rc != SUCCESS) {
939 			tst_resm(TFAIL, "Test Failed");
940 			exit(global_rc);
941 		}
942 
943 	} while ((TRUE) && (!run_once));
944 
945 	if (global_rc != SUCCESS) {
946 		tst_resm(TFAIL, "Test Failed");
947 	} else {
948 		tst_resm(TPASS, "Test Passed");
949 	}
950 	exit(global_rc);
951 }
952