test_kill.c (5442B)
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}