Home | History | Annotate | Download | only in mtest06
      1 /******************************************************************************/
      2 /*									      */
      3 /* Copyright (c) International Business Machines  Corp., 2001		      */
      4 /* Copyright (c) 2001 Manoj Iyer <manjo (at) austin.ibm.com>                       */
      5 /* Copyright (c) 2003 Robbie Williamson <robbiew (at) us.ibm.com>                  */
      6 /* Copyright (c) 2004 Paul Larson <plars (at) linuxtestproject.org>                */
      7 /* Copyright (c) 2007 <rsalveti (at) linux.vnet.ibm.com>                           */
      8 /* Copyright (c) 2007 Suzuki K P <suzuki (at) in.ibm.com>                          */
      9 /* Copyright (c) 2011 Cyril Hrubis <chrubis (at) suse.cz>                          */
     10 /*									      */
     11 /* This program is free software;  you can redistribute it and/or modify      */
     12 /* it under the terms of the GNU General Public License as published by       */
     13 /* the Free Software Foundation; either version 2 of the License, or          */
     14 /* (at your option) any later version.					      */
     15 /*									      */
     16 /* This program is distributed in the hope that it will be useful,	      */
     17 /* but WITHOUT ANY WARRANTY;  without even the implied warranty of	      */
     18 /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See	              */
     19 /* the GNU General Public License for more details.			      */
     20 /*									      */
     21 /* You should have received a copy of the GNU General Public License	      */
     22 /* along with this program;  if not, write to the Free Software		      */
     23 /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA    */
     24 /*									      */
     25 /******************************************************************************/
     26 /******************************************************************************/
     27 /* Description:	Test the LINUX memory manager. The program is aimed at        */
     28 /*		stressing the memory manager by simultanious map/unmap/read   */
     29 /*		by light weight processes, the test is scheduled to run for   */
     30 /*		a mininum of 24 hours.					      */
     31 /*									      */
     32 /*		Create two light weight processes X and Y.                    */
     33 /*		X - maps, writes  and unmap a file in a loop.	              */
     34 /*		Y - read from this mapped region in a loop.		      */
     35 /*	        read must be a success between map and unmap of the region.   */
     36 /*									      */
     37 /******************************************************************************/
     38 #include <stdio.h>
     39 #include <sys/types.h>
     40 #include <sys/stat.h>
     41 #include <fcntl.h>
     42 #include <sched.h>
     43 #include <unistd.h>
     44 #include <errno.h>
     45 #include <sys/mman.h>
     46 #include <sched.h>
     47 #include <stdlib.h>
     48 #include <signal.h>
     49 #include <sys/time.h>
     50 #include <sys/wait.h>
     51 #include <setjmp.h>
     52 #include <pthread.h>
     53 #include <signal.h>
     54 #include <string.h>
     55 #include "test.h"
     56 
     57 #define DISTANT_MMAP_SIZE (64*1024*1024)
     58 #define OPT_MISSING(prog, opt) do { \
     59 	fprintf(stderr, "%s: option -%c ", prog, opt); \
     60         fprintf(stderr, "requires an argument\n"); \
     61 	usage(prog); \
     62 } while (0)
     63 
     64 static int verbose_print = 0;
     65 static char *volatile map_address;
     66 static jmp_buf jmpbuf;
     67 static volatile char read_lock = 0;
     68 static void *distant_area;
     69 
     70 char *TCID = "mmap1";
     71 int TST_TOTAL = 1;
     72 
     73 static void sig_handler(int signal, siginfo_t * info, void *ut)
     74 {
     75 	switch (signal) {
     76 	case SIGALRM:
     77 		tst_resm(TPASS, "Test ended, success");
     78 		_exit(TPASS);
     79 	case SIGSEGV:
     80 		longjmp(jmpbuf, 1);
     81 		break;
     82 	default:
     83 		fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
     84 		_exit(TBROK);
     85 	}
     86 }
     87 
     88 /*
     89  * Signal handler that is active, when file is mapped, eg. we do not expect
     90  * SIGSEGV to be delivered.
     91  */
     92 static void sig_handler_mapped(int signal, siginfo_t * info, void *ut)
     93 {
     94 	switch (signal) {
     95 	case SIGALRM:
     96 		tst_resm(TPASS, "Test ended, success");
     97 		_exit(TPASS);
     98 	case SIGSEGV:
     99 		tst_resm(TINFO, "[%lu] Unexpected page fault at %p",
    100 			 pthread_self(), info->si_addr);
    101 		_exit(TFAIL);
    102 		break;
    103 	default:
    104 		fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
    105 		_exit(TBROK);
    106 	}
    107 }
    108 
    109 int mkfile(int size)
    110 {
    111 	char template[] = "/tmp/ashfileXXXXXX";
    112 	int fd, i;
    113 
    114 	if ((fd = mkstemp(template)) == -1)
    115 		tst_brkm(TBROK | TERRNO, NULL, "mkstemp() failed");
    116 
    117 	unlink(template);
    118 
    119 	for (i = 0; i < size; i++)
    120 		if (write(fd, "a", 1) == -1)
    121 			tst_brkm(TBROK | TERRNO, NULL, "write() failed");
    122 
    123 	if (write(fd, "\0", 1) == -1)
    124 		tst_brkm(TBROK | TERRNO, NULL, "write() failed");
    125 
    126 	if (fsync(fd) == -1)
    127 		tst_brkm(TBROK | TERRNO, NULL, "fsync() failed");
    128 
    129 	return fd;
    130 }
    131 
    132 void *map_write_unmap(void *ptr)
    133 {
    134 	struct sigaction sa;
    135 	long *args = ptr;
    136 	long i;
    137 	int j;
    138 
    139 	tst_resm(TINFO, "[%lu] - map, change contents, unmap files %ld times",
    140 		 pthread_self(), args[2]);
    141 
    142 	if (verbose_print)
    143 		tst_resm(TINFO, "map_write_unmap() arguments are: "
    144 			 "fd - arg[0]: %ld; "
    145 			 "size of file - arg[1]: %ld; "
    146 			 "num of map/write/unmap - arg[2]: %ld",
    147 			 args[0], args[1], args[2]);
    148 
    149 	for (i = 0; i < args[2]; i++) {
    150 		map_address = mmap(distant_area, (size_t) args[1],
    151 			PROT_WRITE | PROT_READ, MAP_SHARED, (int)args[0], 0);
    152 
    153 		if (map_address == (void *)-1) {
    154 			perror("map_write_unmap(): mmap()");
    155 			pthread_exit((void *)1);
    156 		}
    157 
    158 		while (read_lock)
    159 			sched_yield();
    160 
    161 		sigfillset(&sa.sa_mask);
    162 		sigdelset(&sa.sa_mask, SIGSEGV);
    163 		sa.sa_flags = SA_SIGINFO | SA_NODEFER;
    164 		sa.sa_sigaction = sig_handler_mapped;
    165 
    166 		if (sigaction(SIGSEGV, &sa, NULL)) {
    167 			perror("map_write_unmap(): sigaction()");
    168 			pthread_exit((void *)1);
    169 		}
    170 
    171 		if (verbose_print)
    172 			tst_resm(TINFO, "map address = %p", map_address);
    173 
    174 		for (j = 0; j < args[1]; j++) {
    175 			map_address[j] = 'a';
    176 			if (random() % 2)
    177 				sched_yield();
    178 		}
    179 
    180 		if (verbose_print)
    181 			tst_resm(TINFO,
    182 				 "[%ld] times done: of total [%ld] iterations, "
    183 				 "map_write_unmap():memset() content of memory = %s",
    184 				 i, args[2], (char *)map_address);
    185 
    186 		sigfillset(&sa.sa_mask);
    187 		sigdelset(&sa.sa_mask, SIGSEGV);
    188 		sa.sa_flags = SA_SIGINFO | SA_NODEFER;
    189 		sa.sa_sigaction = sig_handler;
    190 
    191 		if (sigaction(SIGSEGV, &sa, NULL)) {
    192 			perror("map_write_unmap(): sigaction()");
    193 			pthread_exit((void *)1);
    194 		}
    195 
    196 		if (munmap(map_address, (size_t) args[1]) == -1) {
    197 			perror("map_write_unmap(): mmap()");
    198 			pthread_exit((void *)1);
    199 		}
    200 	}
    201 
    202 	pthread_exit(NULL);
    203 }
    204 
    205 void *read_mem(void *ptr)
    206 {
    207 	long i;
    208 	long *args = ptr;
    209 	int j;
    210 
    211 	tst_resm(TINFO, "[%lu] - read contents of memory %p %ld times",
    212 		 pthread_self(), map_address, args[2]);
    213 
    214 	if (verbose_print)
    215 		tst_resm(TINFO, "read_mem() arguments are: "
    216 			 "number of reads to be performed - arg[2]: %ld; "
    217 			 "read from address %p", args[2], map_address);
    218 
    219 	for (i = 0; i < args[2]; i++) {
    220 
    221 		if (verbose_print)
    222 			tst_resm(TINFO, "read_mem() in while loop %ld times "
    223 				 "to go %ld times", i, args[2]);
    224 
    225 		if (setjmp(jmpbuf) == 1) {
    226 			read_lock = 0;
    227 			if (verbose_print)
    228 				tst_resm(TINFO, "page fault occurred due to "
    229 					 "a read after an unmap");
    230 		} else {
    231 			if (verbose_print) {
    232 				read_lock = 1;
    233 				tst_resm(TINFO,
    234 					 "read_mem(): content of memory: %s",
    235 					 (char *)map_address);
    236 				read_lock = 0;
    237 			}
    238 			for (j = 0; j < args[1]; j++) {
    239 				read_lock = 1;
    240 				if (map_address[j] != 'a')
    241 					pthread_exit((void *)-1);
    242 				read_lock = 0;
    243 				if (random() % 2)
    244 					sched_yield();
    245 			}
    246 		}
    247 	}
    248 
    249 	pthread_exit(NULL);
    250 }
    251 
    252 static void usage(char *progname)
    253 {
    254 	fprintf(stderr, "Usage: %s -d -l -s -v -x\n"
    255 		"\t -h help, usage message.\n"
    256 		"\t -l number of mmap/write/unmap     default: 1000\n"
    257 		"\t -s size of the file to be mmapped default: 1024 bytes\n"
    258 		"\t -v print more info.               default: quiet\n"
    259 		"\t -x test execution time            default: 24 Hrs\n",
    260 		progname);
    261 
    262 	exit(-1);
    263 }
    264 
    265 struct signal_info {
    266 	int signum;
    267 	char *signame;
    268 };
    269 
    270 static struct signal_info sig_info[] = {
    271 	{SIGHUP, "SIGHUP"},
    272 	{SIGINT, "SIGINT"},
    273 	{SIGQUIT, "SIGQUIT"},
    274 	{SIGABRT, "SIGABRT"},
    275 	{SIGBUS, "SIGBUS"},
    276 	{SIGSEGV, "SIGSEGV"},
    277 	{SIGALRM, "SIGALRM"},
    278 	{SIGUSR1, "SIGUSR1"},
    279 	{SIGUSR2, "SIGUSR2"},
    280 	{-1, "ENDSIG"}
    281 };
    282 
    283 int main(int argc, char **argv)
    284 {
    285 	int c, i;
    286 	int file_size;
    287 	int num_iter;
    288 	double exec_time;
    289 	int fd;
    290 	void *status;
    291 	pthread_t thid[2];
    292 	long chld_args[3];
    293 	extern char *optarg;
    294 	struct sigaction sigptr;
    295 	int ret;
    296 
    297 	/* set up the default values */
    298 	file_size = 1024;
    299 	num_iter = 1000;
    300 	exec_time = 24;
    301 
    302 	while ((c = getopt(argc, argv, "hvl:s:x:")) != -1) {
    303 		switch (c) {
    304 		case 'h':
    305 			usage(argv[0]);
    306 			break;
    307 		case 'l':
    308 			if ((num_iter = atoi(optarg)) == 0)
    309 				OPT_MISSING(argv[0], optopt);
    310 			else if (num_iter < 0)
    311 				printf
    312 				    ("WARNING: bad argument. Using default %d\n",
    313 				     (num_iter = 1000));
    314 			break;
    315 		case 's':
    316 			if ((file_size = atoi(optarg)) == 0)
    317 				OPT_MISSING(argv[0], optopt);
    318 			else if (file_size < 0)
    319 				printf
    320 				    ("WARNING: bad argument. Using default %d\n",
    321 				     (file_size = 1024));
    322 			break;
    323 		case 'v':
    324 			verbose_print = 1;
    325 			break;
    326 		case 'x':
    327 			exec_time = atof(optarg);
    328 			if (exec_time == 0)
    329 				OPT_MISSING(argv[0], optopt);
    330 			else if (exec_time < 0)
    331 				printf
    332 				    ("WARNING: bad argument. Using default %.0f\n",
    333 				     (exec_time = 24));
    334 			break;
    335 		default:
    336 			usage(argv[0]);
    337 			break;
    338 		}
    339 	}
    340 
    341 	/* We don't want other mmap calls to map into same area as is
    342 	 * used for test (mmap_address). The test expects read to return
    343 	 * test pattern or read must fail with SIGSEGV. Find an area
    344 	 * that we can use, which is unlikely to be chosen for other
    345 	 * mmap calls. */
    346 	distant_area = mmap(0, DISTANT_MMAP_SIZE, PROT_WRITE | PROT_READ,
    347 		MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    348 	if (distant_area == (void *)-1)
    349 		tst_brkm(TBROK | TERRNO, NULL, "distant_area: mmap()");
    350 	if (munmap(distant_area, (size_t) DISTANT_MMAP_SIZE) == -1)
    351 		tst_brkm(TBROK | TERRNO, NULL, "distant_area: munmap()");
    352 	distant_area += DISTANT_MMAP_SIZE / 2;
    353 
    354 	if (verbose_print)
    355 		tst_resm(TINFO, "Input parameters are: File size:  %d; "
    356 			 "Scheduled to run:  %lf hours; "
    357 			 "Number of mmap/write/read:  %d",
    358 			 file_size, exec_time, num_iter);
    359 
    360 	alarm(exec_time * 3600);
    361 
    362 	/* Do not mask SIGSEGV, as we are interested in handling it. */
    363 	sigptr.sa_sigaction = sig_handler;
    364 	sigfillset(&sigptr.sa_mask);
    365 	sigdelset(&sigptr.sa_mask, SIGSEGV);
    366 	sigptr.sa_flags = SA_SIGINFO | SA_NODEFER;
    367 
    368 	for (i = 0; sig_info[i].signum != -1; i++) {
    369 		if (sigaction(sig_info[i].signum, &sigptr, NULL) == -1) {
    370 			perror("man(): sigaction()");
    371 			fprintf(stderr,
    372 				"could not set handler for %s, errno = %d\n",
    373 				sig_info[i].signame, errno);
    374 			exit(-1);
    375 		}
    376 	}
    377 
    378 	for (;;) {
    379 		if ((fd = mkfile(file_size)) == -1)
    380 			tst_brkm(TBROK, NULL,
    381 				 "main(): mkfile(): Failed to create temp file");
    382 
    383 		if (verbose_print)
    384 			tst_resm(TINFO, "Tmp file created");
    385 
    386 		chld_args[0] = fd;
    387 		chld_args[1] = file_size;
    388 		chld_args[2] = num_iter;
    389 
    390 		if ((ret =
    391 		     pthread_create(&thid[0], NULL, map_write_unmap,
    392 				    chld_args)))
    393 			tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
    394 				 strerror(ret));
    395 
    396 		tst_resm(TINFO, "created writing thread[%lu]", thid[0]);
    397 
    398 		if ((ret = pthread_create(&thid[1], NULL, read_mem, chld_args)))
    399 			tst_brkm(TBROK, NULL, "main(): pthread_create(): %s",
    400 				 strerror(ret));
    401 
    402 		tst_resm(TINFO, "created reading thread[%lu]", thid[1]);
    403 
    404 		for (i = 0; i < 2; i++) {
    405 			if ((ret = pthread_join(thid[i], &status)))
    406 				tst_brkm(TBROK, NULL,
    407 					 "main(): pthread_join(): %s",
    408 					 strerror(ret));
    409 
    410 			if (status)
    411 				tst_brkm(TFAIL, NULL,
    412 					 "thread [%lu] - process exited "
    413 					 "with %ld", thid[i], (long)status);
    414 		}
    415 
    416 		close(fd);
    417 	}
    418 
    419 	exit(0);
    420 }
    421