162306a36Sopenharmony_ci/* SPDX-License-Identifier: GPL-2.0 */ 262306a36Sopenharmony_ci 362306a36Sopenharmony_ci#include <errno.h> 462306a36Sopenharmony_ci#include <linux/limits.h> 562306a36Sopenharmony_ci#include <stdbool.h> 662306a36Sopenharmony_ci#include <stdio.h> 762306a36Sopenharmony_ci#include <stdlib.h> 862306a36Sopenharmony_ci#include <string.h> 962306a36Sopenharmony_ci#include <sys/types.h> 1062306a36Sopenharmony_ci#include <unistd.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include "../kselftest.h" 1362306a36Sopenharmony_ci#include "../pidfd/pidfd.h" 1462306a36Sopenharmony_ci#include "cgroup_util.h" 1562306a36Sopenharmony_ci 1662306a36Sopenharmony_ci/* 1762306a36Sopenharmony_ci * Kill the given cgroup and wait for the inotify signal. 1862306a36Sopenharmony_ci * If there are no events in 10 seconds, treat this as an error. 1962306a36Sopenharmony_ci * Then check that the cgroup is in the desired state. 2062306a36Sopenharmony_ci */ 2162306a36Sopenharmony_cistatic int cg_kill_wait(const char *cgroup) 2262306a36Sopenharmony_ci{ 2362306a36Sopenharmony_ci int fd, ret = -1; 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci fd = cg_prepare_for_wait(cgroup); 2662306a36Sopenharmony_ci if (fd < 0) 2762306a36Sopenharmony_ci return fd; 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_ci ret = cg_write(cgroup, "cgroup.kill", "1"); 3062306a36Sopenharmony_ci if (ret) 3162306a36Sopenharmony_ci goto out; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci ret = cg_wait_for(fd); 3462306a36Sopenharmony_ci if (ret) 3562306a36Sopenharmony_ci goto out; 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ciout: 3862306a36Sopenharmony_ci close(fd); 3962306a36Sopenharmony_ci return ret; 4062306a36Sopenharmony_ci} 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* 4362306a36Sopenharmony_ci * A simple process running in a sleep loop until being 4462306a36Sopenharmony_ci * re-parented. 4562306a36Sopenharmony_ci */ 4662306a36Sopenharmony_cistatic int child_fn(const char *cgroup, void *arg) 4762306a36Sopenharmony_ci{ 4862306a36Sopenharmony_ci int ppid = getppid(); 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci while (getppid() == ppid) 5162306a36Sopenharmony_ci usleep(1000); 5262306a36Sopenharmony_ci 5362306a36Sopenharmony_ci return getppid() == ppid; 5462306a36Sopenharmony_ci} 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_cistatic int test_cgkill_simple(const char *root) 5762306a36Sopenharmony_ci{ 5862306a36Sopenharmony_ci pid_t pids[100]; 5962306a36Sopenharmony_ci int ret = KSFT_FAIL; 6062306a36Sopenharmony_ci char *cgroup = NULL; 6162306a36Sopenharmony_ci int i; 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_ci cgroup = cg_name(root, "cg_test_simple"); 6462306a36Sopenharmony_ci if (!cgroup) 6562306a36Sopenharmony_ci goto cleanup; 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci if (cg_create(cgroup)) 6862306a36Sopenharmony_ci goto cleanup; 6962306a36Sopenharmony_ci 7062306a36Sopenharmony_ci for (i = 0; i < 100; i++) 7162306a36Sopenharmony_ci pids[i] = cg_run_nowait(cgroup, child_fn, NULL); 7262306a36Sopenharmony_ci 7362306a36Sopenharmony_ci if (cg_wait_for_proc_count(cgroup, 100)) 7462306a36Sopenharmony_ci goto cleanup; 7562306a36Sopenharmony_ci 7662306a36Sopenharmony_ci if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n")) 7762306a36Sopenharmony_ci goto cleanup; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci if (cg_kill_wait(cgroup)) 8062306a36Sopenharmony_ci goto cleanup; 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci ret = KSFT_PASS; 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_cicleanup: 8562306a36Sopenharmony_ci for (i = 0; i < 100; i++) 8662306a36Sopenharmony_ci wait_for_pid(pids[i]); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (ret == KSFT_PASS && 8962306a36Sopenharmony_ci cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) 9062306a36Sopenharmony_ci ret = KSFT_FAIL; 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci if (cgroup) 9362306a36Sopenharmony_ci cg_destroy(cgroup); 9462306a36Sopenharmony_ci free(cgroup); 9562306a36Sopenharmony_ci return ret; 9662306a36Sopenharmony_ci} 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci/* 9962306a36Sopenharmony_ci * The test creates the following hierarchy: 10062306a36Sopenharmony_ci * A 10162306a36Sopenharmony_ci * / / \ \ 10262306a36Sopenharmony_ci * B E I K 10362306a36Sopenharmony_ci * /\ | 10462306a36Sopenharmony_ci * C D F 10562306a36Sopenharmony_ci * | 10662306a36Sopenharmony_ci * G 10762306a36Sopenharmony_ci * | 10862306a36Sopenharmony_ci * H 10962306a36Sopenharmony_ci * 11062306a36Sopenharmony_ci * with a process in C, H and 3 processes in K. 11162306a36Sopenharmony_ci * Then it tries to kill the whole tree. 11262306a36Sopenharmony_ci */ 11362306a36Sopenharmony_cistatic int test_cgkill_tree(const char *root) 11462306a36Sopenharmony_ci{ 11562306a36Sopenharmony_ci pid_t pids[5]; 11662306a36Sopenharmony_ci char *cgroup[10] = {0}; 11762306a36Sopenharmony_ci int ret = KSFT_FAIL; 11862306a36Sopenharmony_ci int i; 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci cgroup[0] = cg_name(root, "cg_test_tree_A"); 12162306a36Sopenharmony_ci if (!cgroup[0]) 12262306a36Sopenharmony_ci goto cleanup; 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_ci cgroup[1] = cg_name(cgroup[0], "B"); 12562306a36Sopenharmony_ci if (!cgroup[1]) 12662306a36Sopenharmony_ci goto cleanup; 12762306a36Sopenharmony_ci 12862306a36Sopenharmony_ci cgroup[2] = cg_name(cgroup[1], "C"); 12962306a36Sopenharmony_ci if (!cgroup[2]) 13062306a36Sopenharmony_ci goto cleanup; 13162306a36Sopenharmony_ci 13262306a36Sopenharmony_ci cgroup[3] = cg_name(cgroup[1], "D"); 13362306a36Sopenharmony_ci if (!cgroup[3]) 13462306a36Sopenharmony_ci goto cleanup; 13562306a36Sopenharmony_ci 13662306a36Sopenharmony_ci cgroup[4] = cg_name(cgroup[0], "E"); 13762306a36Sopenharmony_ci if (!cgroup[4]) 13862306a36Sopenharmony_ci goto cleanup; 13962306a36Sopenharmony_ci 14062306a36Sopenharmony_ci cgroup[5] = cg_name(cgroup[4], "F"); 14162306a36Sopenharmony_ci if (!cgroup[5]) 14262306a36Sopenharmony_ci goto cleanup; 14362306a36Sopenharmony_ci 14462306a36Sopenharmony_ci cgroup[6] = cg_name(cgroup[5], "G"); 14562306a36Sopenharmony_ci if (!cgroup[6]) 14662306a36Sopenharmony_ci goto cleanup; 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_ci cgroup[7] = cg_name(cgroup[6], "H"); 14962306a36Sopenharmony_ci if (!cgroup[7]) 15062306a36Sopenharmony_ci goto cleanup; 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci cgroup[8] = cg_name(cgroup[0], "I"); 15362306a36Sopenharmony_ci if (!cgroup[8]) 15462306a36Sopenharmony_ci goto cleanup; 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci cgroup[9] = cg_name(cgroup[0], "K"); 15762306a36Sopenharmony_ci if (!cgroup[9]) 15862306a36Sopenharmony_ci goto cleanup; 15962306a36Sopenharmony_ci 16062306a36Sopenharmony_ci for (i = 0; i < 10; i++) 16162306a36Sopenharmony_ci if (cg_create(cgroup[i])) 16262306a36Sopenharmony_ci goto cleanup; 16362306a36Sopenharmony_ci 16462306a36Sopenharmony_ci pids[0] = cg_run_nowait(cgroup[2], child_fn, NULL); 16562306a36Sopenharmony_ci pids[1] = cg_run_nowait(cgroup[7], child_fn, NULL); 16662306a36Sopenharmony_ci pids[2] = cg_run_nowait(cgroup[9], child_fn, NULL); 16762306a36Sopenharmony_ci pids[3] = cg_run_nowait(cgroup[9], child_fn, NULL); 16862306a36Sopenharmony_ci pids[4] = cg_run_nowait(cgroup[9], child_fn, NULL); 16962306a36Sopenharmony_ci 17062306a36Sopenharmony_ci /* 17162306a36Sopenharmony_ci * Wait until all child processes will enter 17262306a36Sopenharmony_ci * corresponding cgroups. 17362306a36Sopenharmony_ci */ 17462306a36Sopenharmony_ci 17562306a36Sopenharmony_ci if (cg_wait_for_proc_count(cgroup[2], 1) || 17662306a36Sopenharmony_ci cg_wait_for_proc_count(cgroup[7], 1) || 17762306a36Sopenharmony_ci cg_wait_for_proc_count(cgroup[9], 3)) 17862306a36Sopenharmony_ci goto cleanup; 17962306a36Sopenharmony_ci 18062306a36Sopenharmony_ci /* 18162306a36Sopenharmony_ci * Kill A and check that we get an empty notification. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci if (cg_kill_wait(cgroup[0])) 18462306a36Sopenharmony_ci goto cleanup; 18562306a36Sopenharmony_ci 18662306a36Sopenharmony_ci ret = KSFT_PASS; 18762306a36Sopenharmony_ci 18862306a36Sopenharmony_cicleanup: 18962306a36Sopenharmony_ci for (i = 0; i < 5; i++) 19062306a36Sopenharmony_ci wait_for_pid(pids[i]); 19162306a36Sopenharmony_ci 19262306a36Sopenharmony_ci if (ret == KSFT_PASS && 19362306a36Sopenharmony_ci cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n")) 19462306a36Sopenharmony_ci ret = KSFT_FAIL; 19562306a36Sopenharmony_ci 19662306a36Sopenharmony_ci for (i = 9; i >= 0 && cgroup[i]; i--) { 19762306a36Sopenharmony_ci cg_destroy(cgroup[i]); 19862306a36Sopenharmony_ci free(cgroup[i]); 19962306a36Sopenharmony_ci } 20062306a36Sopenharmony_ci 20162306a36Sopenharmony_ci return ret; 20262306a36Sopenharmony_ci} 20362306a36Sopenharmony_ci 20462306a36Sopenharmony_cistatic int forkbomb_fn(const char *cgroup, void *arg) 20562306a36Sopenharmony_ci{ 20662306a36Sopenharmony_ci int ppid; 20762306a36Sopenharmony_ci 20862306a36Sopenharmony_ci fork(); 20962306a36Sopenharmony_ci fork(); 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci ppid = getppid(); 21262306a36Sopenharmony_ci 21362306a36Sopenharmony_ci while (getppid() == ppid) 21462306a36Sopenharmony_ci usleep(1000); 21562306a36Sopenharmony_ci 21662306a36Sopenharmony_ci return getppid() == ppid; 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci/* 22062306a36Sopenharmony_ci * The test runs a fork bomb in a cgroup and tries to kill it. 22162306a36Sopenharmony_ci */ 22262306a36Sopenharmony_cistatic int test_cgkill_forkbomb(const char *root) 22362306a36Sopenharmony_ci{ 22462306a36Sopenharmony_ci int ret = KSFT_FAIL; 22562306a36Sopenharmony_ci char *cgroup = NULL; 22662306a36Sopenharmony_ci pid_t pid = -ESRCH; 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_ci cgroup = cg_name(root, "cg_forkbomb_test"); 22962306a36Sopenharmony_ci if (!cgroup) 23062306a36Sopenharmony_ci goto cleanup; 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci if (cg_create(cgroup)) 23362306a36Sopenharmony_ci goto cleanup; 23462306a36Sopenharmony_ci 23562306a36Sopenharmony_ci pid = cg_run_nowait(cgroup, forkbomb_fn, NULL); 23662306a36Sopenharmony_ci if (pid < 0) 23762306a36Sopenharmony_ci goto cleanup; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci usleep(100000); 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_ci if (cg_kill_wait(cgroup)) 24262306a36Sopenharmony_ci goto cleanup; 24362306a36Sopenharmony_ci 24462306a36Sopenharmony_ci if (cg_wait_for_proc_count(cgroup, 0)) 24562306a36Sopenharmony_ci goto cleanup; 24662306a36Sopenharmony_ci 24762306a36Sopenharmony_ci ret = KSFT_PASS; 24862306a36Sopenharmony_ci 24962306a36Sopenharmony_cicleanup: 25062306a36Sopenharmony_ci if (pid > 0) 25162306a36Sopenharmony_ci wait_for_pid(pid); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci if (ret == KSFT_PASS && 25462306a36Sopenharmony_ci cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n")) 25562306a36Sopenharmony_ci ret = KSFT_FAIL; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci if (cgroup) 25862306a36Sopenharmony_ci cg_destroy(cgroup); 25962306a36Sopenharmony_ci free(cgroup); 26062306a36Sopenharmony_ci return ret; 26162306a36Sopenharmony_ci} 26262306a36Sopenharmony_ci 26362306a36Sopenharmony_ci#define T(x) { x, #x } 26462306a36Sopenharmony_cistruct cgkill_test { 26562306a36Sopenharmony_ci int (*fn)(const char *root); 26662306a36Sopenharmony_ci const char *name; 26762306a36Sopenharmony_ci} tests[] = { 26862306a36Sopenharmony_ci T(test_cgkill_simple), 26962306a36Sopenharmony_ci T(test_cgkill_tree), 27062306a36Sopenharmony_ci T(test_cgkill_forkbomb), 27162306a36Sopenharmony_ci}; 27262306a36Sopenharmony_ci#undef T 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ciint main(int argc, char *argv[]) 27562306a36Sopenharmony_ci{ 27662306a36Sopenharmony_ci char root[PATH_MAX]; 27762306a36Sopenharmony_ci int i, ret = EXIT_SUCCESS; 27862306a36Sopenharmony_ci 27962306a36Sopenharmony_ci if (cg_find_unified_root(root, sizeof(root))) 28062306a36Sopenharmony_ci ksft_exit_skip("cgroup v2 isn't mounted\n"); 28162306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(tests); i++) { 28262306a36Sopenharmony_ci switch (tests[i].fn(root)) { 28362306a36Sopenharmony_ci case KSFT_PASS: 28462306a36Sopenharmony_ci ksft_test_result_pass("%s\n", tests[i].name); 28562306a36Sopenharmony_ci break; 28662306a36Sopenharmony_ci case KSFT_SKIP: 28762306a36Sopenharmony_ci ksft_test_result_skip("%s\n", tests[i].name); 28862306a36Sopenharmony_ci break; 28962306a36Sopenharmony_ci default: 29062306a36Sopenharmony_ci ret = EXIT_FAILURE; 29162306a36Sopenharmony_ci ksft_test_result_fail("%s\n", tests[i].name); 29262306a36Sopenharmony_ci break; 29362306a36Sopenharmony_ci } 29462306a36Sopenharmony_ci } 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci return ret; 29762306a36Sopenharmony_ci} 298