1d863cb03SClaudio /* SPDX-License-Identifier: GPL-2.0 */
2d863cb03SClaudio
3bf35a787STejun Heo #define _GNU_SOURCE
4d863cb03SClaudio #include <linux/limits.h>
5bf35a787STejun Heo #include <linux/sched.h>
6d863cb03SClaudio #include <sys/types.h>
704189382SSuren Baghdasaryan #include <sys/mman.h>
804189382SSuren Baghdasaryan #include <sys/wait.h>
9d863cb03SClaudio #include <unistd.h>
1004189382SSuren Baghdasaryan #include <fcntl.h>
11bf35a787STejun Heo #include <sched.h>
12d863cb03SClaudio #include <stdio.h>
13d863cb03SClaudio #include <errno.h>
1411318989SMichal Koutný #include <signal.h>
1511318989SMichal Koutný #include <string.h>
1611318989SMichal Koutný #include <pthread.h>
17d863cb03SClaudio
18d863cb03SClaudio #include "../kselftest.h"
19d863cb03SClaudio #include "cgroup_util.h"
20d863cb03SClaudio
touch_anon(char * buf,size_t size)2104189382SSuren Baghdasaryan static int touch_anon(char *buf, size_t size)
2204189382SSuren Baghdasaryan {
2304189382SSuren Baghdasaryan int fd;
2404189382SSuren Baghdasaryan char *pos = buf;
2504189382SSuren Baghdasaryan
2604189382SSuren Baghdasaryan fd = open("/dev/urandom", O_RDONLY);
2704189382SSuren Baghdasaryan if (fd < 0)
2804189382SSuren Baghdasaryan return -1;
2904189382SSuren Baghdasaryan
3004189382SSuren Baghdasaryan while (size > 0) {
3104189382SSuren Baghdasaryan ssize_t ret = read(fd, pos, size);
3204189382SSuren Baghdasaryan
3304189382SSuren Baghdasaryan if (ret < 0) {
3404189382SSuren Baghdasaryan if (errno != EINTR) {
3504189382SSuren Baghdasaryan close(fd);
3604189382SSuren Baghdasaryan return -1;
3704189382SSuren Baghdasaryan }
3804189382SSuren Baghdasaryan } else {
3904189382SSuren Baghdasaryan pos += ret;
4004189382SSuren Baghdasaryan size -= ret;
4104189382SSuren Baghdasaryan }
4204189382SSuren Baghdasaryan }
4304189382SSuren Baghdasaryan close(fd);
4404189382SSuren Baghdasaryan
4504189382SSuren Baghdasaryan return 0;
4604189382SSuren Baghdasaryan }
4704189382SSuren Baghdasaryan
alloc_and_touch_anon_noexit(const char * cgroup,void * arg)4804189382SSuren Baghdasaryan static int alloc_and_touch_anon_noexit(const char *cgroup, void *arg)
4904189382SSuren Baghdasaryan {
5004189382SSuren Baghdasaryan int ppid = getppid();
5104189382SSuren Baghdasaryan size_t size = (size_t)arg;
5204189382SSuren Baghdasaryan void *buf;
5304189382SSuren Baghdasaryan
5404189382SSuren Baghdasaryan buf = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON,
5504189382SSuren Baghdasaryan 0, 0);
5604189382SSuren Baghdasaryan if (buf == MAP_FAILED)
5704189382SSuren Baghdasaryan return -1;
5804189382SSuren Baghdasaryan
5904189382SSuren Baghdasaryan if (touch_anon((char *)buf, size)) {
6004189382SSuren Baghdasaryan munmap(buf, size);
6104189382SSuren Baghdasaryan return -1;
6204189382SSuren Baghdasaryan }
6304189382SSuren Baghdasaryan
6404189382SSuren Baghdasaryan while (getppid() == ppid)
6504189382SSuren Baghdasaryan sleep(1);
6604189382SSuren Baghdasaryan
6704189382SSuren Baghdasaryan munmap(buf, size);
6804189382SSuren Baghdasaryan return 0;
6904189382SSuren Baghdasaryan }
7004189382SSuren Baghdasaryan
7104189382SSuren Baghdasaryan /*
7204189382SSuren Baghdasaryan * Create a child process that allocates and touches 100MB, then waits to be
7304189382SSuren Baghdasaryan * killed. Wait until the child is attached to the cgroup, kill all processes
7404189382SSuren Baghdasaryan * in that cgroup and wait until "cgroup.procs" is empty. At this point try to
7504189382SSuren Baghdasaryan * destroy the empty cgroup. The test helps detect race conditions between
7604189382SSuren Baghdasaryan * dying processes leaving the cgroup and cgroup destruction path.
7704189382SSuren Baghdasaryan */
test_cgcore_destroy(const char * root)7804189382SSuren Baghdasaryan static int test_cgcore_destroy(const char *root)
7904189382SSuren Baghdasaryan {
8004189382SSuren Baghdasaryan int ret = KSFT_FAIL;
8104189382SSuren Baghdasaryan char *cg_test = NULL;
8204189382SSuren Baghdasaryan int child_pid;
8304189382SSuren Baghdasaryan char buf[PAGE_SIZE];
8404189382SSuren Baghdasaryan
8504189382SSuren Baghdasaryan cg_test = cg_name(root, "cg_test");
8604189382SSuren Baghdasaryan
8704189382SSuren Baghdasaryan if (!cg_test)
8804189382SSuren Baghdasaryan goto cleanup;
8904189382SSuren Baghdasaryan
9004189382SSuren Baghdasaryan for (int i = 0; i < 10; i++) {
9104189382SSuren Baghdasaryan if (cg_create(cg_test))
9204189382SSuren Baghdasaryan goto cleanup;
9304189382SSuren Baghdasaryan
9404189382SSuren Baghdasaryan child_pid = cg_run_nowait(cg_test, alloc_and_touch_anon_noexit,
9504189382SSuren Baghdasaryan (void *) MB(100));
9604189382SSuren Baghdasaryan
9704189382SSuren Baghdasaryan if (child_pid < 0)
9804189382SSuren Baghdasaryan goto cleanup;
9904189382SSuren Baghdasaryan
10004189382SSuren Baghdasaryan /* wait for the child to enter cgroup */
10104189382SSuren Baghdasaryan if (cg_wait_for_proc_count(cg_test, 1))
10204189382SSuren Baghdasaryan goto cleanup;
10304189382SSuren Baghdasaryan
10404189382SSuren Baghdasaryan if (cg_killall(cg_test))
10504189382SSuren Baghdasaryan goto cleanup;
10604189382SSuren Baghdasaryan
10704189382SSuren Baghdasaryan /* wait for cgroup to be empty */
10804189382SSuren Baghdasaryan while (1) {
10904189382SSuren Baghdasaryan if (cg_read(cg_test, "cgroup.procs", buf, sizeof(buf)))
11004189382SSuren Baghdasaryan goto cleanup;
11104189382SSuren Baghdasaryan if (buf[0] == '\0')
11204189382SSuren Baghdasaryan break;
11304189382SSuren Baghdasaryan usleep(1000);
11404189382SSuren Baghdasaryan }
11504189382SSuren Baghdasaryan
11604189382SSuren Baghdasaryan if (rmdir(cg_test))
11704189382SSuren Baghdasaryan goto cleanup;
11804189382SSuren Baghdasaryan
11904189382SSuren Baghdasaryan if (waitpid(child_pid, NULL, 0) < 0)
12004189382SSuren Baghdasaryan goto cleanup;
12104189382SSuren Baghdasaryan }
12204189382SSuren Baghdasaryan ret = KSFT_PASS;
12304189382SSuren Baghdasaryan cleanup:
12404189382SSuren Baghdasaryan if (cg_test)
12504189382SSuren Baghdasaryan cg_destroy(cg_test);
12604189382SSuren Baghdasaryan free(cg_test);
12704189382SSuren Baghdasaryan return ret;
12804189382SSuren Baghdasaryan }
12904189382SSuren Baghdasaryan
130d863cb03SClaudio /*
131d863cb03SClaudio * A(0) - B(0) - C(1)
132d863cb03SClaudio * \ D(0)
133d863cb03SClaudio *
134d863cb03SClaudio * A, B and C's "populated" fields would be 1 while D's 0.
135d863cb03SClaudio * test that after the one process in C is moved to root,
136d863cb03SClaudio * A,B and C's "populated" fields would flip to "0" and file
137d863cb03SClaudio * modified events will be generated on the
138d863cb03SClaudio * "cgroup.events" files of both cgroups.
139d863cb03SClaudio */
test_cgcore_populated(const char * root)140d863cb03SClaudio static int test_cgcore_populated(const char *root)
141d863cb03SClaudio {
142d863cb03SClaudio int ret = KSFT_FAIL;
1439bd5910dSChristian Brauner int err;
144d863cb03SClaudio char *cg_test_a = NULL, *cg_test_b = NULL;
145d863cb03SClaudio char *cg_test_c = NULL, *cg_test_d = NULL;
1469bd5910dSChristian Brauner int cgroup_fd = -EBADF;
1479bd5910dSChristian Brauner pid_t pid;
148d863cb03SClaudio
149d863cb03SClaudio cg_test_a = cg_name(root, "cg_test_a");
150d863cb03SClaudio cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
151d863cb03SClaudio cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
152d863cb03SClaudio cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
153d863cb03SClaudio
154d863cb03SClaudio if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
155d863cb03SClaudio goto cleanup;
156d863cb03SClaudio
157d863cb03SClaudio if (cg_create(cg_test_a))
158d863cb03SClaudio goto cleanup;
159d863cb03SClaudio
160d863cb03SClaudio if (cg_create(cg_test_b))
161d863cb03SClaudio goto cleanup;
162d863cb03SClaudio
163d863cb03SClaudio if (cg_create(cg_test_c))
164d863cb03SClaudio goto cleanup;
165d863cb03SClaudio
166d863cb03SClaudio if (cg_create(cg_test_d))
167d863cb03SClaudio goto cleanup;
168d863cb03SClaudio
169d863cb03SClaudio if (cg_enter_current(cg_test_c))
170d863cb03SClaudio goto cleanup;
171d863cb03SClaudio
172d863cb03SClaudio if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
173d863cb03SClaudio goto cleanup;
174d863cb03SClaudio
175d863cb03SClaudio if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
176d863cb03SClaudio goto cleanup;
177d863cb03SClaudio
178d863cb03SClaudio if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
179d863cb03SClaudio goto cleanup;
180d863cb03SClaudio
181d863cb03SClaudio if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
182d863cb03SClaudio goto cleanup;
183d863cb03SClaudio
184d863cb03SClaudio if (cg_enter_current(root))
185d863cb03SClaudio goto cleanup;
186d863cb03SClaudio
187d863cb03SClaudio if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
188d863cb03SClaudio goto cleanup;
189d863cb03SClaudio
190d863cb03SClaudio if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
191d863cb03SClaudio goto cleanup;
192d863cb03SClaudio
193d863cb03SClaudio if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
194d863cb03SClaudio goto cleanup;
195d863cb03SClaudio
196d863cb03SClaudio if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
197d863cb03SClaudio goto cleanup;
198d863cb03SClaudio
1999bd5910dSChristian Brauner /* Test that we can directly clone into a new cgroup. */
2009bd5910dSChristian Brauner cgroup_fd = dirfd_open_opath(cg_test_d);
2019bd5910dSChristian Brauner if (cgroup_fd < 0)
2029bd5910dSChristian Brauner goto cleanup;
2039bd5910dSChristian Brauner
2049bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd);
2059bd5910dSChristian Brauner if (pid < 0) {
2069bd5910dSChristian Brauner if (errno == ENOSYS)
2079bd5910dSChristian Brauner goto cleanup_pass;
2089bd5910dSChristian Brauner goto cleanup;
2099bd5910dSChristian Brauner }
2109bd5910dSChristian Brauner
2119bd5910dSChristian Brauner if (pid == 0) {
2129bd5910dSChristian Brauner if (raise(SIGSTOP))
2139bd5910dSChristian Brauner exit(EXIT_FAILURE);
2149bd5910dSChristian Brauner exit(EXIT_SUCCESS);
2159bd5910dSChristian Brauner }
2169bd5910dSChristian Brauner
2179bd5910dSChristian Brauner err = cg_read_strcmp(cg_test_d, "cgroup.events", "populated 1\n");
2189bd5910dSChristian Brauner
2199bd5910dSChristian Brauner (void)clone_reap(pid, WSTOPPED);
2209bd5910dSChristian Brauner (void)kill(pid, SIGCONT);
2219bd5910dSChristian Brauner (void)clone_reap(pid, WEXITED);
2229bd5910dSChristian Brauner
2239bd5910dSChristian Brauner if (err)
2249bd5910dSChristian Brauner goto cleanup;
2259bd5910dSChristian Brauner
2269bd5910dSChristian Brauner if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
2279bd5910dSChristian Brauner goto cleanup;
2289bd5910dSChristian Brauner
2299bd5910dSChristian Brauner /* Remove cgroup. */
2309bd5910dSChristian Brauner if (cg_test_d) {
2319bd5910dSChristian Brauner cg_destroy(cg_test_d);
2329bd5910dSChristian Brauner free(cg_test_d);
2339bd5910dSChristian Brauner cg_test_d = NULL;
2349bd5910dSChristian Brauner }
2359bd5910dSChristian Brauner
2369bd5910dSChristian Brauner pid = clone_into_cgroup(cgroup_fd);
2379bd5910dSChristian Brauner if (pid < 0)
2389bd5910dSChristian Brauner goto cleanup_pass;
2399bd5910dSChristian Brauner if (pid == 0)
2409bd5910dSChristian Brauner exit(EXIT_SUCCESS);
2419bd5910dSChristian Brauner (void)clone_reap(pid, WEXITED);
2429bd5910dSChristian Brauner goto cleanup;
2439bd5910dSChristian Brauner
2449bd5910dSChristian Brauner cleanup_pass:
245d863cb03SClaudio ret = KSFT_PASS;
246d863cb03SClaudio
247d863cb03SClaudio cleanup:
248d863cb03SClaudio if (cg_test_d)
249d863cb03SClaudio cg_destroy(cg_test_d);
250d863cb03SClaudio if (cg_test_c)
251d863cb03SClaudio cg_destroy(cg_test_c);
252d863cb03SClaudio if (cg_test_b)
253d863cb03SClaudio cg_destroy(cg_test_b);
254d863cb03SClaudio if (cg_test_a)
255d863cb03SClaudio cg_destroy(cg_test_a);
256d863cb03SClaudio free(cg_test_d);
257d863cb03SClaudio free(cg_test_c);
258d863cb03SClaudio free(cg_test_b);
259d863cb03SClaudio free(cg_test_a);
2609bd5910dSChristian Brauner if (cgroup_fd >= 0)
2619bd5910dSChristian Brauner close(cgroup_fd);
262d863cb03SClaudio return ret;
263d863cb03SClaudio }
264d863cb03SClaudio
265d863cb03SClaudio /*
266d863cb03SClaudio * A (domain threaded) - B (threaded) - C (domain)
267d863cb03SClaudio *
268d863cb03SClaudio * test that C can't be used until it is turned into a
269d863cb03SClaudio * threaded cgroup. "cgroup.type" file will report "domain (invalid)" in
270d863cb03SClaudio * these cases. Operations which fail due to invalid topology use
271d863cb03SClaudio * EOPNOTSUPP as the errno.
272d863cb03SClaudio */
test_cgcore_invalid_domain(const char * root)273d863cb03SClaudio static int test_cgcore_invalid_domain(const char *root)
274d863cb03SClaudio {
275d863cb03SClaudio int ret = KSFT_FAIL;
276d863cb03SClaudio char *grandparent = NULL, *parent = NULL, *child = NULL;
277d863cb03SClaudio
278d863cb03SClaudio grandparent = cg_name(root, "cg_test_grandparent");
279d863cb03SClaudio parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
280d863cb03SClaudio child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
281d863cb03SClaudio if (!parent || !child || !grandparent)
282d863cb03SClaudio goto cleanup;
283d863cb03SClaudio
284d863cb03SClaudio if (cg_create(grandparent))
285d863cb03SClaudio goto cleanup;
286d863cb03SClaudio
287d863cb03SClaudio if (cg_create(parent))
288d863cb03SClaudio goto cleanup;
289d863cb03SClaudio
290d863cb03SClaudio if (cg_create(child))
291d863cb03SClaudio goto cleanup;
292d863cb03SClaudio
293d863cb03SClaudio if (cg_write(parent, "cgroup.type", "threaded"))
294d863cb03SClaudio goto cleanup;
295d863cb03SClaudio
296d863cb03SClaudio if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
297d863cb03SClaudio goto cleanup;
298d863cb03SClaudio
299d863cb03SClaudio if (!cg_enter_current(child))
300d863cb03SClaudio goto cleanup;
301d863cb03SClaudio
302d863cb03SClaudio if (errno != EOPNOTSUPP)
303d863cb03SClaudio goto cleanup;
304d863cb03SClaudio
3059bd5910dSChristian Brauner if (!clone_into_cgroup_run_wait(child))
3069bd5910dSChristian Brauner goto cleanup;
3079bd5910dSChristian Brauner
3089bd5910dSChristian Brauner if (errno == ENOSYS)
3099bd5910dSChristian Brauner goto cleanup_pass;
3109bd5910dSChristian Brauner
3119bd5910dSChristian Brauner if (errno != EOPNOTSUPP)
3129bd5910dSChristian Brauner goto cleanup;
3139bd5910dSChristian Brauner
3149bd5910dSChristian Brauner cleanup_pass:
315d863cb03SClaudio ret = KSFT_PASS;
316d863cb03SClaudio
317d863cb03SClaudio cleanup:
318d863cb03SClaudio cg_enter_current(root);
319d863cb03SClaudio if (child)
320d863cb03SClaudio cg_destroy(child);
321d863cb03SClaudio if (parent)
322d863cb03SClaudio cg_destroy(parent);
323d863cb03SClaudio if (grandparent)
324d863cb03SClaudio cg_destroy(grandparent);
325d863cb03SClaudio free(child);
326d863cb03SClaudio free(parent);
327d863cb03SClaudio free(grandparent);
328d863cb03SClaudio return ret;
329d863cb03SClaudio }
330d863cb03SClaudio
331d863cb03SClaudio /*
332d863cb03SClaudio * Test that when a child becomes threaded
333d863cb03SClaudio * the parent type becomes domain threaded.
334d863cb03SClaudio */
test_cgcore_parent_becomes_threaded(const char * root)335d863cb03SClaudio static int test_cgcore_parent_becomes_threaded(const char *root)
336d863cb03SClaudio {
337d863cb03SClaudio int ret = KSFT_FAIL;
338d863cb03SClaudio char *parent = NULL, *child = NULL;
339d863cb03SClaudio
340d863cb03SClaudio parent = cg_name(root, "cg_test_parent");
341d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child");
342d863cb03SClaudio if (!parent || !child)
343d863cb03SClaudio goto cleanup;
344d863cb03SClaudio
345d863cb03SClaudio if (cg_create(parent))
346d863cb03SClaudio goto cleanup;
347d863cb03SClaudio
348d863cb03SClaudio if (cg_create(child))
349d863cb03SClaudio goto cleanup;
350d863cb03SClaudio
351d863cb03SClaudio if (cg_write(child, "cgroup.type", "threaded"))
352d863cb03SClaudio goto cleanup;
353d863cb03SClaudio
354d863cb03SClaudio if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
355d863cb03SClaudio goto cleanup;
356d863cb03SClaudio
357d863cb03SClaudio ret = KSFT_PASS;
358d863cb03SClaudio
359d863cb03SClaudio cleanup:
360d863cb03SClaudio if (child)
361d863cb03SClaudio cg_destroy(child);
362d863cb03SClaudio if (parent)
363d863cb03SClaudio cg_destroy(parent);
364d863cb03SClaudio free(child);
365d863cb03SClaudio free(parent);
366d863cb03SClaudio return ret;
367d863cb03SClaudio
368d863cb03SClaudio }
369d863cb03SClaudio
370d863cb03SClaudio /*
371d863cb03SClaudio * Test that there's no internal process constrain on threaded cgroups.
372d863cb03SClaudio * You can add threads/processes on a parent with a controller enabled.
373d863cb03SClaudio */
test_cgcore_no_internal_process_constraint_on_threads(const char * root)374d863cb03SClaudio static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
375d863cb03SClaudio {
376d863cb03SClaudio int ret = KSFT_FAIL;
377d863cb03SClaudio char *parent = NULL, *child = NULL;
378d863cb03SClaudio
379d863cb03SClaudio if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
380f97f3f88SAlex Shi cg_write(root, "cgroup.subtree_control", "+cpu")) {
381d863cb03SClaudio ret = KSFT_SKIP;
382d863cb03SClaudio goto cleanup;
383d863cb03SClaudio }
384d863cb03SClaudio
385d863cb03SClaudio parent = cg_name(root, "cg_test_parent");
386d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child");
387d863cb03SClaudio if (!parent || !child)
388d863cb03SClaudio goto cleanup;
389d863cb03SClaudio
390d863cb03SClaudio if (cg_create(parent))
391d863cb03SClaudio goto cleanup;
392d863cb03SClaudio
393d863cb03SClaudio if (cg_create(child))
394d863cb03SClaudio goto cleanup;
395d863cb03SClaudio
396d863cb03SClaudio if (cg_write(parent, "cgroup.type", "threaded"))
397d863cb03SClaudio goto cleanup;
398d863cb03SClaudio
399d863cb03SClaudio if (cg_write(child, "cgroup.type", "threaded"))
400d863cb03SClaudio goto cleanup;
401d863cb03SClaudio
402d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
403d863cb03SClaudio goto cleanup;
404d863cb03SClaudio
405d863cb03SClaudio if (cg_enter_current(parent))
406d863cb03SClaudio goto cleanup;
407d863cb03SClaudio
408d863cb03SClaudio ret = KSFT_PASS;
409d863cb03SClaudio
410d863cb03SClaudio cleanup:
411d863cb03SClaudio cg_enter_current(root);
412d863cb03SClaudio cg_enter_current(root);
413d863cb03SClaudio if (child)
414d863cb03SClaudio cg_destroy(child);
415d863cb03SClaudio if (parent)
416d863cb03SClaudio cg_destroy(parent);
417d863cb03SClaudio free(child);
418d863cb03SClaudio free(parent);
419d863cb03SClaudio return ret;
420d863cb03SClaudio }
421d863cb03SClaudio
422d863cb03SClaudio /*
423d863cb03SClaudio * Test that you can't enable a controller on a child if it's not enabled
424d863cb03SClaudio * on the parent.
425d863cb03SClaudio */
test_cgcore_top_down_constraint_enable(const char * root)426d863cb03SClaudio static int test_cgcore_top_down_constraint_enable(const char *root)
427d863cb03SClaudio {
428d863cb03SClaudio int ret = KSFT_FAIL;
429d863cb03SClaudio char *parent = NULL, *child = NULL;
430d863cb03SClaudio
431d863cb03SClaudio parent = cg_name(root, "cg_test_parent");
432d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child");
433d863cb03SClaudio if (!parent || !child)
434d863cb03SClaudio goto cleanup;
435d863cb03SClaudio
436d863cb03SClaudio if (cg_create(parent))
437d863cb03SClaudio goto cleanup;
438d863cb03SClaudio
439d863cb03SClaudio if (cg_create(child))
440d863cb03SClaudio goto cleanup;
441d863cb03SClaudio
442d863cb03SClaudio if (!cg_write(child, "cgroup.subtree_control", "+memory"))
443d863cb03SClaudio goto cleanup;
444d863cb03SClaudio
445d863cb03SClaudio ret = KSFT_PASS;
446d863cb03SClaudio
447d863cb03SClaudio cleanup:
448d863cb03SClaudio if (child)
449d863cb03SClaudio cg_destroy(child);
450d863cb03SClaudio if (parent)
451d863cb03SClaudio cg_destroy(parent);
452d863cb03SClaudio free(child);
453d863cb03SClaudio free(parent);
454d863cb03SClaudio return ret;
455d863cb03SClaudio }
456d863cb03SClaudio
457d863cb03SClaudio /*
458d863cb03SClaudio * Test that you can't disable a controller on a parent
459d863cb03SClaudio * if it's enabled in a child.
460d863cb03SClaudio */
test_cgcore_top_down_constraint_disable(const char * root)461d863cb03SClaudio static int test_cgcore_top_down_constraint_disable(const char *root)
462d863cb03SClaudio {
463d863cb03SClaudio int ret = KSFT_FAIL;
464d863cb03SClaudio char *parent = NULL, *child = NULL;
465d863cb03SClaudio
466d863cb03SClaudio parent = cg_name(root, "cg_test_parent");
467d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child");
468d863cb03SClaudio if (!parent || !child)
469d863cb03SClaudio goto cleanup;
470d863cb03SClaudio
471d863cb03SClaudio if (cg_create(parent))
472d863cb03SClaudio goto cleanup;
473d863cb03SClaudio
474d863cb03SClaudio if (cg_create(child))
475d863cb03SClaudio goto cleanup;
476d863cb03SClaudio
477d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+memory"))
478d863cb03SClaudio goto cleanup;
479d863cb03SClaudio
480d863cb03SClaudio if (cg_write(child, "cgroup.subtree_control", "+memory"))
481d863cb03SClaudio goto cleanup;
482d863cb03SClaudio
483d863cb03SClaudio if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
484d863cb03SClaudio goto cleanup;
485d863cb03SClaudio
486d863cb03SClaudio ret = KSFT_PASS;
487d863cb03SClaudio
488d863cb03SClaudio cleanup:
489d863cb03SClaudio if (child)
490d863cb03SClaudio cg_destroy(child);
491d863cb03SClaudio if (parent)
492d863cb03SClaudio cg_destroy(parent);
493d863cb03SClaudio free(child);
494d863cb03SClaudio free(parent);
495d863cb03SClaudio return ret;
496d863cb03SClaudio }
497d863cb03SClaudio
498d863cb03SClaudio /*
499d863cb03SClaudio * Test internal process constraint.
500d863cb03SClaudio * You can't add a pid to a domain parent if a controller is enabled.
501d863cb03SClaudio */
test_cgcore_internal_process_constraint(const char * root)502d863cb03SClaudio static int test_cgcore_internal_process_constraint(const char *root)
503d863cb03SClaudio {
504d863cb03SClaudio int ret = KSFT_FAIL;
505d863cb03SClaudio char *parent = NULL, *child = NULL;
506d863cb03SClaudio
507d863cb03SClaudio parent = cg_name(root, "cg_test_parent");
508d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child");
509d863cb03SClaudio if (!parent || !child)
510d863cb03SClaudio goto cleanup;
511d863cb03SClaudio
512d863cb03SClaudio if (cg_create(parent))
513d863cb03SClaudio goto cleanup;
514d863cb03SClaudio
515d863cb03SClaudio if (cg_create(child))
516d863cb03SClaudio goto cleanup;
517d863cb03SClaudio
518d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+memory"))
519d863cb03SClaudio goto cleanup;
520d863cb03SClaudio
521d863cb03SClaudio if (!cg_enter_current(parent))
522d863cb03SClaudio goto cleanup;
523d863cb03SClaudio
5249bd5910dSChristian Brauner if (!clone_into_cgroup_run_wait(parent))
5259bd5910dSChristian Brauner goto cleanup;
5269bd5910dSChristian Brauner
527d863cb03SClaudio ret = KSFT_PASS;
528d863cb03SClaudio
529d863cb03SClaudio cleanup:
530d863cb03SClaudio if (child)
531d863cb03SClaudio cg_destroy(child);
532d863cb03SClaudio if (parent)
533d863cb03SClaudio cg_destroy(parent);
534d863cb03SClaudio free(child);
535d863cb03SClaudio free(parent);
536d863cb03SClaudio return ret;
537d863cb03SClaudio }
538d863cb03SClaudio
dummy_thread_fn(void * arg)53911318989SMichal Koutný static void *dummy_thread_fn(void *arg)
54011318989SMichal Koutný {
54111318989SMichal Koutný return (void *)(size_t)pause();
54211318989SMichal Koutný }
54311318989SMichal Koutný
54411318989SMichal Koutný /*
54511318989SMichal Koutný * Test threadgroup migration.
54611318989SMichal Koutný * All threads of a process are migrated together.
54711318989SMichal Koutný */
test_cgcore_proc_migration(const char * root)54811318989SMichal Koutný static int test_cgcore_proc_migration(const char *root)
54911318989SMichal Koutný {
55011318989SMichal Koutný int ret = KSFT_FAIL;
551192c197cSDan Carpenter int t, c_threads = 0, n_threads = 13;
55211318989SMichal Koutný char *src = NULL, *dst = NULL;
55311318989SMichal Koutný pthread_t threads[n_threads];
55411318989SMichal Koutný
55511318989SMichal Koutný src = cg_name(root, "cg_src");
55611318989SMichal Koutný dst = cg_name(root, "cg_dst");
55711318989SMichal Koutný if (!src || !dst)
55811318989SMichal Koutný goto cleanup;
55911318989SMichal Koutný
56011318989SMichal Koutný if (cg_create(src))
56111318989SMichal Koutný goto cleanup;
56211318989SMichal Koutný if (cg_create(dst))
56311318989SMichal Koutný goto cleanup;
56411318989SMichal Koutný
56511318989SMichal Koutný if (cg_enter_current(src))
56611318989SMichal Koutný goto cleanup;
56711318989SMichal Koutný
56811318989SMichal Koutný for (c_threads = 0; c_threads < n_threads; ++c_threads) {
56911318989SMichal Koutný if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL))
57011318989SMichal Koutný goto cleanup;
57111318989SMichal Koutný }
57211318989SMichal Koutný
57311318989SMichal Koutný cg_enter_current(dst);
57411318989SMichal Koutný if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1)
57511318989SMichal Koutný goto cleanup;
57611318989SMichal Koutný
57711318989SMichal Koutný ret = KSFT_PASS;
57811318989SMichal Koutný
57911318989SMichal Koutný cleanup:
58011318989SMichal Koutný for (t = 0; t < c_threads; ++t) {
58111318989SMichal Koutný pthread_cancel(threads[t]);
58211318989SMichal Koutný }
58311318989SMichal Koutný
58411318989SMichal Koutný for (t = 0; t < c_threads; ++t) {
58511318989SMichal Koutný pthread_join(threads[t], NULL);
58611318989SMichal Koutný }
58711318989SMichal Koutný
58811318989SMichal Koutný cg_enter_current(root);
58911318989SMichal Koutný
59011318989SMichal Koutný if (dst)
59111318989SMichal Koutný cg_destroy(dst);
59211318989SMichal Koutný if (src)
59311318989SMichal Koutný cg_destroy(src);
59411318989SMichal Koutný free(dst);
59511318989SMichal Koutný free(src);
59611318989SMichal Koutný return ret;
59711318989SMichal Koutný }
59811318989SMichal Koutný
migrating_thread_fn(void * arg)59911318989SMichal Koutný static void *migrating_thread_fn(void *arg)
60011318989SMichal Koutný {
60111318989SMichal Koutný int g, i, n_iterations = 1000;
60211318989SMichal Koutný char **grps = arg;
60311318989SMichal Koutný char lines[3][PATH_MAX];
60411318989SMichal Koutný
60511318989SMichal Koutný for (g = 1; g < 3; ++g)
60611318989SMichal Koutný snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0]));
60711318989SMichal Koutný
60811318989SMichal Koutný for (i = 0; i < n_iterations; ++i) {
60911318989SMichal Koutný cg_enter_current_thread(grps[(i % 2) + 1]);
61011318989SMichal Koutný
61111318989SMichal Koutný if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1]))
61211318989SMichal Koutný return (void *)-1;
61311318989SMichal Koutný }
61411318989SMichal Koutný return NULL;
61511318989SMichal Koutný }
61611318989SMichal Koutný
61711318989SMichal Koutný /*
61811318989SMichal Koutný * Test single thread migration.
61911318989SMichal Koutný * Threaded cgroups allow successful migration of a thread.
62011318989SMichal Koutný */
test_cgcore_thread_migration(const char * root)62111318989SMichal Koutný static int test_cgcore_thread_migration(const char *root)
62211318989SMichal Koutný {
62311318989SMichal Koutný int ret = KSFT_FAIL;
62411318989SMichal Koutný char *dom = NULL;
62511318989SMichal Koutný char line[PATH_MAX];
62611318989SMichal Koutný char *grps[3] = { (char *)root, NULL, NULL };
62711318989SMichal Koutný pthread_t thr;
62811318989SMichal Koutný void *retval;
62911318989SMichal Koutný
63011318989SMichal Koutný dom = cg_name(root, "cg_dom");
63111318989SMichal Koutný grps[1] = cg_name(root, "cg_dom/cg_src");
63211318989SMichal Koutný grps[2] = cg_name(root, "cg_dom/cg_dst");
63311318989SMichal Koutný if (!grps[1] || !grps[2] || !dom)
63411318989SMichal Koutný goto cleanup;
63511318989SMichal Koutný
63611318989SMichal Koutný if (cg_create(dom))
63711318989SMichal Koutný goto cleanup;
63811318989SMichal Koutný if (cg_create(grps[1]))
63911318989SMichal Koutný goto cleanup;
64011318989SMichal Koutný if (cg_create(grps[2]))
64111318989SMichal Koutný goto cleanup;
64211318989SMichal Koutný
64311318989SMichal Koutný if (cg_write(grps[1], "cgroup.type", "threaded"))
64411318989SMichal Koutný goto cleanup;
64511318989SMichal Koutný if (cg_write(grps[2], "cgroup.type", "threaded"))
64611318989SMichal Koutný goto cleanup;
64711318989SMichal Koutný
64811318989SMichal Koutný if (cg_enter_current(grps[1]))
64911318989SMichal Koutný goto cleanup;
65011318989SMichal Koutný
65111318989SMichal Koutný if (pthread_create(&thr, NULL, migrating_thread_fn, grps))
65211318989SMichal Koutný goto cleanup;
65311318989SMichal Koutný
65411318989SMichal Koutný if (pthread_join(thr, &retval))
65511318989SMichal Koutný goto cleanup;
65611318989SMichal Koutný
65711318989SMichal Koutný if (retval)
65811318989SMichal Koutný goto cleanup;
65911318989SMichal Koutný
66011318989SMichal Koutný snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0]));
66111318989SMichal Koutný if (proc_read_strstr(0, 1, "cgroup", line))
66211318989SMichal Koutný goto cleanup;
66311318989SMichal Koutný
66411318989SMichal Koutný ret = KSFT_PASS;
66511318989SMichal Koutný
66611318989SMichal Koutný cleanup:
66711318989SMichal Koutný cg_enter_current(root);
66811318989SMichal Koutný if (grps[2])
66911318989SMichal Koutný cg_destroy(grps[2]);
67011318989SMichal Koutný if (grps[1])
67111318989SMichal Koutný cg_destroy(grps[1]);
67211318989SMichal Koutný if (dom)
67311318989SMichal Koutný cg_destroy(dom);
67411318989SMichal Koutný free(grps[2]);
67511318989SMichal Koutný free(grps[1]);
67611318989SMichal Koutný free(dom);
67711318989SMichal Koutný return ret;
67811318989SMichal Koutný }
67911318989SMichal Koutný
680613e040eSTejun Heo /*
681613e040eSTejun Heo * cgroup migration permission check should be performed based on the
682613e040eSTejun Heo * credentials at the time of open instead of write.
683613e040eSTejun Heo */
test_cgcore_lesser_euid_open(const char * root)684613e040eSTejun Heo static int test_cgcore_lesser_euid_open(const char *root)
685613e040eSTejun Heo {
686*12101424SMichal Koutný const uid_t test_euid = TEST_UID;
687613e040eSTejun Heo int ret = KSFT_FAIL;
688613e040eSTejun Heo char *cg_test_a = NULL, *cg_test_b = NULL;
689613e040eSTejun Heo char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
690613e040eSTejun Heo int cg_test_b_procs_fd = -1;
691613e040eSTejun Heo uid_t saved_uid;
692613e040eSTejun Heo
693613e040eSTejun Heo cg_test_a = cg_name(root, "cg_test_a");
694613e040eSTejun Heo cg_test_b = cg_name(root, "cg_test_b");
695613e040eSTejun Heo
696613e040eSTejun Heo if (!cg_test_a || !cg_test_b)
697613e040eSTejun Heo goto cleanup;
698613e040eSTejun Heo
699613e040eSTejun Heo cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs");
700613e040eSTejun Heo cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
701613e040eSTejun Heo
702613e040eSTejun Heo if (!cg_test_a_procs || !cg_test_b_procs)
703613e040eSTejun Heo goto cleanup;
704613e040eSTejun Heo
705613e040eSTejun Heo if (cg_create(cg_test_a) || cg_create(cg_test_b))
706613e040eSTejun Heo goto cleanup;
707613e040eSTejun Heo
708613e040eSTejun Heo if (cg_enter_current(cg_test_a))
709613e040eSTejun Heo goto cleanup;
710613e040eSTejun Heo
711613e040eSTejun Heo if (chown(cg_test_a_procs, test_euid, -1) ||
712613e040eSTejun Heo chown(cg_test_b_procs, test_euid, -1))
713613e040eSTejun Heo goto cleanup;
714613e040eSTejun Heo
715613e040eSTejun Heo saved_uid = geteuid();
716613e040eSTejun Heo if (seteuid(test_euid))
717613e040eSTejun Heo goto cleanup;
718613e040eSTejun Heo
719613e040eSTejun Heo cg_test_b_procs_fd = open(cg_test_b_procs, O_RDWR);
720613e040eSTejun Heo
721613e040eSTejun Heo if (seteuid(saved_uid))
722613e040eSTejun Heo goto cleanup;
723613e040eSTejun Heo
724613e040eSTejun Heo if (cg_test_b_procs_fd < 0)
725613e040eSTejun Heo goto cleanup;
726613e040eSTejun Heo
727613e040eSTejun Heo if (write(cg_test_b_procs_fd, "0", 1) >= 0 || errno != EACCES)
728613e040eSTejun Heo goto cleanup;
729613e040eSTejun Heo
730613e040eSTejun Heo ret = KSFT_PASS;
731613e040eSTejun Heo
732613e040eSTejun Heo cleanup:
733613e040eSTejun Heo cg_enter_current(root);
734613e040eSTejun Heo if (cg_test_b_procs_fd >= 0)
735613e040eSTejun Heo close(cg_test_b_procs_fd);
736613e040eSTejun Heo if (cg_test_b)
737613e040eSTejun Heo cg_destroy(cg_test_b);
738613e040eSTejun Heo if (cg_test_a)
739613e040eSTejun Heo cg_destroy(cg_test_a);
740613e040eSTejun Heo free(cg_test_b_procs);
741613e040eSTejun Heo free(cg_test_a_procs);
742613e040eSTejun Heo free(cg_test_b);
743613e040eSTejun Heo free(cg_test_a);
744613e040eSTejun Heo return ret;
745613e040eSTejun Heo }
746613e040eSTejun Heo
747bf35a787STejun Heo struct lesser_ns_open_thread_arg {
748bf35a787STejun Heo const char *path;
749bf35a787STejun Heo int fd;
750bf35a787STejun Heo int err;
751bf35a787STejun Heo };
752bf35a787STejun Heo
lesser_ns_open_thread_fn(void * arg)753bf35a787STejun Heo static int lesser_ns_open_thread_fn(void *arg)
754bf35a787STejun Heo {
755bf35a787STejun Heo struct lesser_ns_open_thread_arg *targ = arg;
756bf35a787STejun Heo
757bf35a787STejun Heo targ->fd = open(targ->path, O_RDWR);
758bf35a787STejun Heo targ->err = errno;
759bf35a787STejun Heo return 0;
760bf35a787STejun Heo }
761bf35a787STejun Heo
762bf35a787STejun Heo /*
763bf35a787STejun Heo * cgroup migration permission check should be performed based on the cgroup
764bf35a787STejun Heo * namespace at the time of open instead of write.
765bf35a787STejun Heo */
test_cgcore_lesser_ns_open(const char * root)766bf35a787STejun Heo static int test_cgcore_lesser_ns_open(const char *root)
767bf35a787STejun Heo {
768bf35a787STejun Heo static char stack[65536];
769bf35a787STejun Heo const uid_t test_euid = 65534; /* usually nobody, any !root is fine */
770bf35a787STejun Heo int ret = KSFT_FAIL;
771bf35a787STejun Heo char *cg_test_a = NULL, *cg_test_b = NULL;
772bf35a787STejun Heo char *cg_test_a_procs = NULL, *cg_test_b_procs = NULL;
773bf35a787STejun Heo int cg_test_b_procs_fd = -1;
774bf35a787STejun Heo struct lesser_ns_open_thread_arg targ = { .fd = -1 };
775bf35a787STejun Heo pid_t pid;
776bf35a787STejun Heo int status;
777bf35a787STejun Heo
778bf35a787STejun Heo cg_test_a = cg_name(root, "cg_test_a");
779bf35a787STejun Heo cg_test_b = cg_name(root, "cg_test_b");
780bf35a787STejun Heo
781bf35a787STejun Heo if (!cg_test_a || !cg_test_b)
782bf35a787STejun Heo goto cleanup;
783bf35a787STejun Heo
784bf35a787STejun Heo cg_test_a_procs = cg_name(cg_test_a, "cgroup.procs");
785bf35a787STejun Heo cg_test_b_procs = cg_name(cg_test_b, "cgroup.procs");
786bf35a787STejun Heo
787bf35a787STejun Heo if (!cg_test_a_procs || !cg_test_b_procs)
788bf35a787STejun Heo goto cleanup;
789bf35a787STejun Heo
790bf35a787STejun Heo if (cg_create(cg_test_a) || cg_create(cg_test_b))
791bf35a787STejun Heo goto cleanup;
792bf35a787STejun Heo
793bf35a787STejun Heo if (cg_enter_current(cg_test_b))
794bf35a787STejun Heo goto cleanup;
795bf35a787STejun Heo
796bf35a787STejun Heo if (chown(cg_test_a_procs, test_euid, -1) ||
797bf35a787STejun Heo chown(cg_test_b_procs, test_euid, -1))
798bf35a787STejun Heo goto cleanup;
799bf35a787STejun Heo
800bf35a787STejun Heo targ.path = cg_test_b_procs;
801bf35a787STejun Heo pid = clone(lesser_ns_open_thread_fn, stack + sizeof(stack),
802bf35a787STejun Heo CLONE_NEWCGROUP | CLONE_FILES | CLONE_VM | SIGCHLD,
803bf35a787STejun Heo &targ);
804bf35a787STejun Heo if (pid < 0)
805bf35a787STejun Heo goto cleanup;
806bf35a787STejun Heo
807bf35a787STejun Heo if (waitpid(pid, &status, 0) < 0)
808bf35a787STejun Heo goto cleanup;
809bf35a787STejun Heo
810bf35a787STejun Heo if (!WIFEXITED(status))
811bf35a787STejun Heo goto cleanup;
812bf35a787STejun Heo
813bf35a787STejun Heo cg_test_b_procs_fd = targ.fd;
814bf35a787STejun Heo if (cg_test_b_procs_fd < 0)
815bf35a787STejun Heo goto cleanup;
816bf35a787STejun Heo
817bf35a787STejun Heo if (cg_enter_current(cg_test_a))
818bf35a787STejun Heo goto cleanup;
819bf35a787STejun Heo
820bf35a787STejun Heo if ((status = write(cg_test_b_procs_fd, "0", 1)) >= 0 || errno != ENOENT)
821bf35a787STejun Heo goto cleanup;
822bf35a787STejun Heo
823bf35a787STejun Heo ret = KSFT_PASS;
824bf35a787STejun Heo
825bf35a787STejun Heo cleanup:
826bf35a787STejun Heo cg_enter_current(root);
827bf35a787STejun Heo if (cg_test_b_procs_fd >= 0)
828bf35a787STejun Heo close(cg_test_b_procs_fd);
829bf35a787STejun Heo if (cg_test_b)
830bf35a787STejun Heo cg_destroy(cg_test_b);
831bf35a787STejun Heo if (cg_test_a)
832bf35a787STejun Heo cg_destroy(cg_test_a);
833bf35a787STejun Heo free(cg_test_b_procs);
834bf35a787STejun Heo free(cg_test_a_procs);
835bf35a787STejun Heo free(cg_test_b);
836bf35a787STejun Heo free(cg_test_a);
837bf35a787STejun Heo return ret;
838bf35a787STejun Heo }
839bf35a787STejun Heo
840d863cb03SClaudio #define T(x) { x, #x }
841d863cb03SClaudio struct corecg_test {
842d863cb03SClaudio int (*fn)(const char *root);
843d863cb03SClaudio const char *name;
844d863cb03SClaudio } tests[] = {
845d863cb03SClaudio T(test_cgcore_internal_process_constraint),
846d863cb03SClaudio T(test_cgcore_top_down_constraint_enable),
847d863cb03SClaudio T(test_cgcore_top_down_constraint_disable),
848d863cb03SClaudio T(test_cgcore_no_internal_process_constraint_on_threads),
849d863cb03SClaudio T(test_cgcore_parent_becomes_threaded),
850d863cb03SClaudio T(test_cgcore_invalid_domain),
851d863cb03SClaudio T(test_cgcore_populated),
85211318989SMichal Koutný T(test_cgcore_proc_migration),
85311318989SMichal Koutný T(test_cgcore_thread_migration),
85404189382SSuren Baghdasaryan T(test_cgcore_destroy),
855613e040eSTejun Heo T(test_cgcore_lesser_euid_open),
856bf35a787STejun Heo T(test_cgcore_lesser_ns_open),
857d863cb03SClaudio };
858d863cb03SClaudio #undef T
859d863cb03SClaudio
main(int argc,char * argv[])860d863cb03SClaudio int main(int argc, char *argv[])
861d863cb03SClaudio {
862d863cb03SClaudio char root[PATH_MAX];
863d863cb03SClaudio int i, ret = EXIT_SUCCESS;
864d863cb03SClaudio
865d863cb03SClaudio if (cg_find_unified_root(root, sizeof(root)))
866d863cb03SClaudio ksft_exit_skip("cgroup v2 isn't mounted\n");
86700e38a5dSAlex Shi
86800e38a5dSAlex Shi if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
86900e38a5dSAlex Shi if (cg_write(root, "cgroup.subtree_control", "+memory"))
87000e38a5dSAlex Shi ksft_exit_skip("Failed to set memory controller\n");
87100e38a5dSAlex Shi
872d863cb03SClaudio for (i = 0; i < ARRAY_SIZE(tests); i++) {
873d863cb03SClaudio switch (tests[i].fn(root)) {
874d863cb03SClaudio case KSFT_PASS:
875d863cb03SClaudio ksft_test_result_pass("%s\n", tests[i].name);
876d863cb03SClaudio break;
877d863cb03SClaudio case KSFT_SKIP:
878d863cb03SClaudio ksft_test_result_skip("%s\n", tests[i].name);
879d863cb03SClaudio break;
880d863cb03SClaudio default:
881d863cb03SClaudio ret = EXIT_FAILURE;
882d863cb03SClaudio ksft_test_result_fail("%s\n", tests[i].name);
883d863cb03SClaudio break;
884d863cb03SClaudio }
885d863cb03SClaudio }
886d863cb03SClaudio
887d863cb03SClaudio return ret;
888d863cb03SClaudio }
889