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

source code of linux/tools/testing/selftests/cgroup/test_freezer.c