Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/cgroup/test_freezer.c
29270 views
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
*/
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
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
1513