1 /*
2 * mem01.c - Basic memory and swapper stress test
3 *
4 * Copyright (C) 2001 Stephane Fillod <f4cfe@free.fr>
5 * Original idea from Rene Cougnenc
6 *
7 * Copyright (C) 2012 Cyril Hrubis <chrubis@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 "lapi/abisize.h"
43 #include "test.h"
44
45 /* in KB */
46 #define PROGRESS_LEAP 100
47
48 /*
49 * TODO:
50 * - add option for growing direction, when doing linear touching
51 * - add option for touch running time (or infinite loop?)
52 * - make it multithreaded with random access to test r/w mm_sem
53 */
54
55 char *TCID = "mem01";
56 int TST_TOTAL = 1;
57
58 static int m_opt = 0; /* memsize */
59 static char *m_copt;
60
61 static int r_opt = 0; /* random access versus linear */
62 static int v_opt = 0; /* verbose progress indication */
63
cleanup(void)64 static void cleanup(void)
65 {
66 tst_rmdir();
67 tst_exit();
68 }
69
setup(void)70 static void setup(void)
71 {
72 tst_sig(NOFORK, DEF_HANDLER, cleanup);
73
74 TEST_PAUSE;
75
76 tst_tmpdir();
77 }
78
help(void)79 static void help(void)
80 {
81 printf(" -m x size of malloc in MB (default from /proc/meminfo)\n");
82 printf(" -r random touching versus linear\n");
83 printf(" -v verbose progress indication\n");
84 }
85
86 /*
87 * return MemFree+SwapFree, from /proc/meminfo
88 * returned value is in bytes.
89 */
get_memsize(void)90 size_t get_memsize(void)
91 {
92 struct sysinfo info;
93 unsigned long long res;
94 unsigned long long freeswap;
95 unsigned long long freeram;
96 int ret;
97
98 ret = sysinfo(&info);
99 if (ret != 0) {
100 tst_resm(TFAIL,
101 "Could not retrieve memory information using sysinfo()");
102 cleanup();
103 }
104
105 freeram =
106 (unsigned long long)info.freeram *
107 (unsigned long long)info.mem_unit;
108 tst_resm(TINFO, "Free Mem:\t%llu Mb", freeram / 1024 / 1024);
109 res = freeram;
110
111 freeswap =
112 (unsigned long long)info.freeswap *
113 (unsigned long long)info.mem_unit;
114 tst_resm(TINFO, "Free Swap:\t%llu Mb", freeswap / 1024 / 1024);
115 res = res + freeswap;
116
117 tst_resm(TINFO, "Total Free:\t%llu Mb", res / 1024 / 1024);
118 #if defined(__s390__)
119 if (res > 1 * 1024 * 1024 * 1024)
120 res = 500 * 1024 * 1024; /* s390's unique 31bit architecture needs smaller default */
121 #elif defined(TST_ABI32)
122 if (res > 1 * 1024 * 1024 * 1024)
123 res = 1 * 1024 * 1024 * 1024;
124 #elif defined(TST_ABI64)
125 if (res > (unsigned long long)3 * 1024 * 1024 * 1024)
126 res = (unsigned long long)3 *1024 * 1024 * 1024;
127 #endif
128
129 /* Always reserve 16MB memory to avoid OOM Killer. */
130 res -= 16 * 1024 * 1024;
131 tst_resm(TINFO, "Total Tested:\t%llu Mb", res / 1024 / 1024);
132 return (size_t) res;
133 }
134
135 /*
136 * add the -m option whose parameter is the
137 * memory size (MB) to allocate.
138 */
139 option_t options[] = {
140 {"m:", &m_opt, &m_copt}
141 ,
142 {"r", &r_opt, NULL}
143 ,
144 {"v", &v_opt, NULL}
145 ,
146 {NULL, NULL, NULL}
147 };
148
main(int argc,char * argv[])149 int main(int argc, char *argv[])
150 {
151 size_t memsize = 0; /* at first in MB, limited to 4Gb on 32 bits */
152 int pagesize;
153
154 int i;
155 int lc;
156 char *p, *bigmalloc;
157 int loop_count; /* limited to 16Go on 32 bits systems */
158
159 pagesize = sysconf(_SC_PAGESIZE);
160
161 tst_parse_opts(argc, argv, options, help);
162
163 if (m_opt) {
164 memsize = (size_t) atoi(m_copt) * 1024 * 1024;
165
166 if (memsize < 1) {
167 tst_brkm(TBROK, cleanup, "Invalid arg for -m: %s",
168 m_copt);
169 }
170 }
171
172 if (r_opt)
173 srand(time(NULL));
174
175 setup();
176
177 for (lc = 0; TEST_LOOPING(lc); lc++) {
178
179 tst_count = 0;
180
181 if (!m_opt) {
182 /* find out by ourselves! */
183 memsize = get_memsize();
184 if (memsize < 1) {
185 tst_brkm(TBROK, cleanup,
186 "Unable to guess maxmemsize from /proc/meminfo");
187 }
188 }
189
190 /* Allocate (virtual) memory */
191 bigmalloc = p = malloc(memsize);
192
193 if (!p) {
194 tst_resm(TFAIL, "malloc - alloc of %zuMB failed",
195 memsize / 1024 / 1024);
196 cleanup();
197 }
198
199 /*
200 * Dirty all the pages, to force physical RAM allocation
201 * and exercise eventually the swapper
202 */
203 tst_resm(TINFO, "touching %zuMB of malloc'ed memory (%s)",
204 memsize / 1024 / 1024, r_opt ? "random" : "linear");
205
206 loop_count = memsize / pagesize;
207
208 for (i = 0; i < loop_count; i++) {
209 if (v_opt
210 && (i % (PROGRESS_LEAP * 1024 / pagesize) == 0)) {
211 printf(".");
212 fflush(stdout);
213 }
214 /*
215 * Make the page dirty,
216 * and make sure compiler won't optimize it away
217 * Touching more than one word per page is useless
218 * because of cache.
219 */
220 *(int *)p = 0xdeadbeef ^ i;
221
222 if (r_opt) {
223 p = bigmalloc +
224 (size_t) ((double)(memsize - sizeof(int)) *
225 rand() / (RAND_MAX + 1.0));
226 } else {
227 p += pagesize;
228 }
229 }
230
231 if (v_opt)
232 printf("\n");
233
234 /* This is not mandatory (except in a loop), but it exercise mm again */
235 free(bigmalloc);
236
237 /*
238 * seems that if the malloc'ed area was bad, we'd get SEGV (or kicked
239 * somehow by the OOM killer?), hence we can indicate a PASS.
240 */
241 tst_resm(TPASS, "malloc - alloc of %zuMB succeeded",
242 memsize / 1024 / 1024);
243
244 }
245
246 cleanup();
247
248 return 0;
249 }
250