1 /*
2 * Copyright (c) Linux Test Project, 2012-2020
3 * Copyright (C) 2012-2017 Red Hat, Inc.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See
13 * the GNU General Public License for more details.
14 *
15 * Descriptions:
16 *
17 * There are two tunables overcommit_memory and overcommit_ratio under
18 * /proc/sys/vm/, which can control memory overcommitment.
19 *
20 * The overcommit_memory contains a flag that enables memory
21 * overcommitment, it has three values:
22 * - When this flag is 0, the kernel attempts to estimate the amount
23 * of free memory left when userspace requests more memory.
24 * - When this flag is 1, the kernel pretends there is always enough
25 * memory until it actually runs out.
26 * - When this flag is 2, the kernel uses a "never overcommit" policy
27 * that attempts to prevent any overcommit of memory.
28 *
29 * The overcommit_ratio tunable defines the amount by which the kernel
30 * overextends its memory resources in the event that overcommit_memory
31 * is set to the value of 2. The value in this file represents a
32 * percentage added to the amount of actual RAM in a system when
33 * considering whether to grant a particular memory request.
34 * The general formula for this tunable is:
35 * CommitLimit = SwapTotal + MemTotal * overcommit_ratio
36 * CommitLimit, SwapTotal and MemTotal can read from /proc/meminfo.
37 *
38 * The program is designed to test the two tunables:
39 *
40 * When overcommit_memory = 0, allocatable memory can't overextend
41 * the amount of total memory:
42 * a. less than free_total: free_total / 2, alloc should pass.
43 * b. greater than sum_total: sum_total * 2, alloc should fail.
44 *
45 * When overcommit_memory = 1, it can alloc enough much memory, I
46 * choose the three cases:
47 * a. less than sum_total: sum_total / 2, alloc should pass
48 * b. equal to sum_total: sum_total, alloc should pass
49 * c. greater than sum_total: sum_total * 2, alloc should pass
50 * *note: sum_total = SwapTotal + MemTotal
51 *
52 * When overcommit_memory = 2, the total virtual address space on
53 * the system is limited to CommitLimit(Swap+RAM*overcommit_ratio)
54 * commit_left(allocatable memory) = CommitLimit - Committed_AS
55 * a. less than commit_left: commit_left / 2, alloc should pass
56 * b. greater than commit_left: commit_left * 2, alloc should fail
57 * c. overcommit limit: CommitLimit, alloc should fail
58 * *note: CommitLimit is the current overcommit limit.
59 * Committed_AS is the amount of memory that system has used.
60 * it couldn't choose 'equal to commit_left' as a case, because
61 * commit_left rely on Committed_AS, but the Committed_AS is not stable.
62 *
63 * References:
64 * - Documentation/sysctl/vm.txt
65 * - Documentation/vm/overcommit-accounting
66 */
67
68 #include <errno.h>
69 #include <stdio.h>
70 #include <stdlib.h>
71 #include <limits.h>
72 #include "lapi/abisize.h"
73 #include "mem.h"
74
75 #define DEFAULT_OVER_RATIO 50L
76 #define EXPECT_PASS 0
77 #define EXPECT_FAIL 1
78
79 static char *R_opt;
80 static struct tst_option options[] = {
81 {"R:", &R_opt, " -R n Percentage of overcommitting memory"},
82 {NULL, NULL, NULL}
83 };
84
85 static long old_overcommit_memory = -1;
86 static long old_overcommit_ratio = -1;
87 static long overcommit_ratio;
88 static long sum_total;
89 static long free_total;
90 static long commit_limit;
91 static long commit_left;
92
93 static int heavy_malloc(long size);
94 static void alloc_and_check(long size, int expect_result);
95 static void update_mem(void);
96 static void update_mem_commit(void);
97
setup(void)98 static void setup(void)
99 {
100 long mem_total, swap_total;
101 struct rlimit lim;
102
103 if (access(PATH_SYSVM "overcommit_memory", F_OK) == -1 ||
104 access(PATH_SYSVM "overcommit_ratio", F_OK) == -1)
105 tst_brk(TCONF, "system doesn't support overcommit_memory");
106
107 if (R_opt)
108 overcommit_ratio = SAFE_STRTOL(R_opt, 0, LONG_MAX);
109 else
110 overcommit_ratio = DEFAULT_OVER_RATIO;
111
112 old_overcommit_memory = get_sys_tune("overcommit_memory");
113 old_overcommit_ratio = get_sys_tune("overcommit_ratio");
114
115 mem_total = SAFE_READ_MEMINFO("MemTotal:");
116 tst_res(TINFO, "MemTotal is %ld kB", mem_total);
117 swap_total = SAFE_READ_MEMINFO("SwapTotal:");
118 tst_res(TINFO, "SwapTotal is %ld kB", swap_total);
119 sum_total = mem_total + swap_total;
120
121 commit_limit = SAFE_READ_MEMINFO("CommitLimit:");
122 tst_res(TINFO, "CommitLimit is %ld kB", commit_limit);
123
124 SAFE_GETRLIMIT(RLIMIT_AS, &lim);
125
126 if (lim.rlim_cur != RLIM_INFINITY) {
127 lim.rlim_cur = RLIM_INFINITY;
128 lim.rlim_max = RLIM_INFINITY;
129
130 tst_res(TINFO, "Increasing RLIM_AS to INFINITY");
131
132 SAFE_SETRLIMIT(RLIMIT_AS, &lim);
133 }
134
135 set_sys_tune("overcommit_ratio", overcommit_ratio, 1);
136 }
137
cleanup(void)138 static void cleanup(void)
139 {
140 if (old_overcommit_memory != -1)
141 set_sys_tune("overcommit_memory", old_overcommit_memory, 0);
142 if (old_overcommit_ratio != -1)
143 set_sys_tune("overcommit_ratio", old_overcommit_ratio, 0);
144 }
145
overcommit_memory_test(void)146 static void overcommit_memory_test(void)
147 {
148
149 #ifdef TST_ABI32
150 tst_brk(TCONF, "test is not designed for 32-bit system.");
151 #endif
152 /* start to test overcommit_memory=2 */
153 set_sys_tune("overcommit_memory", 2, 1);
154
155 update_mem_commit();
156 alloc_and_check(commit_left * 2, EXPECT_FAIL);
157 alloc_and_check(commit_limit, EXPECT_FAIL);
158 update_mem_commit();
159 alloc_and_check(commit_left / 2, EXPECT_PASS);
160
161 /* start to test overcommit_memory=0 */
162 set_sys_tune("overcommit_memory", 0, 1);
163
164 update_mem();
165 alloc_and_check(free_total / 2, EXPECT_PASS);
166 alloc_and_check(sum_total * 2, EXPECT_FAIL);
167
168 /* start to test overcommit_memory=1 */
169 set_sys_tune("overcommit_memory", 1, 1);
170
171 alloc_and_check(sum_total / 2, EXPECT_PASS);
172 alloc_and_check(sum_total, EXPECT_PASS);
173 alloc_and_check(sum_total * 2, EXPECT_PASS);
174
175 }
176
heavy_malloc(long size)177 static int heavy_malloc(long size)
178 {
179 char *p;
180
181 p = malloc(size * KB);
182 if (p != NULL) {
183 tst_res(TINFO, "malloc %ld kB successfully", size);
184 free(p);
185 return 0;
186 } else {
187 tst_res(TINFO, "malloc %ld kB failed", size);
188 return 1;
189 }
190 }
191
alloc_and_check(long size,int expect_result)192 static void alloc_and_check(long size, int expect_result)
193 {
194 int result;
195
196 /* try to alloc size kB memory */
197 result = heavy_malloc(size);
198
199 switch (expect_result) {
200 case EXPECT_PASS:
201 if (result == 0)
202 tst_res(TPASS, "alloc passed as expected");
203 else
204 tst_res(TFAIL, "alloc failed, expected to pass");
205 break;
206 case EXPECT_FAIL:
207 if (result != 0)
208 tst_res(TPASS, "alloc failed as expected");
209 else
210 tst_res(TFAIL, "alloc passed, expected to fail");
211 break;
212 default:
213 tst_brk(TBROK, "Invaild numbler parameter: %d",
214 expect_result);
215 }
216 }
217
update_mem(void)218 static void update_mem(void)
219 {
220 long mem_free, swap_free;
221
222 mem_free = SAFE_READ_MEMINFO("MemFree:");
223 swap_free = SAFE_READ_MEMINFO("SwapFree:");
224 free_total = mem_free + swap_free;
225 }
226
update_mem_commit(void)227 static void update_mem_commit(void)
228 {
229 long committed;
230
231 commit_limit = SAFE_READ_MEMINFO("CommitLimit:");
232 committed = SAFE_READ_MEMINFO("Committed_AS:");
233 commit_left = commit_limit - committed;
234
235 if (commit_left < 0) {
236 tst_res(TINFO, "CommitLimit is %ld, Committed_AS is %ld",
237 commit_limit, committed);
238
239 if (overcommit_ratio > old_overcommit_ratio) {
240 tst_brk(TBROK, "Unexpected error: "
241 "CommitLimit < Committed_AS");
242 }
243
244 tst_brk(TCONF, "Specified overcommit_ratio %ld <= default %ld, "
245 "so it's possible for CommitLimit < Committed_AS and skip test",
246 overcommit_ratio, old_overcommit_ratio);
247 }
248 }
249
250 static struct tst_test test = {
251 .needs_root = 1,
252 .options = options,
253 .setup = setup,
254 .cleanup = cleanup,
255 .test_all = overcommit_memory_test,
256 };
257