linux/tools/testing/selftests/cgroup/test_core.c
<<
>>
Prefs
   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 */
  22static 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
  80cleanup:
  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 */
 104static 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
 138cleanup:
 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 */
 156static 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
 180cleanup:
 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 */
 195static 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
 231cleanup:
 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 */
 247static 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
 268cleanup:
 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 */
 282static 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
 309cleanup:
 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 */
 323static 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
 347cleanup:
 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 }
 358struct 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
 372int 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}
 401