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