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  * Description:
15  *
16  * The case is designed to test min_free_kbytes tunable.
17  *
18  * The tune is used to control free memory, and system always
19  * reserve min_free_kbytes memory at least.
20  *
21  * Since the tune is not too large or too little, which will
22  * lead to the system hang, so I choose two cases, and test them
23  * on all overcommit_memory policy, at the same time, compare
24  * the current free memory with the tunable value repeatedly.
25  *
26  * a) default min_free_kbytes with all overcommit memory policy
27  * b) 2x default value with all overcommit memory policy
28  * c) 5% of MemFree or %2 MemTotal with all overcommit memory policy
29  */
30 
31 #include <sys/wait.h>
32 #include <errno.h>
33 #include <fcntl.h>
34 #include <signal.h>
35 #include <stdio.h>
36 #include <stdlib.h>
37 #include "lapi/abisize.h"
38 #include "mem.h"
39 
40 #define MAP_SIZE (1UL<<20)
41 
42 volatile int end;
43 static long default_tune = -1;
44 static long orig_overcommit = -1;
45 static unsigned long total_mem;
46 
47 static void test_tune(unsigned long overcommit_policy);
48 static int eatup_mem(unsigned long overcommit_policy);
49 static void check_monitor(void);
50 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED);
51 
min_free_kbytes_test(void)52 static void min_free_kbytes_test(void)
53 {
54 	int pid, status;
55 	struct sigaction sa;
56 
57 	sa.sa_handler = sighandler;
58 	if (sigemptyset(&sa.sa_mask) < 0)
59 		tst_brk(TBROK | TERRNO, "sigemptyset");
60 	sa.sa_flags = 0;
61 	if (sigaction(SIGUSR1, &sa, NULL) < 0)
62 		tst_brk(TBROK | TERRNO, "sigaction");
63 
64 	pid = SAFE_FORK();
65 	if (pid == 0) {
66 		/* startup the check monitor */
67 		check_monitor();
68 		exit(0);
69 	}
70 
71 	test_tune(2);
72 	test_tune(0);
73 	test_tune(1);
74 
75 	SAFE_KILL(pid, SIGUSR1);
76 	SAFE_WAITPID(pid, &status, WUNTRACED | WCONTINUED);
77 
78 	if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
79 		tst_res(TFAIL,
80 			 "check_monitor child exit with status: %d", status);
81 
82 	tst_res(TPASS, "min_free_kbytes test pass");
83 }
84 
test_tune(unsigned long overcommit_policy)85 static void test_tune(unsigned long overcommit_policy)
86 {
87 	int status;
88 	int pid[3];
89 	int ret, i;
90 	unsigned long tune, memfree, memtotal;
91 
92 	set_sys_tune("overcommit_memory", overcommit_policy, 1);
93 
94 	for (i = 0; i < 3; i++) {
95 		/* case1 */
96 		if (i == 0)
97 			set_sys_tune("min_free_kbytes", default_tune, 1);
98 		/* case2 */
99 		else if (i == 1) {
100 			set_sys_tune("min_free_kbytes", 2 * default_tune, 1);
101 			/* case3 */
102 		} else {
103 			memfree = SAFE_READ_MEMINFO("MemFree:");
104 			memtotal = SAFE_READ_MEMINFO("MemTotal:");
105 			tune = memfree / 20;
106 			if (tune > (memtotal / 50))
107 				tune = memtotal / 50;
108 
109 			set_sys_tune("min_free_kbytes", tune, 1);
110 		}
111 
112 		fflush(stdout);
113 		switch (pid[i] = fork()) {
114 		case -1:
115 			tst_brk(TBROK | TERRNO, "fork");
116 		case 0:
117 			ret = eatup_mem(overcommit_policy);
118 			exit(ret);
119 		}
120 
121 		SAFE_WAITPID(pid[i], &status, WUNTRACED | WCONTINUED);
122 
123 		if (overcommit_policy == 2) {
124 			if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
125 				tst_res(TFAIL,
126 					 "child unexpectedly failed: %d",
127 					 status);
128 		} else if (overcommit_policy == 1) {
129 			if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGKILL)
130 #ifdef TST_ABI32
131 			{
132 				if (total_mem < 3145728UL)
133 #endif
134 					tst_res(TFAIL,
135 						 "child unexpectedly failed: %d",
136 						 status);
137 #ifdef TST_ABI32
138 				/* in 32-bit system, a process allocate about 3Gb memory at most */
139 				else
140 					tst_res(TINFO, "Child can't allocate "
141 						 ">3Gb memory in 32bit system");
142 			}
143 #endif
144 		} else {
145 			if (WIFEXITED(status)) {
146 				if (WEXITSTATUS(status) != 0) {
147 					tst_res(TFAIL, "child unexpectedly "
148 						 "failed: %d", status);
149 				}
150 			} else if (!WIFSIGNALED(status) ||
151 				   WTERMSIG(status) != SIGKILL) {
152 				tst_res(TFAIL,
153 					 "child unexpectedly failed: %d",
154 					 status);
155 			}
156 		}
157 	}
158 }
159 
eatup_mem(unsigned long overcommit_policy)160 static int eatup_mem(unsigned long overcommit_policy)
161 {
162 	int ret = 0;
163 	unsigned long memfree;
164 	void *addrs;
165 
166 	memfree = SAFE_READ_MEMINFO("MemFree:");
167 	printf("memfree is %lu kB before eatup mem\n", memfree);
168 	while (1) {
169 		addrs = mmap(NULL, MAP_SIZE, PROT_READ | PROT_WRITE,
170 			     MAP_ANONYMOUS | MAP_PRIVATE, -1, 0);
171 		if (addrs == MAP_FAILED) {
172 			if (overcommit_policy != 1 && errno != ENOMEM) {
173 				perror("mmap");
174 				ret = -1;
175 			}
176 			break;
177 		}
178 		memset(addrs, 1, MAP_SIZE);
179 	}
180 	memfree = SAFE_READ_MEMINFO("MemFree:");
181 	printf("memfree is %lu kB after eatup mem\n", memfree);
182 
183 	return ret;
184 }
185 
check_monitor(void)186 static void check_monitor(void)
187 {
188 	unsigned long tune;
189 	unsigned long memfree;
190 
191 	while (end) {
192 		memfree = SAFE_READ_MEMINFO("MemFree:");
193 		tune = get_sys_tune("min_free_kbytes");
194 
195 		if (memfree < tune) {
196 			tst_res(TINFO, "MemFree is %lu kB, "
197 				 "min_free_kbytes is %lu kB", memfree, tune);
198 			tst_res(TFAIL, "MemFree < min_free_kbytes");
199 		}
200 
201 		sleep(2);
202 	}
203 }
204 
sighandler(int signo LTP_ATTRIBUTE_UNUSED)205 static void sighandler(int signo LTP_ATTRIBUTE_UNUSED)
206 {
207 	end = 1;
208 }
209 
setup(void)210 static void setup(void)
211 {
212 	if (get_sys_tune("panic_on_oom")) {
213 		tst_brk(TCONF,
214 			"panic_on_oom is set, disable it to run these testcases");
215 	}
216 
217 	total_mem = SAFE_READ_MEMINFO("MemTotal:") + SAFE_READ_MEMINFO("SwapTotal:");
218 
219 	default_tune = get_sys_tune("min_free_kbytes");
220 	orig_overcommit = get_sys_tune("overcommit_memory");
221 }
222 
cleanup(void)223 static void cleanup(void)
224 {
225 	if (default_tune != -1)
226 		set_sys_tune("min_free_kbytes", default_tune, 0);
227 	if (orig_overcommit != -1)
228 		set_sys_tune("overcommit_memory", orig_overcommit, 0);
229 }
230 
231 static struct tst_test test = {
232 	.needs_root = 1,
233 	.forks_child = 1,
234 	.timeout = -1,
235 	.setup = setup,
236 	.cleanup = cleanup,
237 	.test_all = min_free_kbytes_test,
238 };
239