linux/tools/testing/selftests/cgroup/test_kill.c
<<
>>
Prefs
   1/* SPDX-License-Identifier: GPL-2.0 */
   2
   3#include <errno.h>
   4#include <linux/limits.h>
   5#include <stdbool.h>
   6#include <stdio.h>
   7#include <stdlib.h>
   8#include <string.h>
   9#include <sys/types.h>
  10#include <unistd.h>
  11
  12#include "../kselftest.h"
  13#include "../pidfd/pidfd.h"
  14#include "cgroup_util.h"
  15
  16/*
  17 * Kill the given cgroup and wait for the inotify signal.
  18 * If there are no events in 10 seconds, treat this as an error.
  19 * Then check that the cgroup is in the desired state.
  20 */
  21static int cg_kill_wait(const char *cgroup)
  22{
  23        int fd, ret = -1;
  24
  25        fd = cg_prepare_for_wait(cgroup);
  26        if (fd < 0)
  27                return fd;
  28
  29        ret = cg_write(cgroup, "cgroup.kill", "1");
  30        if (ret)
  31                goto out;
  32
  33        ret = cg_wait_for(fd);
  34        if (ret)
  35                goto out;
  36
  37out:
  38        close(fd);
  39        return ret;
  40}
  41
  42/*
  43 * A simple process running in a sleep loop until being
  44 * re-parented.
  45 */
  46static int child_fn(const char *cgroup, void *arg)
  47{
  48        int ppid = getppid();
  49
  50        while (getppid() == ppid)
  51                usleep(1000);
  52
  53        return getppid() == ppid;
  54}
  55
  56static int test_cgkill_simple(const char *root)
  57{
  58        pid_t pids[100];
  59        int ret = KSFT_FAIL;
  60        char *cgroup = NULL;
  61        int i;
  62
  63        cgroup = cg_name(root, "cg_test_simple");
  64        if (!cgroup)
  65                goto cleanup;
  66
  67        if (cg_create(cgroup))
  68                goto cleanup;
  69
  70        for (i = 0; i < 100; i++)
  71                pids[i] = cg_run_nowait(cgroup, child_fn, NULL);
  72
  73        if (cg_wait_for_proc_count(cgroup, 100))
  74                goto cleanup;
  75
  76        if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n"))
  77                goto cleanup;
  78
  79        if (cg_kill_wait(cgroup))
  80                goto cleanup;
  81
  82        ret = KSFT_PASS;
  83
  84cleanup:
  85        for (i = 0; i < 100; i++)
  86                wait_for_pid(pids[i]);
  87
  88        if (ret == KSFT_PASS &&
  89            cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
  90                ret = KSFT_FAIL;
  91
  92        if (cgroup)
  93                cg_destroy(cgroup);
  94        free(cgroup);
  95        return ret;
  96}
  97
  98/*
  99 * The test creates the following hierarchy:
 100 *       A
 101 *    / / \ \
 102 *   B  E  I K
 103 *  /\  |
 104 * C  D F
 105 *      |
 106 *      G
 107 *      |
 108 *      H
 109 *
 110 * with a process in C, H and 3 processes in K.
 111 * Then it tries to kill the whole tree.
 112 */
 113static int test_cgkill_tree(const char *root)
 114{
 115        pid_t pids[5];
 116        char *cgroup[10] = {0};
 117        int ret = KSFT_FAIL;
 118        int i;
 119
 120        cgroup[0] = cg_name(root, "cg_test_tree_A");
 121        if (!cgroup[0])
 122                goto cleanup;
 123
 124        cgroup[1] = cg_name(cgroup[0], "B");
 125        if (!cgroup[1])
 126                goto cleanup;
 127
 128        cgroup[2] = cg_name(cgroup[1], "C");
 129        if (!cgroup[2])
 130                goto cleanup;
 131
 132        cgroup[3] = cg_name(cgroup[1], "D");
 133        if (!cgroup[3])
 134                goto cleanup;
 135
 136        cgroup[4] = cg_name(cgroup[0], "E");
 137        if (!cgroup[4])
 138                goto cleanup;
 139
 140        cgroup[5] = cg_name(cgroup[4], "F");
 141        if (!cgroup[5])
 142                goto cleanup;
 143
 144        cgroup[6] = cg_name(cgroup[5], "G");
 145        if (!cgroup[6])
 146                goto cleanup;
 147
 148        cgroup[7] = cg_name(cgroup[6], "H");
 149        if (!cgroup[7])
 150                goto cleanup;
 151
 152        cgroup[8] = cg_name(cgroup[0], "I");
 153        if (!cgroup[8])
 154                goto cleanup;
 155
 156        cgroup[9] = cg_name(cgroup[0], "K");
 157        if (!cgroup[9])
 158                goto cleanup;
 159
 160        for (i = 0; i < 10; i++)
 161                if (cg_create(cgroup[i]))
 162                        goto cleanup;
 163
 164        pids[0] = cg_run_nowait(cgroup[2], child_fn, NULL);
 165        pids[1] = cg_run_nowait(cgroup[7], child_fn, NULL);
 166        pids[2] = cg_run_nowait(cgroup[9], child_fn, NULL);
 167        pids[3] = cg_run_nowait(cgroup[9], child_fn, NULL);
 168        pids[4] = cg_run_nowait(cgroup[9], child_fn, NULL);
 169
 170        /*
 171         * Wait until all child processes will enter
 172         * corresponding cgroups.
 173         */
 174
 175        if (cg_wait_for_proc_count(cgroup[2], 1) ||
 176            cg_wait_for_proc_count(cgroup[7], 1) ||
 177            cg_wait_for_proc_count(cgroup[9], 3))
 178                goto cleanup;
 179
 180        /*
 181         * Kill A and check that we get an empty notification.
 182         */
 183        if (cg_kill_wait(cgroup[0]))
 184                goto cleanup;
 185
 186        ret = KSFT_PASS;
 187
 188cleanup:
 189        for (i = 0; i < 5; i++)
 190                wait_for_pid(pids[i]);
 191
 192        if (ret == KSFT_PASS &&
 193            cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n"))
 194                ret = KSFT_FAIL;
 195
 196        for (i = 9; i >= 0 && cgroup[i]; i--) {
 197                cg_destroy(cgroup[i]);
 198                free(cgroup[i]);
 199        }
 200
 201        return ret;
 202}
 203
 204static int forkbomb_fn(const char *cgroup, void *arg)
 205{
 206        int ppid;
 207
 208        fork();
 209        fork();
 210
 211        ppid = getppid();
 212
 213        while (getppid() == ppid)
 214                usleep(1000);
 215
 216        return getppid() == ppid;
 217}
 218
 219/*
 220 * The test runs a fork bomb in a cgroup and tries to kill it.
 221 */
 222static int test_cgkill_forkbomb(const char *root)
 223{
 224        int ret = KSFT_FAIL;
 225        char *cgroup = NULL;
 226        pid_t pid = -ESRCH;
 227
 228        cgroup = cg_name(root, "cg_forkbomb_test");
 229        if (!cgroup)
 230                goto cleanup;
 231
 232        if (cg_create(cgroup))
 233                goto cleanup;
 234
 235        pid = cg_run_nowait(cgroup, forkbomb_fn, NULL);
 236        if (pid < 0)
 237                goto cleanup;
 238
 239        usleep(100000);
 240
 241        if (cg_kill_wait(cgroup))
 242                goto cleanup;
 243
 244        if (cg_wait_for_proc_count(cgroup, 0))
 245                goto cleanup;
 246
 247        ret = KSFT_PASS;
 248
 249cleanup:
 250        if (pid > 0)
 251                wait_for_pid(pid);
 252
 253        if (ret == KSFT_PASS &&
 254            cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
 255                ret = KSFT_FAIL;
 256
 257        if (cgroup)
 258                cg_destroy(cgroup);
 259        free(cgroup);
 260        return ret;
 261}
 262
 263#define T(x) { x, #x }
 264struct cgkill_test {
 265        int (*fn)(const char *root);
 266        const char *name;
 267} tests[] = {
 268        T(test_cgkill_simple),
 269        T(test_cgkill_tree),
 270        T(test_cgkill_forkbomb),
 271};
 272#undef T
 273
 274int main(int argc, char *argv[])
 275{
 276        char root[PATH_MAX];
 277        int i, ret = EXIT_SUCCESS;
 278
 279        if (cg_find_unified_root(root, sizeof(root)))
 280                ksft_exit_skip("cgroup v2 isn't mounted\n");
 281        for (i = 0; i < ARRAY_SIZE(tests); i++) {
 282                switch (tests[i].fn(root)) {
 283                case KSFT_PASS:
 284                        ksft_test_result_pass("%s\n", tests[i].name);
 285                        break;
 286                case KSFT_SKIP:
 287                        ksft_test_result_skip("%s\n", tests[i].name);
 288                        break;
 289                default:
 290                        ret = EXIT_FAILURE;
 291                        ksft_test_result_fail("%s\n", tests[i].name);
 292                        break;
 293                }
 294        }
 295
 296        return ret;
 297}
 298