]> Git Repo - J-linux.git/blob - tools/testing/selftests/cgroup/test_core.c
Merge tag 'vfs-6.13-rc7.fixes' of git://git.kernel.org/pub/scm/linux/kernel/git/vfs/vfs
[J-linux.git] / tools / testing / selftests / cgroup / test_core.c
1 /* SPDX-License-Identifier: GPL-2.0 */
2
3 #define _GNU_SOURCE
4 #include <linux/limits.h>
5 #include <linux/sched.h>
6 #include <sys/types.h>
7 #include <sys/mman.h>
8 #include <sys/wait.h>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <sched.h>
12 #include <stdio.h>
13 #include <errno.h>
14 #include <signal.h>
15 #include <string.h>
16 #include <pthread.h>
17
18 #include "../kselftest.h"
19 #include "cgroup_util.h"
20
21 static bool nsdelegate;
22
23 static int touch_anon(char *buf, size_t size)
24 {
25         int fd;
26         char *pos = buf;
27
28         fd = open("/dev/urandom", O_RDONLY);
29         if (fd < 0)
30                 return -1;
31
32         while (size > 0) {
33                 ssize_t ret = read(fd, pos, size);
34
35                 if (ret < 0) {
36                         if (errno != EINTR) {
37                                 close(fd);
38                                 return -1;
39                         }
40                 } else {
41                         pos += ret;
42                         size -= ret;
43                 }
44         }
45         close(fd);
46
47         return 0;
48 }
49
50 static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg)
51 {
52         int ppid = getppid();
53         size_t size = (size_t)arg;
54         void *buf;
55
56         buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
57                    0, 0);
58         if (buf == MAP_FAILED)
59                 return -1;
60
61         if (touch_anon((char *)buf, size)) {
62                 munmap(buf, size);
63                 return -1;
64         }
65
66         while (getppid() == ppid)
67                 sleep(1);
68
69         munmap(buf, size);
70         return 0;
71 }
72
73 /*
74  * Create a child process that allocates and touches 100MB, then waits to be
75  * killed. Wait until the child is attached to the cgroup, kill all processes
76  * in that cgroup and wait until "cgroup.procs" is empty. At this point try to
77  * destroy the empty cgroup. The test helps detect race conditions between
78  * dying processes leaving the cgroup and cgroup destruction path.
79  */
80 static int test_cgcore_destroy(const char *root)
81 {
82         int ret = KSFT_FAIL;
83         char *cg_test = NULL;
84         int child_pid;
85         char buf[PAGE_SIZE];
86
87         cg_test = cg_name(root, "cg_test");
88
89         if (!cg_test)
90                 goto cleanup;
91
92         for (int i = 0; i < 10; i++) {
93                 if (cg_create(cg_test))
94                         goto cleanup;
95
96                 child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit,
97                                           (void *) MB(100));
98
99                 if (child_pid < 0)
100                         goto cleanup;
101
102                 /* wait for the child to enter cgroup */
103                 if (cg_wait_for_proc_count(cg_test, 1))
104                         goto cleanup;
105
106                 if (cg_killall(cg_test))
107                         goto cleanup;
108
109                 /* wait for cgroup to be empty */
110                 while (1) {
111                         if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf)))
112                                 goto cleanup;
113                         if (buf[0] == '\0')
114                                 break;
115                         usleep(1000);
116                 }
117
118                 if (rmdir(cg_test))
119                         goto cleanup;
120
121                 if (waitpid(child_pid, NULL, 0) < 0)
122                         goto cleanup;
123         }
124         ret = KSFT_PASS;
125 cleanup:
126         if (cg_test)
127                 cg_destroy(cg_test);
128         free(cg_test);
129         return ret;
130 }
131
132 /*
133  * A(0) - B(0) - C(1)
134  *        \ D(0)
135  *
136  * A, B and C's "populated" fields would be 1 while D's 0.
137  * test that after the one process in C is moved to root,
138  * A,B and C's "populated" fields would flip to "0" and file
139  * modified events will be generated on the
140  * "cgroup.events" files of both cgroups.
141  */
142 static int test_cgcore_populated(const char *root)
143 {
144         int ret = KSFT_FAIL;
145         int err;
146         char *cg_test_a = NULL, *cg_test_b = NULL;
147         char *cg_test_c = NULL, *cg_test_d = NULL;
148         int cgroup_fd = -EBADF;
149         pid_t pid;
150
151         cg_test_a = cg_name(root, "cg_test_a");
152         cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
153         cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
154         cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
155
156         if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
157                 goto cleanup;
158
159         if (cg_create(cg_test_a))
160                 goto cleanup;
161
162         if (cg_create(cg_test_b))
163                 goto cleanup;
164
165         if (cg_create(cg_test_c))
166                 goto cleanup;
167
168         if (cg_create(cg_test_d))
169                 goto cleanup;
170
171         if (cg_enter_current(cg_test_c))
172                 goto cleanup;
173
174         if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
175                 goto cleanup;
176
177         if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
178                 goto cleanup;
179
180         if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
181                 goto cleanup;
182
183         if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
184                 goto cleanup;
185
186         if (cg_enter_current(root))
187                 goto cleanup;
188
189         if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
190                 goto cleanup;
191
192         if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
193                 goto cleanup;
194
195         if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
196                 goto cleanup;
197
198         if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
199                 goto cleanup;
200
201         /* Test that we can directly clone into a new cgroup. */
202         cgroup_fd = dirfd_open_opath(cg_test_d);
203         if (cgroup_fd < 0)
204                 goto cleanup;
205
206         pid = clone_into_cgroup(cgroup_fd);
207         if (pid < 0) {
208                 if (errno == ENOSYS)
209                         goto cleanup_pass;
210                 goto cleanup;
211         }
212
213         if (pid == 0) {
214                 if (raise(SIGSTOP))
215                         exit(EXIT_FAILURE);
216                 exit(EXIT_SUCCESS);
217         }
218
219         err = cg_read_strcmp(cg_test_d, "cgroup.events", "populated 1\n");
220
221         (void)clone_reap(pid, WSTOPPED);
222         (void)kill(pid, SIGCONT);
223         (void)clone_reap(pid, WEXITED);
224
225         if (err)
226                 goto cleanup;
227
228         if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
229                 goto cleanup;
230
231         /* Remove cgroup. */
232         if (cg_test_d) {
233                 cg_destroy(cg_test_d);
234                 free(cg_test_d);
235                 cg_test_d = NULL;
236         }
237
238         pid = clone_into_cgroup(cgroup_fd);
239         if (pid < 0)
240                 goto cleanup_pass;
241         if (pid == 0)
242                 exit(EXIT_SUCCESS);
243         (void)clone_reap(pid, WEXITED);
244         goto cleanup;
245
246 cleanup_pass:
247         ret = KSFT_PASS;
248
249 cleanup:
250         if (cg_test_d)
251                 cg_destroy(cg_test_d);
252         if (cg_test_c)
253                 cg_destroy(cg_test_c);
254         if (cg_test_b)
255                 cg_destroy(cg_test_b);
256         if (cg_test_a)
257                 cg_destroy(cg_test_a);
258         free(cg_test_d);
259         free(cg_test_c);
260         free(cg_test_b);
261         free(cg_test_a);
262         if (cgroup_fd >= 0)
263                 close(cgroup_fd);
264         return ret;
265 }
266
267 /*
268  * A (domain threaded) - B (threaded) - C (domain)
269  *
270  * test that C can't be used until it is turned into a
271  * threaded cgroup.  "cgroup.type" file will report "domain (invalid)" in
272  * these cases. Operations which fail due to invalid topology use
273  * EOPNOTSUPP as the errno.
274  */
275 static int test_cgcore_invalid_domain(const char *root)
276 {
277         int ret = KSFT_FAIL;
278         char *grandparent = NULL, *parent = NULL, *child = NULL;
279
280         grandparent = cg_name(root, "cg_test_grandparent");
281         parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
282         child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
283         if (!parent || !child || !grandparent)
284                 goto cleanup;
285
286         if (cg_create(grandparent))
287                 goto cleanup;
288
289         if (cg_create(parent))
290                 goto cleanup;
291
292         if (cg_create(child))
293                 goto cleanup;
294
295         if (cg_write(parent, "cgroup.type", "threaded"))
296                 goto cleanup;
297
298         if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
299                 goto cleanup;
300
301         if (!cg_enter_current(child))
302                 goto cleanup;
303
304         if (errno != EOPNOTSUPP)
305                 goto cleanup;
306
307         if (!clone_into_cgroup_run_wait(child))
308                 goto cleanup;
309
310         if (errno == ENOSYS)
311                 goto cleanup_pass;
312
313         if (errno != EOPNOTSUPP)
314                 goto cleanup;
315
316 cleanup_pass:
317         ret = KSFT_PASS;
318
319 cleanup:
320         cg_enter_current(root);
321         if (child)
322                 cg_destroy(child);
323         if (parent)
324                 cg_destroy(parent);
325         if (grandparent)
326                 cg_destroy(grandparent);
327         free(child);
328         free(parent);
329         free(grandparent);
330         return ret;
331 }
332
333 /*
334  * Test that when a child becomes threaded
335  * the parent type becomes domain threaded.
336  */
337 static int test_cgcore_parent_becomes_threaded(const char *root)
338 {
339         int ret = KSFT_FAIL;
340         char *parent = NULL, *child = NULL;
341
342         parent = cg_name(root, "cg_test_parent");
343         child = cg_name(root, "cg_test_parent/cg_test_child");
344         if (!parent || !child)
345                 goto cleanup;
346
347         if (cg_create(parent))
348                 goto cleanup;
349
350         if (cg_create(child))
351                 goto cleanup;
352
353         if (cg_write(child, "cgroup.type", "threaded"))
354                 goto cleanup;
355
356         if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
357                 goto cleanup;
358
359         ret = KSFT_PASS;
360
361 cleanup:
362         if (child)
363                 cg_destroy(child);
364         if (parent)
365                 cg_destroy(parent);
366         free(child);
367         free(parent);
368         return ret;
369
370 }
371
372 /*
373  * Test that there's no internal process constrain on threaded cgroups.
374  * You can add threads/processes on a parent with a controller enabled.
375  */
376 static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
377 {
378         int ret = KSFT_FAIL;
379         char *parent = NULL, *child = NULL;
380
381         if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
382             cg_write(root, "cgroup.subtree_control", "+cpu")) {
383                 ret = KSFT_SKIP;
384                 goto cleanup;
385         }
386
387         parent = cg_name(root, "cg_test_parent");
388         child = cg_name(root, "cg_test_parent/cg_test_child");
389         if (!parent || !child)
390                 goto cleanup;
391
392         if (cg_create(parent))
393                 goto cleanup;
394
395         if (cg_create(child))
396                 goto cleanup;
397
398         if (cg_write(parent, "cgroup.type", "threaded"))
399                 goto cleanup;
400
401         if (cg_write(child, "cgroup.type", "threaded"))
402                 goto cleanup;
403
404         if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
405                 goto cleanup;
406
407         if (cg_enter_current(parent))
408                 goto cleanup;
409
410         ret = KSFT_PASS;
411
412 cleanup:
413         cg_enter_current(root);
414         cg_enter_current(root);
415         if (child)
416                 cg_destroy(child);
417         if (parent)
418                 cg_destroy(parent);
419         free(child);
420         free(parent);
421         return ret;
422 }
423
424 /*
425  * Test that you can't enable a controller on a child if it's not enabled
426  * on the parent.
427  */
428 static int test_cgcore_top_down_constraint_enable(const char *root)
429 {
430         int ret = KSFT_FAIL;
431         char *parent = NULL, *child = NULL;
432
433         parent = cg_name(root, "cg_test_parent");
434         child = cg_name(root, "cg_test_parent/cg_test_child");
435         if (!parent || !child)
436                 goto cleanup;
437
438         if (cg_create(parent))
439                 goto cleanup;
440
441         if (cg_create(child))
442                 goto cleanup;
443
444         if (!cg_write(child, "cgroup.subtree_control", "+memory"))
445                 goto cleanup;
446
447         ret = KSFT_PASS;
448
449 cleanup:
450         if (child)
451                 cg_destroy(child);
452         if (parent)
453                 cg_destroy(parent);
454         free(child);
455         free(parent);
456         return ret;
457 }
458
459 /*
460  * Test that you can't disable a controller on a parent
461  * if it's enabled in a child.
462  */
463 static int test_cgcore_top_down_constraint_disable(const char *root)
464 {
465         int ret = KSFT_FAIL;
466         char *parent = NULL, *child = NULL;
467
468         parent = cg_name(root, "cg_test_parent");
469         child = cg_name(root, "cg_test_parent/cg_test_child");
470         if (!parent || !child)
471                 goto cleanup;
472
473         if (cg_create(parent))
474                 goto cleanup;
475
476         if (cg_create(child))
477                 goto cleanup;
478
479         if (cg_write(parent, "cgroup.subtree_control", "+memory"))
480                 goto cleanup;
481
482         if (cg_write(child, "cgroup.subtree_control", "+memory"))
483                 goto cleanup;
484
485         if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
486                 goto cleanup;
487
488         ret = KSFT_PASS;
489
490 cleanup:
491         if (child)
492                 cg_destroy(child);
493         if (parent)
494                 cg_destroy(parent);
495         free(child);
496         free(parent);
497         return ret;
498 }
499
500 /*
501  * Test internal process constraint.
502  * You can't add a pid to a domain parent if a controller is enabled.
503  */
504 static int test_cgcore_internal_process_constraint(const char *root)
505 {
506         int ret = KSFT_FAIL;
507         char *parent = NULL, *child = NULL;
508
509         parent = cg_name(root, "cg_test_parent");
510         child = cg_name(root, "cg_test_parent/cg_test_child");
511         if (!parent || !child)
512                 goto cleanup;
513
514         if (cg_create(parent))
515                 goto cleanup;
516
517         if (cg_create(child))
518                 goto cleanup;
519
520         if (cg_write(parent, "cgroup.subtree_control", "+memory"))
521                 goto cleanup;
522
523         if (!cg_enter_current(parent))
524                 goto cleanup;
525
526         if (!clone_into_cgroup_run_wait(parent))
527                 goto cleanup;
528
529         ret = KSFT_PASS;
530
531 cleanup:
532         if (child)
533                 cg_destroy(child);
534         if (parent)
535                 cg_destroy(parent);
536         free(child);
537         free(parent);
538         return ret;
539 }
540
541 static void *dummy_thread_fn(void *arg)
542 {
543         return (void *)(size_t)pause();
544 }
545
546 /*
547  * Test threadgroup migration.
548  * All threads of a process are migrated together.
549  */
550 static int test_cgcore_proc_migration(const char *root)
551 {
552         int ret = KSFT_FAIL;
553         int t, c_threads = 0, n_threads = 13;
554         char *src = NULL, *dst = NULL;
555         pthread_t threads[n_threads];
556
557         src = cg_name(root, "cg_src");
558         dst = cg_name(root, "cg_dst");
559         if (!src || !dst)
560                 goto cleanup;
561
562         if (cg_create(src))
563                 goto cleanup;
564         if (cg_create(dst))
565                 goto cleanup;
566
567         if (cg_enter_current(src))
568                 goto cleanup;
569
570         for (c_threads = 0; c_threads < n_threads; ++c_threads) {
571                 if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL))
572                         goto cleanup;
573         }
574
575         cg_enter_current(dst);
576         if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1)
577                 goto cleanup;
578
579         ret = KSFT_PASS;
580
581 cleanup:
582         for (t = 0; t < c_threads; ++t) {
583                 pthread_cancel(threads[t]);
584         }
585
586         for (t = 0; t < c_threads; ++t) {
587                 pthread_join(threads[t], NULL);
588         }
589
590         cg_enter_current(root);
591
592         if (dst)
593                 cg_destroy(dst);
594         if (src)
595                 cg_destroy(src);
596         free(dst);
597         free(src);
598         return ret;
599 }
600
601 static void *migrating_thread_fn(void *arg)
602 {
603         int g, i, n_iterations = 1000;
604         char **grps = arg;
605         char lines[3][PATH_MAX];
606
607         for (g = 1; g < 3; ++g)
608                 snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0]));
609
610         for (i = 0; i < n_iterations; ++i) {
611                 cg_enter_current_thread(grps[(i % 2) + 1]);
612
613                 if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1]))
614                         return (void *)-1;
615         }
616         return NULL;
617 }
618
619 /*
620  * Test single thread migration.
621  * Threaded cgroups allow successful migration of a thread.
622  */
623 static int test_cgcore_thread_migration(const char *root)
624 {
625         int ret = KSFT_FAIL;
626         char *dom = NULL;
627         char line[PATH_MAX];
628         char *grps[3] = { (char *)root, NULL, NULL };
629         pthread_t thr;
630         void *retval;
631
632         dom = cg_name(root, "cg_dom");
633         grps[1] = cg_name(root, "cg_dom/cg_src");
634         grps[2] = cg_name(root, "cg_dom/cg_dst");
635         if (!grps[1] || !grps[2] || !dom)
636                 goto cleanup;
637
638         if (cg_create(dom))
639                 goto cleanup;
640         if (cg_create(grps[1]))
641                 goto cleanup;
642         if (cg_create(grps[2]))
643                 goto cleanup;
644
645         if (cg_write(grps[1], "cgroup.type", "threaded"))
646                 goto cleanup;
647         if (cg_write(grps[2], "cgroup.type", "threaded"))
648                 goto cleanup;
649
650         if (cg_enter_current(grps[1]))
651                 goto cleanup;
652
653         if (pthread_create(&thr, NULL, migrating_thread_fn, grps))
654                 goto cleanup;
655
656         if (pthread_join(thr, &retval))
657                 goto cleanup;
658
659         if (retval)
660                 goto cleanup;
661
662         snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0]));
663         if (proc_read_strstr(0, 1, "cgroup", line))
664                 goto cleanup;
665
666         ret = KSFT_PASS;
667
668 cleanup:
669         cg_enter_current(root);
670         if (grps[2])
671                 cg_destroy(grps[2]);
672         if (grps[1])
673                 cg_destroy(grps[1]);
674         if (dom)
675                 cg_destroy(dom);
676         free(grps[2]);
677         free(grps[1]);
678         free(dom);
679         return ret;
680 }
681
682 /*
683  * cgroup migration permission check should be performed based on the
684  * credentials at the time of open instead of write.
685  */
686 static int test_cgcore_lesser_euid_open(const char *root)
687 {
688         const uid_t test_euid = TEST_UID;
689         int ret = KSFT_FAIL;
690         char *cg_test_a = NULL, *cg_test_b = NULL;
691         char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
692         int cg_test_b_procs_fd = -1;
693         uid_t saved_uid;
694
695         cg_test_a = cg_name(root, "cg_test_a");
696         cg_test_b = cg_name(root, "cg_test_b");
697
698         if (!cg_test_a || !cg_test_b)
699                 goto cleanup;
700
701         cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs");
702         cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
703
704         if (!cg_test_a_procs || !cg_test_b_procs)
705                 goto cleanup;
706
707         if (cg_create(cg_test_a) || cg_create(cg_test_b))
708                 goto cleanup;
709
710         if (cg_enter_current(cg_test_a))
711                 goto cleanup;
712
713         if (chown(cg_test_a_procs, test_euid, -1) ||
714             chown(cg_test_b_procs, test_euid, -1))
715                 goto cleanup;
716
717         saved_uid = geteuid();
718         if (seteuid(test_euid))
719                 goto cleanup;
720
721         cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR);
722
723         if (seteuid(saved_uid))
724                 goto cleanup;
725
726         if (cg_test_b_procs_fd < 0)
727                 goto cleanup;
728
729         if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES)
730                 goto cleanup;
731
732         ret = KSFT_PASS;
733
734 cleanup:
735         cg_enter_current(root);
736         if (cg_test_b_procs_fd >= 0)
737                 close(cg_test_b_procs_fd);
738         if (cg_test_b)
739                 cg_destroy(cg_test_b);
740         if (cg_test_a)
741                 cg_destroy(cg_test_a);
742         free(cg_test_b_procs);
743         free(cg_test_a_procs);
744         free(cg_test_b);
745         free(cg_test_a);
746         return ret;
747 }
748
749 struct lesser_ns_open_thread_arg {
750         const char      *path;
751         int             fd;
752         int             err;
753 };
754
755 static int lesser_ns_open_thread_fn(void *arg)
756 {
757         struct lesser_ns_open_thread_arg *targ = arg;
758
759         targ->fd = open(targ->path, O_RDWR);
760         targ->err = errno;
761         return 0;
762 }
763
764 /*
765  * cgroup migration permission check should be performed based on the cgroup
766  * namespace at the time of open instead of write.
767  */
768 static int test_cgcore_lesser_ns_open(const char *root)
769 {
770         static char stack[65536];
771         const uid_t test_euid = 65534;  /* usually nobody, any !root is fine */
772         int ret = KSFT_FAIL;
773         char *cg_test_a = NULL, *cg_test_b = NULL;
774         char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
775         int cg_test_b_procs_fd = -1;
776         struct lesser_ns_open_thread_arg targ = { .fd = -1 };
777         pid_t pid;
778         int status;
779
780         if (!nsdelegate)
781                 return KSFT_SKIP;
782
783         cg_test_a = cg_name(root, "cg_test_a");
784         cg_test_b = cg_name(root, "cg_test_b");
785
786         if (!cg_test_a || !cg_test_b)
787                 goto cleanup;
788
789         cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs");
790         cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
791
792         if (!cg_test_a_procs || !cg_test_b_procs)
793                 goto cleanup;
794
795         if (cg_create(cg_test_a) || cg_create(cg_test_b))
796                 goto cleanup;
797
798         if (cg_enter_current(cg_test_b))
799                 goto cleanup;
800
801         if (chown(cg_test_a_procs, test_euid, -1) ||
802             chown(cg_test_b_procs, test_euid, -1))
803                 goto cleanup;
804
805         targ.path = cg_test_b_procs;
806         pid = clone(lesser_ns_open_thread_fn, stack + sizeof(stack),
807                     CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD,
808                     &targ);
809         if (pid < 0)
810                 goto cleanup;
811
812         if (waitpid(pid, &status, 0) < 0)
813                 goto cleanup;
814
815         if (!WIFEXITED(status))
816                 goto cleanup;
817
818         cg_test_b_procs_fd = targ.fd;
819         if (cg_test_b_procs_fd < 0)
820                 goto cleanup;
821
822         if (cg_enter_current(cg_test_a))
823                 goto cleanup;
824
825         if ((status = write(cg_test_b_procs_fd, "0", 1)) >= 0 || errno != ENOENT)
826                 goto cleanup;
827
828         ret = KSFT_PASS;
829
830 cleanup:
831         cg_enter_current(root);
832         if (cg_test_b_procs_fd >= 0)
833                 close(cg_test_b_procs_fd);
834         if (cg_test_b)
835                 cg_destroy(cg_test_b);
836         if (cg_test_a)
837                 cg_destroy(cg_test_a);
838         free(cg_test_b_procs);
839         free(cg_test_a_procs);
840         free(cg_test_b);
841         free(cg_test_a);
842         return ret;
843 }
844
845 #define T(x) { x, #x }
846 struct corecg_test {
847         int (*fn)(const char *root);
848         const char *name;
849 } tests[] = {
850         T(test_cgcore_internal_process_constraint),
851         T(test_cgcore_top_down_constraint_enable),
852         T(test_cgcore_top_down_constraint_disable),
853         T(test_cgcore_no_internal_process_constraint_on_threads),
854         T(test_cgcore_parent_becomes_threaded),
855         T(test_cgcore_invalid_domain),
856         T(test_cgcore_populated),
857         T(test_cgcore_proc_migration),
858         T(test_cgcore_thread_migration),
859         T(test_cgcore_destroy),
860         T(test_cgcore_lesser_euid_open),
861         T(test_cgcore_lesser_ns_open),
862 };
863 #undef T
864
865 int main(int argc, char *argv[])
866 {
867         char root[PATH_MAX];
868         int i, ret = EXIT_SUCCESS;
869
870         if (cg_find_unified_root(root, sizeof(root), &nsdelegate))
871                 ksft_exit_skip("cgroup v2 isn't mounted\n");
872
873         if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
874                 if (cg_write(root, "cgroup.subtree_control", "+memory"))
875                         ksft_exit_skip("Failed to set memory controller\n");
876
877         for (i = 0; i < ARRAY_SIZE(tests); i++) {
878                 switch (tests[i].fn(root)) {
879                 case KSFT_PASS:
880                         ksft_test_result_pass("%s\n", tests[i].name);
881                         break;
882                 case KSFT_SKIP:
883                         ksft_test_result_skip("%s\n", tests[i].name);
884                         break;
885                 default:
886                         ret = EXIT_FAILURE;
887                         ksft_test_result_fail("%s\n", tests[i].name);
888                         break;
889                 }
890         }
891
892         return ret;
893 }
This page took 0.076197 seconds and 4 git commands to generate.