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