This source file includes following definitions.
- cg_check_frozen
- cg_freeze_nowait
- cg_prepare_for_wait
- cg_wait_for
- cg_enter_and_wait_for_frozen
- cg_freeze_wait
- child_fn
- test_cgfreezer_simple
- test_cgfreezer_tree
- forkbomb_fn
- test_cgfreezer_forkbomb
- test_cgfreezer_mkdir
- test_cgfreezer_rmdir
- test_cgfreezer_migrate
- test_cgfreezer_ptrace
- proc_check_stopped
- test_cgfreezer_stopped
- test_cgfreezer_ptraced
- vfork_fn
- test_cgfreezer_vfork
- main
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 <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
28
29 static 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
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
51
52 static int cg_freeze_nowait(const char *cgroup, bool freeze)
53 {
54 return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");
55 }
56
57
58
59
60 static 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 fd = -1;
76 }
77
78 return fd;
79 }
80
81
82
83
84
85 static int cg_wait_for(int fd)
86 {
87 int ret = -1;
88 struct pollfd fds = {
89 .fd = fd,
90 .events = POLLIN,
91 };
92
93 while (true) {
94 ret = poll(&fds, 1, 10000);
95
96 if (ret == -1) {
97 if (errno == EINTR)
98 continue;
99 debug("Error: poll() failed\n");
100 break;
101 }
102
103 if (ret > 0 && fds.revents & POLLIN) {
104 ret = 0;
105 break;
106 }
107 }
108
109 return ret;
110 }
111
112
113
114
115
116 static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,
117 bool frozen)
118 {
119 int fd, ret = -1;
120 int attempts;
121
122 fd = cg_prepare_for_wait(cgroup);
123 if (fd < 0)
124 return fd;
125
126 ret = cg_enter(cgroup, pid);
127 if (ret)
128 goto out;
129
130 for (attempts = 0; attempts < 10; attempts++) {
131 ret = cg_wait_for(fd);
132 if (ret)
133 break;
134
135 ret = cg_check_frozen(cgroup, frozen);
136 if (ret)
137 continue;
138 }
139
140 out:
141 close(fd);
142 return ret;
143 }
144
145
146
147
148
149
150 static int cg_freeze_wait(const char *cgroup, bool freeze)
151 {
152 int fd, ret = -1;
153
154 fd = cg_prepare_for_wait(cgroup);
155 if (fd < 0)
156 return fd;
157
158 ret = cg_freeze_nowait(cgroup, freeze);
159 if (ret) {
160 debug("Error: cg_freeze_nowait() failed\n");
161 goto out;
162 }
163
164 ret = cg_wait_for(fd);
165 if (ret)
166 goto out;
167
168 ret = cg_check_frozen(cgroup, freeze);
169 out:
170 close(fd);
171 return ret;
172 }
173
174
175
176
177
178 static int child_fn(const char *cgroup, void *arg)
179 {
180 int ppid = getppid();
181
182 while (getppid() == ppid)
183 usleep(1000);
184
185 return getppid() == ppid;
186 }
187
188
189
190
191
192
193 static int test_cgfreezer_simple(const char *root)
194 {
195 int ret = KSFT_FAIL;
196 char *cgroup = NULL;
197 int i;
198
199 cgroup = cg_name(root, "cg_test_simple");
200 if (!cgroup)
201 goto cleanup;
202
203 if (cg_create(cgroup))
204 goto cleanup;
205
206 for (i = 0; i < 100; i++)
207 cg_run_nowait(cgroup, child_fn, NULL);
208
209 if (cg_wait_for_proc_count(cgroup, 100))
210 goto cleanup;
211
212 if (cg_check_frozen(cgroup, false))
213 goto cleanup;
214
215 if (cg_freeze_wait(cgroup, true))
216 goto cleanup;
217
218 if (cg_freeze_wait(cgroup, false))
219 goto cleanup;
220
221 ret = KSFT_PASS;
222
223 cleanup:
224 if (cgroup)
225 cg_destroy(cgroup);
226 free(cgroup);
227 return ret;
228 }
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245 static int test_cgfreezer_tree(const char *root)
246 {
247 char *cgroup[10] = {0};
248 int ret = KSFT_FAIL;
249 int i;
250
251 cgroup[0] = cg_name(root, "cg_test_tree_A");
252 if (!cgroup[0])
253 goto cleanup;
254
255 cgroup[1] = cg_name(cgroup[0], "B");
256 if (!cgroup[1])
257 goto cleanup;
258
259 cgroup[2] = cg_name(cgroup[1], "C");
260 if (!cgroup[2])
261 goto cleanup;
262
263 cgroup[3] = cg_name(cgroup[1], "D");
264 if (!cgroup[3])
265 goto cleanup;
266
267 cgroup[4] = cg_name(cgroup[0], "E");
268 if (!cgroup[4])
269 goto cleanup;
270
271 cgroup[5] = cg_name(cgroup[4], "F");
272 if (!cgroup[5])
273 goto cleanup;
274
275 cgroup[6] = cg_name(cgroup[5], "G");
276 if (!cgroup[6])
277 goto cleanup;
278
279 cgroup[7] = cg_name(cgroup[6], "H");
280 if (!cgroup[7])
281 goto cleanup;
282
283 cgroup[8] = cg_name(cgroup[0], "I");
284 if (!cgroup[8])
285 goto cleanup;
286
287 cgroup[9] = cg_name(cgroup[0], "K");
288 if (!cgroup[9])
289 goto cleanup;
290
291 for (i = 0; i < 10; i++)
292 if (cg_create(cgroup[i]))
293 goto cleanup;
294
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);
300
301
302
303
304
305
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;
310
311
312
313
314 if (cg_freeze_wait(cgroup[1], true))
315 goto cleanup;
316
317
318
319
320 if (cg_freeze_wait(cgroup[5], true))
321 goto cleanup;
322
323
324
325
326 if (cg_freeze_wait(cgroup[6], true))
327 goto cleanup;
328
329
330
331
332 if (cg_check_frozen(cgroup[0], false))
333 goto cleanup;
334
335 if (cg_check_frozen(cgroup[4], false))
336 goto cleanup;
337
338
339
340
341 if (cg_freeze_wait(cgroup[0], true))
342 goto cleanup;
343
344 if (cg_check_frozen(cgroup[1], true))
345 goto cleanup;
346
347 if (cg_check_frozen(cgroup[4], true))
348 goto cleanup;
349
350
351
352
353 if (cg_freeze_nowait(cgroup[1], false))
354 goto cleanup;
355
356 if (cg_freeze_nowait(cgroup[5], false))
357 goto cleanup;
358
359 if (cg_freeze_nowait(cgroup[6], false))
360 goto cleanup;
361
362
363
364
365 if (cg_check_frozen(cgroup[2], true))
366 goto cleanup;
367
368 if (cg_check_frozen(cgroup[7], true))
369 goto cleanup;
370
371
372
373
374 if (cg_freeze_wait(cgroup[0], false))
375 goto cleanup;
376
377 if (cg_check_frozen(cgroup[2], false))
378 goto cleanup;
379
380 if (cg_check_frozen(cgroup[9], false))
381 goto cleanup;
382
383 ret = KSFT_PASS;
384
385 cleanup:
386 for (i = 9; i >= 0 && cgroup[i]; i--) {
387 cg_destroy(cgroup[i]);
388 free(cgroup[i]);
389 }
390
391 return ret;
392 }
393
394
395
396
397 static int forkbomb_fn(const char *cgroup, void *arg)
398 {
399 int ppid;
400
401 fork();
402 fork();
403
404 ppid = getppid();
405
406 while (getppid() == ppid)
407 usleep(1000);
408
409 return getppid() == ppid;
410 }
411
412
413
414
415
416
417 static int test_cgfreezer_forkbomb(const char *root)
418 {
419 int ret = KSFT_FAIL;
420 char *cgroup = NULL;
421
422 cgroup = cg_name(root, "cg_forkbomb_test");
423 if (!cgroup)
424 goto cleanup;
425
426 if (cg_create(cgroup))
427 goto cleanup;
428
429 cg_run_nowait(cgroup, forkbomb_fn, NULL);
430
431 usleep(100000);
432
433 if (cg_freeze_wait(cgroup, true))
434 goto cleanup;
435
436 if (cg_killall(cgroup))
437 goto cleanup;
438
439 if (cg_wait_for_proc_count(cgroup, 0))
440 goto cleanup;
441
442 ret = KSFT_PASS;
443
444 cleanup:
445 if (cgroup)
446 cg_destroy(cgroup);
447 free(cgroup);
448 return ret;
449 }
450
451
452
453
454
455
456 static int test_cgfreezer_mkdir(const char *root)
457 {
458 int ret = KSFT_FAIL;
459 char *parent, *child = NULL;
460 int pid;
461
462 parent = cg_name(root, "cg_test_mkdir_A");
463 if (!parent)
464 goto cleanup;
465
466 child = cg_name(parent, "cg_test_mkdir_B");
467 if (!child)
468 goto cleanup;
469
470 if (cg_create(parent))
471 goto cleanup;
472
473 if (cg_freeze_wait(parent, true))
474 goto cleanup;
475
476 if (cg_create(child))
477 goto cleanup;
478
479 pid = cg_run_nowait(child, child_fn, NULL);
480 if (pid < 0)
481 goto cleanup;
482
483 if (cg_wait_for_proc_count(child, 1))
484 goto cleanup;
485
486 if (cg_check_frozen(child, true))
487 goto cleanup;
488
489 if (cg_check_frozen(parent, true))
490 goto cleanup;
491
492 ret = KSFT_PASS;
493
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;
502 }
503
504
505
506
507
508
509
510 static int test_cgfreezer_rmdir(const char *root)
511 {
512 int ret = KSFT_FAIL;
513 char *parent, *child = NULL;
514
515 parent = cg_name(root, "cg_test_rmdir_A");
516 if (!parent)
517 goto cleanup;
518
519 child = cg_name(parent, "cg_test_rmdir_B");
520 if (!child)
521 goto cleanup;
522
523 if (cg_create(parent))
524 goto cleanup;
525
526 if (cg_create(child))
527 goto cleanup;
528
529 if (cg_freeze_wait(parent, true))
530 goto cleanup;
531
532 if (cg_destroy(child))
533 goto cleanup;
534
535 if (cg_check_frozen(parent, true))
536 goto cleanup;
537
538 if (cg_create(child))
539 goto cleanup;
540
541 if (cg_check_frozen(child, true))
542 goto cleanup;
543
544 ret = KSFT_PASS;
545
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;
554 }
555
556
557
558
559
560
561
562
563
564
565 static int test_cgfreezer_migrate(const char *root)
566 {
567 int ret = KSFT_FAIL;
568 char *cgroup[2] = {0};
569 int pid;
570
571 cgroup[0] = cg_name(root, "cg_test_migrate_A");
572 if (!cgroup[0])
573 goto cleanup;
574
575 cgroup[1] = cg_name(root, "cg_test_migrate_B");
576 if (!cgroup[1])
577 goto cleanup;
578
579 if (cg_create(cgroup[0]))
580 goto cleanup;
581
582 if (cg_create(cgroup[1]))
583 goto cleanup;
584
585 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
586 if (pid < 0)
587 goto cleanup;
588
589 if (cg_wait_for_proc_count(cgroup[0], 1))
590 goto cleanup;
591
592
593
594
595 if (cg_freeze_wait(cgroup[1], true))
596 goto cleanup;
597
598 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
599 goto cleanup;
600
601 if (cg_check_frozen(cgroup[0], false))
602 goto cleanup;
603
604
605
606
607 if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))
608 goto cleanup;
609
610 if (cg_check_frozen(cgroup[1], true))
611 goto cleanup;
612
613
614
615
616 if (cg_freeze_wait(cgroup[0], true))
617 goto cleanup;
618
619 if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))
620 goto cleanup;
621
622 if (cg_check_frozen(cgroup[0], true))
623 goto cleanup;
624
625 ret = KSFT_PASS;
626
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;
635 }
636
637
638
639
640 static int test_cgfreezer_ptrace(const char *root)
641 {
642 int ret = KSFT_FAIL;
643 char *cgroup = NULL;
644 siginfo_t siginfo;
645 int pid;
646
647 cgroup = cg_name(root, "cg_test_ptrace");
648 if (!cgroup)
649 goto cleanup;
650
651 if (cg_create(cgroup))
652 goto cleanup;
653
654 pid = cg_run_nowait(cgroup, child_fn, NULL);
655 if (pid < 0)
656 goto cleanup;
657
658 if (cg_wait_for_proc_count(cgroup, 1))
659 goto cleanup;
660
661 if (cg_freeze_wait(cgroup, true))
662 goto cleanup;
663
664 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
665 goto cleanup;
666
667 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
668 goto cleanup;
669
670 waitpid(pid, NULL, 0);
671
672
673
674
675
676 if (cg_check_frozen(cgroup, true))
677 goto cleanup;
678
679 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
680 goto cleanup;
681
682 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
683 goto cleanup;
684
685 if (cg_check_frozen(cgroup, true))
686 goto cleanup;
687
688 ret = KSFT_PASS;
689
690 cleanup:
691 if (cgroup)
692 cg_destroy(cgroup);
693 free(cgroup);
694 return ret;
695 }
696
697
698
699
700 static int proc_check_stopped(int pid)
701 {
702 char buf[PAGE_SIZE];
703 int len;
704
705 len = proc_read_text(pid, "stat", buf, sizeof(buf));
706 if (len == -1) {
707 debug("Can't get %d stat\n", pid);
708 return -1;
709 }
710
711 if (strstr(buf, "(test_freezer) T ") == NULL) {
712 debug("Process %d in the unexpected state: %s\n", pid, buf);
713 return -1;
714 }
715
716 return 0;
717 }
718
719
720
721
722 static int test_cgfreezer_stopped(const char *root)
723 {
724 int pid, ret = KSFT_FAIL;
725 char *cgroup = NULL;
726
727 cgroup = cg_name(root, "cg_test_stopped");
728 if (!cgroup)
729 goto cleanup;
730
731 if (cg_create(cgroup))
732 goto cleanup;
733
734 pid = cg_run_nowait(cgroup, child_fn, NULL);
735
736 if (cg_wait_for_proc_count(cgroup, 1))
737 goto cleanup;
738
739 if (kill(pid, SIGSTOP))
740 goto cleanup;
741
742 if (cg_check_frozen(cgroup, false))
743 goto cleanup;
744
745 if (cg_freeze_wait(cgroup, true))
746 goto cleanup;
747
748 if (cg_freeze_wait(cgroup, false))
749 goto cleanup;
750
751 if (proc_check_stopped(pid))
752 goto cleanup;
753
754 ret = KSFT_PASS;
755
756 cleanup:
757 if (cgroup)
758 cg_destroy(cgroup);
759 free(cgroup);
760 return ret;
761 }
762
763
764
765
766 static int test_cgfreezer_ptraced(const char *root)
767 {
768 int pid, ret = KSFT_FAIL;
769 char *cgroup = NULL;
770 siginfo_t siginfo;
771
772 cgroup = cg_name(root, "cg_test_ptraced");
773 if (!cgroup)
774 goto cleanup;
775
776 if (cg_create(cgroup))
777 goto cleanup;
778
779 pid = cg_run_nowait(cgroup, child_fn, NULL);
780
781 if (cg_wait_for_proc_count(cgroup, 1))
782 goto cleanup;
783
784 if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))
785 goto cleanup;
786
787 if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))
788 goto cleanup;
789
790 waitpid(pid, NULL, 0);
791
792 if (cg_check_frozen(cgroup, false))
793 goto cleanup;
794
795 if (cg_freeze_wait(cgroup, true))
796 goto cleanup;
797
798
799
800
801
802 if (cg_freeze_wait(cgroup, false))
803 goto cleanup;
804
805 if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))
806 goto cleanup;
807
808 if (ptrace(PTRACE_DETACH, pid, NULL, NULL))
809 goto cleanup;
810
811 ret = KSFT_PASS;
812
813 cleanup:
814 if (cgroup)
815 cg_destroy(cgroup);
816 free(cgroup);
817 return ret;
818 }
819
820 static int vfork_fn(const char *cgroup, void *arg)
821 {
822 int pid = vfork();
823
824 if (pid == 0)
825 while (true)
826 sleep(1);
827
828 return pid;
829 }
830
831
832
833
834
835 static int test_cgfreezer_vfork(const char *root)
836 {
837 int ret = KSFT_FAIL;
838 char *cgroup = NULL;
839
840 cgroup = cg_name(root, "cg_test_vfork");
841 if (!cgroup)
842 goto cleanup;
843
844 if (cg_create(cgroup))
845 goto cleanup;
846
847 cg_run_nowait(cgroup, vfork_fn, NULL);
848
849 if (cg_wait_for_proc_count(cgroup, 2))
850 goto cleanup;
851
852 if (cg_freeze_wait(cgroup, true))
853 goto cleanup;
854
855 ret = KSFT_PASS;
856
857 cleanup:
858 if (cgroup)
859 cg_destroy(cgroup);
860 free(cgroup);
861 return ret;
862 }
863
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),
879 };
880 #undef T
881
882 int main(int argc, char *argv[])
883 {
884 char root[PATH_MAX];
885 int i, ret = EXIT_SUCCESS;
886
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;
901 }
902 }
903
904 return ret;
905 }