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