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 #include "safe_macros.h"
     57 
     58 #define DISTANT_MMAP_SIZE (64*1024*1024)
     59 #define OPT_MISSING(prog, opt) do { \
     60 	fprintf(stderr, "%s: option -%c ", prog, opt); \
     61         fprintf(stderr, "requires an argument\n"); \
     62 	usage(prog); \
     63 } while (0)
     64 
     65 static int verbose_print = 0;
     66 static char *volatile map_address;
     67 static jmp_buf jmpbuf;
     68 static volatile char read_lock = 0;
     69 static void *distant_area;
     70 
     71 char *TCID = "mmap1";
     72 int TST_TOTAL = 1;
     73 
     74 static void sig_handler(int signal, siginfo_t * info, void *ut)
     75 {
     76 	switch (signal) {
     77 	case SIGALRM:
     78 		tst_resm(TPASS, "Test ended, success");
     79 		_exit(TPASS);
     80 	case SIGSEGV:
     81 		longjmp(jmpbuf, 1);
     82 		break;
     83 	default:
     84 		fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
     85 		_exit(TBROK);
     86 	}
     87 }
     88 
     89 /*
     90  * Signal handler that is active, when file is mapped, eg. we do not expect
     91  * SIGSEGV to be delivered.
     92  */
     93 static void sig_handler_mapped(int signal, siginfo_t * info, void *ut)
     94 {
     95 	switch (signal) {
     96 	case SIGALRM:
     97 		tst_resm(TPASS, "Test ended, success");
     98 		_exit(TPASS);
     99 	case SIGSEGV:
    100 		tst_resm(TINFO, "[%lu] Unexpected page fault at %p",
    101 			 pthread_self(), info->si_addr);
    102 		_exit(TFAIL);
    103 		break;
    104 	default:
    105 		fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal);
    106 		_exit(TBROK);
    107 	}
    108 }
    109 
    110 int mkfile(int size)
    111 {
    112 	char template[] = "/tmp/ashfileXXXXXX";
    113 	int fd, i;
    114 
    115 	if ((fd = mkstemp(template)) == -1)
    116 		tst_brkm(TBROK | TERRNO, NULL, "mkstemp() failed");
    117 
    118 	unlink(template);
    119 
    120 	for (i = 0; i < size; i++)
    121 		if (write(fd, "a", 1) == -1)
    122 			tst_brkm(TBROK | TERRNO, NULL, "write() failed");
    123 
    124 	if (write(fd, "\0", 1) == -1)
    125 		tst_brkm(TBROK | TERRNO, NULL, "write() failed");
    126 
    127 	if (fsync(fd) == -1)
    128 		tst_brkm(TBROK | TERRNO, NULL, "fsync() failed");
    129 
    130 	return fd;
    131 }
    132 
    133 void *map_write_unmap(void *ptr)
    134 {
    135 	struct sigaction sa;
    136 	long *args = ptr;
    137 	long i;
    138 	int j;
    139 
    140 	tst_resm(TINFO, "[%lu] - map, change contents, unmap files %ld times",
    141 		 pthread_self(), args[2]);
    142 
    143 	if (verbose_print)
    144 		tst_resm(TINFO, "map_write_unmap() arguments are: "
    145 			 "fd - arg[0]: %ld; "
    146 			 "size of file - arg[1]: %ld; "
    147 			 "num of map/write/unmap - arg[2]: %ld",
    148 			 args[0], args[1], args[2]);
    149 
    150 	for (i = 0; i < args[2]; i++) {
    151 		map_address = mmap(distant_area, (size_t) args[1],
    152 			PROT_WRITE | PROT_READ, MAP_SHARED, (int)args[0], 0);
    153 
    154 		if (map_address == (void *)-1) {
    155 			perror("map_write_unmap(): mmap()");
    156 			pthread_exit((void *)1);
    157 		}
    158 
    159 		while (read_lock)
    160 			sched_yield();
    161 
    162 		sigfillset(&sa.sa_mask);
    163 		sigdelset(&sa.sa_mask, SIGSEGV);
    164 		sa.sa_flags = SA_SIGINFO | SA_NODEFER;
    165 		sa.sa_sigaction = sig_handler_mapped;
    166 
    167 		if (sigaction(SIGSEGV, &sa, NULL)) {
    168 			perror("map_write_unmap(): sigaction()");
    169 			pthread_exit((void *)1);
    170 		}
    171 
    172 		if (verbose_print)
    173 			tst_resm(TINFO, "map address = %p", map_address);
    174 
    175 		for (j = 0; j < args[1]; j++) {
    176 			map_address[j] = 'a';
    177 			if (random() % 2)
    178 				sched_yield();
    179 		}
    180 
    181 		if (verbose_print)
    182 			tst_resm(TINFO,
    183 				 "[%ld] times done: of total [%ld] iterations, "
    184 				 "map_write_unmap():memset() content of memory = %s",
    185 				 i, args[2], (char *)map_address);
    186 
    187 		sigfillset(&sa.sa_mask);
    188 		sigdelset(&sa.sa_mask, SIGSEGV);
    189 		sa.sa_flags = SA_SIGINFO | SA_NODEFER;
    190 		sa.sa_sigaction = sig_handler;
    191 
    192 		if (sigaction(SIGSEGV, &sa, NULL)) {
    193 			perror("map_write_unmap(): sigaction()");
    194 			pthread_exit((void *)1);
    195 		}
    196 
    197 		if (munmap(map_address, (size_t) args[1]) == -1) {
    198 			perror("map_write_unmap(): mmap()");
    199 			pthread_exit((void *)1);
    200 		}
    201 	}
    202 
    203 	pthread_exit(NULL);
    204 }
    205 
    206 void *read_mem(void *ptr)
    207 {
    208 	long i;
    209 	long *args = ptr;
    210 	int j;
    211 
    212 	tst_resm(TINFO, "[%lu] - read contents of memory %p %ld times",
    213 		 pthread_self(), map_address, args[2]);
    214 
    215 	if (verbose_print)
    216 		tst_resm(TINFO, "read_mem() arguments are: "
    217 			 "number of reads to be performed - arg[2]: %ld; "
    218 			 "read from address %p", args[2], map_address);
    219 
    220 	for (i = 0; i < args[2]; i++) {
    221 
    222 		if (verbose_print)
    223 			tst_resm(TINFO, "read_mem() in while loop %ld times "
    224 				 "to go %ld times", i, args[2]);
    225 
    226 		if (setjmp(jmpbuf) == 1) {
    227 			read_lock = 0;
    228 			if (verbose_print)
    229 				tst_resm(TINFO, "page fault occurred due to "
    230 					 "a read after an unmap");
    231 		} else {
    232 			if (verbose_print) {
    233 				read_lock = 1;
    234 				tst_resm(TINFO,
    235 					 "read_mem(): content of memory: %s",
    236 					 (char *)map_address);
    237 				read_lock = 0;
    238 			}
    239 			for (j = 0; j < args[1]; j++) {
    240 				read_lock = 1;
    241 				if (map_address[j] != 'a')
    242 					pthread_exit((void *)-1);
    243 				read_lock = 0;
    244 				if (random() % 2)
    245 					sched_yield();
    246 			}
    247 		}
    248 	}
    249 
    250 	pthread_exit(NULL);
    251 }
    252 
    253 static void usage(char *progname)
    254 {
    255 	fprintf(stderr, "Usage: %s -d -l -s -v -x\n"
    256 		"\t -h help, usage message.\n"
    257 		"\t -l number of mmap/write/unmap     default: 1000\n"
    258 		"\t -s size of the file to be mmapped default: 1024 bytes\n"
    259 		"\t -v print more info.               default: quiet\n"
    260 		"\t -x test execution time            default: 24 Hrs\n",
    261 		progname);
    262 
    263 	exit(-1);
    264 }
    265 
    266 struct signal_info {
    267 	int signum;
    268 	char *signame;
    269 };
    270 
    271 static struct signal_info sig_info[] = {
    272 	{SIGHUP, "SIGHUP"},
    273 	{SIGINT, "SIGINT"},
    274 	{SIGQUIT, "SIGQUIT"},
    275 	{SIGABRT, "SIGABRT"},
    276 	{SIGBUS, "SIGBUS"},
    277 	{SIGSEGV, "SIGSEGV"},
    278 	{SIGALRM, "SIGALRM"},
    279 	{SIGUSR1, "SIGUSR1"},
    280 	{SIGUSR2, "SIGUSR2"},
    281 	{-1, "ENDSIG"}
    282 };
    283 
    284 int main(int argc, char **argv)
    285 {
    286 	int c, i;
    287 	int file_size;
    288 	int num_iter;
    289 	double exec_time;
    290 	int fd;
    291 	void *status;
    292 	pthread_t thid[2];
    293 	long chld_args[3];
    294 	extern char *optarg;
    295 	struct sigaction sigptr;
    296 	int ret;
    297 
    298 	/* set up the default values */
    299 	file_size = 1024;
    300 	num_iter = 1000;
    301 	exec_time = 24;
    302 
    303 	while ((c = getopt(argc, argv, "hvl:s:x:")) != -1) {
    304 		switch (c) {
    305 		case 'h':
    306 			usage(argv[0]);
    307 			break;
    308 		case 'l':
    309 			if ((num_iter = atoi(optarg)) == 0)
    310 				OPT_MISSING(argv[0], optopt);
    311 			else if (num_iter < 0)
    312 				printf
    313 				    ("WARNING: bad argument. Using default %d\n",
    314 				     (num_iter = 1000));
    315 			break;
    316 		case 's':
    317 			if ((file_size = atoi(optarg)) == 0)
    318 				OPT_MISSING(argv[0], optopt);
    319 			else if (file_size < 0)
    320 				printf
    321 				    ("WARNING: bad argument. Using default %d\n",
    322 				     (file_size = 1024));
    323 			break;
    324 		case 'v':
    325 			verbose_print = 1;
    326 			break;
    327 		case 'x':
    328 			exec_time = atof(optarg);
    329 			if (exec_time == 0)
    330 				OPT_MISSING(argv[0], optopt);
    331 			else if (exec_time < 0)
    332 				printf
    333 				    ("WARNING: bad argument. Using default %.0f\n",
    334 				     (exec_time = 24));
    335 			break;
    336 		default:
    337 			usage(argv[0]);
    338 			break;
    339 		}
    340 	}
    341 
    342 	/* We don't want other mmap calls to map into same area as is
    343 	 * used for test (mmap_address). The test expects read to return
    344 	 * test pattern or read must fail with SIGSEGV. Find an area
    345 	 * that we can use, which is unlikely to be chosen for other
    346 	 * mmap calls. */
    347 	distant_area = mmap(0, DISTANT_MMAP_SIZE, PROT_WRITE | PROT_READ,
    348 		MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
    349 	if (distant_area == (void *)-1)
    350 		tst_brkm(TBROK | TERRNO, NULL, "distant_area: mmap()");
    351 	SAFE_MUNMAP(NULL, distant_area, (size_t)DISTANT_MMAP_SIZE);
    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