selftests: cgroup/memcontrol: add basic test for swap controls
[linux-2.6-block.git] / tools / testing / selftests / cgroup / test_memcontrol.c
CommitLineData
84092dbc
RG
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 */
20static 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
64cleanup:
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
78static 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;
104cleanup:
105 free(buf);
106 return ret;
107}
108
109static 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
136cleanup:
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 */
146static 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
171cleanup:
172 cg_destroy(memcg);
173 free(memcg);
174
175 return ret;
176}
177
178static 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
185static 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 */
223static 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
336cleanup:
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 */
379static 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
495cleanup:
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
515static 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
535cleanup:
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 */
546static 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
583cleanup:
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 */
595static 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
634cleanup:
635 cg_destroy(memcg);
636 free(memcg);
637
638 return ret;
639}
640
478b2784
MR
641static int alloc_anon_50M_check_swap(const char *cgroup, void *arg)
642{
643 long mem_max = (long)arg;
644 size_t size = MB(50);
645 char *buf, *ptr;
646 long mem_current, swap_current;
647 int ret = -1;
648
649 buf = malloc(size);
650 for (ptr = buf; ptr < buf + size; ptr += PAGE_SIZE)
651 *ptr = 0;
652
653 mem_current = cg_read_long(cgroup, "memory.current");
654 if (!mem_current || !values_close(mem_current, mem_max, 3))
655 goto cleanup;
656
657 swap_current = cg_read_long(cgroup, "memory.swap.current");
658 if (!swap_current ||
659 !values_close(mem_current + swap_current, size, 3))
660 goto cleanup;
661
662 ret = 0;
663cleanup:
664 free(buf);
665 return ret;
666}
667
668/*
669 * This test checks that memory.swap.max limits the amount of
670 * anonymous memory which can be swapped out.
671 */
672static int test_memcg_swap_max(const char *root)
673{
674 int ret = KSFT_FAIL;
675 char *memcg;
676 long max;
677
678 if (!is_swap_enabled())
679 return KSFT_SKIP;
680
681 memcg = cg_name(root, "memcg_test");
682 if (!memcg)
683 goto cleanup;
684
685 if (cg_create(memcg))
686 goto cleanup;
687
688 if (cg_read_long(memcg, "memory.swap.current")) {
689 ret = KSFT_SKIP;
690 goto cleanup;
691 }
692
693 if (cg_read_strcmp(memcg, "memory.max", "max\n"))
694 goto cleanup;
695
696 if (cg_read_strcmp(memcg, "memory.swap.max", "max\n"))
697 goto cleanup;
698
699 if (cg_write(memcg, "memory.swap.max", "30M"))
700 goto cleanup;
701
702 if (cg_write(memcg, "memory.max", "30M"))
703 goto cleanup;
704
705 /* Should be killed by OOM killer */
706 if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
707 goto cleanup;
708
709 if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
710 goto cleanup;
711
712 if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
713 goto cleanup;
714
715 if (cg_run(memcg, alloc_anon_50M_check_swap, (void *)MB(30)))
716 goto cleanup;
717
718 max = cg_read_key_long(memcg, "memory.events", "max ");
719 if (max <= 0)
720 goto cleanup;
721
722 ret = KSFT_PASS;
723
724cleanup:
725 cg_destroy(memcg);
726 free(memcg);
727
728 return ret;
729}
730
84092dbc
RG
731/*
732 * This test disables swapping and tries to allocate anonymous memory
733 * up to OOM. Then it checks for oom and oom_kill events in
734 * memory.events.
735 */
736static int test_memcg_oom_events(const char *root)
737{
738 int ret = KSFT_FAIL;
739 char *memcg;
740
741 memcg = cg_name(root, "memcg_test");
742 if (!memcg)
743 goto cleanup;
744
745 if (cg_create(memcg))
746 goto cleanup;
747
748 if (cg_write(memcg, "memory.max", "30M"))
749 goto cleanup;
750
751 if (cg_write(memcg, "memory.swap.max", "0"))
752 goto cleanup;
753
754 if (!cg_run(memcg, alloc_anon, (void *)MB(100)))
755 goto cleanup;
756
757 if (cg_read_strcmp(memcg, "cgroup.procs", ""))
758 goto cleanup;
759
760 if (cg_read_key_long(memcg, "memory.events", "oom ") != 1)
761 goto cleanup;
762
763 if (cg_read_key_long(memcg, "memory.events", "oom_kill ") != 1)
764 goto cleanup;
765
766 ret = KSFT_PASS;
767
768cleanup:
769 cg_destroy(memcg);
770 free(memcg);
771
772 return ret;
773}
774
775#define T(x) { x, #x }
776struct memcg_test {
777 int (*fn)(const char *root);
778 const char *name;
779} tests[] = {
780 T(test_memcg_subtree_control),
781 T(test_memcg_current),
782 T(test_memcg_min),
783 T(test_memcg_low),
784 T(test_memcg_high),
785 T(test_memcg_max),
786 T(test_memcg_oom_events),
478b2784 787 T(test_memcg_swap_max),
84092dbc
RG
788};
789#undef T
790
791int main(int argc, char **argv)
792{
793 char root[PATH_MAX];
794 int i, ret = EXIT_SUCCESS;
795
796 if (cg_find_unified_root(root, sizeof(root)))
797 ksft_exit_skip("cgroup v2 isn't mounted\n");
798
799 /*
800 * Check that memory controller is available:
801 * memory is listed in cgroup.controllers
802 */
803 if (cg_read_strstr(root, "cgroup.controllers", "memory"))
804 ksft_exit_skip("memory controller isn't available\n");
805
806 for (i = 0; i < ARRAY_SIZE(tests); i++) {
807 switch (tests[i].fn(root)) {
808 case KSFT_PASS:
809 ksft_test_result_pass("%s\n", tests[i].name);
810 break;
811 case KSFT_SKIP:
812 ksft_test_result_skip("%s\n", tests[i].name);
813 break;
814 default:
815 ret = EXIT_FAILURE;
816 ksft_test_result_fail("%s\n", tests[i].name);
817 break;
818 }
819 }
820
821 return ret;
822}