root/tools/testing/selftests/cgroup/test_core.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. test_cgcore_populated
  2. test_cgcore_invalid_domain
  3. test_cgcore_parent_becomes_threaded
  4. test_cgcore_no_internal_process_constraint_on_threads
  5. test_cgcore_top_down_constraint_enable
  6. test_cgcore_top_down_constraint_disable
  7. test_cgcore_internal_process_constraint
  8. main

   1 /* SPDX-License-Identifier: GPL-2.0 */
   2 
   3 #include <linux/limits.h>
   4 #include <sys/types.h>
   5 #include <unistd.h>
   6 #include <stdio.h>
   7 #include <errno.h>
   8 
   9 #include "../kselftest.h"
  10 #include "cgroup_util.h"
  11 
  12 /*
  13  * A(0) - B(0) - C(1)
  14  *        \ D(0)
  15  *
  16  * A, B and C's "populated" fields would be 1 while D's 0.
  17  * test that after the one process in C is moved to root,
  18  * A,B and C's "populated" fields would flip to "0" and file
  19  * modified events will be generated on the
  20  * "cgroup.events" files of both cgroups.
  21  */
  22 static int test_cgcore_populated(const char *root)
  23 {
  24         int ret = KSFT_FAIL;
  25         char *cg_test_a = NULL, *cg_test_b = NULL;
  26         char *cg_test_c = NULL, *cg_test_d = NULL;
  27 
  28         cg_test_a = cg_name(root, "cg_test_a");
  29         cg_test_b = cg_name(root, "cg_test_a/cg_test_b");
  30         cg_test_c = cg_name(root, "cg_test_a/cg_test_b/cg_test_c");
  31         cg_test_d = cg_name(root, "cg_test_a/cg_test_b/cg_test_d");
  32 
  33         if (!cg_test_a || !cg_test_b || !cg_test_c || !cg_test_d)
  34                 goto cleanup;
  35 
  36         if (cg_create(cg_test_a))
  37                 goto cleanup;
  38 
  39         if (cg_create(cg_test_b))
  40                 goto cleanup;
  41 
  42         if (cg_create(cg_test_c))
  43                 goto cleanup;
  44 
  45         if (cg_create(cg_test_d))
  46                 goto cleanup;
  47 
  48         if (cg_enter_current(cg_test_c))
  49                 goto cleanup;
  50 
  51         if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 1\n"))
  52                 goto cleanup;
  53 
  54         if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 1\n"))
  55                 goto cleanup;
  56 
  57         if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 1\n"))
  58                 goto cleanup;
  59 
  60         if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
  61                 goto cleanup;
  62 
  63         if (cg_enter_current(root))
  64                 goto cleanup;
  65 
  66         if (cg_read_strcmp(cg_test_a, "cgroup.events", "populated 0\n"))
  67                 goto cleanup;
  68 
  69         if (cg_read_strcmp(cg_test_b, "cgroup.events", "populated 0\n"))
  70                 goto cleanup;
  71 
  72         if (cg_read_strcmp(cg_test_c, "cgroup.events", "populated 0\n"))
  73                 goto cleanup;
  74 
  75         if (cg_read_strcmp(cg_test_d, "cgroup.events", "populated 0\n"))
  76                 goto cleanup;
  77 
  78         ret = KSFT_PASS;
  79 
  80 cleanup:
  81         if (cg_test_d)
  82                 cg_destroy(cg_test_d);
  83         if (cg_test_c)
  84                 cg_destroy(cg_test_c);
  85         if (cg_test_b)
  86                 cg_destroy(cg_test_b);
  87         if (cg_test_a)
  88                 cg_destroy(cg_test_a);
  89         free(cg_test_d);
  90         free(cg_test_c);
  91         free(cg_test_b);
  92         free(cg_test_a);
  93         return ret;
  94 }
  95 
  96 /*
  97  * A (domain threaded) - B (threaded) - C (domain)
  98  *
  99  * test that C can't be used until it is turned into a
 100  * threaded cgroup.  "cgroup.type" file will report "domain (invalid)" in
 101  * these cases. Operations which fail due to invalid topology use
 102  * EOPNOTSUPP as the errno.
 103  */
 104 static int test_cgcore_invalid_domain(const char *root)
 105 {
 106         int ret = KSFT_FAIL;
 107         char *grandparent = NULL, *parent = NULL, *child = NULL;
 108 
 109         grandparent = cg_name(root, "cg_test_grandparent");
 110         parent = cg_name(root, "cg_test_grandparent/cg_test_parent");
 111         child = cg_name(root, "cg_test_grandparent/cg_test_parent/cg_test_child");
 112         if (!parent || !child || !grandparent)
 113                 goto cleanup;
 114 
 115         if (cg_create(grandparent))
 116                 goto cleanup;
 117 
 118         if (cg_create(parent))
 119                 goto cleanup;
 120 
 121         if (cg_create(child))
 122                 goto cleanup;
 123 
 124         if (cg_write(parent, "cgroup.type", "threaded"))
 125                 goto cleanup;
 126 
 127         if (cg_read_strcmp(child, "cgroup.type", "domain invalid\n"))
 128                 goto cleanup;
 129 
 130         if (!cg_enter_current(child))
 131                 goto cleanup;
 132 
 133         if (errno != EOPNOTSUPP)
 134                 goto cleanup;
 135 
 136         ret = KSFT_PASS;
 137 
 138 cleanup:
 139         cg_enter_current(root);
 140         if (child)
 141                 cg_destroy(child);
 142         if (parent)
 143                 cg_destroy(parent);
 144         if (grandparent)
 145                 cg_destroy(grandparent);
 146         free(child);
 147         free(parent);
 148         free(grandparent);
 149         return ret;
 150 }
 151 
 152 /*
 153  * Test that when a child becomes threaded
 154  * the parent type becomes domain threaded.
 155  */
 156 static int test_cgcore_parent_becomes_threaded(const char *root)
 157 {
 158         int ret = KSFT_FAIL;
 159         char *parent = NULL, *child = NULL;
 160 
 161         parent = cg_name(root, "cg_test_parent");
 162         child = cg_name(root, "cg_test_parent/cg_test_child");
 163         if (!parent || !child)
 164                 goto cleanup;
 165 
 166         if (cg_create(parent))
 167                 goto cleanup;
 168 
 169         if (cg_create(child))
 170                 goto cleanup;
 171 
 172         if (cg_write(child, "cgroup.type", "threaded"))
 173                 goto cleanup;
 174 
 175         if (cg_read_strcmp(parent, "cgroup.type", "domain threaded\n"))
 176                 goto cleanup;
 177 
 178         ret = KSFT_PASS;
 179 
 180 cleanup:
 181         if (child)
 182                 cg_destroy(child);
 183         if (parent)
 184                 cg_destroy(parent);
 185         free(child);
 186         free(parent);
 187         return ret;
 188 
 189 }
 190 
 191 /*
 192  * Test that there's no internal process constrain on threaded cgroups.
 193  * You can add threads/processes on a parent with a controller enabled.
 194  */
 195 static int test_cgcore_no_internal_process_constraint_on_threads(const char *root)
 196 {
 197         int ret = KSFT_FAIL;
 198         char *parent = NULL, *child = NULL;
 199 
 200         if (cg_read_strstr(root, "cgroup.controllers", "cpu") ||
 201             cg_write(root, "cgroup.subtree_control", "+cpu")) {
 202                 ret = KSFT_SKIP;
 203                 goto cleanup;
 204         }
 205 
 206         parent = cg_name(root, "cg_test_parent");
 207         child = cg_name(root, "cg_test_parent/cg_test_child");
 208         if (!parent || !child)
 209                 goto cleanup;
 210 
 211         if (cg_create(parent))
 212                 goto cleanup;
 213 
 214         if (cg_create(child))
 215                 goto cleanup;
 216 
 217         if (cg_write(parent, "cgroup.type", "threaded"))
 218                 goto cleanup;
 219 
 220         if (cg_write(child, "cgroup.type", "threaded"))
 221                 goto cleanup;
 222 
 223         if (cg_write(parent, "cgroup.subtree_control", "+cpu"))
 224                 goto cleanup;
 225 
 226         if (cg_enter_current(parent))
 227                 goto cleanup;
 228 
 229         ret = KSFT_PASS;
 230 
 231 cleanup:
 232         cg_enter_current(root);
 233         cg_enter_current(root);
 234         if (child)
 235                 cg_destroy(child);
 236         if (parent)
 237                 cg_destroy(parent);
 238         free(child);
 239         free(parent);
 240         return ret;
 241 }
 242 
 243 /*
 244  * Test that you can't enable a controller on a child if it's not enabled
 245  * on the parent.
 246  */
 247 static int test_cgcore_top_down_constraint_enable(const char *root)
 248 {
 249         int ret = KSFT_FAIL;
 250         char *parent = NULL, *child = NULL;
 251 
 252         parent = cg_name(root, "cg_test_parent");
 253         child = cg_name(root, "cg_test_parent/cg_test_child");
 254         if (!parent || !child)
 255                 goto cleanup;
 256 
 257         if (cg_create(parent))
 258                 goto cleanup;
 259 
 260         if (cg_create(child))
 261                 goto cleanup;
 262 
 263         if (!cg_write(child, "cgroup.subtree_control", "+memory"))
 264                 goto cleanup;
 265 
 266         ret = KSFT_PASS;
 267 
 268 cleanup:
 269         if (child)
 270                 cg_destroy(child);
 271         if (parent)
 272                 cg_destroy(parent);
 273         free(child);
 274         free(parent);
 275         return ret;
 276 }
 277 
 278 /*
 279  * Test that you can't disable a controller on a parent
 280  * if it's enabled in a child.
 281  */
 282 static int test_cgcore_top_down_constraint_disable(const char *root)
 283 {
 284         int ret = KSFT_FAIL;
 285         char *parent = NULL, *child = NULL;
 286 
 287         parent = cg_name(root, "cg_test_parent");
 288         child = cg_name(root, "cg_test_parent/cg_test_child");
 289         if (!parent || !child)
 290                 goto cleanup;
 291 
 292         if (cg_create(parent))
 293                 goto cleanup;
 294 
 295         if (cg_create(child))
 296                 goto cleanup;
 297 
 298         if (cg_write(parent, "cgroup.subtree_control", "+memory"))
 299                 goto cleanup;
 300 
 301         if (cg_write(child, "cgroup.subtree_control", "+memory"))
 302                 goto cleanup;
 303 
 304         if (!cg_write(parent, "cgroup.subtree_control", "-memory"))
 305                 goto cleanup;
 306 
 307         ret = KSFT_PASS;
 308 
 309 cleanup:
 310         if (child)
 311                 cg_destroy(child);
 312         if (parent)
 313                 cg_destroy(parent);
 314         free(child);
 315         free(parent);
 316         return ret;
 317 }
 318 
 319 /*
 320  * Test internal process constraint.
 321  * You can't add a pid to a domain parent if a controller is enabled.
 322  */
 323 static int test_cgcore_internal_process_constraint(const char *root)
 324 {
 325         int ret = KSFT_FAIL;
 326         char *parent = NULL, *child = NULL;
 327 
 328         parent = cg_name(root, "cg_test_parent");
 329         child = cg_name(root, "cg_test_parent/cg_test_child");
 330         if (!parent || !child)
 331                 goto cleanup;
 332 
 333         if (cg_create(parent))
 334                 goto cleanup;
 335 
 336         if (cg_create(child))
 337                 goto cleanup;
 338 
 339         if (cg_write(parent, "cgroup.subtree_control", "+memory"))
 340                 goto cleanup;
 341 
 342         if (!cg_enter_current(parent))
 343                 goto cleanup;
 344 
 345         ret = KSFT_PASS;
 346 
 347 cleanup:
 348         if (child)
 349                 cg_destroy(child);
 350         if (parent)
 351                 cg_destroy(parent);
 352         free(child);
 353         free(parent);
 354         return ret;
 355 }
 356 
 357 #define T(x) { x, #x }
 358 struct corecg_test {
 359         int (*fn)(const char *root);
 360         const char *name;
 361 } tests[] = {
 362         T(test_cgcore_internal_process_constraint),
 363         T(test_cgcore_top_down_constraint_enable),
 364         T(test_cgcore_top_down_constraint_disable),
 365         T(test_cgcore_no_internal_process_constraint_on_threads),
 366         T(test_cgcore_parent_becomes_threaded),
 367         T(test_cgcore_invalid_domain),
 368         T(test_cgcore_populated),
 369 };
 370 #undef T
 371 
 372 int main(int argc, char *argv[])
 373 {
 374         char root[PATH_MAX];
 375         int i, ret = EXIT_SUCCESS;
 376 
 377         if (cg_find_unified_root(root, sizeof(root)))
 378                 ksft_exit_skip("cgroup v2 isn't mounted\n");
 379 
 380         if (cg_read_strstr(root, "cgroup.subtree_control", "memory"))
 381                 if (cg_write(root, "cgroup.subtree_control", "+memory"))
 382                         ksft_exit_skip("Failed to set memory controller\n");
 383 
 384         for (i = 0; i < ARRAY_SIZE(tests); i++) {
 385                 switch (tests[i].fn(root)) {
 386                 case KSFT_PASS:
 387                         ksft_test_result_pass("%s\n", tests[i].name);
 388                         break;
 389                 case KSFT_SKIP:
 390                         ksft_test_result_skip("%s\n", tests[i].name);
 391                         break;
 392                 default:
 393                         ret = EXIT_FAILURE;
 394                         ksft_test_result_fail("%s\n", tests[i].name);
 395                         break;
 396                 }
 397         }
 398 
 399         return ret;
 400 }

/* [<][>][^][v][top][bottom][index][help] */