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