1d863cb03SClaudio /* SPDX-License-Identifier: GPL-2.0 */ 2d863cb03SClaudio 3d863cb03SClaudio #include <linux/limits.h> 4d863cb03SClaudio #include <sys/types.h> 5d863cb03SClaudio #include <unistd.h> 6d863cb03SClaudio #include <stdio.h> 7d863cb03SClaudio #include <errno.h> 811318989SMichal Koutný #include <signal.h> 911318989SMichal Koutný #include <string.h> 1011318989SMichal Koutný #include <pthread.h> 11d863cb03SClaudio 12d863cb03SClaudio #include "../kselftest.h" 13d863cb03SClaudio #include "cgroup_util.h" 14d863cb03SClaudio 15d863cb03SClaudio /* 16d863cb03SClaudio * A(0) - B(0) - C(1) 17d863cb03SClaudio * \ D(0) 18d863cb03SClaudio * 19d863cb03SClaudio * A, B and C's "populated" fields would be 1 while D's 0. 20d863cb03SClaudio * test that after the one process in C is moved to root, 21d863cb03SClaudio * A,B and C's "populated" fields would flip to "0" and file 22d863cb03SClaudio * modified events will be generated on the 23d863cb03SClaudio * "cgroup.events" files of both cgroups. 24d863cb03SClaudio */ 25d863cb03SClaudio static int test_cgcore_populated(const char *root) 26d863cb03SClaudio { 27d863cb03SClaudio int ret = KSFT_FAIL; 28d863cb03SClaudio char *cg_test_a = NULL, *cg_test_b = NULL; 29d863cb03SClaudio char *cg_test_c = NULL, *cg_test_d = NULL; 30d863cb03SClaudio 31d863cb03SClaudio cg_test_a = cg_name(root, "cg_test_a"); 32d863cb03SClaudio cg_test_b = cg_name(root, "cg_test_a/cg_test_b"); 33d863cb03SClaudio cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c"); 34d863cb03SClaudio cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d"); 35d863cb03SClaudio 36d863cb03SClaudio if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d) 37d863cb03SClaudio goto cleanup; 38d863cb03SClaudio 39d863cb03SClaudio if (cg_create(cg_test_a)) 40d863cb03SClaudio goto cleanup; 41d863cb03SClaudio 42d863cb03SClaudio if (cg_create(cg_test_b)) 43d863cb03SClaudio goto cleanup; 44d863cb03SClaudio 45d863cb03SClaudio if (cg_create(cg_test_c)) 46d863cb03SClaudio goto cleanup; 47d863cb03SClaudio 48d863cb03SClaudio if (cg_create(cg_test_d)) 49d863cb03SClaudio goto cleanup; 50d863cb03SClaudio 51d863cb03SClaudio if (cg_enter_current(cg_test_c)) 52d863cb03SClaudio goto cleanup; 53d863cb03SClaudio 54d863cb03SClaudio if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n")) 55d863cb03SClaudio goto cleanup; 56d863cb03SClaudio 57d863cb03SClaudio if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n")) 58d863cb03SClaudio goto cleanup; 59d863cb03SClaudio 60d863cb03SClaudio if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n")) 61d863cb03SClaudio goto cleanup; 62d863cb03SClaudio 63d863cb03SClaudio if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 64d863cb03SClaudio goto cleanup; 65d863cb03SClaudio 66d863cb03SClaudio if (cg_enter_current(root)) 67d863cb03SClaudio goto cleanup; 68d863cb03SClaudio 69d863cb03SClaudio if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n")) 70d863cb03SClaudio goto cleanup; 71d863cb03SClaudio 72d863cb03SClaudio if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n")) 73d863cb03SClaudio goto cleanup; 74d863cb03SClaudio 75d863cb03SClaudio if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n")) 76d863cb03SClaudio goto cleanup; 77d863cb03SClaudio 78d863cb03SClaudio if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n")) 79d863cb03SClaudio goto cleanup; 80d863cb03SClaudio 81d863cb03SClaudio ret = KSFT_PASS; 82d863cb03SClaudio 83d863cb03SClaudio cleanup: 84d863cb03SClaudio if (cg_test_d) 85d863cb03SClaudio cg_destroy(cg_test_d); 86d863cb03SClaudio if (cg_test_c) 87d863cb03SClaudio cg_destroy(cg_test_c); 88d863cb03SClaudio if (cg_test_b) 89d863cb03SClaudio cg_destroy(cg_test_b); 90d863cb03SClaudio if (cg_test_a) 91d863cb03SClaudio cg_destroy(cg_test_a); 92d863cb03SClaudio free(cg_test_d); 93d863cb03SClaudio free(cg_test_c); 94d863cb03SClaudio free(cg_test_b); 95d863cb03SClaudio free(cg_test_a); 96d863cb03SClaudio return ret; 97d863cb03SClaudio } 98d863cb03SClaudio 99d863cb03SClaudio /* 100d863cb03SClaudio * A (domain threaded) - B (threaded) - C (domain) 101d863cb03SClaudio * 102d863cb03SClaudio * test that C can't be used until it is turned into a 103d863cb03SClaudio * threaded cgroup. "cgroup.type" file will report "domain (invalid)" in 104d863cb03SClaudio * these cases. Operations which fail due to invalid topology use 105d863cb03SClaudio * EOPNOTSUPP as the errno. 106d863cb03SClaudio */ 107d863cb03SClaudio static int test_cgcore_invalid_domain(const char *root) 108d863cb03SClaudio { 109d863cb03SClaudio int ret = KSFT_FAIL; 110d863cb03SClaudio char *grandparent = NULL, *parent = NULL, *child = NULL; 111d863cb03SClaudio 112d863cb03SClaudio grandparent = cg_name(root, "cg_test_grandparent"); 113d863cb03SClaudio parent = cg_name(root, "cg_test_grandparent/cg_test_parent"); 114d863cb03SClaudio child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child"); 115d863cb03SClaudio if (!parent || !child || !grandparent) 116d863cb03SClaudio goto cleanup; 117d863cb03SClaudio 118d863cb03SClaudio if (cg_create(grandparent)) 119d863cb03SClaudio goto cleanup; 120d863cb03SClaudio 121d863cb03SClaudio if (cg_create(parent)) 122d863cb03SClaudio goto cleanup; 123d863cb03SClaudio 124d863cb03SClaudio if (cg_create(child)) 125d863cb03SClaudio goto cleanup; 126d863cb03SClaudio 127d863cb03SClaudio if (cg_write(parent, "cgroup.type", "threaded")) 128d863cb03SClaudio goto cleanup; 129d863cb03SClaudio 130d863cb03SClaudio if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n")) 131d863cb03SClaudio goto cleanup; 132d863cb03SClaudio 133d863cb03SClaudio if (!cg_enter_current(child)) 134d863cb03SClaudio goto cleanup; 135d863cb03SClaudio 136d863cb03SClaudio if (errno != EOPNOTSUPP) 137d863cb03SClaudio goto cleanup; 138d863cb03SClaudio 139d863cb03SClaudio ret = KSFT_PASS; 140d863cb03SClaudio 141d863cb03SClaudio cleanup: 142d863cb03SClaudio cg_enter_current(root); 143d863cb03SClaudio if (child) 144d863cb03SClaudio cg_destroy(child); 145d863cb03SClaudio if (parent) 146d863cb03SClaudio cg_destroy(parent); 147d863cb03SClaudio if (grandparent) 148d863cb03SClaudio cg_destroy(grandparent); 149d863cb03SClaudio free(child); 150d863cb03SClaudio free(parent); 151d863cb03SClaudio free(grandparent); 152d863cb03SClaudio return ret; 153d863cb03SClaudio } 154d863cb03SClaudio 155d863cb03SClaudio /* 156d863cb03SClaudio * Test that when a child becomes threaded 157d863cb03SClaudio * the parent type becomes domain threaded. 158d863cb03SClaudio */ 159d863cb03SClaudio static int test_cgcore_parent_becomes_threaded(const char *root) 160d863cb03SClaudio { 161d863cb03SClaudio int ret = KSFT_FAIL; 162d863cb03SClaudio char *parent = NULL, *child = NULL; 163d863cb03SClaudio 164d863cb03SClaudio parent = cg_name(root, "cg_test_parent"); 165d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child"); 166d863cb03SClaudio if (!parent || !child) 167d863cb03SClaudio goto cleanup; 168d863cb03SClaudio 169d863cb03SClaudio if (cg_create(parent)) 170d863cb03SClaudio goto cleanup; 171d863cb03SClaudio 172d863cb03SClaudio if (cg_create(child)) 173d863cb03SClaudio goto cleanup; 174d863cb03SClaudio 175d863cb03SClaudio if (cg_write(child, "cgroup.type", "threaded")) 176d863cb03SClaudio goto cleanup; 177d863cb03SClaudio 178d863cb03SClaudio if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n")) 179d863cb03SClaudio goto cleanup; 180d863cb03SClaudio 181d863cb03SClaudio ret = KSFT_PASS; 182d863cb03SClaudio 183d863cb03SClaudio cleanup: 184d863cb03SClaudio if (child) 185d863cb03SClaudio cg_destroy(child); 186d863cb03SClaudio if (parent) 187d863cb03SClaudio cg_destroy(parent); 188d863cb03SClaudio free(child); 189d863cb03SClaudio free(parent); 190d863cb03SClaudio return ret; 191d863cb03SClaudio 192d863cb03SClaudio } 193d863cb03SClaudio 194d863cb03SClaudio /* 195d863cb03SClaudio * Test that there's no internal process constrain on threaded cgroups. 196d863cb03SClaudio * You can add threads/processes on a parent with a controller enabled. 197d863cb03SClaudio */ 198d863cb03SClaudio static int test_cgcore_no_internal_process_constraint_on_threads(const char *root) 199d863cb03SClaudio { 200d863cb03SClaudio int ret = KSFT_FAIL; 201d863cb03SClaudio char *parent = NULL, *child = NULL; 202d863cb03SClaudio 203d863cb03SClaudio if (cg_read_strstr(root, "cgroup.controllers", "cpu") || 204f97f3f88SAlex Shi cg_write(root, "cgroup.subtree_control", "+cpu")) { 205d863cb03SClaudio ret = KSFT_SKIP; 206d863cb03SClaudio goto cleanup; 207d863cb03SClaudio } 208d863cb03SClaudio 209d863cb03SClaudio parent = cg_name(root, "cg_test_parent"); 210d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child"); 211d863cb03SClaudio if (!parent || !child) 212d863cb03SClaudio goto cleanup; 213d863cb03SClaudio 214d863cb03SClaudio if (cg_create(parent)) 215d863cb03SClaudio goto cleanup; 216d863cb03SClaudio 217d863cb03SClaudio if (cg_create(child)) 218d863cb03SClaudio goto cleanup; 219d863cb03SClaudio 220d863cb03SClaudio if (cg_write(parent, "cgroup.type", "threaded")) 221d863cb03SClaudio goto cleanup; 222d863cb03SClaudio 223d863cb03SClaudio if (cg_write(child, "cgroup.type", "threaded")) 224d863cb03SClaudio goto cleanup; 225d863cb03SClaudio 226d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+cpu")) 227d863cb03SClaudio goto cleanup; 228d863cb03SClaudio 229d863cb03SClaudio if (cg_enter_current(parent)) 230d863cb03SClaudio goto cleanup; 231d863cb03SClaudio 232d863cb03SClaudio ret = KSFT_PASS; 233d863cb03SClaudio 234d863cb03SClaudio cleanup: 235d863cb03SClaudio cg_enter_current(root); 236d863cb03SClaudio cg_enter_current(root); 237d863cb03SClaudio if (child) 238d863cb03SClaudio cg_destroy(child); 239d863cb03SClaudio if (parent) 240d863cb03SClaudio cg_destroy(parent); 241d863cb03SClaudio free(child); 242d863cb03SClaudio free(parent); 243d863cb03SClaudio return ret; 244d863cb03SClaudio } 245d863cb03SClaudio 246d863cb03SClaudio /* 247d863cb03SClaudio * Test that you can't enable a controller on a child if it's not enabled 248d863cb03SClaudio * on the parent. 249d863cb03SClaudio */ 250d863cb03SClaudio static int test_cgcore_top_down_constraint_enable(const char *root) 251d863cb03SClaudio { 252d863cb03SClaudio int ret = KSFT_FAIL; 253d863cb03SClaudio char *parent = NULL, *child = NULL; 254d863cb03SClaudio 255d863cb03SClaudio parent = cg_name(root, "cg_test_parent"); 256d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child"); 257d863cb03SClaudio if (!parent || !child) 258d863cb03SClaudio goto cleanup; 259d863cb03SClaudio 260d863cb03SClaudio if (cg_create(parent)) 261d863cb03SClaudio goto cleanup; 262d863cb03SClaudio 263d863cb03SClaudio if (cg_create(child)) 264d863cb03SClaudio goto cleanup; 265d863cb03SClaudio 266d863cb03SClaudio if (!cg_write(child, "cgroup.subtree_control", "+memory")) 267d863cb03SClaudio goto cleanup; 268d863cb03SClaudio 269d863cb03SClaudio ret = KSFT_PASS; 270d863cb03SClaudio 271d863cb03SClaudio cleanup: 272d863cb03SClaudio if (child) 273d863cb03SClaudio cg_destroy(child); 274d863cb03SClaudio if (parent) 275d863cb03SClaudio cg_destroy(parent); 276d863cb03SClaudio free(child); 277d863cb03SClaudio free(parent); 278d863cb03SClaudio return ret; 279d863cb03SClaudio } 280d863cb03SClaudio 281d863cb03SClaudio /* 282d863cb03SClaudio * Test that you can't disable a controller on a parent 283d863cb03SClaudio * if it's enabled in a child. 284d863cb03SClaudio */ 285d863cb03SClaudio static int test_cgcore_top_down_constraint_disable(const char *root) 286d863cb03SClaudio { 287d863cb03SClaudio int ret = KSFT_FAIL; 288d863cb03SClaudio char *parent = NULL, *child = NULL; 289d863cb03SClaudio 290d863cb03SClaudio parent = cg_name(root, "cg_test_parent"); 291d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child"); 292d863cb03SClaudio if (!parent || !child) 293d863cb03SClaudio goto cleanup; 294d863cb03SClaudio 295d863cb03SClaudio if (cg_create(parent)) 296d863cb03SClaudio goto cleanup; 297d863cb03SClaudio 298d863cb03SClaudio if (cg_create(child)) 299d863cb03SClaudio goto cleanup; 300d863cb03SClaudio 301d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+memory")) 302d863cb03SClaudio goto cleanup; 303d863cb03SClaudio 304d863cb03SClaudio if (cg_write(child, "cgroup.subtree_control", "+memory")) 305d863cb03SClaudio goto cleanup; 306d863cb03SClaudio 307d863cb03SClaudio if (!cg_write(parent, "cgroup.subtree_control", "-memory")) 308d863cb03SClaudio goto cleanup; 309d863cb03SClaudio 310d863cb03SClaudio ret = KSFT_PASS; 311d863cb03SClaudio 312d863cb03SClaudio cleanup: 313d863cb03SClaudio if (child) 314d863cb03SClaudio cg_destroy(child); 315d863cb03SClaudio if (parent) 316d863cb03SClaudio cg_destroy(parent); 317d863cb03SClaudio free(child); 318d863cb03SClaudio free(parent); 319d863cb03SClaudio return ret; 320d863cb03SClaudio } 321d863cb03SClaudio 322d863cb03SClaudio /* 323d863cb03SClaudio * Test internal process constraint. 324d863cb03SClaudio * You can't add a pid to a domain parent if a controller is enabled. 325d863cb03SClaudio */ 326d863cb03SClaudio static int test_cgcore_internal_process_constraint(const char *root) 327d863cb03SClaudio { 328d863cb03SClaudio int ret = KSFT_FAIL; 329d863cb03SClaudio char *parent = NULL, *child = NULL; 330d863cb03SClaudio 331d863cb03SClaudio parent = cg_name(root, "cg_test_parent"); 332d863cb03SClaudio child = cg_name(root, "cg_test_parent/cg_test_child"); 333d863cb03SClaudio if (!parent || !child) 334d863cb03SClaudio goto cleanup; 335d863cb03SClaudio 336d863cb03SClaudio if (cg_create(parent)) 337d863cb03SClaudio goto cleanup; 338d863cb03SClaudio 339d863cb03SClaudio if (cg_create(child)) 340d863cb03SClaudio goto cleanup; 341d863cb03SClaudio 342d863cb03SClaudio if (cg_write(parent, "cgroup.subtree_control", "+memory")) 343d863cb03SClaudio goto cleanup; 344d863cb03SClaudio 345d863cb03SClaudio if (!cg_enter_current(parent)) 346d863cb03SClaudio goto cleanup; 347d863cb03SClaudio 348d863cb03SClaudio ret = KSFT_PASS; 349d863cb03SClaudio 350d863cb03SClaudio cleanup: 351d863cb03SClaudio if (child) 352d863cb03SClaudio cg_destroy(child); 353d863cb03SClaudio if (parent) 354d863cb03SClaudio cg_destroy(parent); 355d863cb03SClaudio free(child); 356d863cb03SClaudio free(parent); 357d863cb03SClaudio return ret; 358d863cb03SClaudio } 359d863cb03SClaudio 36011318989SMichal Koutný static void *dummy_thread_fn(void *arg) 36111318989SMichal Koutný { 36211318989SMichal Koutný return (void *)(size_t)pause(); 36311318989SMichal Koutný } 36411318989SMichal Koutný 36511318989SMichal Koutný /* 36611318989SMichal Koutný * Test threadgroup migration. 36711318989SMichal Koutný * All threads of a process are migrated together. 36811318989SMichal Koutný */ 36911318989SMichal Koutný static int test_cgcore_proc_migration(const char *root) 37011318989SMichal Koutný { 37111318989SMichal Koutný int ret = KSFT_FAIL; 37211318989SMichal Koutný int t, c_threads, n_threads = 13; 37311318989SMichal Koutný char *src = NULL, *dst = NULL; 37411318989SMichal Koutný pthread_t threads[n_threads]; 37511318989SMichal Koutný 37611318989SMichal Koutný src = cg_name(root, "cg_src"); 37711318989SMichal Koutný dst = cg_name(root, "cg_dst"); 37811318989SMichal Koutný if (!src || !dst) 37911318989SMichal Koutný goto cleanup; 38011318989SMichal Koutný 38111318989SMichal Koutný if (cg_create(src)) 38211318989SMichal Koutný goto cleanup; 38311318989SMichal Koutný if (cg_create(dst)) 38411318989SMichal Koutný goto cleanup; 38511318989SMichal Koutný 38611318989SMichal Koutný if (cg_enter_current(src)) 38711318989SMichal Koutný goto cleanup; 38811318989SMichal Koutný 38911318989SMichal Koutný for (c_threads = 0; c_threads < n_threads; ++c_threads) { 39011318989SMichal Koutný if (pthread_create(&threads[c_threads], NULL, dummy_thread_fn, NULL)) 39111318989SMichal Koutný goto cleanup; 39211318989SMichal Koutný } 39311318989SMichal Koutný 39411318989SMichal Koutný cg_enter_current(dst); 39511318989SMichal Koutný if (cg_read_lc(dst, "cgroup.threads") != n_threads + 1) 39611318989SMichal Koutný goto cleanup; 39711318989SMichal Koutný 39811318989SMichal Koutný ret = KSFT_PASS; 39911318989SMichal Koutný 40011318989SMichal Koutný cleanup: 40111318989SMichal Koutný for (t = 0; t < c_threads; ++t) { 40211318989SMichal Koutný pthread_cancel(threads[t]); 40311318989SMichal Koutný } 40411318989SMichal Koutný 40511318989SMichal Koutný for (t = 0; t < c_threads; ++t) { 40611318989SMichal Koutný pthread_join(threads[t], NULL); 40711318989SMichal Koutný } 40811318989SMichal Koutný 40911318989SMichal Koutný cg_enter_current(root); 41011318989SMichal Koutný 41111318989SMichal Koutný if (dst) 41211318989SMichal Koutný cg_destroy(dst); 41311318989SMichal Koutný if (src) 41411318989SMichal Koutný cg_destroy(src); 41511318989SMichal Koutný free(dst); 41611318989SMichal Koutný free(src); 41711318989SMichal Koutný return ret; 41811318989SMichal Koutný } 41911318989SMichal Koutný 42011318989SMichal Koutný static void *migrating_thread_fn(void *arg) 42111318989SMichal Koutný { 42211318989SMichal Koutný int g, i, n_iterations = 1000; 42311318989SMichal Koutný char **grps = arg; 42411318989SMichal Koutný char lines[3][PATH_MAX]; 42511318989SMichal Koutný 42611318989SMichal Koutný for (g = 1; g < 3; ++g) 42711318989SMichal Koutný snprintf(lines[g], sizeof(lines[g]), "0::%s", grps[g] + strlen(grps[0])); 42811318989SMichal Koutný 42911318989SMichal Koutný for (i = 0; i < n_iterations; ++i) { 43011318989SMichal Koutný cg_enter_current_thread(grps[(i % 2) + 1]); 43111318989SMichal Koutný 43211318989SMichal Koutný if (proc_read_strstr(0, 1, "cgroup", lines[(i % 2) + 1])) 43311318989SMichal Koutný return (void *)-1; 43411318989SMichal Koutný } 43511318989SMichal Koutný return NULL; 43611318989SMichal Koutný } 43711318989SMichal Koutný 43811318989SMichal Koutný /* 43911318989SMichal Koutný * Test single thread migration. 44011318989SMichal Koutný * Threaded cgroups allow successful migration of a thread. 44111318989SMichal Koutný */ 44211318989SMichal Koutný static int test_cgcore_thread_migration(const char *root) 44311318989SMichal Koutný { 44411318989SMichal Koutný int ret = KSFT_FAIL; 44511318989SMichal Koutný char *dom = NULL; 44611318989SMichal Koutný char line[PATH_MAX]; 44711318989SMichal Koutný char *grps[3] = { (char *)root, NULL, NULL }; 44811318989SMichal Koutný pthread_t thr; 44911318989SMichal Koutný void *retval; 45011318989SMichal Koutný 45111318989SMichal Koutný dom = cg_name(root, "cg_dom"); 45211318989SMichal Koutný grps[1] = cg_name(root, "cg_dom/cg_src"); 45311318989SMichal Koutný grps[2] = cg_name(root, "cg_dom/cg_dst"); 45411318989SMichal Koutný if (!grps[1] || !grps[2] || !dom) 45511318989SMichal Koutný goto cleanup; 45611318989SMichal Koutný 45711318989SMichal Koutný if (cg_create(dom)) 45811318989SMichal Koutný goto cleanup; 45911318989SMichal Koutný if (cg_create(grps[1])) 46011318989SMichal Koutný goto cleanup; 46111318989SMichal Koutný if (cg_create(grps[2])) 46211318989SMichal Koutný goto cleanup; 46311318989SMichal Koutný 46411318989SMichal Koutný if (cg_write(grps[1], "cgroup.type", "threaded")) 46511318989SMichal Koutný goto cleanup; 46611318989SMichal Koutný if (cg_write(grps[2], "cgroup.type", "threaded")) 46711318989SMichal Koutný goto cleanup; 46811318989SMichal Koutný 46911318989SMichal Koutný if (cg_enter_current(grps[1])) 47011318989SMichal Koutný goto cleanup; 47111318989SMichal Koutný 47211318989SMichal Koutný if (pthread_create(&thr, NULL, migrating_thread_fn, grps)) 47311318989SMichal Koutný goto cleanup; 47411318989SMichal Koutný 47511318989SMichal Koutný if (pthread_join(thr, &retval)) 47611318989SMichal Koutný goto cleanup; 47711318989SMichal Koutný 47811318989SMichal Koutný if (retval) 47911318989SMichal Koutný goto cleanup; 48011318989SMichal Koutný 48111318989SMichal Koutný snprintf(line, sizeof(line), "0::%s", grps[1] + strlen(grps[0])); 48211318989SMichal Koutný if (proc_read_strstr(0, 1, "cgroup", line)) 48311318989SMichal Koutný goto cleanup; 48411318989SMichal Koutný 48511318989SMichal Koutný ret = KSFT_PASS; 48611318989SMichal Koutný 48711318989SMichal Koutný cleanup: 48811318989SMichal Koutný cg_enter_current(root); 48911318989SMichal Koutný if (grps[2]) 49011318989SMichal Koutný cg_destroy(grps[2]); 49111318989SMichal Koutný if (grps[1]) 49211318989SMichal Koutný cg_destroy(grps[1]); 49311318989SMichal Koutný if (dom) 49411318989SMichal Koutný cg_destroy(dom); 49511318989SMichal Koutný free(grps[2]); 49611318989SMichal Koutný free(grps[1]); 49711318989SMichal Koutný free(dom); 49811318989SMichal Koutný return ret; 49911318989SMichal Koutný } 50011318989SMichal Koutný 501d863cb03SClaudio #define T(x) { x, #x } 502d863cb03SClaudio struct corecg_test { 503d863cb03SClaudio int (*fn)(const char *root); 504d863cb03SClaudio const char *name; 505d863cb03SClaudio } tests[] = { 506d863cb03SClaudio T(test_cgcore_internal_process_constraint), 507d863cb03SClaudio T(test_cgcore_top_down_constraint_enable), 508d863cb03SClaudio T(test_cgcore_top_down_constraint_disable), 509d863cb03SClaudio T(test_cgcore_no_internal_process_constraint_on_threads), 510d863cb03SClaudio T(test_cgcore_parent_becomes_threaded), 511d863cb03SClaudio T(test_cgcore_invalid_domain), 512d863cb03SClaudio T(test_cgcore_populated), 51311318989SMichal Koutný T(test_cgcore_proc_migration), 51411318989SMichal Koutný T(test_cgcore_thread_migration), 515d863cb03SClaudio }; 516d863cb03SClaudio #undef T 517d863cb03SClaudio 518d863cb03SClaudio int main(int argc, char *argv[]) 519d863cb03SClaudio { 520d863cb03SClaudio char root[PATH_MAX]; 521d863cb03SClaudio int i, ret = EXIT_SUCCESS; 522d863cb03SClaudio 523d863cb03SClaudio if (cg_find_unified_root(root, sizeof(root))) 524d863cb03SClaudio ksft_exit_skip("cgroup v2 isn't mounted\n"); 52500e38a5dSAlex Shi 52600e38a5dSAlex Shi if (cg_read_strstr(root, "cgroup.subtree_control", "memory")) 52700e38a5dSAlex Shi if (cg_write(root, "cgroup.subtree_control", "+memory")) 52800e38a5dSAlex Shi ksft_exit_skip("Failed to set memory controller\n"); 52900e38a5dSAlex Shi 530d863cb03SClaudio for (i = 0; i < ARRAY_SIZE(tests); i++) { 531d863cb03SClaudio switch (tests[i].fn(root)) { 532d863cb03SClaudio case KSFT_PASS: 533d863cb03SClaudio ksft_test_result_pass("%s\n", tests[i].name); 534d863cb03SClaudio break; 535d863cb03SClaudio case KSFT_SKIP: 536d863cb03SClaudio ksft_test_result_skip("%s\n", tests[i].name); 537d863cb03SClaudio break; 538d863cb03SClaudio default: 539d863cb03SClaudio ret = EXIT_FAILURE; 540d863cb03SClaudio ksft_test_result_fail("%s\n", tests[i].name); 541d863cb03SClaudio break; 542d863cb03SClaudio } 543d863cb03SClaudio } 544d863cb03SClaudio 545d863cb03SClaudio return ret; 546d863cb03SClaudio } 547