brintos

brintos / linux-shallow public Read only

0
0
Text · 5.3 KiB · 0e5bb6c Raw
298 lines · c
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 being44 * 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 *       A101 *    / / \ \102 *   B  E  I K103 *  /\  |104 * C  D F105 *      |106 *      G107 *      |108 *      H109 *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 enter172	 * 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 T273 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), NULL))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