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 */
cg_check_frozen(const char * cgroup,bool frozen)27 static 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 * Check the cgroup.events::frozen value.
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 * Freeze the given cgroup.
49 */
cg_freeze_nowait(const char * cgroup,bool freeze)50 static int cg_freeze_nowait(const char *cgroup, bool freeze)
51 {
52 return cg_write(cgroup, "cgroup.freeze", 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 */
cg_enter_and_wait_for_frozen(const char * cgroup,int pid,bool frozen)59 static 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
83 out:
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 */
cg_freeze_wait(const char * cgroup,bool freeze)93 static 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);
112 out:
113 close(fd);
114 return ret;
115 }
116
117 /*
118 * A simple process running in a sleep loop until being
119 * re-parented.
120 */
child_fn(const char * cgroup,void * arg)121 static 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 */
test_cgfreezer_simple(const char * root)136 static 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
166 cleanup:
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 */
test_cgfreezer_tree(const char * root)188 static 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 * Wait until all child processes will enter
246 * corresponding cgroups.
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 * Freeze B.
256 */
257 if (cg_freeze_wait(cgroup[1], true))
258 goto cleanup;
259
260 /*
261 * Freeze F.
262 */
263 if (cg_freeze_wait(cgroup[5], true))
264 goto cleanup;
265
266 /*
267 * Freeze G.
268 */
269 if (cg_freeze_wait(cgroup[6], true))
270 goto cleanup;
271
272 /*
273 * Check that A and E are not frozen.
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 * Freeze A. Check that A, B and E are frozen.
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 * Unfreeze B, F and G
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 * Check that C and H are still frozen.
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 * Unfreeze A. Check that A, C and K are not frozen.
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
328 cleanup:
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 * A fork bomb emulator.
339 */
forkbomb_fn(const char * cgroup,void * arg)340 static 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 */
test_cgfreezer_forkbomb(const char * root)360 static 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
387 cleanup:
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 */
test_cgfreezer_mkdir(const char * root)399 static 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
437 cleanup:
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 * 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 */
test_cgfreezer_rmdir(const char * root)453 static 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
489 cleanup:
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 * 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 */
test_cgfreezer_migrate(const char * root)508 static 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 * Migrate from A (running) to B (frozen)
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 * Migrate from B (frozen) to A (running)
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 * Migrate from A (frozen) to B (frozen)
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
570 cleanup:
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 * The test checks that ptrace works with a tracing process in a frozen cgroup.
582 */
test_cgfreezer_ptrace(const char * root)583 static 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 * Cgroup has to remain frozen, however the test task
617 * is in traced state.
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
633 cleanup:
634 if (cgroup)
635 cg_destroy(cgroup);
636 free(cgroup);
637 return ret;
638 }
639
640 /*
641 * Check if the process is stopped.
642 */
proc_check_stopped(int pid)643 static 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 * Test that it's possible to freeze a cgroup with a stopped process.
664 */
test_cgfreezer_stopped(const char * root)665 static 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
699 cleanup:
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 */
test_cgfreezer_ptraced(const char * root)709 static 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 * cg_check_frozen(cgroup, true) will fail here,
743 * because the task is in the TRACEd state.
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
756 cleanup:
757 if (cgroup)
758 cg_destroy(cgroup);
759 free(cgroup);
760 return ret;
761 }
762
vfork_fn(const char * cgroup,void * arg)763 static 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 */
test_cgfreezer_vfork(const char * root)778 static 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
800 cleanup:
801 if (cgroup)
802 cg_destroy(cgroup);
803 free(cgroup);
804 return ret;
805 }
806
807 /*
808 * Get the current frozen_usec for the cgroup.
809 */
cg_check_freezetime(const char * cgroup)810 static long cg_check_freezetime(const char *cgroup)
811 {
812 return cg_read_key_long(cgroup, "cgroup.stat.local",
813 "frozen_usec ");
814 }
815
816 /*
817 * Test that the freeze time will behave as expected for an empty cgroup.
818 */
test_cgfreezer_time_empty(const char * root)819 static int test_cgfreezer_time_empty(const char *root)
820 {
821 int ret = KSFT_FAIL;
822 char *cgroup = NULL;
823 long prev, curr;
824
825 cgroup = cg_name(root, "cg_time_test_empty");
826 if (!cgroup)
827 goto cleanup;
828
829 /*
830 * 1) Create an empty cgroup and check that its freeze time
831 * is 0.
832 */
833 if (cg_create(cgroup))
834 goto cleanup;
835
836 curr = cg_check_freezetime(cgroup);
837 if (curr < 0) {
838 ret = KSFT_SKIP;
839 goto cleanup;
840 }
841 if (curr > 0) {
842 debug("Expect time (%ld) to be 0\n", curr);
843 goto cleanup;
844 }
845
846 if (cg_freeze_nowait(cgroup, true))
847 goto cleanup;
848
849 /*
850 * 2) Sleep for 1000 us. Check that the freeze time is at
851 * least 1000 us.
852 */
853 usleep(1000);
854 curr = cg_check_freezetime(cgroup);
855 if (curr < 1000) {
856 debug("Expect time (%ld) to be at least 1000 us\n",
857 curr);
858 goto cleanup;
859 }
860
861 /*
862 * 3) Unfreeze the cgroup. Check that the freeze time is
863 * larger than at 2).
864 */
865 if (cg_freeze_nowait(cgroup, false))
866 goto cleanup;
867 prev = curr;
868 curr = cg_check_freezetime(cgroup);
869 if (curr <= prev) {
870 debug("Expect time (%ld) to be more than previous check (%ld)\n",
871 curr, prev);
872 goto cleanup;
873 }
874
875 /*
876 * 4) Check the freeze time again to ensure that it has not
877 * changed.
878 */
879 prev = curr;
880 curr = cg_check_freezetime(cgroup);
881 if (curr != prev) {
882 debug("Expect time (%ld) to be unchanged from previous check (%ld)\n",
883 curr, prev);
884 goto cleanup;
885 }
886
887 ret = KSFT_PASS;
888
889 cleanup:
890 if (cgroup)
891 cg_destroy(cgroup);
892 free(cgroup);
893 return ret;
894 }
895
896 /*
897 * A simple test for cgroup freezer time accounting. This test follows
898 * the same flow as test_cgfreezer_time_empty, but with a single process
899 * in the cgroup.
900 */
test_cgfreezer_time_simple(const char * root)901 static int test_cgfreezer_time_simple(const char *root)
902 {
903 int ret = KSFT_FAIL;
904 char *cgroup = NULL;
905 long prev, curr;
906
907 cgroup = cg_name(root, "cg_time_test_simple");
908 if (!cgroup)
909 goto cleanup;
910
911 /*
912 * 1) Create a cgroup and check that its freeze time is 0.
913 */
914 if (cg_create(cgroup))
915 goto cleanup;
916
917 curr = cg_check_freezetime(cgroup);
918 if (curr < 0) {
919 ret = KSFT_SKIP;
920 goto cleanup;
921 }
922 if (curr > 0) {
923 debug("Expect time (%ld) to be 0\n", curr);
924 goto cleanup;
925 }
926
927 /*
928 * 2) Populate the cgroup with one child and check that the
929 * freeze time is still 0.
930 */
931 cg_run_nowait(cgroup, child_fn, NULL);
932 prev = curr;
933 curr = cg_check_freezetime(cgroup);
934 if (curr > prev) {
935 debug("Expect time (%ld) to be 0\n", curr);
936 goto cleanup;
937 }
938
939 if (cg_freeze_nowait(cgroup, true))
940 goto cleanup;
941
942 /*
943 * 3) Sleep for 1000 us. Check that the freeze time is at
944 * least 1000 us.
945 */
946 usleep(1000);
947 prev = curr;
948 curr = cg_check_freezetime(cgroup);
949 if (curr < 1000) {
950 debug("Expect time (%ld) to be at least 1000 us\n",
951 curr);
952 goto cleanup;
953 }
954
955 /*
956 * 4) Unfreeze the cgroup. Check that the freeze time is
957 * larger than at 3).
958 */
959 if (cg_freeze_nowait(cgroup, false))
960 goto cleanup;
961 prev = curr;
962 curr = cg_check_freezetime(cgroup);
963 if (curr <= prev) {
964 debug("Expect time (%ld) to be more than previous check (%ld)\n",
965 curr, prev);
966 goto cleanup;
967 }
968
969 /*
970 * 5) Sleep for 1000 us. Check that the freeze time is the
971 * same as at 4).
972 */
973 usleep(1000);
974 prev = curr;
975 curr = cg_check_freezetime(cgroup);
976 if (curr != prev) {
977 debug("Expect time (%ld) to be unchanged from previous check (%ld)\n",
978 curr, prev);
979 goto cleanup;
980 }
981
982 ret = KSFT_PASS;
983
984 cleanup:
985 if (cgroup)
986 cg_destroy(cgroup);
987 free(cgroup);
988 return ret;
989 }
990
991 /*
992 * Test that freezer time accounting works as expected, even while we're
993 * populating a cgroup with processes.
994 */
test_cgfreezer_time_populate(const char * root)995 static int test_cgfreezer_time_populate(const char *root)
996 {
997 int ret = KSFT_FAIL;
998 char *cgroup = NULL;
999 long prev, curr;
1000 int i;
1001
1002 cgroup = cg_name(root, "cg_time_test_populate");
1003 if (!cgroup)
1004 goto cleanup;
1005
1006 if (cg_create(cgroup))
1007 goto cleanup;
1008
1009 curr = cg_check_freezetime(cgroup);
1010 if (curr < 0) {
1011 ret = KSFT_SKIP;
1012 goto cleanup;
1013 }
1014 if (curr > 0) {
1015 debug("Expect time (%ld) to be 0\n", curr);
1016 goto cleanup;
1017 }
1018
1019 /*
1020 * 1) Populate the cgroup with 100 processes. Check that
1021 * the freeze time is 0.
1022 */
1023 for (i = 0; i < 100; i++)
1024 cg_run_nowait(cgroup, child_fn, NULL);
1025 prev = curr;
1026 curr = cg_check_freezetime(cgroup);
1027 if (curr != prev) {
1028 debug("Expect time (%ld) to be 0\n", curr);
1029 goto cleanup;
1030 }
1031
1032 /*
1033 * 2) Wait for the group to become fully populated. Check
1034 * that the freeze time is 0.
1035 */
1036 if (cg_wait_for_proc_count(cgroup, 100))
1037 goto cleanup;
1038 prev = curr;
1039 curr = cg_check_freezetime(cgroup);
1040 if (curr != prev) {
1041 debug("Expect time (%ld) to be 0\n", curr);
1042 goto cleanup;
1043 }
1044
1045 /*
1046 * 3) Freeze the cgroup and then populate it with 100 more
1047 * processes. Check that the freeze time continues to grow.
1048 */
1049 if (cg_freeze_nowait(cgroup, true))
1050 goto cleanup;
1051 prev = curr;
1052 curr = cg_check_freezetime(cgroup);
1053 if (curr <= prev) {
1054 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1055 curr, prev);
1056 goto cleanup;
1057 }
1058
1059 for (i = 0; i < 100; i++)
1060 cg_run_nowait(cgroup, child_fn, NULL);
1061 prev = curr;
1062 curr = cg_check_freezetime(cgroup);
1063 if (curr <= prev) {
1064 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1065 curr, prev);
1066 goto cleanup;
1067 }
1068
1069 /*
1070 * 4) Wait for the group to become fully populated. Check
1071 * that the freeze time is larger than at 3).
1072 */
1073 if (cg_wait_for_proc_count(cgroup, 200))
1074 goto cleanup;
1075 prev = curr;
1076 curr = cg_check_freezetime(cgroup);
1077 if (curr <= prev) {
1078 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1079 curr, prev);
1080 goto cleanup;
1081 }
1082
1083 /*
1084 * 5) Unfreeze the cgroup. Check that the freeze time is
1085 * larger than at 4).
1086 */
1087 if (cg_freeze_nowait(cgroup, false))
1088 goto cleanup;
1089 prev = curr;
1090 curr = cg_check_freezetime(cgroup);
1091 if (curr <= prev) {
1092 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1093 curr, prev);
1094 goto cleanup;
1095 }
1096
1097 /*
1098 * 6) Kill the processes. Check that the freeze time is the
1099 * same as it was at 5).
1100 */
1101 if (cg_killall(cgroup))
1102 goto cleanup;
1103 prev = curr;
1104 curr = cg_check_freezetime(cgroup);
1105 if (curr != prev) {
1106 debug("Expect time (%ld) to be unchanged from previous check (%ld)\n",
1107 curr, prev);
1108 goto cleanup;
1109 }
1110
1111 /*
1112 * 7) Freeze and unfreeze the cgroup. Check that the freeze
1113 * time is larger than it was at 6).
1114 */
1115 if (cg_freeze_nowait(cgroup, true))
1116 goto cleanup;
1117 if (cg_freeze_nowait(cgroup, false))
1118 goto cleanup;
1119 prev = curr;
1120 curr = cg_check_freezetime(cgroup);
1121 if (curr <= prev) {
1122 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1123 curr, prev);
1124 goto cleanup;
1125 }
1126
1127 ret = KSFT_PASS;
1128
1129 cleanup:
1130 if (cgroup)
1131 cg_destroy(cgroup);
1132 free(cgroup);
1133 return ret;
1134 }
1135
1136 /*
1137 * Test that frozen time for a cgroup continues to work as expected,
1138 * even as processes are migrated. Frozen cgroup A's freeze time should
1139 * continue to increase and running cgroup B's should stay 0.
1140 */
test_cgfreezer_time_migrate(const char * root)1141 static int test_cgfreezer_time_migrate(const char *root)
1142 {
1143 long prev_A, curr_A, curr_B;
1144 char *cgroup[2] = {0};
1145 int ret = KSFT_FAIL;
1146 int pid;
1147
1148 cgroup[0] = cg_name(root, "cg_time_test_migrate_A");
1149 if (!cgroup[0])
1150 goto cleanup;
1151
1152 cgroup[1] = cg_name(root, "cg_time_test_migrate_B");
1153 if (!cgroup[1])
1154 goto cleanup;
1155
1156 if (cg_create(cgroup[0]))
1157 goto cleanup;
1158
1159 if (cg_check_freezetime(cgroup[0]) < 0) {
1160 ret = KSFT_SKIP;
1161 goto cleanup;
1162 }
1163
1164 if (cg_create(cgroup[1]))
1165 goto cleanup;
1166
1167 pid = cg_run_nowait(cgroup[0], child_fn, NULL);
1168 if (pid < 0)
1169 goto cleanup;
1170
1171 if (cg_wait_for_proc_count(cgroup[0], 1))
1172 goto cleanup;
1173
1174 curr_A = cg_check_freezetime(cgroup[0]);
1175 if (curr_A) {
1176 debug("Expect time (%ld) to be 0\n", curr_A);
1177 goto cleanup;
1178 }
1179 curr_B = cg_check_freezetime(cgroup[1]);
1180 if (curr_B) {
1181 debug("Expect time (%ld) to be 0\n", curr_B);
1182 goto cleanup;
1183 }
1184
1185 /*
1186 * Freeze cgroup A.
1187 */
1188 if (cg_freeze_wait(cgroup[0], true))
1189 goto cleanup;
1190 prev_A = curr_A;
1191 curr_A = cg_check_freezetime(cgroup[0]);
1192 if (curr_A <= prev_A) {
1193 debug("Expect time (%ld) to be > 0\n", curr_A);
1194 goto cleanup;
1195 }
1196
1197 /*
1198 * Migrate from A (frozen) to B (running).
1199 */
1200 if (cg_enter(cgroup[1], pid))
1201 goto cleanup;
1202
1203 usleep(1000);
1204 curr_B = cg_check_freezetime(cgroup[1]);
1205 if (curr_B) {
1206 debug("Expect time (%ld) to be 0\n", curr_B);
1207 goto cleanup;
1208 }
1209
1210 prev_A = curr_A;
1211 curr_A = cg_check_freezetime(cgroup[0]);
1212 if (curr_A <= prev_A) {
1213 debug("Expect time (%ld) to be more than previous check (%ld)\n",
1214 curr_A, prev_A);
1215 goto cleanup;
1216 }
1217
1218 ret = KSFT_PASS;
1219
1220 cleanup:
1221 if (cgroup[0])
1222 cg_destroy(cgroup[0]);
1223 free(cgroup[0]);
1224 if (cgroup[1])
1225 cg_destroy(cgroup[1]);
1226 free(cgroup[1]);
1227 return ret;
1228 }
1229
1230 /*
1231 * The test creates a cgroup and freezes it. Then it creates a child cgroup.
1232 * After that it checks that the child cgroup has a non-zero freeze time
1233 * that is less than the parent's. Next, it freezes the child, unfreezes
1234 * the parent, and sleeps. Finally, it checks that the child's freeze
1235 * time has grown larger than the parent's.
1236 */
test_cgfreezer_time_parent(const char * root)1237 static int test_cgfreezer_time_parent(const char *root)
1238 {
1239 char *parent, *child = NULL;
1240 int ret = KSFT_FAIL;
1241 long ptime, ctime;
1242
1243 parent = cg_name(root, "cg_test_parent_A");
1244 if (!parent)
1245 goto cleanup;
1246
1247 child = cg_name(parent, "cg_test_parent_B");
1248 if (!child)
1249 goto cleanup;
1250
1251 if (cg_create(parent))
1252 goto cleanup;
1253
1254 if (cg_check_freezetime(parent) < 0) {
1255 ret = KSFT_SKIP;
1256 goto cleanup;
1257 }
1258
1259 if (cg_freeze_wait(parent, true))
1260 goto cleanup;
1261
1262 usleep(1000);
1263 if (cg_create(child))
1264 goto cleanup;
1265
1266 if (cg_check_frozen(child, true))
1267 goto cleanup;
1268
1269 /*
1270 * Since the parent was frozen the entire time the child cgroup
1271 * was being created, we expect the parent's freeze time to be
1272 * larger than the child's.
1273 *
1274 * Ideally, we would be able to check both times simultaneously,
1275 * but here we get the child's after we get the parent's.
1276 */
1277 ptime = cg_check_freezetime(parent);
1278 ctime = cg_check_freezetime(child);
1279 if (ptime <= ctime) {
1280 debug("Expect ptime (%ld) > ctime (%ld)\n", ptime, ctime);
1281 goto cleanup;
1282 }
1283
1284 if (cg_freeze_nowait(child, true))
1285 goto cleanup;
1286
1287 if (cg_freeze_wait(parent, false))
1288 goto cleanup;
1289
1290 if (cg_check_frozen(child, true))
1291 goto cleanup;
1292
1293 usleep(100000);
1294
1295 ctime = cg_check_freezetime(child);
1296 ptime = cg_check_freezetime(parent);
1297
1298 if (ctime <= ptime) {
1299 debug("Expect ctime (%ld) > ptime (%ld)\n", ctime, ptime);
1300 goto cleanup;
1301 }
1302
1303 ret = KSFT_PASS;
1304
1305 cleanup:
1306 if (child)
1307 cg_destroy(child);
1308 free(child);
1309 if (parent)
1310 cg_destroy(parent);
1311 free(parent);
1312 return ret;
1313 }
1314
1315 /*
1316 * The test creates a parent cgroup and a child cgroup. Then, it freezes
1317 * the child and checks that the child's freeze time is greater than the
1318 * parent's, which should be zero.
1319 */
test_cgfreezer_time_child(const char * root)1320 static int test_cgfreezer_time_child(const char *root)
1321 {
1322 char *parent, *child = NULL;
1323 int ret = KSFT_FAIL;
1324 long ptime, ctime;
1325
1326 parent = cg_name(root, "cg_test_child_A");
1327 if (!parent)
1328 goto cleanup;
1329
1330 child = cg_name(parent, "cg_test_child_B");
1331 if (!child)
1332 goto cleanup;
1333
1334 if (cg_create(parent))
1335 goto cleanup;
1336
1337 if (cg_check_freezetime(parent) < 0) {
1338 ret = KSFT_SKIP;
1339 goto cleanup;
1340 }
1341
1342 if (cg_create(child))
1343 goto cleanup;
1344
1345 if (cg_freeze_wait(child, true))
1346 goto cleanup;
1347
1348 ctime = cg_check_freezetime(child);
1349 ptime = cg_check_freezetime(parent);
1350 if (ptime != 0) {
1351 debug("Expect ptime (%ld) to be 0\n", ptime);
1352 goto cleanup;
1353 }
1354
1355 if (ctime <= ptime) {
1356 debug("Expect ctime (%ld) <= ptime (%ld)\n", ctime, ptime);
1357 goto cleanup;
1358 }
1359
1360 ret = KSFT_PASS;
1361
1362 cleanup:
1363 if (child)
1364 cg_destroy(child);
1365 free(child);
1366 if (parent)
1367 cg_destroy(parent);
1368 free(parent);
1369 return ret;
1370 }
1371
1372 /*
1373 * The test creates the following hierarchy:
1374 * A
1375 * |
1376 * B
1377 * |
1378 * C
1379 *
1380 * Then it freezes the cgroups in the order C, B, A.
1381 * Then it unfreezes the cgroups in the order A, B, C.
1382 * Then it checks that C's freeze time is larger than B's and
1383 * that B's is larger than A's.
1384 */
test_cgfreezer_time_nested(const char * root)1385 static int test_cgfreezer_time_nested(const char *root)
1386 {
1387 char *cgroup[3] = {0};
1388 int ret = KSFT_FAIL;
1389 long time[3] = {0};
1390 int i;
1391
1392 cgroup[0] = cg_name(root, "cg_test_time_A");
1393 if (!cgroup[0])
1394 goto cleanup;
1395
1396 cgroup[1] = cg_name(cgroup[0], "B");
1397 if (!cgroup[1])
1398 goto cleanup;
1399
1400 cgroup[2] = cg_name(cgroup[1], "C");
1401 if (!cgroup[2])
1402 goto cleanup;
1403
1404 if (cg_create(cgroup[0]))
1405 goto cleanup;
1406
1407 if (cg_check_freezetime(cgroup[0]) < 0) {
1408 ret = KSFT_SKIP;
1409 goto cleanup;
1410 }
1411
1412 if (cg_create(cgroup[1]))
1413 goto cleanup;
1414
1415 if (cg_create(cgroup[2]))
1416 goto cleanup;
1417
1418 if (cg_freeze_nowait(cgroup[2], true))
1419 goto cleanup;
1420
1421 if (cg_freeze_nowait(cgroup[1], true))
1422 goto cleanup;
1423
1424 if (cg_freeze_nowait(cgroup[0], true))
1425 goto cleanup;
1426
1427 usleep(1000);
1428
1429 if (cg_freeze_nowait(cgroup[0], false))
1430 goto cleanup;
1431
1432 if (cg_freeze_nowait(cgroup[1], false))
1433 goto cleanup;
1434
1435 if (cg_freeze_nowait(cgroup[2], false))
1436 goto cleanup;
1437
1438 time[2] = cg_check_freezetime(cgroup[2]);
1439 time[1] = cg_check_freezetime(cgroup[1]);
1440 time[0] = cg_check_freezetime(cgroup[0]);
1441
1442 if (time[2] <= time[1]) {
1443 debug("Expect C's time (%ld) > B's time (%ld)", time[2], time[1]);
1444 goto cleanup;
1445 }
1446
1447 if (time[1] <= time[0]) {
1448 debug("Expect B's time (%ld) > A's time (%ld)", time[1], time[0]);
1449 goto cleanup;
1450 }
1451
1452 ret = KSFT_PASS;
1453
1454 cleanup:
1455 for (i = 2; i >= 0 && cgroup[i]; i--) {
1456 cg_destroy(cgroup[i]);
1457 free(cgroup[i]);
1458 }
1459
1460 return ret;
1461 }
1462
1463 #define T(x) { x, #x }
1464 struct cgfreezer_test {
1465 int (*fn)(const char *root);
1466 const char *name;
1467 } tests[] = {
1468 T(test_cgfreezer_simple),
1469 T(test_cgfreezer_tree),
1470 T(test_cgfreezer_forkbomb),
1471 T(test_cgfreezer_mkdir),
1472 T(test_cgfreezer_rmdir),
1473 T(test_cgfreezer_migrate),
1474 T(test_cgfreezer_ptrace),
1475 T(test_cgfreezer_stopped),
1476 T(test_cgfreezer_ptraced),
1477 T(test_cgfreezer_vfork),
1478 T(test_cgfreezer_time_empty),
1479 T(test_cgfreezer_time_simple),
1480 T(test_cgfreezer_time_populate),
1481 T(test_cgfreezer_time_migrate),
1482 T(test_cgfreezer_time_parent),
1483 T(test_cgfreezer_time_child),
1484 T(test_cgfreezer_time_nested),
1485 };
1486 #undef T
1487
main(int argc,char * argv[])1488 int main(int argc, char *argv[])
1489 {
1490 char root[PATH_MAX];
1491 int i, ret = EXIT_SUCCESS;
1492
1493 if (cg_find_unified_root(root, sizeof(root), NULL))
1494 ksft_exit_skip("cgroup v2 isn't mounted\n");
1495 for (i = 0; i < ARRAY_SIZE(tests); i++) {
1496 switch (tests[i].fn(root)) {
1497 case KSFT_PASS:
1498 ksft_test_result_pass("%s\n", tests[i].name);
1499 break;
1500 case KSFT_SKIP:
1501 ksft_test_result_skip("%s\n", tests[i].name);
1502 break;
1503 default:
1504 ret = EXIT_FAILURE;
1505 ksft_test_result_fail("%s\n", tests[i].name);
1506 break;
1507 }
1508 }
1509
1510 return ret;
1511 }
1512