1 /* 2 * mem01.c - Basic memory and swapper stress test 3 * 4 * Copyright (C) 2001 Stephane Fillod <f4cfe (at) free.fr> 5 * Original idea from Rene Cougnenc (on t'a pas oubli mec) 6 * 7 * Copyright (C) 2012 Cyril Hrubis <chrubis (at) suse.cz> 8 * 9 * This program is free software; you can redistribute it and/or modify it 10 * under the terms of version 2 of the GNU General Public License as 11 * published by the Free Software Foundation. 12 * 13 * This program is distributed in the hope that it would be useful, but 14 * WITHOUT ANY WARRANTY; without even the implied warranty of 15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 16 * 17 * Further, this software is distributed without any warranty that it is 18 * free of the rightful claim of any third person regarding infringement 19 * or the like. Any license provided herein, whether implied or 20 * otherwise, applies only to this software file. Patent licenses, if 21 * any, provided herein do not apply to combinations of this program with 22 * other software, or any other product whatsoever. 23 * 24 * You should have received a copy of the GNU General Public License along 25 * with this program; if not, write the Free Software Foundation, Inc., 26 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 27 * 28 */ 29 30 #include <unistd.h> 31 #include <errno.h> 32 #include <string.h> 33 #include <signal.h> 34 #include <sys/types.h> 35 #include <sys/sysinfo.h> 36 #include <stdio.h> 37 #include <stdlib.h> 38 #include <sys/user.h> 39 #include <time.h> 40 #include <limits.h> 41 42 #include "test.h" 43 44 /* in KB */ 45 #define PROGRESS_LEAP 100 46 47 /* 48 * TODO: 49 * - add option for growing direction, when doing linear touching 50 * - add option for touch running time (or infinite loop?) 51 * - make it multithreaded with random access to test r/w mm_sem 52 */ 53 54 char *TCID = "mem01"; 55 int TST_TOTAL = 1; 56 57 static int m_opt = 0; /* memsize */ 58 static char *m_copt; 59 60 static int r_opt = 0; /* random access versus linear */ 61 static int v_opt = 0; /* verbose progress indication */ 62 63 static void cleanup(void) 64 { 65 tst_rmdir(); 66 tst_exit(); 67 } 68 69 static void setup(void) 70 { 71 tst_sig(NOFORK, DEF_HANDLER, cleanup); 72 73 TEST_PAUSE; 74 75 tst_tmpdir(); 76 } 77 78 static void help(void) 79 { 80 printf(" -m x size of malloc in MB (default from /proc/meminfo)\n"); 81 printf(" -r random touching versus linear\n"); 82 printf(" -v verbose progress indication\n"); 83 } 84 85 /* 86 * return MemFree+SwapFree, from /proc/meminfo 87 * returned value is in bytes. 88 */ 89 size_t get_memsize(void) 90 { 91 struct sysinfo info; 92 unsigned long long res; 93 unsigned long long freeswap; 94 unsigned long long freeram; 95 int ret; 96 97 ret = sysinfo(&info); 98 if (ret != 0) { 99 tst_resm(TFAIL, 100 "Could not retrieve memory information using sysinfo()"); 101 cleanup(); 102 } 103 104 freeram = 105 (unsigned long long)info.freeram * 106 (unsigned long long)info.mem_unit; 107 tst_resm(TINFO, "Free Mem:\t%llu Mb", freeram / 1024 / 1024); 108 res = freeram; 109 110 freeswap = 111 (unsigned long long)info.freeswap * 112 (unsigned long long)info.mem_unit; 113 tst_resm(TINFO, "Free Swap:\t%llu Mb", freeswap / 1024 / 1024); 114 res = res + freeswap; 115 116 tst_resm(TINFO, "Total Free:\t%llu Mb", res / 1024 / 1024); 117 #if defined (__s390__) 118 if (res > 1 * 1024 * 1024 * 1024) 119 res = 500 * 1024 * 1024; /* s390's unique 31bit architecture needs smaller default */ 120 #elif __WORDSIZE == 32 121 if (res > 1 * 1024 * 1024 * 1024) 122 res = 1 * 1024 * 1024 * 1024; 123 #elif __WORDSIZE == 64 124 if (res > (unsigned long long)3 * 1024 * 1024 * 1024) 125 res = (unsigned long long)3 *1024 * 1024 * 1024; 126 #endif 127 128 /* Always reserve 16MB memory to avoid OOM Killer. */ 129 res -= 16 * 1024 * 1024; 130 tst_resm(TINFO, "Total Tested:\t%llu Mb", res / 1024 / 1024); 131 return (size_t) res; 132 } 133 134 /* 135 * add the -m option whose parameter is the 136 * memory size (MB) to allocate. 137 */ 138 option_t options[] = { 139 {"m:", &m_opt, &m_copt} 140 , 141 {"r", &r_opt, NULL} 142 , 143 {"v", &v_opt, NULL} 144 , 145 {NULL, NULL, NULL} 146 }; 147 148 int main(int argc, char *argv[]) 149 { 150 size_t memsize = 0; /* at first in MB, limited to 4Gb on 32 bits */ 151 int pagesize; 152 153 int i; 154 int lc; 155 char *p, *bigmalloc; 156 int loop_count; /* limited to 16Go on 32 bits systems */ 157 158 pagesize = sysconf(_SC_PAGESIZE); 159 160 tst_parse_opts(argc, argv, options, help); 161 162 if (m_opt) { 163 memsize = (size_t) atoi(m_copt) * 1024 * 1024; 164 165 if (memsize < 1) { 166 tst_brkm(TBROK, cleanup, "Invalid arg for -m: %s", 167 m_copt); 168 } 169 } 170 171 if (r_opt) 172 srand(time(NULL)); 173 174 setup(); 175 176 for (lc = 0; TEST_LOOPING(lc); lc++) { 177 178 tst_count = 0; 179 180 if (!m_opt) { 181 /* find out by ourselves! */ 182 memsize = get_memsize(); 183 if (memsize < 1) { 184 tst_brkm(TBROK, cleanup, 185 "Unable to guess maxmemsize from /proc/meminfo"); 186 } 187 } 188 189 /* Allocate (virtual) memory */ 190 bigmalloc = p = malloc(memsize); 191 192 if (!p) { 193 tst_resm(TFAIL, "malloc - alloc of %zuMB failed", 194 memsize / 1024 / 1024); 195 cleanup(); 196 } 197 198 /* 199 * Dirty all the pages, to force physical RAM allocation 200 * and exercise eventually the swapper 201 */ 202 tst_resm(TINFO, "touching %zuMB of malloc'ed memory (%s)", 203 memsize / 1024 / 1024, r_opt ? "random" : "linear"); 204 205 loop_count = memsize / pagesize; 206 207 for (i = 0; i < loop_count; i++) { 208 if (v_opt 209 && (i % (PROGRESS_LEAP * 1024 / pagesize) == 0)) { 210 printf("."); 211 fflush(stdout); 212 } 213 /* 214 * Make the page dirty, 215 * and make sure compiler won't optimize it away 216 * Touching more than one word per page is useless 217 * because of cache. 218 */ 219 *(int *)p = 0xdeadbeef ^ i; 220 221 if (r_opt) { 222 p = bigmalloc + 223 (size_t) ((double)(memsize - sizeof(int)) * 224 rand() / (RAND_MAX + 1.0)); 225 } else { 226 p += pagesize; 227 } 228 } 229 230 if (v_opt) 231 printf("\n"); 232 233 /* This is not mandatory (except in a loop), but it exercise mm again */ 234 free(bigmalloc); 235 236 /* 237 * seems that if the malloc'ed area was bad, we'd get SEGV (or kicked 238 * somehow by the OOM killer?), hence we can indicate a PASS. 239 */ 240 tst_resm(TPASS, "malloc - alloc of %zuMB succeeded", 241 memsize / 1024 / 1024); 242 243 } 244 245 cleanup(); 246 247 return 0; 248 } 249