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