| /******************************************************************************/ |
| /* */ |
| /* Copyright (c) International Business Machines Corp., 2001 */ |
| /* Copyright (c) 2001 Manoj Iyer <manjo@austin.ibm.com> */ |
| /* Copyright (c) 2003 Robbie Williamson <robbiew@us.ibm.com> */ |
| /* Copyright (c) 2004 Paul Larson <plars@linuxtestproject.org> */ |
| /* Copyright (c) 2007 <rsalveti@linux.vnet.ibm.com> */ |
| /* Copyright (c) 2007 Suzuki K P <suzuki@in.ibm.com> */ |
| /* Copyright (c) 2011 Cyril Hrubis <chrubis@suse.cz> */ |
| /* */ |
| /* This program is free software; you can redistribute it and/or modify */ |
| /* it under the terms of the GNU General Public License as published by */ |
| /* the Free Software Foundation; either version 2 of the License, or */ |
| /* (at your option) any later version. */ |
| /* */ |
| /* This program is distributed in the hope that it will be useful, */ |
| /* but WITHOUT ANY WARRANTY; without even the implied warranty of */ |
| /* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See */ |
| /* the GNU General Public License for more details. */ |
| /* */ |
| /* You should have received a copy of the GNU General Public License */ |
| /* along with this program; if not, write to the Free Software */ |
| /* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ |
| /* */ |
| /******************************************************************************/ |
| /******************************************************************************/ |
| /* Description: Test the LINUX memory manager. The program is aimed at */ |
| /* stressing the memory manager by simultanious map/unmap/read */ |
| /* by light weight processes, the test is scheduled to run for */ |
| /* a mininum of 24 hours. */ |
| /* */ |
| /* Create two light weight processes X and Y. */ |
| /* X - maps, writes and unmap a file in a loop. */ |
| /* Y - read from this mapped region in a loop. */ |
| /* read must be a success between map and unmap of the region. */ |
| /* */ |
| /******************************************************************************/ |
| #include <stdio.h> |
| #include <sys/types.h> |
| #include <sys/stat.h> |
| #include <fcntl.h> |
| #include <sched.h> |
| #include <unistd.h> |
| #include <errno.h> |
| #include <sys/mman.h> |
| #include <sched.h> |
| #include <stdlib.h> |
| #include <signal.h> |
| #include <sys/time.h> |
| #include <sys/wait.h> |
| #include <setjmp.h> |
| #include <pthread.h> |
| #include <signal.h> |
| #include <string.h> |
| #include "test.h" |
| |
| #define OPT_MISSING(prog, opt) do { \ |
| fprintf(stderr, "%s: option -%c ", prog, opt); \ |
| fprintf(stderr, "requires an argument\n"); \ |
| usage(prog); \ |
| } while (0) |
| |
| static int verbose_print = 0; |
| static char *volatile map_address; |
| static jmp_buf jmpbuf; |
| static volatile char read_lock = 0; |
| |
| char *TCID = "mmap1"; |
| int TST_TOTAL = 1; |
| |
| static void sig_handler(int signal, siginfo_t * info, void *ut) |
| { |
| switch (signal) { |
| case SIGALRM: |
| tst_resm(TPASS, "Test ended, success"); |
| _exit(TPASS); |
| case SIGSEGV: |
| longjmp(jmpbuf, 1); |
| break; |
| default: |
| fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal); |
| _exit(TBROK); |
| } |
| } |
| |
| /* |
| * Signal handler that is active, when file is mapped, eg. we do not expect |
| * SIGSEGV to be delivered. |
| */ |
| static void sig_handler_mapped(int signal, siginfo_t * info, void *ut) |
| { |
| switch (signal) { |
| case SIGALRM: |
| tst_resm(TPASS, "Test ended, success"); |
| _exit(TPASS); |
| case SIGSEGV: |
| tst_resm(TINFO, "[%lu] Unexpected page fault at %p", |
| pthread_self(), info->si_addr); |
| _exit(TFAIL); |
| break; |
| default: |
| fprintf(stderr, "Unexpected signal - %d --- exiting\n", signal); |
| _exit(TBROK); |
| } |
| } |
| |
| int mkfile(int size) |
| { |
| char template[] = "/tmp/ashfileXXXXXX"; |
| int fd, i; |
| |
| if ((fd = mkstemp(template)) == -1) |
| tst_brkm(TBROK | TERRNO, NULL, "mkstemp() failed"); |
| |
| unlink(template); |
| |
| for (i = 0; i < size; i++) |
| if (write(fd, "a", 1) == -1) |
| tst_brkm(TBROK | TERRNO, NULL, "write() failed"); |
| |
| if (write(fd, "\0", 1) == -1) |
| tst_brkm(TBROK | TERRNO, NULL, "write() failed"); |
| |
| if (fsync(fd) == -1) |
| tst_brkm(TBROK | TERRNO, NULL, "fsync() failed"); |
| |
| return fd; |
| } |
| |
| void *map_write_unmap(void *ptr) |
| { |
| struct sigaction sa; |
| long *args = ptr; |
| long i; |
| int j; |
| |
| tst_resm(TINFO, "[%lu] - map, change contents, unmap files %ld times", |
| pthread_self(), args[2]); |
| |
| if (verbose_print) |
| tst_resm(TINFO, "map_write_unmap() arguments are: " |
| "fd - arg[0]: %ld; " |
| "size of file - arg[1]: %ld; " |
| "num of map/write/unmap - arg[2]: %ld", |
| args[0], args[1], args[2]); |
| |
| for (i = 0; i < args[2]; i++) { |
| |
| map_address = mmap(0, (size_t) args[1], PROT_WRITE | PROT_READ, |
| MAP_SHARED, (int)args[0], 0); |
| |
| if (map_address == (void *)-1) { |
| perror("map_write_unmap(): mmap()"); |
| pthread_exit((void *)1); |
| } |
| |
| while (read_lock) |
| sched_yield(); |
| |
| sigfillset(&sa.sa_mask); |
| sigdelset(&sa.sa_mask, SIGSEGV); |
| sa.sa_flags = SA_SIGINFO | SA_NODEFER; |
| sa.sa_sigaction = sig_handler_mapped; |
| |
| if (sigaction(SIGSEGV, &sa, NULL)) { |
| perror("map_write_unmap(): sigaction()"); |
| pthread_exit((void *)1); |
| } |
| |
| if (verbose_print) |
| tst_resm(TINFO, "map address = %p", map_address); |
| |
| for (j = 0; j < args[1]; j++) { |
| map_address[j] = 'a'; |
| if (random() % 2) |
| sched_yield(); |
| } |
| |
| if (verbose_print) |
| tst_resm(TINFO, |
| "[%ld] times done: of total [%ld] iterations, " |
| "map_write_unmap():memset() content of memory = %s", |
| i, args[2], (char *)map_address); |
| |
| sigfillset(&sa.sa_mask); |
| sigdelset(&sa.sa_mask, SIGSEGV); |
| sa.sa_flags = SA_SIGINFO | SA_NODEFER; |
| sa.sa_sigaction = sig_handler; |
| |
| if (sigaction(SIGSEGV, &sa, NULL)) { |
| perror("map_write_unmap(): sigaction()"); |
| pthread_exit((void *)1); |
| } |
| |
| if (munmap(map_address, (size_t) args[1]) == -1) { |
| perror("map_write_unmap(): mmap()"); |
| pthread_exit((void *)1); |
| } |
| } |
| |
| pthread_exit(NULL); |
| } |
| |
| void *read_mem(void *ptr) |
| { |
| long i; |
| long *args = ptr; |
| int j; |
| |
| tst_resm(TINFO, "[%lu] - read contents of memory %p %ld times", |
| pthread_self(), map_address, args[2]); |
| |
| if (verbose_print) |
| tst_resm(TINFO, "read_mem() arguments are: " |
| "number of reads to be performed - arg[2]: %ld; " |
| "read from address %p", args[2], map_address); |
| |
| for (i = 0; i < args[2]; i++) { |
| |
| if (verbose_print) |
| tst_resm(TINFO, "read_mem() in while loop %ld times " |
| "to go %ld times", i, args[2]); |
| |
| if (setjmp(jmpbuf) == 1) { |
| read_lock = 0; |
| if (verbose_print) |
| tst_resm(TINFO, "page fault occurred due to " |
| "a read after an unmap"); |
| } else { |
| if (verbose_print) { |
| read_lock = 1; |
| tst_resm(TINFO, |
| "read_mem(): content of memory: %s", |
| (char *)map_address); |
| read_lock = 0; |
| } |
| for (j = 0; j < args[1]; j++) { |
| read_lock = 1; |
| if (map_address[j] != 'a') |
| pthread_exit((void *)-1); |
| read_lock = 0; |
| if (random() % 2) |
| sched_yield(); |
| } |
| } |
| } |
| |
| pthread_exit(NULL); |
| } |
| |
| static void usage(char *progname) |
| { |
| fprintf(stderr, "Usage: %s -d -l -s -v -x\n" |
| "\t -h help, usage message.\n" |
| "\t -l number of mmap/write/unmap default: 1000\n" |
| "\t -s size of the file to be mmapped default: 1024 bytes\n" |
| "\t -v print more info. default: quiet\n" |
| "\t -x test execution time default: 24 Hrs\n", |
| progname); |
| |
| exit(-1); |
| } |
| |
| struct signal_info { |
| int signum; |
| char *signame; |
| }; |
| |
| static struct signal_info sig_info[] = { |
| {SIGHUP, "SIGHUP"}, |
| {SIGINT, "SIGINT"}, |
| {SIGQUIT, "SIGQUIT"}, |
| {SIGABRT, "SIGABRT"}, |
| {SIGBUS, "SIGBUS"}, |
| {SIGSEGV, "SIGSEGV"}, |
| {SIGALRM, "SIGALRM"}, |
| {SIGUSR1, "SIGUSR1"}, |
| {SIGUSR2, "SIGUSR2"}, |
| {-1, "ENDSIG"} |
| }; |
| |
| int main(int argc, char **argv) |
| { |
| int c, i; |
| int file_size; |
| int num_iter; |
| double exec_time; |
| int fd; |
| void *status; |
| pthread_t thid[2]; |
| long chld_args[3]; |
| extern char *optarg; |
| struct sigaction sigptr; |
| int ret; |
| |
| /* set up the default values */ |
| file_size = 1024; |
| num_iter = 1000; |
| exec_time = 24; |
| |
| while ((c = getopt(argc, argv, "hvl:s:x:")) != -1) { |
| switch (c) { |
| case 'h': |
| usage(argv[0]); |
| break; |
| case 'l': |
| if ((num_iter = atoi(optarg)) == 0) |
| OPT_MISSING(argv[0], optopt); |
| else if (num_iter < 0) |
| printf |
| ("WARNING: bad argument. Using default %d\n", |
| (num_iter = 1000)); |
| break; |
| case 's': |
| if ((file_size = atoi(optarg)) == 0) |
| OPT_MISSING(argv[0], optopt); |
| else if (file_size < 0) |
| printf |
| ("WARNING: bad argument. Using default %d\n", |
| (file_size = 1024)); |
| break; |
| case 'v': |
| verbose_print = 1; |
| break; |
| case 'x': |
| exec_time = atof(optarg); |
| if (exec_time == 0) |
| OPT_MISSING(argv[0], optopt); |
| else if (exec_time < 0) |
| printf |
| ("WARNING: bad argument. Using default %.0f\n", |
| (exec_time = 24)); |
| break; |
| default: |
| usage(argv[0]); |
| break; |
| } |
| } |
| |
| if (verbose_print) |
| tst_resm(TINFO, "Input parameters are: File size: %d; " |
| "Scheduled to run: %lf hours; " |
| "Number of mmap/write/read: %d", |
| file_size, exec_time, num_iter); |
| |
| alarm(exec_time * 3600); |
| |
| /* Do not mask SIGSEGV, as we are interested in handling it. */ |
| sigptr.sa_sigaction = sig_handler; |
| sigfillset(&sigptr.sa_mask); |
| sigdelset(&sigptr.sa_mask, SIGSEGV); |
| sigptr.sa_flags = SA_SIGINFO | SA_NODEFER; |
| |
| for (i = 0; sig_info[i].signum != -1; i++) { |
| if (sigaction(sig_info[i].signum, &sigptr, NULL) == -1) { |
| perror("man(): sigaction()"); |
| fprintf(stderr, |
| "could not set handler for %s, errno = %d\n", |
| sig_info[i].signame, errno); |
| exit(-1); |
| } |
| } |
| |
| for (;;) { |
| if ((fd = mkfile(file_size)) == -1) |
| tst_brkm(TBROK, NULL, |
| "main(): mkfile(): Failed to create temp file"); |
| |
| if (verbose_print) |
| tst_resm(TINFO, "Tmp file created"); |
| |
| chld_args[0] = fd; |
| chld_args[1] = file_size; |
| chld_args[2] = num_iter; |
| |
| if ((ret = |
| pthread_create(&thid[0], NULL, map_write_unmap, |
| chld_args))) |
| tst_brkm(TBROK, NULL, "main(): pthread_create(): %s", |
| strerror(ret)); |
| |
| tst_resm(TINFO, "created writing thread[%lu]", thid[0]); |
| |
| if ((ret = pthread_create(&thid[1], NULL, read_mem, chld_args))) |
| tst_brkm(TBROK, NULL, "main(): pthread_create(): %s", |
| strerror(ret)); |
| |
| tst_resm(TINFO, "created reading thread[%lu]", thid[1]); |
| |
| for (i = 0; i < 2; i++) { |
| if ((ret = pthread_join(thid[i], &status))) |
| tst_brkm(TBROK, NULL, |
| "main(): pthread_join(): %s", |
| strerror(ret)); |
| |
| if (status) |
| tst_brkm(TFAIL, NULL, |
| "thread [%lu] - process exited " |
| "with %ld", thid[i], (long)status); |
| } |
| |
| close(fd); |
| } |
| |
| exit(0); |
| } |