1
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
26
27static int cg_check_frozen(const char *cgroup, bool frozen)
28{
29 if (frozen) {
30 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {
31 debug("Cgroup %s isn't frozen\n", cgroup);
32 return -1;
33 }
34 } else {
35
36
37
38 if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {
39 debug("Cgroup %s is frozen\n", cgroup);
40 return -1;
41 }
42 }
43
44 return 0;
45}
46
47
48
49
50static int cg_freeze_nowait(const char *cgroup, bool freeze)
51{
52 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
53}
54
55
56
57
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
90
91
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, freeze);
112out:
113 close(fd);
114 return ret;
115}
116
117
118
119
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
133
134
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, "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, child_fn, NULL);
151
152 if (cg_wait_for_proc_count(cgroup, 100))
153 goto cleanup;
154
155 if (cg_check_frozen(cgroup, false))
156 goto cleanup;
157
158 if (cg_freeze_wait(cgroup, true))
159 goto cleanup;
160
161 if (cg_freeze_wait(cgroup, 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
175
176
177
178
179
180
181
182
183
184
185
186
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, "cg_test_tree_A");
195 if (!cgroup[0])
196 goto cleanup;
197
198 cgroup[1] = cg_name(cgroup[0], "B");
199 if (!cgroup[1])
200 goto cleanup;
201
202 cgroup[2] = cg_name(cgroup[1], "C");
203 if (!cgroup[2])
204 goto cleanup;
205
206 cgroup[3] = cg_name(cgroup[1], "D");
207 if (!cgroup[3])
208 goto cleanup;
209
210 cgroup[4] = cg_name(cgroup[0], "E");
211 if (!cgroup[4])
212 goto cleanup;
213
214 cgroup[5] = cg_name(cgroup[4], "F");
215 if (!cgroup[5])
216 goto cleanup;
217
218 cgroup[6] = cg_name(cgroup[5], "G");
219 if (!cgroup[6])
220 goto cleanup;
221
222 cgroup[7] = cg_name(cgroup[6], "H");
223 if (!cgroup[7])
224 goto cleanup;
225
226 cgroup[8] = cg_name(cgroup[0], "I");
227 if (!cgroup[8])
228 goto cleanup;
229
230 cgroup[9] = cg_name(cgroup[0], "K");
231 if (!cgroup[9])
232 goto cleanup;
233
234 for (i = 0; i < 10; i++)
235 if (cg_create(cgroup[i]))
236 goto cleanup;
237
238 cg_run_nowait(cgroup[2], child_fn, NULL);
239 cg_run_nowait(cgroup[7], child_fn, NULL);
240 cg_run_nowait(cgroup[9], child_fn, NULL);
241 cg_run_nowait(cgroup[9], child_fn, NULL);
242 cg_run_nowait(cgroup[9], child_fn, NULL);
243
244
245
246
247
248
249 if (cg_wait_for_proc_count(cgroup[2], 1) ||
250 cg_wait_for_proc_count(cgroup[7], 1) ||
251 cg_wait_for_proc_count(cgroup[9], 3))
252 goto cleanup;
253
254
255
256
257 if (cg_freeze_wait(cgroup[1], true))
258 goto cleanup;
259
260
261
262
263 if (cg_freeze_wait(cgroup[5], true))
264 goto cleanup;
265
266
267
268
269 if (cg_freeze_wait(cgroup[6], true))
270 goto cleanup;
271
272
273
274
275 if (cg_check_frozen(cgroup[0], false))
276 goto cleanup;
277
278 if (cg_check_frozen(cgroup[4], false))
279 goto cleanup;
280
281
282
283
284 if (cg_freeze_wait(cgroup[0], true))
285 goto cleanup;
286
287 if (cg_check_frozen(cgroup[1], true))
288 goto cleanup;
289
290 if (cg_check_frozen(cgroup[4], true))
291 goto cleanup;
292
293
294
295
296 if (cg_freeze_nowait(cgroup[1], false))
297 goto cleanup;
298
299 if (cg_freeze_nowait(cgroup[5], false))
300 goto cleanup;
301
302 if (cg_freeze_nowait(cgroup[6], false))
303 goto cleanup;
304
305
306
307
308 if (cg_check_frozen(cgroup[2], true))
309 goto cleanup;
310
311 if (cg_check_frozen(cgroup[7], true))
312 goto cleanup;
313
314
315
316
317 if (cg_freeze_wait(cgroup[0], false))
318 goto cleanup;
319
320 if (cg_check_frozen(cgroup[2], false))
321 goto cleanup;
322
323 if (cg_check_frozen(cgroup[9], false))
324 goto cleanup;
325
326 ret = KSFT_PASS;
327
328cleanup:
329 for (i = 9; i >= 0 && cgroup[i]; i--) {
330 cg_destroy(cgroup[i]);
331 free(cgroup[i]);
332 }
333
334 return ret;
335}
336
337
338
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
357
358
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, "cg_forkbomb_test");
366 if (!cgroup)
367 goto cleanup;
368
369 if (cg_create(cgroup))
370 goto cleanup;
371
372 cg_run_nowait(cgroup, forkbomb_fn, NULL);
373
374 usleep(100000);
375
376 if (cg_freeze_wait(cgroup, true))
377 goto cleanup;
378
379 if (cg_killall(cgroup))
380 goto cleanup;
381
382 if (cg_wait_for_proc_count(cgroup, 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
396
397
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, "cg_test_mkdir_A");
406 if (!parent)
407 goto cleanup;
408
409 child = cg_name(parent, "cg_test_mkdir_B");
410 if (!child)
411 goto cleanup;
412
413 if (cg_create(parent))
414 goto cleanup;
415
416 if (cg_freeze_wait(parent, true))
417 goto cleanup;
418
419 if (cg_create(child))
420 goto cleanup;
421
422 pid = cg_run_nowait(child, child_fn, NULL);
423 if (pid < 0)
424 goto cleanup;
425
426 if (cg_wait_for_proc_count(child, 1))
427 goto cleanup;
428
429 if (cg_check_frozen(child, true))
430 goto cleanup;
431
432 if (cg_check_frozen(parent, true))
433 goto cleanup;
434
435 ret = KSFT_PASS;
436
437cleanup:
438 if (child)
439 cg_destroy(child);
440 free(child);
441 if (parent)
442 cg_destroy(parent);
443 free(parent);
444 return ret;
445}
446
447
448
449
450
451
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, "cg_test_rmdir_A");
459 if (!parent)
460 goto cleanup;
461
462 child = cg_name(parent, "cg_test_rmdir_B");
463 if (!child)
464 goto cleanup;
465
466 if (cg_create(parent))
467 goto cleanup;
468
469 if (cg_create(child))
470 goto cleanup;
471
472 if (cg_freeze_wait(parent, true))
473 goto cleanup;
474
475 if (cg_destroy(child))
476 goto cleanup;
477
478 if (cg_check_frozen(parent, true))
479 goto cleanup;
480
481 if (cg_create(child))
482 goto cleanup;
483
484 if (cg_check_frozen(child, true))
485 goto cleanup;
486
487 ret = KSFT_PASS;
488
489cleanup:
490 if (child)
491 cg_destroy(child);
492 free(child);
493 if (parent)
494 cg_destroy(parent);
495 free(parent);
496 return ret;
497}
498
499
500
501
502
503
504
505
506
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, "cg_test_migrate_A");
515 if (!cgroup[0])
516 goto cleanup;
517
518 cgroup[1] = cg_name(root, "cg_test_migrate_B");
519 if (!cgroup[1])
520 goto cleanup;
521
522 if (cg_create(cgroup[0]))
523 goto cleanup;
524
525 if (cg_create(cgroup[1]))
526 goto cleanup;
527
528 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
529 if (pid < 0)
530 goto cleanup;
531
532 if (cg_wait_for_proc_count(cgroup[0], 1))
533 goto cleanup;
534
535
536
537
538 if (cg_freeze_wait(cgroup[1], true))
539 goto cleanup;
540
541 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
542 goto cleanup;
543
544 if (cg_check_frozen(cgroup[0], false))
545 goto cleanup;
546
547
548
549
550 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
551 goto cleanup;
552
553 if (cg_check_frozen(cgroup[1], true))
554 goto cleanup;
555
556
557
558
559 if (cg_freeze_wait(cgroup[0], true))
560 goto cleanup;
561
562 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
563 goto cleanup;
564
565 if (cg_check_frozen(cgroup[0], true))
566 goto cleanup;
567
568 ret = KSFT_PASS;
569
570cleanup:
571 if (cgroup[0])
572 cg_destroy(cgroup[0]);
573 free(cgroup[0]);
574 if (cgroup[1])
575 cg_destroy(cgroup[1]);
576 free(cgroup[1]);
577 return ret;
578}
579
580
581
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, "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, child_fn, NULL);
598 if (pid < 0)
599 goto cleanup;
600
601 if (cg_wait_for_proc_count(cgroup, 1))
602 goto cleanup;
603
604 if (cg_freeze_wait(cgroup, 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
617
618
619 if (cg_check_frozen(cgroup, 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, 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
642
643static int proc_check_stopped(int pid)
644{
645 char buf[PAGE_SIZE];
646 int len;
647
648 len = proc_read_text(pid, 0, "stat", buf, 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
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, "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, child_fn, NULL);
678
679 if (cg_wait_for_proc_count(cgroup, 1))
680 goto cleanup;
681
682 if (kill(pid, SIGSTOP))
683 goto cleanup;
684
685 if (cg_check_frozen(cgroup, false))
686 goto cleanup;
687
688 if (cg_freeze_wait(cgroup, true))
689 goto cleanup;
690
691 if (cg_freeze_wait(cgroup, 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
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, "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, child_fn, NULL);
723
724 if (cg_wait_for_proc_count(cgroup, 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, false))
736 goto cleanup;
737
738 if (cg_freeze_wait(cgroup, true))
739 goto cleanup;
740
741
742
743
744
745 if (cg_freeze_wait(cgroup, 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
776
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, "cg_test_vfork");
784 if (!cgroup)
785 goto cleanup;
786
787 if (cg_create(cgroup))
788 goto cleanup;
789
790 cg_run_nowait(cgroup, vfork_fn, NULL);
791
792 if (cg_wait_for_proc_count(cgroup, 2))
793 goto cleanup;
794
795 if (cg_freeze_wait(cgroup, 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, sizeof(root)))
831 ksft_exit_skip("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("%s\n", tests[i].name);
836 break;
837 case KSFT_SKIP:
838 ksft_test_result_skip("%s\n", tests[i].name);
839 break;
840 default:
841 ret = EXIT_FAILURE;
842 ksft_test_result_fail("%s\n", tests[i].name);
843 break;
844 }
845 }
846
847 return ret;
848}
849