]> asedeno.scripts.mit.edu Git - linux.git/blob - tools/testing/selftests/cgroup/test_memcontrol.c
selftests: cgroup: add memory controller self-tests
[linux.git] / tools / testing / selftests / cgroup / test_memcontrol.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #define _GNU_SOURCE
3
4 #include <linux/limits.h>
5 #include <fcntl.h>
6 #include <stdio.h>
7 #include <stdlib.h>
8 #include <string.h>
9 #include <sys/stat.h>
10 #include <sys/types.h>
11 #include <unistd.h>
12
13 #include "../kselftest.h"
14 #include "cgroup_util.h"
15
16 /*
17  * This test creates two nested cgroups with and without enabling
18  * the memory controller.
19  */
20 static int test_memcg_subtree_control(const char *root)
21 {
22         char *parent, *child, *parent2, *child2;
23         int ret = KSFT_FAIL;
24         char buf[PAGE_SIZE];
25
26         /* Create two nested cgroups with the memory controller enabled */
27         parent = cg_name(root, "memcg_test_0");
28         child = cg_name(root, "memcg_test_0/memcg_test_1");
29         if (!parent || !child)
30                 goto cleanup;
31
32         if (cg_create(parent))
33                 goto cleanup;
34
35         if (cg_write(parent, "cgroup.subtree_control", "+memory"))
36                 goto cleanup;
37
38         if (cg_create(child))
39                 goto cleanup;
40
41         if (cg_read_strstr(child, "cgroup.controllers", "memory"))
42                 goto cleanup;
43
44         /* Create two nested cgroups without enabling memory controller */
45         parent2 = cg_name(root, "memcg_test_1");
46         child2 = cg_name(root, "memcg_test_1/memcg_test_1");
47         if (!parent2 || !child2)
48                 goto cleanup;
49
50         if (cg_create(parent2))
51                 goto cleanup;
52
53         if (cg_create(child2))
54                 goto cleanup;
55
56         if (cg_read(child2, "cgroup.controllers", buf, sizeof(buf)))
57                 goto cleanup;
58
59         if (!cg_read_strstr(child2, "cgroup.controllers", "memory"))
60                 goto cleanup;
61
62         ret = KSFT_PASS;
63
64 cleanup:
65         cg_destroy(child);
66         cg_destroy(parent);
67         free(parent);
68         free(child);
69
70         cg_destroy(child2);
71         cg_destroy(parent2);
72         free(parent2);
73         free(child2);
74
75         return ret;
76 }
77
78 static int alloc_anon_50M_check(const char *cgroup, void *arg)
79 {
80         size_t size = MB(50);
81         char *buf, *ptr;
82         long anon, current;
83         int ret = -1;
84
85         buf = malloc(size);
86         for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
87                 *ptr = 0;
88
89         current = cg_read_long(cgroup, "memory.current");
90         if (current < size)
91                 goto cleanup;
92
93         if (!values_close(size, current, 3))
94                 goto cleanup;
95
96         anon = cg_read_key_long(cgroup, "memory.stat", "anon ");
97         if (anon < 0)
98                 goto cleanup;
99
100         if (!values_close(anon, current, 3))
101                 goto cleanup;
102
103         ret = 0;
104 cleanup:
105         free(buf);
106         return ret;
107 }
108
109 static int alloc_pagecache_50M_check(const char *cgroup, void *arg)
110 {
111         size_t size = MB(50);
112         int ret = -1;
113         long current, file;
114         int fd;
115
116         fd = get_temp_fd();
117         if (fd < 0)
118                 return -1;
119
120         if (alloc_pagecache(fd, size))
121                 goto cleanup;
122
123         current = cg_read_long(cgroup, "memory.current");
124         if (current < size)
125                 goto cleanup;
126
127         file = cg_read_key_long(cgroup, "memory.stat", "file ");
128         if (file < 0)
129                 goto cleanup;
130
131         if (!values_close(file, current, 10))
132                 goto cleanup;
133
134         ret = 0;
135
136 cleanup:
137         close(fd);
138         return ret;
139 }
140
141 /*
142  * This test create a memory cgroup, allocates
143  * some anonymous memory and some pagecache
144  * and check memory.current and some memory.stat values.
145  */
146 static int test_memcg_current(const char *root)
147 {
148         int ret = KSFT_FAIL;
149         long current;
150         char *memcg;
151
152         memcg = cg_name(root, "memcg_test");
153         if (!memcg)
154                 goto cleanup;
155
156         if (cg_create(memcg))
157                 goto cleanup;
158
159         current = cg_read_long(memcg, "memory.current");
160         if (current != 0)
161                 goto cleanup;
162
163         if (cg_run(memcg, alloc_anon_50M_check, NULL))
164                 goto cleanup;
165
166         if (cg_run(memcg, alloc_pagecache_50M_check, NULL))
167                 goto cleanup;
168
169         ret = KSFT_PASS;
170
171 cleanup:
172         cg_destroy(memcg);
173         free(memcg);
174
175         return ret;
176 }
177
178 static int alloc_pagecache_50M(const char *cgroup, void *arg)
179 {
180         int fd = (long)arg;
181
182         return alloc_pagecache(fd, MB(50));
183 }
184
185 static int alloc_pagecache_50M_noexit(const char *cgroup, void *arg)
186 {
187         int fd = (long)arg;
188         int ppid = getppid();
189
190         if (alloc_pagecache(fd, MB(50)))
191                 return -1;
192
193         while (getppid() == ppid)
194                 sleep(1);
195
196         return 0;
197 }
198
199 /*
200  * First, this test creates the following hierarchy:
201  * A       memory.min = 50M,  memory.max = 200M
202  * A/B     memory.min = 50M,  memory.current = 50M
203  * A/B/C   memory.min = 75M,  memory.current = 50M
204  * A/B/D   memory.min = 25M,  memory.current = 50M
205  * A/B/E   memory.min = 500M, memory.current = 0
206  * A/B/F   memory.min = 0,    memory.current = 50M
207  *
208  * Usages are pagecache, but the test keeps a running
209  * process in every leaf cgroup.
210  * Then it creates A/G and creates a significant
211  * memory pressure in it.
212  *
213  * A/B    memory.current ~= 50M
214  * A/B/C  memory.current ~= 33M
215  * A/B/D  memory.current ~= 17M
216  * A/B/E  memory.current ~= 0
217  *
218  * After that it tries to allocate more than there is
219  * unprotected memory in A available, and checks
220  * checks that memory.min protects pagecache even
221  * in this case.
222  */
223 static int test_memcg_min(const char *root)
224 {
225         int ret = KSFT_FAIL;
226         char *parent[3] = {NULL};
227         char *children[4] = {NULL};
228         long c[4];
229         int i, attempts;
230         int fd;
231
232         fd = get_temp_fd();
233         if (fd < 0)
234                 goto cleanup;
235
236         parent[0] = cg_name(root, "memcg_test_0");
237         if (!parent[0])
238                 goto cleanup;
239
240         parent[1] = cg_name(parent[0], "memcg_test_1");
241         if (!parent[1])
242                 goto cleanup;
243
244         parent[2] = cg_name(parent[0], "memcg_test_2");
245         if (!parent[2])
246                 goto cleanup;
247
248         if (cg_create(parent[0]))
249                 goto cleanup;
250
251         if (cg_read_long(parent[0], "memory.min")) {
252                 ret = KSFT_SKIP;
253                 goto cleanup;
254         }
255
256         if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
257                 goto cleanup;
258
259         if (cg_write(parent[0], "memory.max", "200M"))
260                 goto cleanup;
261
262         if (cg_write(parent[0], "memory.swap.max", "0"))
263                 goto cleanup;
264
265         if (cg_create(parent[1]))
266                 goto cleanup;
267
268         if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
269                 goto cleanup;
270
271         if (cg_create(parent[2]))
272                 goto cleanup;
273
274         for (i = 0; i < ARRAY_SIZE(children); i++) {
275                 children[i] = cg_name_indexed(parent[1], "child_memcg", i);
276                 if (!children[i])
277                         goto cleanup;
278
279                 if (cg_create(children[i]))
280                         goto cleanup;
281
282                 if (i == 2)
283                         continue;
284
285                 cg_run_nowait(children[i], alloc_pagecache_50M_noexit,
286                               (void *)(long)fd);
287         }
288
289         if (cg_write(parent[0], "memory.min", "50M"))
290                 goto cleanup;
291         if (cg_write(parent[1], "memory.min", "50M"))
292                 goto cleanup;
293         if (cg_write(children[0], "memory.min", "75M"))
294                 goto cleanup;
295         if (cg_write(children[1], "memory.min", "25M"))
296                 goto cleanup;
297         if (cg_write(children[2], "memory.min", "500M"))
298                 goto cleanup;
299         if (cg_write(children[3], "memory.min", "0"))
300                 goto cleanup;
301
302         attempts = 0;
303         while (!values_close(cg_read_long(parent[1], "memory.current"),
304                              MB(150), 3)) {
305                 if (attempts++ > 5)
306                         break;
307                 sleep(1);
308         }
309
310         if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
311                 goto cleanup;
312
313         if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
314                 goto cleanup;
315
316         for (i = 0; i < ARRAY_SIZE(children); i++)
317                 c[i] = cg_read_long(children[i], "memory.current");
318
319         if (!values_close(c[0], MB(33), 10))
320                 goto cleanup;
321
322         if (!values_close(c[1], MB(17), 10))
323                 goto cleanup;
324
325         if (!values_close(c[2], 0, 1))
326                 goto cleanup;
327
328         if (!cg_run(parent[2], alloc_anon, (void *)MB(170)))
329                 goto cleanup;
330
331         if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
332                 goto cleanup;
333
334         ret = KSFT_PASS;
335
336 cleanup:
337         for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
338                 if (!children[i])
339                         continue;
340
341                 cg_destroy(children[i]);
342                 free(children[i]);
343         }
344
345         for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
346                 if (!parent[i])
347                         continue;
348
349                 cg_destroy(parent[i]);
350                 free(parent[i]);
351         }
352         close(fd);
353         return ret;
354 }
355
356 /*
357  * First, this test creates the following hierarchy:
358  * A       memory.low = 50M,  memory.max = 200M
359  * A/B     memory.low = 50M,  memory.current = 50M
360  * A/B/C   memory.low = 75M,  memory.current = 50M
361  * A/B/D   memory.low = 25M,  memory.current = 50M
362  * A/B/E   memory.low = 500M, memory.current = 0
363  * A/B/F   memory.low = 0,    memory.current = 50M
364  *
365  * Usages are pagecache.
366  * Then it creates A/G an creates a significant
367  * memory pressure in it.
368  *
369  * Then it checks actual memory usages and expects that:
370  * A/B    memory.current ~= 50M
371  * A/B/   memory.current ~= 33M
372  * A/B/D  memory.current ~= 17M
373  * A/B/E  memory.current ~= 0
374  *
375  * After that it tries to allocate more than there is
376  * unprotected memory in A available,
377  * and checks low and oom events in memory.events.
378  */
379 static int test_memcg_low(const char *root)
380 {
381         int ret = KSFT_FAIL;
382         char *parent[3] = {NULL};
383         char *children[4] = {NULL};
384         long low, oom;
385         long c[4];
386         int i;
387         int fd;
388
389         fd = get_temp_fd();
390         if (fd < 0)
391                 goto cleanup;
392
393         parent[0] = cg_name(root, "memcg_test_0");
394         if (!parent[0])
395                 goto cleanup;
396
397         parent[1] = cg_name(parent[0], "memcg_test_1");
398         if (!parent[1])
399                 goto cleanup;
400
401         parent[2] = cg_name(parent[0], "memcg_test_2");
402         if (!parent[2])
403                 goto cleanup;
404
405         if (cg_create(parent[0]))
406                 goto cleanup;
407
408         if (cg_read_long(parent[0], "memory.low"))
409                 goto cleanup;
410
411         if (cg_write(parent[0], "cgroup.subtree_control", "+memory"))
412                 goto cleanup;
413
414         if (cg_write(parent[0], "memory.max", "200M"))
415                 goto cleanup;
416
417         if (cg_write(parent[0], "memory.swap.max", "0"))
418                 goto cleanup;
419
420         if (cg_create(parent[1]))
421                 goto cleanup;
422
423         if (cg_write(parent[1], "cgroup.subtree_control", "+memory"))
424                 goto cleanup;
425
426         if (cg_create(parent[2]))
427                 goto cleanup;
428
429         for (i = 0; i < ARRAY_SIZE(children); i++) {
430                 children[i] = cg_name_indexed(parent[1], "child_memcg", i);
431                 if (!children[i])
432                         goto cleanup;
433
434                 if (cg_create(children[i]))
435                         goto cleanup;
436
437                 if (i == 2)
438                         continue;
439
440                 if (cg_run(children[i], alloc_pagecache_50M, (void *)(long)fd))
441                         goto cleanup;
442         }
443
444         if (cg_write(parent[0], "memory.low", "50M"))
445                 goto cleanup;
446         if (cg_write(parent[1], "memory.low", "50M"))
447                 goto cleanup;
448         if (cg_write(children[0], "memory.low", "75M"))
449                 goto cleanup;
450         if (cg_write(children[1], "memory.low", "25M"))
451                 goto cleanup;
452         if (cg_write(children[2], "memory.low", "500M"))
453                 goto cleanup;
454         if (cg_write(children[3], "memory.low", "0"))
455                 goto cleanup;
456
457         if (cg_run(parent[2], alloc_anon, (void *)MB(148)))
458                 goto cleanup;
459
460         if (!values_close(cg_read_long(parent[1], "memory.current"), MB(50), 3))
461                 goto cleanup;
462
463         for (i = 0; i < ARRAY_SIZE(children); i++)
464                 c[i] = cg_read_long(children[i], "memory.current");
465
466         if (!values_close(c[0], MB(33), 10))
467                 goto cleanup;
468
469         if (!values_close(c[1], MB(17), 10))
470                 goto cleanup;
471
472         if (!values_close(c[2], 0, 1))
473                 goto cleanup;
474
475         if (cg_run(parent[2], alloc_anon, (void *)MB(166))) {
476                 fprintf(stderr,
477                         "memory.low prevents from allocating anon memory\n");
478                 goto cleanup;
479         }
480
481         for (i = 0; i < ARRAY_SIZE(children); i++) {
482                 oom = cg_read_key_long(children[i], "memory.events", "oom ");
483                 low = cg_read_key_long(children[i], "memory.events", "low ");
484
485                 if (oom)
486                         goto cleanup;
487                 if (i < 2 && low <= 0)
488                         goto cleanup;
489                 if (i >= 2 && low)
490                         goto cleanup;
491         }
492
493         ret = KSFT_PASS;
494
495 cleanup:
496         for (i = ARRAY_SIZE(children) - 1; i >= 0; i--) {
497                 if (!children[i])
498                         continue;
499
500                 cg_destroy(children[i]);
501                 free(children[i]);
502         }
503
504         for (i = ARRAY_SIZE(parent) - 1; i >= 0; i--) {
505                 if (!parent[i])
506                         continue;
507
508                 cg_destroy(parent[i]);
509                 free(parent[i]);
510         }
511         close(fd);
512         return ret;
513 }
514
515 static int alloc_pagecache_max_30M(const char *cgroup, void *arg)
516 {
517         size_t size = MB(50);
518         int ret = -1;
519         long current;
520         int fd;
521
522         fd = get_temp_fd();
523         if (fd < 0)
524                 return -1;
525
526         if (alloc_pagecache(fd, size))
527                 goto cleanup;
528
529         current = cg_read_long(cgroup, "memory.current");
530         if (current <= MB(29) || current > MB(30))
531                 goto cleanup;
532
533         ret = 0;
534
535 cleanup:
536         close(fd);
537         return ret;
538
539 }
540
541 /*
542  * This test checks that memory.high limits the amount of
543  * memory which can be consumed by either anonymous memory
544  * or pagecache.
545  */
546 static int test_memcg_high(const char *root)
547 {
548         int ret = KSFT_FAIL;
549         char *memcg;
550         long high;
551
552         memcg = cg_name(root, "memcg_test");
553         if (!memcg)
554                 goto cleanup;
555
556         if (cg_create(memcg))
557                 goto cleanup;
558
559         if (cg_read_strcmp(memcg, "memory.high", "max\n"))
560                 goto cleanup;
561
562         if (cg_write(memcg, "memory.swap.max", "0"))
563                 goto cleanup;
564
565         if (cg_write(memcg, "memory.high", "30M"))
566                 goto cleanup;
567
568         if (cg_run(memcg, alloc_anon, (void *)MB(100)))
569                 goto cleanup;
570
571         if (!cg_run(memcg, alloc_pagecache_50M_check, NULL))
572                 goto cleanup;
573
574         if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
575                 goto cleanup;
576
577         high = cg_read_key_long(memcg, "memory.events", "high ");
578         if (high <= 0)
579                 goto cleanup;
580
581         ret = KSFT_PASS;
582
583 cleanup:
584         cg_destroy(memcg);
585         free(memcg);
586
587         return ret;
588 }
589
590 /*
591  * This test checks that memory.max limits the amount of
592  * memory which can be consumed by either anonymous memory
593  * or pagecache.
594  */
595 static int test_memcg_max(const char *root)
596 {
597         int ret = KSFT_FAIL;
598         char *memcg;
599         long current, max;
600
601         memcg = cg_name(root, "memcg_test");
602         if (!memcg)
603                 goto cleanup;
604
605         if (cg_create(memcg))
606                 goto cleanup;
607
608         if (cg_read_strcmp(memcg, "memory.max", "max\n"))
609                 goto cleanup;
610
611         if (cg_write(memcg, "memory.swap.max", "0"))
612                 goto cleanup;
613
614         if (cg_write(memcg, "memory.max", "30M"))
615                 goto cleanup;
616
617         /* Should be killed by OOM killer */
618         if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
619                 goto cleanup;
620
621         if (cg_run(memcg, alloc_pagecache_max_30M, NULL))
622                 goto cleanup;
623
624         current = cg_read_long(memcg, "memory.current");
625         if (current > MB(30) || !current)
626                 goto cleanup;
627
628         max = cg_read_key_long(memcg, "memory.events", "max ");
629         if (max <= 0)
630                 goto cleanup;
631
632         ret = KSFT_PASS;
633
634 cleanup:
635         cg_destroy(memcg);
636         free(memcg);
637
638         return ret;
639 }
640
641 /*
642  * This test disables swapping and tries to allocate anonymous memory
643  * up to OOM. Then it checks for oom and oom_kill events in
644  * memory.events.
645  */
646 static int test_memcg_oom_events(const char *root)
647 {
648         int ret = KSFT_FAIL;
649         char *memcg;
650
651         memcg = cg_name(root, "memcg_test");
652         if (!memcg)
653                 goto cleanup;
654
655         if (cg_create(memcg))
656                 goto cleanup;
657
658         if (cg_write(memcg, "memory.max", "30M"))
659                 goto cleanup;
660
661         if (cg_write(memcg, "memory.swap.max", "0"))
662                 goto cleanup;
663
664         if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
665                 goto cleanup;
666
667         if (cg_read_strcmp(memcg, "cgroup.procs", ""))
668                 goto cleanup;
669
670         if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
671                 goto cleanup;
672
673         if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
674                 goto cleanup;
675
676         ret = KSFT_PASS;
677
678 cleanup:
679         cg_destroy(memcg);
680         free(memcg);
681
682         return ret;
683 }
684
685 #define T(x) { x, #x }
686 struct memcg_test {
687         int (*fn)(const char *root);
688         const char *name;
689 } tests[] = {
690         T(test_memcg_subtree_control),
691         T(test_memcg_current),
692         T(test_memcg_min),
693         T(test_memcg_low),
694         T(test_memcg_high),
695         T(test_memcg_max),
696         T(test_memcg_oom_events),
697 };
698 #undef T
699
700 int main(int argc, char **argv)
701 {
702         char root[PATH_MAX];
703         int i, ret = EXIT_SUCCESS;
704
705         if (cg_find_unified_root(root, sizeof(root)))
706                 ksft_exit_skip("cgroup v2 isn't mounted\n");
707
708         /*
709          * Check that memory controller is available:
710          * memory is listed in cgroup.controllers
711          */
712         if (cg_read_strstr(root, "cgroup.controllers", "memory"))
713                 ksft_exit_skip("memory controller isn't available\n");
714
715         for (i = 0; i < ARRAY_SIZE(tests); i++) {
716                 switch (tests[i].fn(root)) {
717                 case KSFT_PASS:
718                         ksft_test_result_pass("%s\n", tests[i].name);
719                         break;
720                 case KSFT_SKIP:
721                         ksft_test_result_skip("%s\n", tests[i].name);
722                         break;
723                 default:
724                         ret = EXIT_FAILURE;
725                         ksft_test_result_fail("%s\n", tests[i].name);
726                         break;
727                 }
728         }
729
730         return ret;
731 }