1 /****************************************************************************** 2 * * 3 * Copyright (c) International Business Machines Corp., 2001 * 4 * 2001 Created by Manoj Iyer, IBM Austin TX <manjo (at) austin.ibm.com> * 5 * Copyright (C) 2016 Cyril Hrubis <chrubis (at) suse.cz> * 6 * * 7 * This program is free software; you can redistribute it and/or modify * 8 * it under the terms of the GNU General Public License as published by * 9 * the Free Software Foundation; either version 2 of the License, or * 10 * (at your option) any later version. * 11 * * 12 * This program is distributed in the hope that it will be useful, * 13 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * 15 * the GNU General Public License for more details. * 16 * * 17 * You should have received a copy of the GNU General Public License * 18 * along with this program; if not, write to the Free Software Foundation, * 19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA * 20 * * 21 ******************************************************************************/ 22 23 /* 24 * Tests the LINUX memory manager. The program is aimed at stressing the memory 25 * manager by repeaded map/write/unmap of file/memory of random size (maximum 26 * 1GB) this is done by multiple threads. 27 * 28 * Create a file of random size upto 1000 times 4096, map it, change the 29 * contents of the file and unmap it. This is repeated several times for the 30 * specified number of hours by a certain number of threads. 31 */ 32 33 #include <stdio.h> 34 #include <stdlib.h> 35 #include <limits.h> 36 #include <pthread.h> 37 #include "tst_safe_pthread.h" 38 #include "tst_test.h" 39 40 static char *str_loops; 41 static char *str_threads; 42 static char *map_private; 43 static char *str_exec_time; 44 45 static int loops = 1000; 46 static int threads = 40; 47 static float exec_time = 24; 48 49 static volatile int sig_caught; 50 static int threads_running; 51 52 static int mkfile(int *size) 53 { 54 int fd; 55 int index = 0; 56 char buf[4096]; 57 char template[PATH_MAX]; 58 59 memset(buf, 'a', 4096); 60 snprintf(template, PATH_MAX, "ashfileXXXXXX"); 61 if ((fd = mkstemp(template)) == -1) 62 tst_brk(TBROK | TERRNO, "mkstemp()"); 63 unlink(template); 64 65 *size = (1 + (int)(1000.0 * rand() / (RAND_MAX + 1.0))) * 4096; 66 67 while (index < *size) { 68 index += sizeof(buf); 69 SAFE_WRITE(1, fd, buf, sizeof(buf)); 70 } 71 72 fsync(fd); 73 74 return fd; 75 } 76 77 static void exit_thread(void) __attribute__ ((noreturn)); 78 79 static void exit_thread(void) 80 { 81 tst_atomic_dec(&threads_running); 82 pthread_exit(NULL); 83 } 84 85 void *map_write_unmap(void *args) 86 { 87 int fsize; 88 int fd; 89 int i; 90 void *addr; 91 long tid = (long)args; 92 93 tst_atomic_inc(&threads_running); 94 95 for (i = 0; i < loops; i++) { 96 if (sig_caught) 97 exit_thread(); 98 99 if ((fd = mkfile(&fsize)) == -1) 100 exit_thread(); 101 102 addr = SAFE_MMAP(NULL, fsize, PROT_WRITE | PROT_READ, 103 map_private ? MAP_PRIVATE : MAP_SHARED, fd, 0); 104 105 memset(addr, 'A', fsize); 106 107 tst_res(TINFO, "Thread %4li, addr [%p], size %4ikB, iter %4d", 108 tid, addr, fsize/1024, i); 109 110 usleep(1); 111 112 SAFE_MUNMAP(addr, fsize); 113 SAFE_CLOSE(fd); 114 } 115 116 exit_thread(); 117 } 118 119 static void sig_handler(int signal) 120 { 121 sig_caught = signal; 122 } 123 124 static void test_mmap(void) 125 { 126 long i; 127 pthread_t thids[threads]; 128 129 alarm(exec_time * 3600); 130 131 while (!sig_caught) { 132 for (i = 0; i < threads; i++) { 133 SAFE_PTHREAD_CREATE(&thids[i], NULL, 134 map_write_unmap, (void*)i); 135 sched_yield(); 136 } 137 138 for (i = 0; i < threads; i++) 139 SAFE_PTHREAD_JOIN(thids[i], NULL); 140 } 141 142 if (sig_caught == SIGALRM) { 143 tst_res(TPASS, "Test passed"); 144 } else { 145 tst_res(TFAIL, "Unexpected signal caught %s", 146 tst_strsig(sig_caught)); 147 } 148 } 149 150 static void setup(void) 151 { 152 if (tst_parse_int(str_loops, &loops, 1, INT_MAX)) 153 tst_brk(TBROK, "Invalid number of loops '%s'", str_loops); 154 155 if (tst_parse_int(str_threads, &threads, 1, INT_MAX)) 156 tst_brk(TBROK, "Invalid number of threads '%s'", str_threads); 157 158 if (tst_parse_float(str_exec_time, &exec_time, 0.0005, 9000)) 159 tst_brk(TBROK, "Invalid execution time '%s'", str_exec_time); 160 161 tst_set_timeout(exec_time * 3600 + 300); 162 163 SAFE_SIGNAL(SIGALRM, sig_handler); 164 SAFE_SIGNAL(SIGBUS, sig_handler); 165 SAFE_SIGNAL(SIGSEGV, sig_handler); 166 167 unsigned int seed = time(NULL) % 100; 168 169 srand(seed); 170 171 tst_res(TINFO, "Seed %u", seed); 172 tst_res(TINFO, "Number of loops %i", loops); 173 tst_res(TINFO, "Number of threads %i", threads); 174 tst_res(TINFO, "MAP_PRIVATE = %i", map_private ? 1 : 0); 175 tst_res(TINFO, "Execution time %fH", exec_time); 176 } 177 178 static void cleanup(void) 179 { 180 static int flag; 181 182 if (tst_atomic_inc(&flag) != 1) 183 exit_thread(); 184 185 if (!threads_running) 186 return; 187 188 tst_res(TINFO, "Waiting for %i threads to terminate", threads_running); 189 190 sig_caught = 1; 191 192 while ((volatile int)threads_running > 1) { 193 tst_res(TINFO, "Running threads %i", 194 (volatile int)threads_running); 195 usleep(500000); 196 } 197 } 198 199 static struct tst_option options[] = { 200 {"l:", &str_loops, "-l uint Number of map-write-unmap loops"}, 201 {"n:", &str_threads, "-n uint Number of worker threads"}, 202 {"p", &map_private, "-p Turns on MAP_PRIVATE (default MAP_SHARED)"}, 203 {"x:", &str_exec_time, "-x float Execution time in hours (default 24H)"}, 204 {NULL, NULL, NULL} 205 }; 206 207 static struct tst_test test = { 208 .options = options, 209 .needs_tmpdir = 1, 210 .setup = setup, 211 .cleanup = cleanup, 212 .test_all = test_mmap, 213 }; 214