xref: /aosp_15_r20/external/ltp/testcases/kernel/controllers/memcg/memcontrol04.c (revision 49cdfc7efb34551c7342be41a7384b9c40d7cab7)
1*49cdfc7eSAndroid Build Coastguard Worker // SPDX-License-Identifier: GPL-2.0-only
2*49cdfc7eSAndroid Build Coastguard Worker /*\
3*49cdfc7eSAndroid Build Coastguard Worker  *
4*49cdfc7eSAndroid Build Coastguard Worker  * [Description]
5*49cdfc7eSAndroid Build Coastguard Worker  *
6*49cdfc7eSAndroid Build Coastguard Worker  * Conversion of the forth kself test in cgroup/test_memcontrol.c.
7*49cdfc7eSAndroid Build Coastguard Worker  *
8*49cdfc7eSAndroid Build Coastguard Worker  * Original description:
9*49cdfc7eSAndroid Build Coastguard Worker  * "First, this test creates the following hierarchy:
10*49cdfc7eSAndroid Build Coastguard Worker  * A       memory.low = 50M,  memory.max = 200M
11*49cdfc7eSAndroid Build Coastguard Worker  * A/B     memory.low = 50M,  memory.current = 50M
12*49cdfc7eSAndroid Build Coastguard Worker  * A/B/C   memory.low = 75M,  memory.current = 50M
13*49cdfc7eSAndroid Build Coastguard Worker  * A/B/D   memory.low = 25M,  memory.current = 50M
14*49cdfc7eSAndroid Build Coastguard Worker  * A/B/E   memory.low = 500M, memory.current = 0
15*49cdfc7eSAndroid Build Coastguard Worker  * A/B/F   memory.low = 0,    memory.current = 50M
16*49cdfc7eSAndroid Build Coastguard Worker  *
17*49cdfc7eSAndroid Build Coastguard Worker  * Usages are pagecache
18*49cdfc7eSAndroid Build Coastguard Worker  * Then it creates A/G and creates a significant
19*49cdfc7eSAndroid Build Coastguard Worker  * memory pressure in it.
20*49cdfc7eSAndroid Build Coastguard Worker  *
21*49cdfc7eSAndroid Build Coastguard Worker  * A/B    memory.current ~= 50M
22*49cdfc7eSAndroid Build Coastguard Worker  * A/B/C  memory.current ~= 33M
23*49cdfc7eSAndroid Build Coastguard Worker  * A/B/D  memory.current ~= 17M
24*49cdfc7eSAndroid Build Coastguard Worker  * A/B/E  memory.current ~= 0
25*49cdfc7eSAndroid Build Coastguard Worker  *
26*49cdfc7eSAndroid Build Coastguard Worker  * After that it tries to allocate more than there is unprotected
27*49cdfc7eSAndroid Build Coastguard Worker  * memory in A available, and checks that memory.low protects
28*49cdfc7eSAndroid Build Coastguard Worker  * pagecache even in this case."
29*49cdfc7eSAndroid Build Coastguard Worker  *
30*49cdfc7eSAndroid Build Coastguard Worker  * The closest thing to memory.low on V1 is soft_limit_in_bytes which
31*49cdfc7eSAndroid Build Coastguard Worker  * uses a different mechanism and has different semantics. So we only
32*49cdfc7eSAndroid Build Coastguard Worker  * test on V2 like the selftest. We do test on more file systems, but
33*49cdfc7eSAndroid Build Coastguard Worker  * not tempfs becaue it can't evict the page cache without swap. Also
34*49cdfc7eSAndroid Build Coastguard Worker  * we avoid filesystems which allocate extra memory for buffer heads.
35*49cdfc7eSAndroid Build Coastguard Worker  *
36*49cdfc7eSAndroid Build Coastguard Worker  * The tolerances have been increased from the self tests.
37*49cdfc7eSAndroid Build Coastguard Worker  */
38*49cdfc7eSAndroid Build Coastguard Worker 
39*49cdfc7eSAndroid Build Coastguard Worker #define _GNU_SOURCE
40*49cdfc7eSAndroid Build Coastguard Worker 
41*49cdfc7eSAndroid Build Coastguard Worker #include <inttypes.h>
42*49cdfc7eSAndroid Build Coastguard Worker 
43*49cdfc7eSAndroid Build Coastguard Worker #include "memcontrol_common.h"
44*49cdfc7eSAndroid Build Coastguard Worker 
45*49cdfc7eSAndroid Build Coastguard Worker #define TMPDIR "mntdir"
46*49cdfc7eSAndroid Build Coastguard Worker 
47*49cdfc7eSAndroid Build Coastguard Worker static struct tst_cg_group *trunk_cg[3];
48*49cdfc7eSAndroid Build Coastguard Worker static struct tst_cg_group *leaf_cg[4];
49*49cdfc7eSAndroid Build Coastguard Worker static int fd = -1;
50*49cdfc7eSAndroid Build Coastguard Worker 
51*49cdfc7eSAndroid Build Coastguard Worker enum checkpoints {
52*49cdfc7eSAndroid Build Coastguard Worker 	CHILD_IDLE
53*49cdfc7eSAndroid Build Coastguard Worker };
54*49cdfc7eSAndroid Build Coastguard Worker 
55*49cdfc7eSAndroid Build Coastguard Worker enum trunk_cg {
56*49cdfc7eSAndroid Build Coastguard Worker 	A,
57*49cdfc7eSAndroid Build Coastguard Worker 	B,
58*49cdfc7eSAndroid Build Coastguard Worker 	G
59*49cdfc7eSAndroid Build Coastguard Worker };
60*49cdfc7eSAndroid Build Coastguard Worker 
61*49cdfc7eSAndroid Build Coastguard Worker enum leaf_cg {
62*49cdfc7eSAndroid Build Coastguard Worker 	C,
63*49cdfc7eSAndroid Build Coastguard Worker 	D,
64*49cdfc7eSAndroid Build Coastguard Worker 	E,
65*49cdfc7eSAndroid Build Coastguard Worker 	F
66*49cdfc7eSAndroid Build Coastguard Worker };
67*49cdfc7eSAndroid Build Coastguard Worker 
cleanup_sub_groups(void)68*49cdfc7eSAndroid Build Coastguard Worker static void cleanup_sub_groups(void)
69*49cdfc7eSAndroid Build Coastguard Worker {
70*49cdfc7eSAndroid Build Coastguard Worker 	size_t i;
71*49cdfc7eSAndroid Build Coastguard Worker 
72*49cdfc7eSAndroid Build Coastguard Worker 	for (i = ARRAY_SIZE(leaf_cg); i > 0; i--) {
73*49cdfc7eSAndroid Build Coastguard Worker 		if (!leaf_cg[i - 1])
74*49cdfc7eSAndroid Build Coastguard Worker 			continue;
75*49cdfc7eSAndroid Build Coastguard Worker 
76*49cdfc7eSAndroid Build Coastguard Worker 		leaf_cg[i - 1] = tst_cg_group_rm(leaf_cg[i - 1]);
77*49cdfc7eSAndroid Build Coastguard Worker 	}
78*49cdfc7eSAndroid Build Coastguard Worker 
79*49cdfc7eSAndroid Build Coastguard Worker 	for (i = ARRAY_SIZE(trunk_cg); i > 0; i--) {
80*49cdfc7eSAndroid Build Coastguard Worker 		if (!trunk_cg[i - 1])
81*49cdfc7eSAndroid Build Coastguard Worker 			continue;
82*49cdfc7eSAndroid Build Coastguard Worker 
83*49cdfc7eSAndroid Build Coastguard Worker 		trunk_cg[i - 1] = tst_cg_group_rm(trunk_cg[i - 1]);
84*49cdfc7eSAndroid Build Coastguard Worker 	}
85*49cdfc7eSAndroid Build Coastguard Worker }
86*49cdfc7eSAndroid Build Coastguard Worker 
alloc_anon_in_child(const struct tst_cg_group * const cg,const size_t size)87*49cdfc7eSAndroid Build Coastguard Worker static void alloc_anon_in_child(const struct tst_cg_group *const cg,
88*49cdfc7eSAndroid Build Coastguard Worker 				const size_t size)
89*49cdfc7eSAndroid Build Coastguard Worker {
90*49cdfc7eSAndroid Build Coastguard Worker 	const pid_t pid = SAFE_FORK();
91*49cdfc7eSAndroid Build Coastguard Worker 
92*49cdfc7eSAndroid Build Coastguard Worker 	if (pid) {
93*49cdfc7eSAndroid Build Coastguard Worker 		tst_reap_children();
94*49cdfc7eSAndroid Build Coastguard Worker 		return;
95*49cdfc7eSAndroid Build Coastguard Worker 	}
96*49cdfc7eSAndroid Build Coastguard Worker 
97*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid());
98*49cdfc7eSAndroid Build Coastguard Worker 
99*49cdfc7eSAndroid Build Coastguard Worker 	tst_res(TINFO, "Child %d in %s: Allocating anon: %"PRIdPTR,
100*49cdfc7eSAndroid Build Coastguard Worker 		getpid(), tst_cg_group_name(cg), size);
101*49cdfc7eSAndroid Build Coastguard Worker 	alloc_anon(size);
102*49cdfc7eSAndroid Build Coastguard Worker 
103*49cdfc7eSAndroid Build Coastguard Worker 	exit(0);
104*49cdfc7eSAndroid Build Coastguard Worker }
105*49cdfc7eSAndroid Build Coastguard Worker 
alloc_pagecache_in_child(const struct tst_cg_group * const cg,const size_t size)106*49cdfc7eSAndroid Build Coastguard Worker static void alloc_pagecache_in_child(const struct tst_cg_group *const cg,
107*49cdfc7eSAndroid Build Coastguard Worker 				     const size_t size)
108*49cdfc7eSAndroid Build Coastguard Worker {
109*49cdfc7eSAndroid Build Coastguard Worker 	const pid_t pid = SAFE_FORK();
110*49cdfc7eSAndroid Build Coastguard Worker 
111*49cdfc7eSAndroid Build Coastguard Worker 	if (pid) {
112*49cdfc7eSAndroid Build Coastguard Worker 		tst_reap_children();
113*49cdfc7eSAndroid Build Coastguard Worker 		return;
114*49cdfc7eSAndroid Build Coastguard Worker 	}
115*49cdfc7eSAndroid Build Coastguard Worker 
116*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid());
117*49cdfc7eSAndroid Build Coastguard Worker 
118*49cdfc7eSAndroid Build Coastguard Worker 	tst_res(TINFO, "Child %d in %s: Allocating pagecache: %"PRIdPTR,
119*49cdfc7eSAndroid Build Coastguard Worker 		getpid(), tst_cg_group_name(cg), size);
120*49cdfc7eSAndroid Build Coastguard Worker 	alloc_pagecache(fd, size);
121*49cdfc7eSAndroid Build Coastguard Worker 
122*49cdfc7eSAndroid Build Coastguard Worker 	exit(0);
123*49cdfc7eSAndroid Build Coastguard Worker }
124*49cdfc7eSAndroid Build Coastguard Worker 
test_memcg_low(void)125*49cdfc7eSAndroid Build Coastguard Worker static void test_memcg_low(void)
126*49cdfc7eSAndroid Build Coastguard Worker {
127*49cdfc7eSAndroid Build Coastguard Worker 	long c[4];
128*49cdfc7eSAndroid Build Coastguard Worker 	unsigned int i;
129*49cdfc7eSAndroid Build Coastguard Worker 
130*49cdfc7eSAndroid Build Coastguard Worker 	fd = SAFE_OPEN(TMPDIR"/tmpfile", O_RDWR | O_CREAT, 0600);
131*49cdfc7eSAndroid Build Coastguard Worker 	trunk_cg[A] = tst_cg_group_mk(tst_cg, "trunk_A");
132*49cdfc7eSAndroid Build Coastguard Worker 
133*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_SCANF(trunk_cg[A], "memory.low", "%ld", c);
134*49cdfc7eSAndroid Build Coastguard Worker 	if (c[0]) {
135*49cdfc7eSAndroid Build Coastguard Worker 		tst_brk(TCONF,
136*49cdfc7eSAndroid Build Coastguard Worker 			"memory.low already set to %ld on parent group", c[0]);
137*49cdfc7eSAndroid Build Coastguard Worker 	}
138*49cdfc7eSAndroid Build Coastguard Worker 
139*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[A], "cgroup.subtree_control", "+memory");
140*49cdfc7eSAndroid Build Coastguard Worker 
141*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[A], "memory.max", "200M");
142*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[A], "memory.swap.max", "0");
143*49cdfc7eSAndroid Build Coastguard Worker 
144*49cdfc7eSAndroid Build Coastguard Worker 	trunk_cg[B] = tst_cg_group_mk(trunk_cg[A], "trunk_B");
145*49cdfc7eSAndroid Build Coastguard Worker 
146*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[B], "cgroup.subtree_control", "+memory");
147*49cdfc7eSAndroid Build Coastguard Worker 
148*49cdfc7eSAndroid Build Coastguard Worker 	trunk_cg[G] = tst_cg_group_mk(trunk_cg[A], "trunk_G");
149*49cdfc7eSAndroid Build Coastguard Worker 
150*49cdfc7eSAndroid Build Coastguard Worker 	for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
151*49cdfc7eSAndroid Build Coastguard Worker 		leaf_cg[i] = tst_cg_group_mk(trunk_cg[B],
152*49cdfc7eSAndroid Build Coastguard Worker 						 "leaf_%c", 'C' + i);
153*49cdfc7eSAndroid Build Coastguard Worker 
154*49cdfc7eSAndroid Build Coastguard Worker 		if (i == E)
155*49cdfc7eSAndroid Build Coastguard Worker 			continue;
156*49cdfc7eSAndroid Build Coastguard Worker 
157*49cdfc7eSAndroid Build Coastguard Worker 		alloc_pagecache_in_child(leaf_cg[i], MB(50));
158*49cdfc7eSAndroid Build Coastguard Worker 	}
159*49cdfc7eSAndroid Build Coastguard Worker 
160*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[A], "memory.low", "50M");
161*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(trunk_cg[B], "memory.low", "50M");
162*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(leaf_cg[C], "memory.low", "75M");
163*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(leaf_cg[D], "memory.low", "25M");
164*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(leaf_cg[E], "memory.low", "500M");
165*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_PRINT(leaf_cg[F], "memory.low", "0");
166*49cdfc7eSAndroid Build Coastguard Worker 
167*49cdfc7eSAndroid Build Coastguard Worker 	alloc_anon_in_child(trunk_cg[G], MB(148));
168*49cdfc7eSAndroid Build Coastguard Worker 
169*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CG_SCANF(trunk_cg[B], "memory.current", "%ld", c);
170*49cdfc7eSAndroid Build Coastguard Worker 	TST_EXP_EXPR(values_close(c[0], MB(50), 5),
171*49cdfc7eSAndroid Build Coastguard Worker 		     "(A/B memory.current=%ld) ~= %d", c[0], MB(50));
172*49cdfc7eSAndroid Build Coastguard Worker 
173*49cdfc7eSAndroid Build Coastguard Worker 	for (i = 0; i < ARRAY_SIZE(leaf_cg); i++)
174*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CG_SCANF(leaf_cg[i], "memory.current", "%ld", c + i);
175*49cdfc7eSAndroid Build Coastguard Worker 
176*49cdfc7eSAndroid Build Coastguard Worker 	TST_EXP_EXPR(values_close(c[0], MB(33), 20),
177*49cdfc7eSAndroid Build Coastguard Worker 		     "(A/B/C memory.current=%ld) ~= %d", c[C], MB(33));
178*49cdfc7eSAndroid Build Coastguard Worker 	TST_EXP_EXPR(values_close(c[1], MB(17), 20),
179*49cdfc7eSAndroid Build Coastguard Worker 		     "(A/B/D memory.current=%ld) ~= %d", c[D], MB(17));
180*49cdfc7eSAndroid Build Coastguard Worker 	TST_EXP_EXPR(values_close(c[2], 0, 1),
181*49cdfc7eSAndroid Build Coastguard Worker 		     "(A/B/E memory.current=%ld) ~= 0", c[E]);
182*49cdfc7eSAndroid Build Coastguard Worker 	tst_res(TINFO, "A/B/F memory.current=%ld", c[F]);
183*49cdfc7eSAndroid Build Coastguard Worker 
184*49cdfc7eSAndroid Build Coastguard Worker 	alloc_anon_in_child(trunk_cg[G], MB(166));
185*49cdfc7eSAndroid Build Coastguard Worker 
186*49cdfc7eSAndroid Build Coastguard Worker 	for (i = 0; i < ARRAY_SIZE(trunk_cg); i++) {
187*49cdfc7eSAndroid Build Coastguard Worker 		long low, oom;
188*49cdfc7eSAndroid Build Coastguard Worker 		const char id = "ABG"[i];
189*49cdfc7eSAndroid Build Coastguard Worker 
190*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CG_LINES_SCANF(trunk_cg[i], "memory.events",
191*49cdfc7eSAndroid Build Coastguard Worker 				    "low %ld", &low);
192*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CG_LINES_SCANF(trunk_cg[i], "memory.events",
193*49cdfc7eSAndroid Build Coastguard Worker 				    "oom %ld", &oom);
194*49cdfc7eSAndroid Build Coastguard Worker 
195*49cdfc7eSAndroid Build Coastguard Worker 		tst_res(TINFO, "%c: low events=%ld, oom events=%ld",
196*49cdfc7eSAndroid Build Coastguard Worker 			id, low, oom);
197*49cdfc7eSAndroid Build Coastguard Worker 	}
198*49cdfc7eSAndroid Build Coastguard Worker 
199*49cdfc7eSAndroid Build Coastguard Worker 	for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
200*49cdfc7eSAndroid Build Coastguard Worker 		long low, oom;
201*49cdfc7eSAndroid Build Coastguard Worker 		const char id = 'C' + i;
202*49cdfc7eSAndroid Build Coastguard Worker 
203*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CG_LINES_SCANF(leaf_cg[i], "memory.events",
204*49cdfc7eSAndroid Build Coastguard Worker 				    "low %ld", &low);
205*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CG_LINES_SCANF(leaf_cg[i], "memory.events",
206*49cdfc7eSAndroid Build Coastguard Worker 				    "oom %ld", &oom);
207*49cdfc7eSAndroid Build Coastguard Worker 
208*49cdfc7eSAndroid Build Coastguard Worker 		TST_EXP_EXPR(oom == 0, "(%c oom events=%ld) == 0", id, oom);
209*49cdfc7eSAndroid Build Coastguard Worker 
210*49cdfc7eSAndroid Build Coastguard Worker 		if (i < E) {
211*49cdfc7eSAndroid Build Coastguard Worker 			TST_EXP_EXPR(low > 0,
212*49cdfc7eSAndroid Build Coastguard Worker 				     "(%c low events=%ld) > 0", id, low);
213*49cdfc7eSAndroid Build Coastguard Worker 		} else {
214*49cdfc7eSAndroid Build Coastguard Worker 			TST_EXP_EXPR(low == 0,
215*49cdfc7eSAndroid Build Coastguard Worker 				     "(%c low events=%ld) == 0", id, low);
216*49cdfc7eSAndroid Build Coastguard Worker 		}
217*49cdfc7eSAndroid Build Coastguard Worker 	}
218*49cdfc7eSAndroid Build Coastguard Worker 
219*49cdfc7eSAndroid Build Coastguard Worker 	cleanup_sub_groups();
220*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_CLOSE(fd);
221*49cdfc7eSAndroid Build Coastguard Worker 	SAFE_UNLINK(TMPDIR"/tmpfile");
222*49cdfc7eSAndroid Build Coastguard Worker }
223*49cdfc7eSAndroid Build Coastguard Worker 
cleanup(void)224*49cdfc7eSAndroid Build Coastguard Worker static void cleanup(void)
225*49cdfc7eSAndroid Build Coastguard Worker {
226*49cdfc7eSAndroid Build Coastguard Worker 	cleanup_sub_groups();
227*49cdfc7eSAndroid Build Coastguard Worker 	if (fd > -1)
228*49cdfc7eSAndroid Build Coastguard Worker 		SAFE_CLOSE(fd);
229*49cdfc7eSAndroid Build Coastguard Worker }
230*49cdfc7eSAndroid Build Coastguard Worker 
231*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
232*49cdfc7eSAndroid Build Coastguard Worker 	.cleanup = cleanup,
233*49cdfc7eSAndroid Build Coastguard Worker 	.test_all = test_memcg_low,
234*49cdfc7eSAndroid Build Coastguard Worker 	.mount_device = 1,
235*49cdfc7eSAndroid Build Coastguard Worker 	.mntpoint = TMPDIR,
236*49cdfc7eSAndroid Build Coastguard Worker 	.all_filesystems = 1,
237*49cdfc7eSAndroid Build Coastguard Worker 	.skip_filesystems = (const char *const[]){
238*49cdfc7eSAndroid Build Coastguard Worker 		"exfat", "vfat", "fuse", "ntfs", "tmpfs", NULL
239*49cdfc7eSAndroid Build Coastguard Worker 	},
240*49cdfc7eSAndroid Build Coastguard Worker 	.forks_child = 1,
241*49cdfc7eSAndroid Build Coastguard Worker 	.needs_root = 1,
242*49cdfc7eSAndroid Build Coastguard Worker 	.needs_checkpoints = 1,
243*49cdfc7eSAndroid Build Coastguard Worker 	.needs_cgroup_ver = TST_CG_V2,
244*49cdfc7eSAndroid Build Coastguard Worker 	.needs_cgroup_ctrls = (const char *const[]){ "memory", NULL },
245*49cdfc7eSAndroid Build Coastguard Worker 	.tags = (const struct tst_tag[]) {
246*49cdfc7eSAndroid Build Coastguard Worker 		{
247*49cdfc7eSAndroid Build Coastguard Worker 			"known-fail",
248*49cdfc7eSAndroid Build Coastguard Worker 			"Low events in F: https://bugzilla.suse.com/show_bug.cgi?id=1196298"
249*49cdfc7eSAndroid Build Coastguard Worker 		},
250*49cdfc7eSAndroid Build Coastguard Worker 		{}
251*49cdfc7eSAndroid Build Coastguard Worker 	},
252*49cdfc7eSAndroid Build Coastguard Worker };
253