Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/tools/testing/selftests/cgroup/test_freezer.c
25923 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
#define T(x) { x, #x }
808
struct 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
825
int 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), NULL))
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
850