Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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>
15
16#include "../kselftest.h"
17#include "cgroup_util.h"
18
19#define DEBUG
20#ifdef DEBUG
21#define debug(args...) fprintf(stderr, args)
22#else
23#define debug(args...)
24#endif
25
26/*
27 * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.
28 */
29static int cg_check_frozen(const char *cgroup, bool frozen)
30{
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;
35 }
36 } else {
37 /*
38 * Check the cgroup.events::frozen value.
39 */
40 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
41 debug("Cgroup %s is frozen\n", cgroup);
42 return -1;
43 }
44 }
45
46 return 0;
47}
48
49/*
50 * Freeze the given cgroup.
51 */
52static int cg_freeze_nowait(const char *cgroup, bool freeze)
53{
54 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
55}
56
57/*
58 * Prepare for waiting on cgroup.events file.
59 */
60static int cg_prepare_for_wait(const char *cgroup)
61{
62 int fd, ret = -1;
63
64 fd = inotify_init1(0);
65 if (fd == -1) {
66 debug("Error: inotify_init1() failed\n");
67 return fd;
68 }
69
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 }
76
77 return fd;
78}
79
80/*
81 * Wait for an event. If there are no events for 10 seconds,
82 * treat this an error.
83 */
84static int cg_wait_for(int fd)
85{
86 int ret = -1;
87 struct pollfd fds = {
88 .fd = fd,
89 .events = POLLIN,
90 };
91
92 while (true) {
93 ret = poll(&fds, 1, 10000);
94
95 if (ret == -1) {
96 if (errno == EINTR)
97 continue;
98 debug("Error: poll() failed\n");
99 break;
100 }
101
102 if (ret > 0 && fds.revents & POLLIN) {
103 ret = 0;
104 break;
105 }
106 }
107
108 return ret;
109}
110
111/*
112 * Attach a task to the given cgroup and wait for a cgroup frozen event.
113 * All transient events (e.g. populated) are ignored.
114 */
115static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
116 bool frozen)
117{
118 int fd, ret = -1;
119 int attempts;
120
121 fd = cg_prepare_for_wait(cgroup);
122 if (fd < 0)
123 return fd;
124
125 ret = cg_enter(cgroup, pid);
126 if (ret)
127 goto out;
128
129 for (attempts = 0; attempts < 10; attempts++) {
130 ret = cg_wait_for(fd);
131 if (ret)
132 break;
133
134 ret = cg_check_frozen(cgroup, frozen);
135 if (ret)
136 continue;
137 }
138
139out:
140 close(fd);
141 return ret;
142}
143
144/*
145 * Freeze the given cgroup and wait for the inotify signal.
146 * If there are no events in 10 seconds, treat this as an error.
147 * Then check that the cgroup is in the desired state.
148 */
149static int cg_freeze_wait(const char *cgroup, bool freeze)
150{
151 int fd, ret = -1;
152
153 fd = cg_prepare_for_wait(cgroup);
154 if (fd < 0)
155 return fd;
156
157 ret = cg_freeze_nowait(cgroup, freeze);
158 if (ret) {
159 debug("Error: cg_freeze_nowait() failed\n");
160 goto out;
161 }
162
163 ret = cg_wait_for(fd);
164 if (ret)
165 goto out;
166
167 ret = cg_check_frozen(cgroup, freeze);
168out:
169 close(fd);
170 return ret;
171}
172
173/*
174 * A simple process running in a sleep loop until being
175 * re-parented.
176 */
177static int child_fn(const char *cgroup, void *arg)
178{
179 int ppid = getppid();
180
181 while (getppid() == ppid)
182 usleep(1000);
183
184 return getppid() == ppid;
185}
186
187/*
188 * A simple test for the cgroup freezer: populated the cgroup with 100
189 * running processes and freeze it. Then unfreeze it. Then it kills all
190 * processes and destroys the cgroup.
191 */
192static int test_cgfreezer_simple(const char *root)
193{
194 int ret = KSFT_FAIL;
195 char *cgroup = NULL;
196 int i;
197
198 cgroup = cg_name(root, "cg_test_simple");
199 if (!cgroup)
200 goto cleanup;
201
202 if (cg_create(cgroup))
203 goto cleanup;
204
205 for (i = 0; i < 100; i++)
206 cg_run_nowait(cgroup, child_fn, NULL);
207
208 if (cg_wait_for_proc_count(cgroup, 100))
209 goto cleanup;
210
211 if (cg_check_frozen(cgroup, false))
212 goto cleanup;
213
214 if (cg_freeze_wait(cgroup, true))
215 goto cleanup;
216
217 if (cg_freeze_wait(cgroup, false))
218 goto cleanup;
219
220 ret = KSFT_PASS;
221
222cleanup:
223 if (cgroup)
224 cg_destroy(cgroup);
225 free(cgroup);
226 return ret;
227}
228
229/*
230 * The test creates the following hierarchy:
231 * A
232 * / / \ \
233 * B E I K
234 * /\ |
235 * C D F
236 * |
237 * G
238 * |
239 * H
240 *
241 * with a process in C, H and 3 processes in K.
242 * Then it tries to freeze and unfreeze the whole tree.
243 */
244static int test_cgfreezer_tree(const char *root)
245{
246 char *cgroup[10] = {0};
247 int ret = KSFT_FAIL;
248 int i;
249
250 cgroup[0] = cg_name(root, "cg_test_tree_A");
251 if (!cgroup[0])
252 goto cleanup;
253
254 cgroup[1] = cg_name(cgroup[0], "B");
255 if (!cgroup[1])
256 goto cleanup;
257
258 cgroup[2] = cg_name(cgroup[1], "C");
259 if (!cgroup[2])
260 goto cleanup;
261
262 cgroup[3] = cg_name(cgroup[1], "D");
263 if (!cgroup[3])
264 goto cleanup;
265
266 cgroup[4] = cg_name(cgroup[0], "E");
267 if (!cgroup[4])
268 goto cleanup;
269
270 cgroup[5] = cg_name(cgroup[4], "F");
271 if (!cgroup[5])
272 goto cleanup;
273
274 cgroup[6] = cg_name(cgroup[5], "G");
275 if (!cgroup[6])
276 goto cleanup;
277
278 cgroup[7] = cg_name(cgroup[6], "H");
279 if (!cgroup[7])
280 goto cleanup;
281
282 cgroup[8] = cg_name(cgroup[0], "I");
283 if (!cgroup[8])
284 goto cleanup;
285
286 cgroup[9] = cg_name(cgroup[0], "K");
287 if (!cgroup[9])
288 goto cleanup;
289
290 for (i = 0; i < 10; i++)
291 if (cg_create(cgroup[i]))
292 goto cleanup;
293
294 cg_run_nowait(cgroup[2], child_fn, NULL);
295 cg_run_nowait(cgroup[7], child_fn, NULL);
296 cg_run_nowait(cgroup[9], child_fn, NULL);
297 cg_run_nowait(cgroup[9], child_fn, NULL);
298 cg_run_nowait(cgroup[9], child_fn, NULL);
299
300 /*
301 * Wait until all child processes will enter
302 * corresponding cgroups.
303 */
304
305 if (cg_wait_for_proc_count(cgroup[2], 1) ||
306 cg_wait_for_proc_count(cgroup[7], 1) ||
307 cg_wait_for_proc_count(cgroup[9], 3))
308 goto cleanup;
309
310 /*
311 * Freeze B.
312 */
313 if (cg_freeze_wait(cgroup[1], true))
314 goto cleanup;
315
316 /*
317 * Freeze F.
318 */
319 if (cg_freeze_wait(cgroup[5], true))
320 goto cleanup;
321
322 /*
323 * Freeze G.
324 */
325 if (cg_freeze_wait(cgroup[6], true))
326 goto cleanup;
327
328 /*
329 * Check that A and E are not frozen.
330 */
331 if (cg_check_frozen(cgroup[0], false))
332 goto cleanup;
333
334 if (cg_check_frozen(cgroup[4], false))
335 goto cleanup;
336
337 /*
338 * Freeze A. Check that A, B and E are frozen.
339 */
340 if (cg_freeze_wait(cgroup[0], true))
341 goto cleanup;
342
343 if (cg_check_frozen(cgroup[1], true))
344 goto cleanup;
345
346 if (cg_check_frozen(cgroup[4], true))
347 goto cleanup;
348
349 /*
350 * Unfreeze B, F and G
351 */
352 if (cg_freeze_nowait(cgroup[1], false))
353 goto cleanup;
354
355 if (cg_freeze_nowait(cgroup[5], false))
356 goto cleanup;
357
358 if (cg_freeze_nowait(cgroup[6], false))
359 goto cleanup;
360
361 /*
362 * Check that C and H are still frozen.
363 */
364 if (cg_check_frozen(cgroup[2], true))
365 goto cleanup;
366
367 if (cg_check_frozen(cgroup[7], true))
368 goto cleanup;
369
370 /*
371 * Unfreeze A. Check that A, C and K are not frozen.
372 */
373 if (cg_freeze_wait(cgroup[0], false))
374 goto cleanup;
375
376 if (cg_check_frozen(cgroup[2], false))
377 goto cleanup;
378
379 if (cg_check_frozen(cgroup[9], false))
380 goto cleanup;
381
382 ret = KSFT_PASS;
383
384cleanup:
385 for (i = 9; i >= 0 && cgroup[i]; i--) {
386 cg_destroy(cgroup[i]);
387 free(cgroup[i]);
388 }
389
390 return ret;
391}
392
393/*
394 * A fork bomb emulator.
395 */
396static int forkbomb_fn(const char *cgroup, void *arg)
397{
398 int ppid;
399
400 fork();
401 fork();
402
403 ppid = getppid();
404
405 while (getppid() == ppid)
406 usleep(1000);
407
408 return getppid() == ppid;
409}
410
411/*
412 * The test runs a fork bomb in a cgroup and tries to freeze it.
413 * Then it kills all processes and checks that cgroup isn't populated
414 * anymore.
415 */
416static int test_cgfreezer_forkbomb(const char *root)
417{
418 int ret = KSFT_FAIL;
419 char *cgroup = NULL;
420
421 cgroup = cg_name(root, "cg_forkbomb_test");
422 if (!cgroup)
423 goto cleanup;
424
425 if (cg_create(cgroup))
426 goto cleanup;
427
428 cg_run_nowait(cgroup, forkbomb_fn, NULL);
429
430 usleep(100000);
431
432 if (cg_freeze_wait(cgroup, true))
433 goto cleanup;
434
435 if (cg_killall(cgroup))
436 goto cleanup;
437
438 if (cg_wait_for_proc_count(cgroup, 0))
439 goto cleanup;
440
441 ret = KSFT_PASS;
442
443cleanup:
444 if (cgroup)
445 cg_destroy(cgroup);
446 free(cgroup);
447 return ret;
448}
449
450/*
451 * The test creates two nested cgroups, freezes the parent
452 * and removes the child. Then it checks that the parent cgroup
453 * remains frozen and it's possible to create a new child
454 * without unfreezing. The new child is frozen too.
455 */
456static int test_cgfreezer_rmdir(const char *root)
457{
458 int ret = KSFT_FAIL;
459 char *parent, *child = NULL;
460
461 parent = cg_name(root, "cg_test_rmdir_A");
462 if (!parent)
463 goto cleanup;
464
465 child = cg_name(parent, "cg_test_rmdir_B");
466 if (!child)
467 goto cleanup;
468
469 if (cg_create(parent))
470 goto cleanup;
471
472 if (cg_create(child))
473 goto cleanup;
474
475 if (cg_freeze_wait(parent, true))
476 goto cleanup;
477
478 if (cg_destroy(child))
479 goto cleanup;
480
481 if (cg_check_frozen(parent, true))
482 goto cleanup;
483
484 if (cg_create(child))
485 goto cleanup;
486
487 if (cg_check_frozen(child, true))
488 goto cleanup;
489
490 ret = KSFT_PASS;
491
492cleanup:
493 if (child)
494 cg_destroy(child);
495 free(child);
496 if (parent)
497 cg_destroy(parent);
498 free(parent);
499 return ret;
500}
501
502/*
503 * The test creates two cgroups: A and B, runs a process in A
504 * and performs several migrations:
505 * 1) A (running) -> B (frozen)
506 * 2) B (frozen) -> A (running)
507 * 3) A (frozen) -> B (frozen)
508 *
509 * On each step it checks the actual state of both cgroups.
510 */
511static int test_cgfreezer_migrate(const char *root)
512{
513 int ret = KSFT_FAIL;
514 char *cgroup[2] = {0};
515 int pid;
516
517 cgroup[0] = cg_name(root, "cg_test_migrate_A");
518 if (!cgroup[0])
519 goto cleanup;
520
521 cgroup[1] = cg_name(root, "cg_test_migrate_B");
522 if (!cgroup[1])
523 goto cleanup;
524
525 if (cg_create(cgroup[0]))
526 goto cleanup;
527
528 if (cg_create(cgroup[1]))
529 goto cleanup;
530
531 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
532 if (pid < 0)
533 goto cleanup;
534
535 if (cg_wait_for_proc_count(cgroup[0], 1))
536 goto cleanup;
537
538 /*
539 * Migrate from A (running) to B (frozen)
540 */
541 if (cg_freeze_wait(cgroup[1], true))
542 goto cleanup;
543
544 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
545 goto cleanup;
546
547 if (cg_check_frozen(cgroup[0], false))
548 goto cleanup;
549
550 /*
551 * Migrate from B (frozen) to A (running)
552 */
553 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
554 goto cleanup;
555
556 if (cg_check_frozen(cgroup[1], true))
557 goto cleanup;
558
559 /*
560 * Migrate from A (frozen) to B (frozen)
561 */
562 if (cg_freeze_wait(cgroup[0], true))
563 goto cleanup;
564
565 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
566 goto cleanup;
567
568 if (cg_check_frozen(cgroup[0], true))
569 goto cleanup;
570
571 ret = KSFT_PASS;
572
573cleanup:
574 if (cgroup[0])
575 cg_destroy(cgroup[0]);
576 free(cgroup[0]);
577 if (cgroup[1])
578 cg_destroy(cgroup[1]);
579 free(cgroup[1]);
580 return ret;
581}
582
583/*
584 * The test checks that ptrace works with a tracing process in a frozen cgroup.
585 */
586static int test_cgfreezer_ptrace(const char *root)
587{
588 int ret = KSFT_FAIL;
589 char *cgroup = NULL;
590 siginfo_t siginfo;
591 int pid;
592
593 cgroup = cg_name(root, "cg_test_ptrace");
594 if (!cgroup)
595 goto cleanup;
596
597 if (cg_create(cgroup))
598 goto cleanup;
599
600 pid = cg_run_nowait(cgroup, child_fn, NULL);
601 if (pid < 0)
602 goto cleanup;
603
604 if (cg_wait_for_proc_count(cgroup, 1))
605 goto cleanup;
606
607 if (cg_freeze_wait(cgroup, true))
608 goto cleanup;
609
610 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
611 goto cleanup;
612
613 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
614 goto cleanup;
615
616 waitpid(pid, NULL, 0);
617
618 /*
619 * Cgroup has to remain frozen, however the test task
620 * is in traced state.
621 */
622 if (cg_check_frozen(cgroup, true))
623 goto cleanup;
624
625 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
626 goto cleanup;
627
628 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
629 goto cleanup;
630
631 if (cg_check_frozen(cgroup, true))
632 goto cleanup;
633
634 ret = KSFT_PASS;
635
636cleanup:
637 if (cgroup)
638 cg_destroy(cgroup);
639 free(cgroup);
640 return ret;
641}
642
643/*
644 * Check if the process is stopped.
645 */
646static int proc_check_stopped(int pid)
647{
648 char buf[PAGE_SIZE];
649 int len;
650
651 len = proc_read_text(pid, "stat", buf, sizeof(buf));
652 if (len == -1) {
653 debug("Can't get %d stat\n", pid);
654 return -1;
655 }
656
657 if (strstr(buf, "(test_freezer) T ") == NULL) {
658 debug("Process %d in the unexpected state: %s\n", pid, buf);
659 return -1;
660 }
661
662 return 0;
663}
664
665/*
666 * Test that it's possible to freeze a cgroup with a stopped process.
667 */
668static int test_cgfreezer_stopped(const char *root)
669{
670 int pid, ret = KSFT_FAIL;
671 char *cgroup = NULL;
672
673 cgroup = cg_name(root, "cg_test_stopped");
674 if (!cgroup)
675 goto cleanup;
676
677 if (cg_create(cgroup))
678 goto cleanup;
679
680 pid = cg_run_nowait(cgroup, child_fn, NULL);
681
682 if (cg_wait_for_proc_count(cgroup, 1))
683 goto cleanup;
684
685 if (kill(pid, SIGSTOP))
686 goto cleanup;
687
688 if (cg_check_frozen(cgroup, false))
689 goto cleanup;
690
691 if (cg_freeze_wait(cgroup, true))
692 goto cleanup;
693
694 if (cg_freeze_wait(cgroup, false))
695 goto cleanup;
696
697 if (proc_check_stopped(pid))
698 goto cleanup;
699
700 ret = KSFT_PASS;
701
702cleanup:
703 if (cgroup)
704 cg_destroy(cgroup);
705 free(cgroup);
706 return ret;
707}
708
709/*
710 * Test that it's possible to freeze a cgroup with a ptraced process.
711 */
712static int test_cgfreezer_ptraced(const char *root)
713{
714 int pid, ret = KSFT_FAIL;
715 char *cgroup = NULL;
716 siginfo_t siginfo;
717
718 cgroup = cg_name(root, "cg_test_ptraced");
719 if (!cgroup)
720 goto cleanup;
721
722 if (cg_create(cgroup))
723 goto cleanup;
724
725 pid = cg_run_nowait(cgroup, child_fn, NULL);
726
727 if (cg_wait_for_proc_count(cgroup, 1))
728 goto cleanup;
729
730 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
731 goto cleanup;
732
733 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
734 goto cleanup;
735
736 waitpid(pid, NULL, 0);
737
738 if (cg_check_frozen(cgroup, false))
739 goto cleanup;
740
741 if (cg_freeze_wait(cgroup, true))
742 goto cleanup;
743
744 /*
745 * cg_check_frozen(cgroup, true) will fail here,
746 * because the task in in the TRACEd state.
747 */
748 if (cg_freeze_wait(cgroup, false))
749 goto cleanup;
750
751 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
752 goto cleanup;
753
754 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
755 goto cleanup;
756
757 ret = KSFT_PASS;
758
759cleanup:
760 if (cgroup)
761 cg_destroy(cgroup);
762 free(cgroup);
763 return ret;
764}
765
766static int vfork_fn(const char *cgroup, void *arg)
767{
768 int pid = vfork();
769
770 if (pid == 0)
771 while (true)
772 sleep(1);
773
774 return pid;
775}
776
777/*
778 * Test that it's possible to freeze a cgroup with a process,
779 * which called vfork() and is waiting for a child.
780 */
781static int test_cgfreezer_vfork(const char *root)
782{
783 int ret = KSFT_FAIL;
784 char *cgroup = NULL;
785
786 cgroup = cg_name(root, "cg_test_vfork");
787 if (!cgroup)
788 goto cleanup;
789
790 if (cg_create(cgroup))
791 goto cleanup;
792
793 cg_run_nowait(cgroup, vfork_fn, NULL);
794
795 if (cg_wait_for_proc_count(cgroup, 2))
796 goto cleanup;
797
798 if (cg_freeze_wait(cgroup, true))
799 goto cleanup;
800
801 ret = KSFT_PASS;
802
803cleanup:
804 if (cgroup)
805 cg_destroy(cgroup);
806 free(cgroup);
807 return ret;
808}
809
810#define T(x) { x, #x }
811struct cgfreezer_test {
812 int (*fn)(const char *root);
813 const char *name;
814} tests[] = {
815 T(test_cgfreezer_simple),
816 T(test_cgfreezer_tree),
817 T(test_cgfreezer_forkbomb),
818 T(test_cgfreezer_rmdir),
819 T(test_cgfreezer_migrate),
820 T(test_cgfreezer_ptrace),
821 T(test_cgfreezer_stopped),
822 T(test_cgfreezer_ptraced),
823 T(test_cgfreezer_vfork),
824};
825#undef T
826
827int main(int argc, char *argv[])
828{
829 char root[PATH_MAX];
830 int i, ret = EXIT_SUCCESS;
831
832 if (cg_find_unified_root(root, sizeof(root)))
833 ksft_exit_skip("cgroup v2 isn't mounted\n");
834 for (i = 0; i < ARRAY_SIZE(tests); i++) {
835 switch (tests[i].fn(root)) {
836 case KSFT_PASS:
837 ksft_test_result_pass("%s\n", tests[i].name);
838 break;
839 case KSFT_SKIP:
840 ksft_test_result_skip("%s\n", tests[i].name);
841 break;
842 default:
843 ret = EXIT_FAILURE;
844 ksft_test_result_fail("%s\n", tests[i].name);
845 break;
846 }
847 }
848
849 return ret;
850}