0001
0002
0003 #include <errno.h>
0004 #include <linux/limits.h>
0005 #include <stdbool.h>
0006 #include <stdio.h>
0007 #include <stdlib.h>
0008 #include <string.h>
0009 #include <sys/types.h>
0010 #include <unistd.h>
0011
0012 #include "../kselftest.h"
0013 #include "../pidfd/pidfd.h"
0014 #include "cgroup_util.h"
0015
0016
0017
0018
0019
0020
0021 static int cg_kill_wait(const char *cgroup)
0022 {
0023 int fd, ret = -1;
0024
0025 fd = cg_prepare_for_wait(cgroup);
0026 if (fd < 0)
0027 return fd;
0028
0029 ret = cg_write(cgroup, "cgroup.kill", "1");
0030 if (ret)
0031 goto out;
0032
0033 ret = cg_wait_for(fd);
0034 if (ret)
0035 goto out;
0036
0037 out:
0038 close(fd);
0039 return ret;
0040 }
0041
0042
0043
0044
0045
0046 static int child_fn(const char *cgroup, void *arg)
0047 {
0048 int ppid = getppid();
0049
0050 while (getppid() == ppid)
0051 usleep(1000);
0052
0053 return getppid() == ppid;
0054 }
0055
0056 static int test_cgkill_simple(const char *root)
0057 {
0058 pid_t pids[100];
0059 int ret = KSFT_FAIL;
0060 char *cgroup = NULL;
0061 int i;
0062
0063 cgroup = cg_name(root, "cg_test_simple");
0064 if (!cgroup)
0065 goto cleanup;
0066
0067 if (cg_create(cgroup))
0068 goto cleanup;
0069
0070 for (i = 0; i < 100; i++)
0071 pids[i] = cg_run_nowait(cgroup, child_fn, NULL);
0072
0073 if (cg_wait_for_proc_count(cgroup, 100))
0074 goto cleanup;
0075
0076 if (cg_read_strcmp(cgroup, "cgroup.events", "populated 1\n"))
0077 goto cleanup;
0078
0079 if (cg_kill_wait(cgroup))
0080 goto cleanup;
0081
0082 ret = KSFT_PASS;
0083
0084 cleanup:
0085 for (i = 0; i < 100; i++)
0086 wait_for_pid(pids[i]);
0087
0088 if (ret == KSFT_PASS &&
0089 cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
0090 ret = KSFT_FAIL;
0091
0092 if (cgroup)
0093 cg_destroy(cgroup);
0094 free(cgroup);
0095 return ret;
0096 }
0097
0098
0099
0100
0101
0102
0103
0104
0105
0106
0107
0108
0109
0110
0111
0112
0113 static int test_cgkill_tree(const char *root)
0114 {
0115 pid_t pids[5];
0116 char *cgroup[10] = {0};
0117 int ret = KSFT_FAIL;
0118 int i;
0119
0120 cgroup[0] = cg_name(root, "cg_test_tree_A");
0121 if (!cgroup[0])
0122 goto cleanup;
0123
0124 cgroup[1] = cg_name(cgroup[0], "B");
0125 if (!cgroup[1])
0126 goto cleanup;
0127
0128 cgroup[2] = cg_name(cgroup[1], "C");
0129 if (!cgroup[2])
0130 goto cleanup;
0131
0132 cgroup[3] = cg_name(cgroup[1], "D");
0133 if (!cgroup[3])
0134 goto cleanup;
0135
0136 cgroup[4] = cg_name(cgroup[0], "E");
0137 if (!cgroup[4])
0138 goto cleanup;
0139
0140 cgroup[5] = cg_name(cgroup[4], "F");
0141 if (!cgroup[5])
0142 goto cleanup;
0143
0144 cgroup[6] = cg_name(cgroup[5], "G");
0145 if (!cgroup[6])
0146 goto cleanup;
0147
0148 cgroup[7] = cg_name(cgroup[6], "H");
0149 if (!cgroup[7])
0150 goto cleanup;
0151
0152 cgroup[8] = cg_name(cgroup[0], "I");
0153 if (!cgroup[8])
0154 goto cleanup;
0155
0156 cgroup[9] = cg_name(cgroup[0], "K");
0157 if (!cgroup[9])
0158 goto cleanup;
0159
0160 for (i = 0; i < 10; i++)
0161 if (cg_create(cgroup[i]))
0162 goto cleanup;
0163
0164 pids[0] = cg_run_nowait(cgroup[2], child_fn, NULL);
0165 pids[1] = cg_run_nowait(cgroup[7], child_fn, NULL);
0166 pids[2] = cg_run_nowait(cgroup[9], child_fn, NULL);
0167 pids[3] = cg_run_nowait(cgroup[9], child_fn, NULL);
0168 pids[4] = cg_run_nowait(cgroup[9], child_fn, NULL);
0169
0170
0171
0172
0173
0174
0175 if (cg_wait_for_proc_count(cgroup[2], 1) ||
0176 cg_wait_for_proc_count(cgroup[7], 1) ||
0177 cg_wait_for_proc_count(cgroup[9], 3))
0178 goto cleanup;
0179
0180
0181
0182
0183 if (cg_kill_wait(cgroup[0]))
0184 goto cleanup;
0185
0186 ret = KSFT_PASS;
0187
0188 cleanup:
0189 for (i = 0; i < 5; i++)
0190 wait_for_pid(pids[i]);
0191
0192 if (ret == KSFT_PASS &&
0193 cg_read_strcmp(cgroup[0], "cgroup.events", "populated 0\n"))
0194 ret = KSFT_FAIL;
0195
0196 for (i = 9; i >= 0 && cgroup[i]; i--) {
0197 cg_destroy(cgroup[i]);
0198 free(cgroup[i]);
0199 }
0200
0201 return ret;
0202 }
0203
0204 static int forkbomb_fn(const char *cgroup, void *arg)
0205 {
0206 int ppid;
0207
0208 fork();
0209 fork();
0210
0211 ppid = getppid();
0212
0213 while (getppid() == ppid)
0214 usleep(1000);
0215
0216 return getppid() == ppid;
0217 }
0218
0219
0220
0221
0222 static int test_cgkill_forkbomb(const char *root)
0223 {
0224 int ret = KSFT_FAIL;
0225 char *cgroup = NULL;
0226 pid_t pid = -ESRCH;
0227
0228 cgroup = cg_name(root, "cg_forkbomb_test");
0229 if (!cgroup)
0230 goto cleanup;
0231
0232 if (cg_create(cgroup))
0233 goto cleanup;
0234
0235 pid = cg_run_nowait(cgroup, forkbomb_fn, NULL);
0236 if (pid < 0)
0237 goto cleanup;
0238
0239 usleep(100000);
0240
0241 if (cg_kill_wait(cgroup))
0242 goto cleanup;
0243
0244 if (cg_wait_for_proc_count(cgroup, 0))
0245 goto cleanup;
0246
0247 ret = KSFT_PASS;
0248
0249 cleanup:
0250 if (pid > 0)
0251 wait_for_pid(pid);
0252
0253 if (ret == KSFT_PASS &&
0254 cg_read_strcmp(cgroup, "cgroup.events", "populated 0\n"))
0255 ret = KSFT_FAIL;
0256
0257 if (cgroup)
0258 cg_destroy(cgroup);
0259 free(cgroup);
0260 return ret;
0261 }
0262
0263 #define T(x) { x, #x }
0264 struct cgkill_test {
0265 int (*fn)(const char *root);
0266 const char *name;
0267 } tests[] = {
0268 T(test_cgkill_simple),
0269 T(test_cgkill_tree),
0270 T(test_cgkill_forkbomb),
0271 };
0272 #undef T
0273
0274 int main(int argc, char *argv[])
0275 {
0276 char root[PATH_MAX];
0277 int i, ret = EXIT_SUCCESS;
0278
0279 if (cg_find_unified_root(root, sizeof(root)))
0280 ksft_exit_skip("cgroup v2 isn't mounted\n");
0281 for (i = 0; i < ARRAY_SIZE(tests); i++) {
0282 switch (tests[i].fn(root)) {
0283 case KSFT_PASS:
0284 ksft_test_result_pass("%s\n", tests[i].name);
0285 break;
0286 case KSFT_SKIP:
0287 ksft_test_result_skip("%s\n", tests[i].name);
0288 break;
0289 default:
0290 ret = EXIT_FAILURE;
0291 ksft_test_result_fail("%s\n", tests[i].name);
0292 break;
0293 }
0294 }
0295
0296 return ret;
0297 }