WIP FPC-III support
[linux/fpc-iii.git] / tools / testing / selftests / cgroup / test_freezer.c
blob23d8fa4a3e4e1ff8f668b4808ad4279d61557ee6
1 /* SPDX-License-Identifier: GPL-2.0 */
2 #include <stdbool.h>
3 #include <linux/limits.h>
4 #include <sys/ptrace.h>
5 #include <sys/types.h>
6 #include <sys/mman.h>
7 #include <unistd.h>
8 #include <stdio.h>
9 #include <errno.h>
10 #include <poll.h>
11 #include <stdlib.h>
12 #include <sys/inotify.h>
13 #include <string.h>
14 #include <sys/wait.h>
16 #include "../kselftest.h"
17 #include "cgroup_util.h"
19 #define DEBUG
20 #ifdef DEBUG
21 #define debug(args...) fprintf(stderr, args)
22 #else
23 #define debug(args...)
24 #endif
27 * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
29 static int cg_check_frozen(const char *cgroup, bool frozen)
31 if (frozen) {
32 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
33 debug("Cgroup %s isn't frozen\n", cgroup);
34 return -1;
36 } else {
38 * Check the cgroup.events::frozen value.
40 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
41 debug("Cgroup %s is frozen\n", cgroup);
42 return -1;
46 return 0;
50 * Freeze the given cgroup.
52 static int cg_freeze_nowait(const char *cgroup, bool freeze)
54 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
58 * Prepare for waiting on cgroup.events file.
60 static int cg_prepare_for_wait(const char *cgroup)
62 int fd, ret = -1;
64 fd = inotify_init1(0);
65 if (fd == -1) {
66 debug("Error: inotify_init1() failed\n");
67 return fd;
70 ret = inotify_add_watch(fd, cg_control(cgroup, "cgroup.events"),
71 IN_MODIFY);
72 if (ret == -1) {
73 debug("Error: inotify_add_watch() failed\n");
74 close(fd);
75 fd = -1;
78 return fd;
82 * Wait for an event. If there are no events for 10 seconds,
83 * treat this an error.
85 static int cg_wait_for(int fd)
87 int ret = -1;
88 struct pollfd fds = {
89 .fd = fd,
90 .events = POLLIN,
93 while (true) {
94 ret = poll(&fds, 1, 10000);
96 if (ret == -1) {
97 if (errno == EINTR)
98 continue;
99 debug("Error: poll() failed\n");
100 break;
103 if (ret > 0 && fds.revents & POLLIN) {
104 ret = 0;
105 break;
109 return ret;
113 * Attach a task to the given cgroup and wait for a cgroup frozen event.
114 * All transient events (e.g. populated) are ignored.
116 static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
117 bool frozen)
119 int fd, ret = -1;
120 int attempts;
122 fd = cg_prepare_for_wait(cgroup);
123 if (fd < 0)
124 return fd;
126 ret = cg_enter(cgroup, pid);
127 if (ret)
128 goto out;
130 for (attempts = 0; attempts < 10; attempts++) {
131 ret = cg_wait_for(fd);
132 if (ret)
133 break;
135 ret = cg_check_frozen(cgroup, frozen);
136 if (ret)
137 continue;
140 out:
141 close(fd);
142 return ret;
146 * Freeze the given cgroup and wait for the inotify signal.
147 * If there are no events in 10 seconds, treat this as an error.
148 * Then check that the cgroup is in the desired state.
150 static int cg_freeze_wait(const char *cgroup, bool freeze)
152 int fd, ret = -1;
154 fd = cg_prepare_for_wait(cgroup);
155 if (fd < 0)
156 return fd;
158 ret = cg_freeze_nowait(cgroup, freeze);
159 if (ret) {
160 debug("Error: cg_freeze_nowait() failed\n");
161 goto out;
164 ret = cg_wait_for(fd);
165 if (ret)
166 goto out;
168 ret = cg_check_frozen(cgroup, freeze);
169 out:
170 close(fd);
171 return ret;
175 * A simple process running in a sleep loop until being
176 * re-parented.
178 static int child_fn(const char *cgroup, void *arg)
180 int ppid = getppid();
182 while (getppid() == ppid)
183 usleep(1000);
185 return getppid() == ppid;
189 * A simple test for the cgroup freezer: populated the cgroup with 100
190 * running processes and freeze it. Then unfreeze it. Then it kills all
191 * processes and destroys the cgroup.
193 static int test_cgfreezer_simple(const char *root)
195 int ret = KSFT_FAIL;
196 char *cgroup = NULL;
197 int i;
199 cgroup = cg_name(root, "cg_test_simple");
200 if (!cgroup)
201 goto cleanup;
203 if (cg_create(cgroup))
204 goto cleanup;
206 for (i = 0; i < 100; i++)
207 cg_run_nowait(cgroup, child_fn, NULL);
209 if (cg_wait_for_proc_count(cgroup, 100))
210 goto cleanup;
212 if (cg_check_frozen(cgroup, false))
213 goto cleanup;
215 if (cg_freeze_wait(cgroup, true))
216 goto cleanup;
218 if (cg_freeze_wait(cgroup, false))
219 goto cleanup;
221 ret = KSFT_PASS;
223 cleanup:
224 if (cgroup)
225 cg_destroy(cgroup);
226 free(cgroup);
227 return ret;
231 * The test creates the following hierarchy:
233 * / / \ \
234 * B E I K
235 * /\ |
236 * C D F
242 * with a process in C, H and 3 processes in K.
243 * Then it tries to freeze and unfreeze the whole tree.
245 static int test_cgfreezer_tree(const char *root)
247 char *cgroup[10] = {0};
248 int ret = KSFT_FAIL;
249 int i;
251 cgroup[0] = cg_name(root, "cg_test_tree_A");
252 if (!cgroup[0])
253 goto cleanup;
255 cgroup[1] = cg_name(cgroup[0], "B");
256 if (!cgroup[1])
257 goto cleanup;
259 cgroup[2] = cg_name(cgroup[1], "C");
260 if (!cgroup[2])
261 goto cleanup;
263 cgroup[3] = cg_name(cgroup[1], "D");
264 if (!cgroup[3])
265 goto cleanup;
267 cgroup[4] = cg_name(cgroup[0], "E");
268 if (!cgroup[4])
269 goto cleanup;
271 cgroup[5] = cg_name(cgroup[4], "F");
272 if (!cgroup[5])
273 goto cleanup;
275 cgroup[6] = cg_name(cgroup[5], "G");
276 if (!cgroup[6])
277 goto cleanup;
279 cgroup[7] = cg_name(cgroup[6], "H");
280 if (!cgroup[7])
281 goto cleanup;
283 cgroup[8] = cg_name(cgroup[0], "I");
284 if (!cgroup[8])
285 goto cleanup;
287 cgroup[9] = cg_name(cgroup[0], "K");
288 if (!cgroup[9])
289 goto cleanup;
291 for (i = 0; i < 10; i++)
292 if (cg_create(cgroup[i]))
293 goto cleanup;
295 cg_run_nowait(cgroup[2], child_fn, NULL);
296 cg_run_nowait(cgroup[7], child_fn, NULL);
297 cg_run_nowait(cgroup[9], child_fn, NULL);
298 cg_run_nowait(cgroup[9], child_fn, NULL);
299 cg_run_nowait(cgroup[9], child_fn, NULL);
302 * Wait until all child processes will enter
303 * corresponding cgroups.
306 if (cg_wait_for_proc_count(cgroup[2], 1) ||
307 cg_wait_for_proc_count(cgroup[7], 1) ||
308 cg_wait_for_proc_count(cgroup[9], 3))
309 goto cleanup;
312 * Freeze B.
314 if (cg_freeze_wait(cgroup[1], true))
315 goto cleanup;
318 * Freeze F.
320 if (cg_freeze_wait(cgroup[5], true))
321 goto cleanup;
324 * Freeze G.
326 if (cg_freeze_wait(cgroup[6], true))
327 goto cleanup;
330 * Check that A and E are not frozen.
332 if (cg_check_frozen(cgroup[0], false))
333 goto cleanup;
335 if (cg_check_frozen(cgroup[4], false))
336 goto cleanup;
339 * Freeze A. Check that A, B and E are frozen.
341 if (cg_freeze_wait(cgroup[0], true))
342 goto cleanup;
344 if (cg_check_frozen(cgroup[1], true))
345 goto cleanup;
347 if (cg_check_frozen(cgroup[4], true))
348 goto cleanup;
351 * Unfreeze B, F and G
353 if (cg_freeze_nowait(cgroup[1], false))
354 goto cleanup;
356 if (cg_freeze_nowait(cgroup[5], false))
357 goto cleanup;
359 if (cg_freeze_nowait(cgroup[6], false))
360 goto cleanup;
363 * Check that C and H are still frozen.
365 if (cg_check_frozen(cgroup[2], true))
366 goto cleanup;
368 if (cg_check_frozen(cgroup[7], true))
369 goto cleanup;
372 * Unfreeze A. Check that A, C and K are not frozen.
374 if (cg_freeze_wait(cgroup[0], false))
375 goto cleanup;
377 if (cg_check_frozen(cgroup[2], false))
378 goto cleanup;
380 if (cg_check_frozen(cgroup[9], false))
381 goto cleanup;
383 ret = KSFT_PASS;
385 cleanup:
386 for (i = 9; i >= 0 && cgroup[i]; i--) {
387 cg_destroy(cgroup[i]);
388 free(cgroup[i]);
391 return ret;
395 * A fork bomb emulator.
397 static int forkbomb_fn(const char *cgroup, void *arg)
399 int ppid;
401 fork();
402 fork();
404 ppid = getppid();
406 while (getppid() == ppid)
407 usleep(1000);
409 return getppid() == ppid;
413 * The test runs a fork bomb in a cgroup and tries to freeze it.
414 * Then it kills all processes and checks that cgroup isn't populated
415 * anymore.
417 static int test_cgfreezer_forkbomb(const char *root)
419 int ret = KSFT_FAIL;
420 char *cgroup = NULL;
422 cgroup = cg_name(root, "cg_forkbomb_test");
423 if (!cgroup)
424 goto cleanup;
426 if (cg_create(cgroup))
427 goto cleanup;
429 cg_run_nowait(cgroup, forkbomb_fn, NULL);
431 usleep(100000);
433 if (cg_freeze_wait(cgroup, true))
434 goto cleanup;
436 if (cg_killall(cgroup))
437 goto cleanup;
439 if (cg_wait_for_proc_count(cgroup, 0))
440 goto cleanup;
442 ret = KSFT_PASS;
444 cleanup:
445 if (cgroup)
446 cg_destroy(cgroup);
447 free(cgroup);
448 return ret;
452 * The test creates a cgroups and freezes it. Then it creates a child cgroup
453 * and populates it with a task. After that it checks that the child cgroup
454 * is frozen and the parent cgroup remains frozen too.
456 static int test_cgfreezer_mkdir(const char *root)
458 int ret = KSFT_FAIL;
459 char *parent, *child = NULL;
460 int pid;
462 parent = cg_name(root, "cg_test_mkdir_A");
463 if (!parent)
464 goto cleanup;
466 child = cg_name(parent, "cg_test_mkdir_B");
467 if (!child)
468 goto cleanup;
470 if (cg_create(parent))
471 goto cleanup;
473 if (cg_freeze_wait(parent, true))
474 goto cleanup;
476 if (cg_create(child))
477 goto cleanup;
479 pid = cg_run_nowait(child, child_fn, NULL);
480 if (pid < 0)
481 goto cleanup;
483 if (cg_wait_for_proc_count(child, 1))
484 goto cleanup;
486 if (cg_check_frozen(child, true))
487 goto cleanup;
489 if (cg_check_frozen(parent, true))
490 goto cleanup;
492 ret = KSFT_PASS;
494 cleanup:
495 if (child)
496 cg_destroy(child);
497 free(child);
498 if (parent)
499 cg_destroy(parent);
500 free(parent);
501 return ret;
505 * The test creates two nested cgroups, freezes the parent
506 * and removes the child. Then it checks that the parent cgroup
507 * remains frozen and it's possible to create a new child
508 * without unfreezing. The new child is frozen too.
510 static int test_cgfreezer_rmdir(const char *root)
512 int ret = KSFT_FAIL;
513 char *parent, *child = NULL;
515 parent = cg_name(root, "cg_test_rmdir_A");
516 if (!parent)
517 goto cleanup;
519 child = cg_name(parent, "cg_test_rmdir_B");
520 if (!child)
521 goto cleanup;
523 if (cg_create(parent))
524 goto cleanup;
526 if (cg_create(child))
527 goto cleanup;
529 if (cg_freeze_wait(parent, true))
530 goto cleanup;
532 if (cg_destroy(child))
533 goto cleanup;
535 if (cg_check_frozen(parent, true))
536 goto cleanup;
538 if (cg_create(child))
539 goto cleanup;
541 if (cg_check_frozen(child, true))
542 goto cleanup;
544 ret = KSFT_PASS;
546 cleanup:
547 if (child)
548 cg_destroy(child);
549 free(child);
550 if (parent)
551 cg_destroy(parent);
552 free(parent);
553 return ret;
557 * The test creates two cgroups: A and B, runs a process in A
558 * and performs several migrations:
559 * 1) A (running) -> B (frozen)
560 * 2) B (frozen) -> A (running)
561 * 3) A (frozen) -> B (frozen)
563 * On each step it checks the actual state of both cgroups.
565 static int test_cgfreezer_migrate(const char *root)
567 int ret = KSFT_FAIL;
568 char *cgroup[2] = {0};
569 int pid;
571 cgroup[0] = cg_name(root, "cg_test_migrate_A");
572 if (!cgroup[0])
573 goto cleanup;
575 cgroup[1] = cg_name(root, "cg_test_migrate_B");
576 if (!cgroup[1])
577 goto cleanup;
579 if (cg_create(cgroup[0]))
580 goto cleanup;
582 if (cg_create(cgroup[1]))
583 goto cleanup;
585 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
586 if (pid < 0)
587 goto cleanup;
589 if (cg_wait_for_proc_count(cgroup[0], 1))
590 goto cleanup;
593 * Migrate from A (running) to B (frozen)
595 if (cg_freeze_wait(cgroup[1], true))
596 goto cleanup;
598 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
599 goto cleanup;
601 if (cg_check_frozen(cgroup[0], false))
602 goto cleanup;
605 * Migrate from B (frozen) to A (running)
607 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
608 goto cleanup;
610 if (cg_check_frozen(cgroup[1], true))
611 goto cleanup;
614 * Migrate from A (frozen) to B (frozen)
616 if (cg_freeze_wait(cgroup[0], true))
617 goto cleanup;
619 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
620 goto cleanup;
622 if (cg_check_frozen(cgroup[0], true))
623 goto cleanup;
625 ret = KSFT_PASS;
627 cleanup:
628 if (cgroup[0])
629 cg_destroy(cgroup[0]);
630 free(cgroup[0]);
631 if (cgroup[1])
632 cg_destroy(cgroup[1]);
633 free(cgroup[1]);
634 return ret;
638 * The test checks that ptrace works with a tracing process in a frozen cgroup.
640 static int test_cgfreezer_ptrace(const char *root)
642 int ret = KSFT_FAIL;
643 char *cgroup = NULL;
644 siginfo_t siginfo;
645 int pid;
647 cgroup = cg_name(root, "cg_test_ptrace");
648 if (!cgroup)
649 goto cleanup;
651 if (cg_create(cgroup))
652 goto cleanup;
654 pid = cg_run_nowait(cgroup, child_fn, NULL);
655 if (pid < 0)
656 goto cleanup;
658 if (cg_wait_for_proc_count(cgroup, 1))
659 goto cleanup;
661 if (cg_freeze_wait(cgroup, true))
662 goto cleanup;
664 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
665 goto cleanup;
667 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
668 goto cleanup;
670 waitpid(pid, NULL, 0);
673 * Cgroup has to remain frozen, however the test task
674 * is in traced state.
676 if (cg_check_frozen(cgroup, true))
677 goto cleanup;
679 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
680 goto cleanup;
682 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
683 goto cleanup;
685 if (cg_check_frozen(cgroup, true))
686 goto cleanup;
688 ret = KSFT_PASS;
690 cleanup:
691 if (cgroup)
692 cg_destroy(cgroup);
693 free(cgroup);
694 return ret;
698 * Check if the process is stopped.
700 static int proc_check_stopped(int pid)
702 char buf[PAGE_SIZE];
703 int len;
705 len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));
706 if (len == -1) {
707 debug("Can't get %d stat\n", pid);
708 return -1;
711 if (strstr(buf, "(test_freezer) T ") == NULL) {
712 debug("Process %d in the unexpected state: %s\n", pid, buf);
713 return -1;
716 return 0;
720 * Test that it's possible to freeze a cgroup with a stopped process.
722 static int test_cgfreezer_stopped(const char *root)
724 int pid, ret = KSFT_FAIL;
725 char *cgroup = NULL;
727 cgroup = cg_name(root, "cg_test_stopped");
728 if (!cgroup)
729 goto cleanup;
731 if (cg_create(cgroup))
732 goto cleanup;
734 pid = cg_run_nowait(cgroup, child_fn, NULL);
736 if (cg_wait_for_proc_count(cgroup, 1))
737 goto cleanup;
739 if (kill(pid, SIGSTOP))
740 goto cleanup;
742 if (cg_check_frozen(cgroup, false))
743 goto cleanup;
745 if (cg_freeze_wait(cgroup, true))
746 goto cleanup;
748 if (cg_freeze_wait(cgroup, false))
749 goto cleanup;
751 if (proc_check_stopped(pid))
752 goto cleanup;
754 ret = KSFT_PASS;
756 cleanup:
757 if (cgroup)
758 cg_destroy(cgroup);
759 free(cgroup);
760 return ret;
764 * Test that it's possible to freeze a cgroup with a ptraced process.
766 static int test_cgfreezer_ptraced(const char *root)
768 int pid, ret = KSFT_FAIL;
769 char *cgroup = NULL;
770 siginfo_t siginfo;
772 cgroup = cg_name(root, "cg_test_ptraced");
773 if (!cgroup)
774 goto cleanup;
776 if (cg_create(cgroup))
777 goto cleanup;
779 pid = cg_run_nowait(cgroup, child_fn, NULL);
781 if (cg_wait_for_proc_count(cgroup, 1))
782 goto cleanup;
784 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
785 goto cleanup;
787 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
788 goto cleanup;
790 waitpid(pid, NULL, 0);
792 if (cg_check_frozen(cgroup, false))
793 goto cleanup;
795 if (cg_freeze_wait(cgroup, true))
796 goto cleanup;
799 * cg_check_frozen(cgroup, true) will fail here,
800 * because the task in in the TRACEd state.
802 if (cg_freeze_wait(cgroup, false))
803 goto cleanup;
805 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
806 goto cleanup;
808 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
809 goto cleanup;
811 ret = KSFT_PASS;
813 cleanup:
814 if (cgroup)
815 cg_destroy(cgroup);
816 free(cgroup);
817 return ret;
820 static int vfork_fn(const char *cgroup, void *arg)
822 int pid = vfork();
824 if (pid == 0)
825 while (true)
826 sleep(1);
828 return pid;
832 * Test that it's possible to freeze a cgroup with a process,
833 * which called vfork() and is waiting for a child.
835 static int test_cgfreezer_vfork(const char *root)
837 int ret = KSFT_FAIL;
838 char *cgroup = NULL;
840 cgroup = cg_name(root, "cg_test_vfork");
841 if (!cgroup)
842 goto cleanup;
844 if (cg_create(cgroup))
845 goto cleanup;
847 cg_run_nowait(cgroup, vfork_fn, NULL);
849 if (cg_wait_for_proc_count(cgroup, 2))
850 goto cleanup;
852 if (cg_freeze_wait(cgroup, true))
853 goto cleanup;
855 ret = KSFT_PASS;
857 cleanup:
858 if (cgroup)
859 cg_destroy(cgroup);
860 free(cgroup);
861 return ret;
864 #define T(x) { x, #x }
865 struct cgfreezer_test {
866 int (*fn)(const char *root);
867 const char *name;
868 } tests[] = {
869 T(test_cgfreezer_simple),
870 T(test_cgfreezer_tree),
871 T(test_cgfreezer_forkbomb),
872 T(test_cgfreezer_mkdir),
873 T(test_cgfreezer_rmdir),
874 T(test_cgfreezer_migrate),
875 T(test_cgfreezer_ptrace),
876 T(test_cgfreezer_stopped),
877 T(test_cgfreezer_ptraced),
878 T(test_cgfreezer_vfork),
880 #undef T
882 int main(int argc, char *argv[])
884 char root[PATH_MAX];
885 int i, ret = EXIT_SUCCESS;
887 if (cg_find_unified_root(root, sizeof(root)))
888 ksft_exit_skip("cgroup v2 isn't mounted\n");
889 for (i = 0; i < ARRAY_SIZE(tests); i++) {
890 switch (tests[i].fn(root)) {
891 case KSFT_PASS:
892 ksft_test_result_pass("%s\n", tests[i].name);
893 break;
894 case KSFT_SKIP:
895 ksft_test_result_skip("%s\n", tests[i].name);
896 break;
897 default:
898 ret = EXIT_FAILURE;
899 ksft_test_result_fail("%s\n", tests[i].name);
900 break;
904 return ret;