Path: blob/master/tools/testing/selftests/cgroup/test_kill.c
26285 views
/* SPDX-License-Identifier: GPL-2.0 */12#include <errno.h>3#include <linux/limits.h>4#include <stdbool.h>5#include <stdio.h>6#include <stdlib.h>7#include <string.h>8#include <sys/types.h>9#include <unistd.h>1011#include "../kselftest.h"12#include "../pidfd/pidfd.h"13#include "cgroup_util.h"1415/*16* Kill the given cgroup and wait for the inotify signal.17* If there are no events in 10 seconds, treat this as an error.18* Then check that the cgroup is in the desired state.19*/20static int cg_kill_wait(const char *cgroup)21{22int fd, ret = -1;2324fd = cg_prepare_for_wait(cgroup);25if (fd < 0)26return fd;2728ret = cg_write(cgroup, "cgroup.kill", "1");29if (ret)30goto out;3132ret = cg_wait_for(fd);33if (ret)34goto out;3536out:37close(fd);38return ret;39}4041/*42* A simple process running in a sleep loop until being43* re-parented.44*/45static int child_fn(const char *cgroup, void *arg)46{47int ppid = getppid();4849while (getppid() == ppid)50usleep(1000);5152return getppid() == ppid;53}5455static int test_cgkill_simple(const char *root)56{57pid_t pids[100];58int ret = KSFT_FAIL;59char *cgroup = NULL;60int i;6162cgroup = cg_name(root, "cg_test_simple");63if (!cgroup)64goto cleanup;6566if (cg_create(cgroup))67goto cleanup;6869for (i = 0; i < 100; i++)70pids[i] = cg_run_nowait(cgroup, child_fn, NULL);7172if (cg_wait_for_proc_count(cgroup, 100))73goto cleanup;7475if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n"))76goto cleanup;7778if (cg_kill_wait(cgroup))79goto cleanup;8081ret = KSFT_PASS;8283cleanup:84for (i = 0; i < 100; i++)85wait_for_pid(pids[i]);8687if (ret == KSFT_PASS &&88cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))89ret = KSFT_FAIL;9091if (cgroup)92cg_destroy(cgroup);93free(cgroup);94return ret;95}9697/*98* The test creates the following hierarchy:99* A100* / / \ \101* B E I K102* /\ |103* C D F104* |105* G106* |107* H108*109* with a process in C, H and 3 processes in K.110* Then it tries to kill the whole tree.111*/112static int test_cgkill_tree(const char *root)113{114pid_t pids[5];115char *cgroup[10] = {0};116int ret = KSFT_FAIL;117int i;118119cgroup[0] = cg_name(root, "cg_test_tree_A");120if (!cgroup[0])121goto cleanup;122123cgroup[1] = cg_name(cgroup[0], "B");124if (!cgroup[1])125goto cleanup;126127cgroup[2] = cg_name(cgroup[1], "C");128if (!cgroup[2])129goto cleanup;130131cgroup[3] = cg_name(cgroup[1], "D");132if (!cgroup[3])133goto cleanup;134135cgroup[4] = cg_name(cgroup[0], "E");136if (!cgroup[4])137goto cleanup;138139cgroup[5] = cg_name(cgroup[4], "F");140if (!cgroup[5])141goto cleanup;142143cgroup[6] = cg_name(cgroup[5], "G");144if (!cgroup[6])145goto cleanup;146147cgroup[7] = cg_name(cgroup[6], "H");148if (!cgroup[7])149goto cleanup;150151cgroup[8] = cg_name(cgroup[0], "I");152if (!cgroup[8])153goto cleanup;154155cgroup[9] = cg_name(cgroup[0], "K");156if (!cgroup[9])157goto cleanup;158159for (i = 0; i < 10; i++)160if (cg_create(cgroup[i]))161goto cleanup;162163pids[0] = cg_run_nowait(cgroup[2], child_fn, NULL);164pids[1] = cg_run_nowait(cgroup[7], child_fn, NULL);165pids[2] = cg_run_nowait(cgroup[9], child_fn, NULL);166pids[3] = cg_run_nowait(cgroup[9], child_fn, NULL);167pids[4] = cg_run_nowait(cgroup[9], child_fn, NULL);168169/*170* Wait until all child processes will enter171* corresponding cgroups.172*/173174if (cg_wait_for_proc_count(cgroup[2], 1) ||175cg_wait_for_proc_count(cgroup[7], 1) ||176cg_wait_for_proc_count(cgroup[9], 3))177goto cleanup;178179/*180* Kill A and check that we get an empty notification.181*/182if (cg_kill_wait(cgroup[0]))183goto cleanup;184185ret = KSFT_PASS;186187cleanup:188for (i = 0; i < 5; i++)189wait_for_pid(pids[i]);190191if (ret == KSFT_PASS &&192cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n"))193ret = KSFT_FAIL;194195for (i = 9; i >= 0 && cgroup[i]; i--) {196cg_destroy(cgroup[i]);197free(cgroup[i]);198}199200return ret;201}202203static int forkbomb_fn(const char *cgroup, void *arg)204{205int ppid;206207fork();208fork();209210ppid = getppid();211212while (getppid() == ppid)213usleep(1000);214215return getppid() == ppid;216}217218/*219* The test runs a fork bomb in a cgroup and tries to kill it.220*/221static int test_cgkill_forkbomb(const char *root)222{223int ret = KSFT_FAIL;224char *cgroup = NULL;225pid_t pid = -ESRCH;226227cgroup = cg_name(root, "cg_forkbomb_test");228if (!cgroup)229goto cleanup;230231if (cg_create(cgroup))232goto cleanup;233234pid = cg_run_nowait(cgroup, forkbomb_fn, NULL);235if (pid < 0)236goto cleanup;237238usleep(100000);239240if (cg_kill_wait(cgroup))241goto cleanup;242243if (cg_wait_for_proc_count(cgroup, 0))244goto cleanup;245246ret = KSFT_PASS;247248cleanup:249if (pid > 0)250wait_for_pid(pid);251252if (ret == KSFT_PASS &&253cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))254ret = KSFT_FAIL;255256if (cgroup)257cg_destroy(cgroup);258free(cgroup);259return ret;260}261262#define T(x) { x, #x }263struct cgkill_test {264int (*fn)(const char *root);265const char *name;266} tests[] = {267T(test_cgkill_simple),268T(test_cgkill_tree),269T(test_cgkill_forkbomb),270};271#undef T272273int main(int argc, char *argv[])274{275char root[PATH_MAX];276int i, ret = EXIT_SUCCESS;277278if (cg_find_unified_root(root, sizeof(root), NULL))279ksft_exit_skip("cgroup v2 isn't mounted\n");280for (i = 0; i < ARRAY_SIZE(tests); i++) {281switch (tests[i].fn(root)) {282case KSFT_PASS:283ksft_test_result_pass("%s\n", tests[i].name);284break;285case KSFT_SKIP:286ksft_test_result_skip("%s\n", tests[i].name);287break;288default:289ret = EXIT_FAILURE;290ksft_test_result_fail("%s\n", tests[i].name);291break;292}293}294295return ret;296}297298299