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 78static char *R_opt; 79static struct tst_option options[] = { 80 {"R:", &R_opt, " -R n Percentage of overcommitting memory"}, 81 {NULL, NULL, NULL} 82}; 83 84static long old_overcommit_memory; 85static long old_overcommit_ratio; 86static long overcommit_ratio; 87static long sum_total; 88static long free_total; 89static long commit_limit; 90static long commit_left; 91 92static int heavy_malloc(long size); 93static void alloc_and_check(long size, int expect_result); 94static void update_mem(void); 95 96static void setup(void) 97{ 98 long mem_total, swap_total; 99 struct rlimit lim; 100 101 if (access(PATH_SYSVM "overcommit_memory", F_OK) == -1 || 102 access(PATH_SYSVM "overcommit_ratio", F_OK) == -1) 103 tst_brk(TCONF, "The system " 104 "can't support to test %s", TCID); 105 106 if (R_opt) 107 overcommit_ratio = SAFE_STRTOL(R_opt, 0, LONG_MAX); 108 else 109 overcommit_ratio = DEFAULT_OVER_RATIO; 110 111 old_overcommit_memory = get_sys_tune("overcommit_memory"); 112 old_overcommit_ratio = get_sys_tune("overcommit_ratio"); 113 114 mem_total = SAFE_READ_MEMINFO("MemTotal:"); 115 tst_res(TINFO, "MemTotal is %ld kB", mem_total); 116 swap_total = SAFE_READ_MEMINFO("SwapTotal:"); 117 tst_res(TINFO, "SwapTotal is %ld kB", swap_total); 118 sum_total = mem_total + swap_total; 119 120 commit_limit = SAFE_READ_MEMINFO("CommitLimit:"); 121 tst_res(TINFO, "CommitLimit is %ld kB", commit_limit); 122 123 SAFE_GETRLIMIT(RLIMIT_AS, &lim); 124 125 if (lim.rlim_cur != RLIM_INFINITY) { 126 lim.rlim_cur = RLIM_INFINITY; 127 lim.rlim_max = RLIM_INFINITY; 128 129 tst_res(TINFO, "Increasing RLIM_AS to INFINITY"); 130 131 SAFE_SETRLIMIT(RLIMIT_AS, &lim); 132 } 133 134 set_sys_tune("overcommit_ratio", overcommit_ratio, 1); 135} 136 137static void cleanup(void) 138{ 139 set_sys_tune("overcommit_memory", old_overcommit_memory, 0); 140 set_sys_tune("overcommit_ratio", old_overcommit_ratio, 0); 141} 142 143static void overcommit_memory_test(void) 144{ 145 146#if __WORDSIZE == 32 147 tst_brk(TCONF, "test is not designed for 32-bit system."); 148#endif 149 /* start to test overcommit_memory=2 */ 150 set_sys_tune("overcommit_memory", 2, 1); 151 152 update_mem(); 153 alloc_and_check(commit_left * 2, EXPECT_FAIL); 154 alloc_and_check(commit_limit, EXPECT_FAIL); 155 update_mem(); 156 alloc_and_check(commit_left / 2, EXPECT_PASS); 157 158 /* start to test overcommit_memory=0 */ 159 set_sys_tune("overcommit_memory", 0, 1); 160 161 update_mem(); 162 alloc_and_check(free_total / 2, EXPECT_PASS); 163 update_mem(); 164 alloc_and_check(free_total * 2, EXPECT_FAIL); 165 alloc_and_check(sum_total, EXPECT_FAIL); 166 167 /* start to test overcommit_memory=1 */ 168 set_sys_tune("overcommit_memory", 1, 1); 169 170 alloc_and_check(sum_total / 2, EXPECT_PASS); 171 alloc_and_check(sum_total, EXPECT_PASS); 172 alloc_and_check(sum_total * 2, EXPECT_PASS); 173 174} 175 176static int heavy_malloc(long size) 177{ 178 char *p; 179 180 p = malloc(size * KB); 181 if (p != NULL) { 182 tst_res(TINFO, "malloc %ld kB successfully", size); 183 free(p); 184 return 0; 185 } else { 186 tst_res(TINFO, "malloc %ld kB failed", size); 187 return 1; 188 } 189} 190 191static void alloc_and_check(long size, int expect_result) 192{ 193 int result; 194 195 /* try to alloc size kB memory */ 196 result = heavy_malloc(size); 197 198 switch (expect_result) { 199 case EXPECT_PASS: 200 if (result == 0) 201 tst_res(TPASS, "alloc passed as expected"); 202 else 203 tst_res(TFAIL, "alloc failed, expected to pass"); 204 break; 205 case EXPECT_FAIL: 206 if (result != 0) 207 tst_res(TPASS, "alloc failed as expected"); 208 else 209 tst_res(TFAIL, "alloc passed, expected to fail"); 210 break; 211 default: 212 tst_brk(TBROK, "Invaild numbler parameter: %d", 213 expect_result); 214 } 215} 216 217static void update_mem(void) 218{ 219 long mem_free, swap_free; 220 long committed; 221 222 mem_free = SAFE_READ_MEMINFO("MemFree:"); 223 swap_free = SAFE_READ_MEMINFO("SwapFree:"); 224 free_total = mem_free + swap_free; 225 commit_limit = SAFE_READ_MEMINFO("CommitLimit:"); 226 227 if (get_sys_tune("overcommit_memory") == 2) { 228 committed = SAFE_READ_MEMINFO("Committed_AS:"); 229 commit_left = commit_limit - committed; 230 231 if (commit_left < 0) { 232 tst_res(TINFO, "CommitLimit is %ld, Committed_AS" 233 " is %ld", commit_limit, committed); 234 tst_brk(TBROK, "Unexpected error: " 235 "CommitLimit < Committed_AS"); 236 } 237 } 238} 239 240static struct tst_test test = { 241 .needs_root = 1, 242 .options = options, 243 .setup = setup, 244 .cleanup = cleanup, 245 .test_all = overcommit_memory_test, 246}; 247