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