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 third 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.min = 50M, memory.max = 200M
11*49cdfc7eSAndroid Build Coastguard Worker * A/B memory.min = 50M, memory.current = 50M
12*49cdfc7eSAndroid Build Coastguard Worker * A/B/C memory.min = 75M, memory.current = 50M
13*49cdfc7eSAndroid Build Coastguard Worker * A/B/D memory.min = 25M, memory.current = 50M
14*49cdfc7eSAndroid Build Coastguard Worker * A/B/E memory.min = 500M, memory.current = 0
15*49cdfc7eSAndroid Build Coastguard Worker * A/B/F memory.min = 0, memory.current = 50M
16*49cdfc7eSAndroid Build Coastguard Worker *
17*49cdfc7eSAndroid Build Coastguard Worker * Usages are pagecache, but the test keeps a running
18*49cdfc7eSAndroid Build Coastguard Worker * process in every leaf cgroup.
19*49cdfc7eSAndroid Build Coastguard Worker * Then it creates A/G and creates a significant
20*49cdfc7eSAndroid Build Coastguard Worker * memory pressure in it.
21*49cdfc7eSAndroid Build Coastguard Worker *
22*49cdfc7eSAndroid Build Coastguard Worker * A/B memory.current ~= 50M
23*49cdfc7eSAndroid Build Coastguard Worker * A/B/C memory.current ~= 33M
24*49cdfc7eSAndroid Build Coastguard Worker * A/B/D memory.current ~= 17M
25*49cdfc7eSAndroid Build Coastguard Worker * A/B/E memory.current ~= 0
26*49cdfc7eSAndroid Build Coastguard Worker *
27*49cdfc7eSAndroid Build Coastguard Worker * After that it tries to allocate more than there is unprotected
28*49cdfc7eSAndroid Build Coastguard Worker * memory in A available, and checks that memory.min protects
29*49cdfc7eSAndroid Build Coastguard Worker * pagecache even in this case."
30*49cdfc7eSAndroid Build Coastguard Worker *
31*49cdfc7eSAndroid Build Coastguard Worker * memory.min doesn't appear to exist on V1 so we only test on V2 like
32*49cdfc7eSAndroid Build Coastguard Worker * the selftest. We do test on more file systems, but not tempfs
33*49cdfc7eSAndroid Build Coastguard Worker * becaue it can't evict the page cache without swap. Also we avoid
34*49cdfc7eSAndroid Build Coastguard Worker * 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 TEST_DONE,
54*49cdfc7eSAndroid Build Coastguard Worker };
55*49cdfc7eSAndroid Build Coastguard Worker
56*49cdfc7eSAndroid Build Coastguard Worker enum trunk_cg {
57*49cdfc7eSAndroid Build Coastguard Worker A,
58*49cdfc7eSAndroid Build Coastguard Worker B,
59*49cdfc7eSAndroid Build Coastguard Worker G
60*49cdfc7eSAndroid Build Coastguard Worker };
61*49cdfc7eSAndroid Build Coastguard Worker
62*49cdfc7eSAndroid Build Coastguard Worker enum leaf_cg {
63*49cdfc7eSAndroid Build Coastguard Worker C,
64*49cdfc7eSAndroid Build Coastguard Worker D,
65*49cdfc7eSAndroid Build Coastguard Worker E,
66*49cdfc7eSAndroid Build Coastguard Worker F
67*49cdfc7eSAndroid Build Coastguard Worker };
68*49cdfc7eSAndroid Build Coastguard Worker
cleanup_sub_groups(void)69*49cdfc7eSAndroid Build Coastguard Worker static void cleanup_sub_groups(void)
70*49cdfc7eSAndroid Build Coastguard Worker {
71*49cdfc7eSAndroid Build Coastguard Worker size_t i;
72*49cdfc7eSAndroid Build Coastguard Worker
73*49cdfc7eSAndroid Build Coastguard Worker for (i = ARRAY_SIZE(leaf_cg); i > 0; i--) {
74*49cdfc7eSAndroid Build Coastguard Worker if (!leaf_cg[i - 1])
75*49cdfc7eSAndroid Build Coastguard Worker continue;
76*49cdfc7eSAndroid Build Coastguard Worker
77*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE2(TEST_DONE,
78*49cdfc7eSAndroid Build Coastguard Worker ARRAY_SIZE(leaf_cg) - 1);
79*49cdfc7eSAndroid Build Coastguard Worker tst_reap_children();
80*49cdfc7eSAndroid Build Coastguard Worker break;
81*49cdfc7eSAndroid Build Coastguard Worker }
82*49cdfc7eSAndroid Build Coastguard Worker
83*49cdfc7eSAndroid Build Coastguard Worker for (i = ARRAY_SIZE(leaf_cg); i > 0; i--) {
84*49cdfc7eSAndroid Build Coastguard Worker if (!leaf_cg[i - 1])
85*49cdfc7eSAndroid Build Coastguard Worker continue;
86*49cdfc7eSAndroid Build Coastguard Worker
87*49cdfc7eSAndroid Build Coastguard Worker leaf_cg[i - 1] = tst_cg_group_rm(leaf_cg[i - 1]);
88*49cdfc7eSAndroid Build Coastguard Worker }
89*49cdfc7eSAndroid Build Coastguard Worker
90*49cdfc7eSAndroid Build Coastguard Worker for (i = ARRAY_SIZE(trunk_cg); i > 0; i--) {
91*49cdfc7eSAndroid Build Coastguard Worker if (!trunk_cg[i - 1])
92*49cdfc7eSAndroid Build Coastguard Worker continue;
93*49cdfc7eSAndroid Build Coastguard Worker
94*49cdfc7eSAndroid Build Coastguard Worker trunk_cg[i - 1] = tst_cg_group_rm(trunk_cg[i - 1]);
95*49cdfc7eSAndroid Build Coastguard Worker }
96*49cdfc7eSAndroid Build Coastguard Worker }
97*49cdfc7eSAndroid Build Coastguard Worker
alloc_anon_in_child(const struct tst_cg_group * const cg,const size_t size,const int expect_oom)98*49cdfc7eSAndroid Build Coastguard Worker static void alloc_anon_in_child(const struct tst_cg_group *const cg,
99*49cdfc7eSAndroid Build Coastguard Worker const size_t size, const int expect_oom)
100*49cdfc7eSAndroid Build Coastguard Worker {
101*49cdfc7eSAndroid Build Coastguard Worker int status;
102*49cdfc7eSAndroid Build Coastguard Worker const pid_t pid = SAFE_FORK();
103*49cdfc7eSAndroid Build Coastguard Worker
104*49cdfc7eSAndroid Build Coastguard Worker if (!pid) {
105*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid());
106*49cdfc7eSAndroid Build Coastguard Worker
107*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Child %d in %s: Allocating anon: %"PRIdPTR,
108*49cdfc7eSAndroid Build Coastguard Worker getpid(), tst_cg_group_name(cg), size);
109*49cdfc7eSAndroid Build Coastguard Worker alloc_anon(size);
110*49cdfc7eSAndroid Build Coastguard Worker exit(0);
111*49cdfc7eSAndroid Build Coastguard Worker }
112*49cdfc7eSAndroid Build Coastguard Worker
113*49cdfc7eSAndroid Build Coastguard Worker SAFE_WAITPID(pid, &status, 0);
114*49cdfc7eSAndroid Build Coastguard Worker
115*49cdfc7eSAndroid Build Coastguard Worker if (expect_oom && WIFSIGNALED(status) && WTERMSIG(status) == SIGKILL) {
116*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Child %d killed by OOM", pid);
117*49cdfc7eSAndroid Build Coastguard Worker return;
118*49cdfc7eSAndroid Build Coastguard Worker }
119*49cdfc7eSAndroid Build Coastguard Worker
120*49cdfc7eSAndroid Build Coastguard Worker if (!expect_oom && WIFEXITED(status) && WEXITSTATUS(status) == 0) {
121*49cdfc7eSAndroid Build Coastguard Worker tst_res(TPASS, "Child %d exited", pid);
122*49cdfc7eSAndroid Build Coastguard Worker return;
123*49cdfc7eSAndroid Build Coastguard Worker }
124*49cdfc7eSAndroid Build Coastguard Worker
125*49cdfc7eSAndroid Build Coastguard Worker tst_res(TFAIL,
126*49cdfc7eSAndroid Build Coastguard Worker "Expected child %d to %s, but instead %s",
127*49cdfc7eSAndroid Build Coastguard Worker pid,
128*49cdfc7eSAndroid Build Coastguard Worker expect_oom ? "be killed" : "exit(0)",
129*49cdfc7eSAndroid Build Coastguard Worker tst_strstatus(status));
130*49cdfc7eSAndroid Build Coastguard Worker }
131*49cdfc7eSAndroid Build Coastguard Worker
alloc_pagecache_in_child(const struct tst_cg_group * const cg,const size_t size)132*49cdfc7eSAndroid Build Coastguard Worker static void alloc_pagecache_in_child(const struct tst_cg_group *const cg,
133*49cdfc7eSAndroid Build Coastguard Worker const size_t size)
134*49cdfc7eSAndroid Build Coastguard Worker {
135*49cdfc7eSAndroid Build Coastguard Worker const pid_t pid = SAFE_FORK();
136*49cdfc7eSAndroid Build Coastguard Worker
137*49cdfc7eSAndroid Build Coastguard Worker if (pid) {
138*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(CHILD_IDLE);
139*49cdfc7eSAndroid Build Coastguard Worker return;
140*49cdfc7eSAndroid Build Coastguard Worker }
141*49cdfc7eSAndroid Build Coastguard Worker
142*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", getpid());
143*49cdfc7eSAndroid Build Coastguard Worker
144*49cdfc7eSAndroid Build Coastguard Worker tst_res(TINFO, "Child %d in %s: Allocating pagecache: %"PRIdPTR,
145*49cdfc7eSAndroid Build Coastguard Worker getpid(), tst_cg_group_name(cg), size);
146*49cdfc7eSAndroid Build Coastguard Worker alloc_pagecache(fd, size);
147*49cdfc7eSAndroid Build Coastguard Worker SAFE_FSYNC(fd);
148*49cdfc7eSAndroid Build Coastguard Worker
149*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAKE(CHILD_IDLE);
150*49cdfc7eSAndroid Build Coastguard Worker TST_CHECKPOINT_WAIT(TEST_DONE);
151*49cdfc7eSAndroid Build Coastguard Worker exit(0);
152*49cdfc7eSAndroid Build Coastguard Worker }
153*49cdfc7eSAndroid Build Coastguard Worker
test_memcg_min(void)154*49cdfc7eSAndroid Build Coastguard Worker static void test_memcg_min(void)
155*49cdfc7eSAndroid Build Coastguard Worker {
156*49cdfc7eSAndroid Build Coastguard Worker long c[4];
157*49cdfc7eSAndroid Build Coastguard Worker unsigned int i;
158*49cdfc7eSAndroid Build Coastguard Worker size_t attempts;
159*49cdfc7eSAndroid Build Coastguard Worker
160*49cdfc7eSAndroid Build Coastguard Worker fd = SAFE_OPEN(TMPDIR"/tmpfile", O_RDWR | O_CREAT, 0600);
161*49cdfc7eSAndroid Build Coastguard Worker trunk_cg[A] = tst_cg_group_mk(tst_cg, "trunk_A");
162*49cdfc7eSAndroid Build Coastguard Worker
163*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_SCANF(trunk_cg[A], "memory.min", "%ld", c);
164*49cdfc7eSAndroid Build Coastguard Worker if (c[0]) {
165*49cdfc7eSAndroid Build Coastguard Worker tst_brk(TCONF,
166*49cdfc7eSAndroid Build Coastguard Worker "memory.min already set to %ld on parent group", c[0]);
167*49cdfc7eSAndroid Build Coastguard Worker }
168*49cdfc7eSAndroid Build Coastguard Worker
169*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[A], "cgroup.subtree_control", "+memory");
170*49cdfc7eSAndroid Build Coastguard Worker
171*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[A], "memory.max", "200M");
172*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[A], "memory.swap.max", "0");
173*49cdfc7eSAndroid Build Coastguard Worker
174*49cdfc7eSAndroid Build Coastguard Worker trunk_cg[B] = tst_cg_group_mk(trunk_cg[A], "trunk_B");
175*49cdfc7eSAndroid Build Coastguard Worker
176*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[B], "cgroup.subtree_control", "+memory");
177*49cdfc7eSAndroid Build Coastguard Worker
178*49cdfc7eSAndroid Build Coastguard Worker trunk_cg[G] = tst_cg_group_mk(trunk_cg[A], "trunk_G");
179*49cdfc7eSAndroid Build Coastguard Worker
180*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < ARRAY_SIZE(leaf_cg); i++) {
181*49cdfc7eSAndroid Build Coastguard Worker leaf_cg[i] = tst_cg_group_mk(trunk_cg[B],
182*49cdfc7eSAndroid Build Coastguard Worker "leaf_%c", 'C' + i);
183*49cdfc7eSAndroid Build Coastguard Worker
184*49cdfc7eSAndroid Build Coastguard Worker if (i == E)
185*49cdfc7eSAndroid Build Coastguard Worker continue;
186*49cdfc7eSAndroid Build Coastguard Worker
187*49cdfc7eSAndroid Build Coastguard Worker alloc_pagecache_in_child(leaf_cg[i], MB(50));
188*49cdfc7eSAndroid Build Coastguard Worker }
189*49cdfc7eSAndroid Build Coastguard Worker
190*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[A], "memory.min", "50M");
191*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(trunk_cg[B], "memory.min", "50M");
192*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(leaf_cg[C], "memory.min", "75M");
193*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(leaf_cg[D], "memory.min", "25M");
194*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(leaf_cg[E], "memory.min", "500M");
195*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_PRINT(leaf_cg[F], "memory.min", "0");
196*49cdfc7eSAndroid Build Coastguard Worker
197*49cdfc7eSAndroid Build Coastguard Worker for (attempts = 0; attempts < 5; attempts++) {
198*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_SCANF(trunk_cg[B], "memory.current", "%ld", c);
199*49cdfc7eSAndroid Build Coastguard Worker if (values_close(c[0], MB(150), 3))
200*49cdfc7eSAndroid Build Coastguard Worker break;
201*49cdfc7eSAndroid Build Coastguard Worker
202*49cdfc7eSAndroid Build Coastguard Worker sleep(1);
203*49cdfc7eSAndroid Build Coastguard Worker }
204*49cdfc7eSAndroid Build Coastguard Worker
205*49cdfc7eSAndroid Build Coastguard Worker alloc_anon_in_child(trunk_cg[G], MB(148), 0);
206*49cdfc7eSAndroid Build Coastguard Worker
207*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_SCANF(trunk_cg[B], "memory.current", "%ld", c);
208*49cdfc7eSAndroid Build Coastguard Worker TST_EXP_EXPR(values_close(c[0], MB(50), 5),
209*49cdfc7eSAndroid Build Coastguard Worker "(A/B memory.current=%ld) ~= %d", c[0], MB(50));
210*49cdfc7eSAndroid Build Coastguard Worker
211*49cdfc7eSAndroid Build Coastguard Worker for (i = 0; i < ARRAY_SIZE(leaf_cg); i++)
212*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_SCANF(leaf_cg[i], "memory.current", "%ld", c + i);
213*49cdfc7eSAndroid Build Coastguard Worker
214*49cdfc7eSAndroid Build Coastguard Worker TST_EXP_EXPR(values_close(c[0], MB(33), 20),
215*49cdfc7eSAndroid Build Coastguard Worker "(A/B/C memory.current=%ld) ~= %d", c[0], MB(33));
216*49cdfc7eSAndroid Build Coastguard Worker TST_EXP_EXPR(values_close(c[1], MB(17), 20),
217*49cdfc7eSAndroid Build Coastguard Worker "(A/B/D memory.current=%ld) ~= %d", c[1], MB(17));
218*49cdfc7eSAndroid Build Coastguard Worker TST_EXP_EXPR(values_close(c[2], 0, 1),
219*49cdfc7eSAndroid Build Coastguard Worker "(A/B/E memory.current=%ld) ~= 0", c[2]);
220*49cdfc7eSAndroid Build Coastguard Worker
221*49cdfc7eSAndroid Build Coastguard Worker alloc_anon_in_child(trunk_cg[G], MB(170), 1);
222*49cdfc7eSAndroid Build Coastguard Worker
223*49cdfc7eSAndroid Build Coastguard Worker SAFE_CG_SCANF(trunk_cg[B], "memory.current", "%ld", c);
224*49cdfc7eSAndroid Build Coastguard Worker TST_EXP_EXPR(values_close(c[0], MB(50), 5),
225*49cdfc7eSAndroid Build Coastguard Worker "(A/B memory.current=%ld) ~= %d", c[0], MB(50));
226*49cdfc7eSAndroid Build Coastguard Worker
227*49cdfc7eSAndroid Build Coastguard Worker cleanup_sub_groups();
228*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
229*49cdfc7eSAndroid Build Coastguard Worker SAFE_UNLINK(TMPDIR"/tmpfile");
230*49cdfc7eSAndroid Build Coastguard Worker }
231*49cdfc7eSAndroid Build Coastguard Worker
cleanup(void)232*49cdfc7eSAndroid Build Coastguard Worker static void cleanup(void)
233*49cdfc7eSAndroid Build Coastguard Worker {
234*49cdfc7eSAndroid Build Coastguard Worker cleanup_sub_groups();
235*49cdfc7eSAndroid Build Coastguard Worker if (fd > -1)
236*49cdfc7eSAndroid Build Coastguard Worker SAFE_CLOSE(fd);
237*49cdfc7eSAndroid Build Coastguard Worker }
238*49cdfc7eSAndroid Build Coastguard Worker
239*49cdfc7eSAndroid Build Coastguard Worker static struct tst_test test = {
240*49cdfc7eSAndroid Build Coastguard Worker .cleanup = cleanup,
241*49cdfc7eSAndroid Build Coastguard Worker .test_all = test_memcg_min,
242*49cdfc7eSAndroid Build Coastguard Worker .mount_device = 1,
243*49cdfc7eSAndroid Build Coastguard Worker .mntpoint = TMPDIR,
244*49cdfc7eSAndroid Build Coastguard Worker .all_filesystems = 1,
245*49cdfc7eSAndroid Build Coastguard Worker .skip_filesystems = (const char *const[]){
246*49cdfc7eSAndroid Build Coastguard Worker "exfat", "vfat", "fuse", "ntfs", "tmpfs", NULL
247*49cdfc7eSAndroid Build Coastguard Worker },
248*49cdfc7eSAndroid Build Coastguard Worker .forks_child = 1,
249*49cdfc7eSAndroid Build Coastguard Worker .needs_root = 1,
250*49cdfc7eSAndroid Build Coastguard Worker .needs_checkpoints = 1,
251*49cdfc7eSAndroid Build Coastguard Worker .needs_cgroup_ver = TST_CG_V2,
252*49cdfc7eSAndroid Build Coastguard Worker .needs_cgroup_ctrls = (const char *const[]){ "memory", NULL },
253*49cdfc7eSAndroid Build Coastguard Worker };
254