• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2012-2017  Red Hat, Inc.
3  *
4  * This program is free software;  you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY;  without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See
12  * the GNU General Public License for more details.
13  *
14  * Descriptions:
15  *
16  * There are two tunables overcommit_memory and overcommit_ratio under
17  * /proc/sys/vm/, which can control memory overcommitment.
18  *
19  * The overcommit_memory contains a flag that enables memory
20  * overcommitment, it has three values:
21  * - When this flag is 0, the kernel attempts to estimate the amount
22  *   of free memory left when userspace requests more memory.
23  * - When this flag is 1, the kernel pretends there is always enough
24  *   memory until it actually runs out.
25  * - When this flag is 2, the kernel uses a "never overcommit" policy
26  *   that attempts to prevent any overcommit of memory.
27  *
28  * The overcommit_ratio tunable defines the amount by which the kernel
29  * overextends its memory resources in the event that overcommit_memory
30  * is set to the value of 2. The value in this file represents a
31  * percentage added to the amount of actual RAM in a system when
32  * considering whether to grant a particular memory request.
33  * The general formula for this tunable is:
34  * CommitLimit = SwapTotal + MemTotal * overcommit_ratio
35  * CommitLimit, SwapTotal and MemTotal can read from /proc/meminfo.
36  *
37  * The program is designed to test the two tunables:
38  *
39  * When overcommit_memory = 0, allocatable memory can't overextends
40  * the amount of free memory. I choose the three cases:
41  * a. less than free_total:    free_total / 2, alloc should pass.
42  * b. greater than free_total: free_total * 2, alloc should fail.
43  * c. equal to sum_total:      sum_tatal,      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 "mem.h"
73 
74 #define DEFAULT_OVER_RATIO	50L
75 #define EXPECT_PASS		0
76 #define EXPECT_FAIL		1
77 
78 static char *R_opt;
79 static struct tst_option options[] = {
80 	{"R:", &R_opt, "  -R n    Percentage of overcommitting memory"},
81 	{NULL, NULL, NULL}
82 };
83 
84 static long old_overcommit_memory = -1;
85 static long old_overcommit_ratio = -1;
86 static long overcommit_ratio;
87 static long sum_total;
88 static long free_total;
89 static long commit_limit;
90 static long commit_left;
91 
92 static int heavy_malloc(long size);
93 static void alloc_and_check(long size, int expect_result);
94 static void update_mem(void);
95 static void update_mem_commit(void);
96 
setup(void)97 static void setup(void)
98 {
99 	long mem_total, swap_total;
100 	struct rlimit lim;
101 
102 	if (access(PATH_SYSVM "overcommit_memory", F_OK) == -1 ||
103 	    access(PATH_SYSVM "overcommit_ratio", F_OK) == -1)
104 		tst_brk(TCONF, "The system "
105 			 "can't support to test %s", TCID);
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 #if __WORDSIZE == 32
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 	update_mem();
167 	alloc_and_check(free_total * 2, EXPECT_FAIL);
168 	alloc_and_check(sum_total, EXPECT_FAIL);
169 
170 	/* start to test overcommit_memory=1 */
171 	set_sys_tune("overcommit_memory", 1, 1);
172 
173 	alloc_and_check(sum_total / 2, EXPECT_PASS);
174 	alloc_and_check(sum_total, EXPECT_PASS);
175 	alloc_and_check(sum_total * 2, EXPECT_PASS);
176 
177 }
178 
heavy_malloc(long size)179 static int heavy_malloc(long size)
180 {
181 	char *p;
182 
183 	p = malloc(size * KB);
184 	if (p != NULL) {
185 		tst_res(TINFO, "malloc %ld kB successfully", size);
186 		free(p);
187 		return 0;
188 	} else {
189 		tst_res(TINFO, "malloc %ld kB failed", size);
190 		return 1;
191 	}
192 }
193 
alloc_and_check(long size,int expect_result)194 static void alloc_and_check(long size, int expect_result)
195 {
196 	int result;
197 
198 	/* try to alloc size kB memory */
199 	result = heavy_malloc(size);
200 
201 	switch (expect_result) {
202 	case EXPECT_PASS:
203 		if (result == 0)
204 			tst_res(TPASS, "alloc passed as expected");
205 		else
206 			tst_res(TFAIL, "alloc failed, expected to pass");
207 		break;
208 	case EXPECT_FAIL:
209 		if (result != 0)
210 			tst_res(TPASS, "alloc failed as expected");
211 		else
212 			tst_res(TFAIL, "alloc passed, expected to fail");
213 		break;
214 	default:
215 		tst_brk(TBROK, "Invaild numbler parameter: %d",
216 			 expect_result);
217 	}
218 }
219 
update_mem(void)220 static void update_mem(void)
221 {
222 	long mem_free, swap_free;
223 
224 	mem_free = SAFE_READ_MEMINFO("MemFree:");
225 	swap_free = SAFE_READ_MEMINFO("SwapFree:");
226 	free_total = mem_free + swap_free;
227 }
228 
update_mem_commit(void)229 static void update_mem_commit(void)
230 {
231 	long committed;
232 
233 	commit_limit = SAFE_READ_MEMINFO("CommitLimit:");
234 	committed = SAFE_READ_MEMINFO("Committed_AS:");
235 	commit_left = commit_limit - committed;
236 
237 	if (commit_left < 0) {
238 		tst_res(TINFO, "CommitLimit is %ld, Committed_AS is %ld",
239 			commit_limit, committed);
240 
241 		if (overcommit_ratio > old_overcommit_ratio) {
242 			tst_brk(TBROK, "Unexpected error: "
243 				"CommitLimit < Committed_AS");
244 		}
245 
246 		tst_brk(TCONF, "Specified overcommit_ratio %ld <= default %ld, "
247 			"so it's possible for CommitLimit < Committed_AS and skip test",
248 			overcommit_ratio, old_overcommit_ratio);
249 	}
250 }
251 
252 static struct tst_test test = {
253 	.needs_root = 1,
254 	.options = options,
255 	.setup = setup,
256 	.cleanup = cleanup,
257 	.test_all = overcommit_memory_test,
258 };
259