brintos

brintos / linux-shallow public Read only

0
0
Text · 15.2 KiB · 8730645 Raw
849 lines · c
1/* SPDX-License-Identifier: GPL-2.0 */2#include <stdbool.h>3#include <linux/limits.h>4#include <sys/ptrace.h>5#include <sys/types.h>6#include <sys/mman.h>7#include <unistd.h>8#include <stdio.h>9#include <errno.h>10#include <stdlib.h>11#include <string.h>12#include <sys/wait.h>13 14#include "../kselftest.h"15#include "cgroup_util.h"16 17#define DEBUG18#ifdef DEBUG19#define debug(args...) fprintf(stderr, args)20#else21#define debug(args...)22#endif23 24/*25 * Check if the cgroup is frozen by looking at the cgroup.events::frozen value.26 */27static int cg_check_frozen(const char *cgroup, bool frozen)28{29	if (frozen) {30		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 1") != 0) {31			debug("Cgroup %s isn't frozen\n", cgroup);32			return -1;33		}34	} else {35		/*36		 * Check the cgroup.events::frozen value.37		 */38		if (cg_read_strstr(cgroup, "cgroup.events", "frozen 0") != 0) {39			debug("Cgroup %s is frozen\n", cgroup);40			return -1;41		}42	}43 44	return 0;45}46 47/*48 * Freeze the given cgroup.49 */50static int cg_freeze_nowait(const char *cgroup, bool freeze)51{52	return cg_write(cgroup, "cgroup.freeze", freeze ? "1" : "0");53}54 55/*56 * Attach a task to the given cgroup and wait for a cgroup frozen event.57 * All transient events (e.g. populated) are ignored.58 */59static int cg_enter_and_wait_for_frozen(const char *cgroup, int pid,60					bool frozen)61{62	int fd, ret = -1;63	int attempts;64 65	fd = cg_prepare_for_wait(cgroup);66	if (fd < 0)67		return fd;68 69	ret = cg_enter(cgroup, pid);70	if (ret)71		goto out;72 73	for (attempts = 0; attempts < 10; attempts++) {74		ret = cg_wait_for(fd);75		if (ret)76			break;77 78		ret = cg_check_frozen(cgroup, frozen);79		if (ret)80			continue;81	}82 83out:84	close(fd);85	return ret;86}87 88/*89 * Freeze the given cgroup and wait for the inotify signal.90 * If there are no events in 10 seconds, treat this as an error.91 * Then check that the cgroup is in the desired state.92 */93static int cg_freeze_wait(const char *cgroup, bool freeze)94{95	int fd, ret = -1;96 97	fd = cg_prepare_for_wait(cgroup);98	if (fd < 0)99		return fd;100 101	ret = cg_freeze_nowait(cgroup, freeze);102	if (ret) {103		debug("Error: cg_freeze_nowait() failed\n");104		goto out;105	}106 107	ret = cg_wait_for(fd);108	if (ret)109		goto out;110 111	ret = cg_check_frozen(cgroup, freeze);112out:113	close(fd);114	return ret;115}116 117/*118 * A simple process running in a sleep loop until being119 * re-parented.120 */121static int child_fn(const char *cgroup, void *arg)122{123	int ppid = getppid();124 125	while (getppid() == ppid)126		usleep(1000);127 128	return getppid() == ppid;129}130 131/*132 * A simple test for the cgroup freezer: populated the cgroup with 100133 * running processes and freeze it. Then unfreeze it. Then it kills all134 * processes and destroys the cgroup.135 */136static int test_cgfreezer_simple(const char *root)137{138	int ret = KSFT_FAIL;139	char *cgroup = NULL;140	int i;141 142	cgroup = cg_name(root, "cg_test_simple");143	if (!cgroup)144		goto cleanup;145 146	if (cg_create(cgroup))147		goto cleanup;148 149	for (i = 0; i < 100; i++)150		cg_run_nowait(cgroup, child_fn, NULL);151 152	if (cg_wait_for_proc_count(cgroup, 100))153		goto cleanup;154 155	if (cg_check_frozen(cgroup, false))156		goto cleanup;157 158	if (cg_freeze_wait(cgroup, true))159		goto cleanup;160 161	if (cg_freeze_wait(cgroup, false))162		goto cleanup;163 164	ret = KSFT_PASS;165 166cleanup:167	if (cgroup)168		cg_destroy(cgroup);169	free(cgroup);170	return ret;171}172 173/*174 * The test creates the following hierarchy:175 *       A176 *    / / \ \177 *   B  E  I K178 *  /\  |179 * C  D F180 *      |181 *      G182 *      |183 *      H184 *185 * with a process in C, H and 3 processes in K.186 * Then it tries to freeze and unfreeze the whole tree.187 */188static int test_cgfreezer_tree(const char *root)189{190	char *cgroup[10] = {0};191	int ret = KSFT_FAIL;192	int i;193 194	cgroup[0] = cg_name(root, "cg_test_tree_A");195	if (!cgroup[0])196		goto cleanup;197 198	cgroup[1] = cg_name(cgroup[0], "B");199	if (!cgroup[1])200		goto cleanup;201 202	cgroup[2] = cg_name(cgroup[1], "C");203	if (!cgroup[2])204		goto cleanup;205 206	cgroup[3] = cg_name(cgroup[1], "D");207	if (!cgroup[3])208		goto cleanup;209 210	cgroup[4] = cg_name(cgroup[0], "E");211	if (!cgroup[4])212		goto cleanup;213 214	cgroup[5] = cg_name(cgroup[4], "F");215	if (!cgroup[5])216		goto cleanup;217 218	cgroup[6] = cg_name(cgroup[5], "G");219	if (!cgroup[6])220		goto cleanup;221 222	cgroup[7] = cg_name(cgroup[6], "H");223	if (!cgroup[7])224		goto cleanup;225 226	cgroup[8] = cg_name(cgroup[0], "I");227	if (!cgroup[8])228		goto cleanup;229 230	cgroup[9] = cg_name(cgroup[0], "K");231	if (!cgroup[9])232		goto cleanup;233 234	for (i = 0; i < 10; i++)235		if (cg_create(cgroup[i]))236			goto cleanup;237 238	cg_run_nowait(cgroup[2], child_fn, NULL);239	cg_run_nowait(cgroup[7], child_fn, NULL);240	cg_run_nowait(cgroup[9], child_fn, NULL);241	cg_run_nowait(cgroup[9], child_fn, NULL);242	cg_run_nowait(cgroup[9], child_fn, NULL);243 244	/*245	 * Wait until all child processes will enter246	 * corresponding cgroups.247	 */248 249	if (cg_wait_for_proc_count(cgroup[2], 1) ||250	    cg_wait_for_proc_count(cgroup[7], 1) ||251	    cg_wait_for_proc_count(cgroup[9], 3))252		goto cleanup;253 254	/*255	 * Freeze B.256	 */257	if (cg_freeze_wait(cgroup[1], true))258		goto cleanup;259 260	/*261	 * Freeze F.262	 */263	if (cg_freeze_wait(cgroup[5], true))264		goto cleanup;265 266	/*267	 * Freeze G.268	 */269	if (cg_freeze_wait(cgroup[6], true))270		goto cleanup;271 272	/*273	 * Check that A and E are not frozen.274	 */275	if (cg_check_frozen(cgroup[0], false))276		goto cleanup;277 278	if (cg_check_frozen(cgroup[4], false))279		goto cleanup;280 281	/*282	 * Freeze A. Check that A, B and E are frozen.283	 */284	if (cg_freeze_wait(cgroup[0], true))285		goto cleanup;286 287	if (cg_check_frozen(cgroup[1], true))288		goto cleanup;289 290	if (cg_check_frozen(cgroup[4], true))291		goto cleanup;292 293	/*294	 * Unfreeze B, F and G295	 */296	if (cg_freeze_nowait(cgroup[1], false))297		goto cleanup;298 299	if (cg_freeze_nowait(cgroup[5], false))300		goto cleanup;301 302	if (cg_freeze_nowait(cgroup[6], false))303		goto cleanup;304 305	/*306	 * Check that C and H are still frozen.307	 */308	if (cg_check_frozen(cgroup[2], true))309		goto cleanup;310 311	if (cg_check_frozen(cgroup[7], true))312		goto cleanup;313 314	/*315	 * Unfreeze A. Check that A, C and K are not frozen.316	 */317	if (cg_freeze_wait(cgroup[0], false))318		goto cleanup;319 320	if (cg_check_frozen(cgroup[2], false))321		goto cleanup;322 323	if (cg_check_frozen(cgroup[9], false))324		goto cleanup;325 326	ret = KSFT_PASS;327 328cleanup:329	for (i = 9; i >= 0 && cgroup[i]; i--) {330		cg_destroy(cgroup[i]);331		free(cgroup[i]);332	}333 334	return ret;335}336 337/*338 * A fork bomb emulator.339 */340static int forkbomb_fn(const char *cgroup, void *arg)341{342	int ppid;343 344	fork();345	fork();346 347	ppid = getppid();348 349	while (getppid() == ppid)350		usleep(1000);351 352	return getppid() == ppid;353}354 355/*356 * The test runs a fork bomb in a cgroup and tries to freeze it.357 * Then it kills all processes and checks that cgroup isn't populated358 * anymore.359 */360static int test_cgfreezer_forkbomb(const char *root)361{362	int ret = KSFT_FAIL;363	char *cgroup = NULL;364 365	cgroup = cg_name(root, "cg_forkbomb_test");366	if (!cgroup)367		goto cleanup;368 369	if (cg_create(cgroup))370		goto cleanup;371 372	cg_run_nowait(cgroup, forkbomb_fn, NULL);373 374	usleep(100000);375 376	if (cg_freeze_wait(cgroup, true))377		goto cleanup;378 379	if (cg_killall(cgroup))380		goto cleanup;381 382	if (cg_wait_for_proc_count(cgroup, 0))383		goto cleanup;384 385	ret = KSFT_PASS;386 387cleanup:388	if (cgroup)389		cg_destroy(cgroup);390	free(cgroup);391	return ret;392}393 394/*395 * The test creates a cgroups and freezes it. Then it creates a child cgroup396 * and populates it with a task. After that it checks that the child cgroup397 * is frozen and the parent cgroup remains frozen too.398 */399static int test_cgfreezer_mkdir(const char *root)400{401	int ret = KSFT_FAIL;402	char *parent, *child = NULL;403	int pid;404 405	parent = cg_name(root, "cg_test_mkdir_A");406	if (!parent)407		goto cleanup;408 409	child = cg_name(parent, "cg_test_mkdir_B");410	if (!child)411		goto cleanup;412 413	if (cg_create(parent))414		goto cleanup;415 416	if (cg_freeze_wait(parent, true))417		goto cleanup;418 419	if (cg_create(child))420		goto cleanup;421 422	pid = cg_run_nowait(child, child_fn, NULL);423	if (pid < 0)424		goto cleanup;425 426	if (cg_wait_for_proc_count(child, 1))427		goto cleanup;428 429	if (cg_check_frozen(child, true))430		goto cleanup;431 432	if (cg_check_frozen(parent, true))433		goto cleanup;434 435	ret = KSFT_PASS;436 437cleanup:438	if (child)439		cg_destroy(child);440	free(child);441	if (parent)442		cg_destroy(parent);443	free(parent);444	return ret;445}446 447/*448 * The test creates two nested cgroups, freezes the parent449 * and removes the child. Then it checks that the parent cgroup450 * remains frozen and it's possible to create a new child451 * without unfreezing. The new child is frozen too.452 */453static int test_cgfreezer_rmdir(const char *root)454{455	int ret = KSFT_FAIL;456	char *parent, *child = NULL;457 458	parent = cg_name(root, "cg_test_rmdir_A");459	if (!parent)460		goto cleanup;461 462	child = cg_name(parent, "cg_test_rmdir_B");463	if (!child)464		goto cleanup;465 466	if (cg_create(parent))467		goto cleanup;468 469	if (cg_create(child))470		goto cleanup;471 472	if (cg_freeze_wait(parent, true))473		goto cleanup;474 475	if (cg_destroy(child))476		goto cleanup;477 478	if (cg_check_frozen(parent, true))479		goto cleanup;480 481	if (cg_create(child))482		goto cleanup;483 484	if (cg_check_frozen(child, true))485		goto cleanup;486 487	ret = KSFT_PASS;488 489cleanup:490	if (child)491		cg_destroy(child);492	free(child);493	if (parent)494		cg_destroy(parent);495	free(parent);496	return ret;497}498 499/*500 * The test creates two cgroups: A and B, runs a process in A501 * and performs several migrations:502 * 1) A (running) -> B (frozen)503 * 2) B (frozen) -> A (running)504 * 3) A (frozen) -> B (frozen)505 *506 * On each step it checks the actual state of both cgroups.507 */508static int test_cgfreezer_migrate(const char *root)509{510	int ret = KSFT_FAIL;511	char *cgroup[2] = {0};512	int pid;513 514	cgroup[0] = cg_name(root, "cg_test_migrate_A");515	if (!cgroup[0])516		goto cleanup;517 518	cgroup[1] = cg_name(root, "cg_test_migrate_B");519	if (!cgroup[1])520		goto cleanup;521 522	if (cg_create(cgroup[0]))523		goto cleanup;524 525	if (cg_create(cgroup[1]))526		goto cleanup;527 528	pid = cg_run_nowait(cgroup[0], child_fn, NULL);529	if (pid < 0)530		goto cleanup;531 532	if (cg_wait_for_proc_count(cgroup[0], 1))533		goto cleanup;534 535	/*536	 * Migrate from A (running) to B (frozen)537	 */538	if (cg_freeze_wait(cgroup[1], true))539		goto cleanup;540 541	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))542		goto cleanup;543 544	if (cg_check_frozen(cgroup[0], false))545		goto cleanup;546 547	/*548	 * Migrate from B (frozen) to A (running)549	 */550	if (cg_enter_and_wait_for_frozen(cgroup[0], pid, false))551		goto cleanup;552 553	if (cg_check_frozen(cgroup[1], true))554		goto cleanup;555 556	/*557	 * Migrate from A (frozen) to B (frozen)558	 */559	if (cg_freeze_wait(cgroup[0], true))560		goto cleanup;561 562	if (cg_enter_and_wait_for_frozen(cgroup[1], pid, true))563		goto cleanup;564 565	if (cg_check_frozen(cgroup[0], true))566		goto cleanup;567 568	ret = KSFT_PASS;569 570cleanup:571	if (cgroup[0])572		cg_destroy(cgroup[0]);573	free(cgroup[0]);574	if (cgroup[1])575		cg_destroy(cgroup[1]);576	free(cgroup[1]);577	return ret;578}579 580/*581 * The test checks that ptrace works with a tracing process in a frozen cgroup.582 */583static int test_cgfreezer_ptrace(const char *root)584{585	int ret = KSFT_FAIL;586	char *cgroup = NULL;587	siginfo_t siginfo;588	int pid;589 590	cgroup = cg_name(root, "cg_test_ptrace");591	if (!cgroup)592		goto cleanup;593 594	if (cg_create(cgroup))595		goto cleanup;596 597	pid = cg_run_nowait(cgroup, child_fn, NULL);598	if (pid < 0)599		goto cleanup;600 601	if (cg_wait_for_proc_count(cgroup, 1))602		goto cleanup;603 604	if (cg_freeze_wait(cgroup, true))605		goto cleanup;606 607	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))608		goto cleanup;609 610	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))611		goto cleanup;612 613	waitpid(pid, NULL, 0);614 615	/*616	 * Cgroup has to remain frozen, however the test task617	 * is in traced state.618	 */619	if (cg_check_frozen(cgroup, true))620		goto cleanup;621 622	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))623		goto cleanup;624 625	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))626		goto cleanup;627 628	if (cg_check_frozen(cgroup, true))629		goto cleanup;630 631	ret = KSFT_PASS;632 633cleanup:634	if (cgroup)635		cg_destroy(cgroup);636	free(cgroup);637	return ret;638}639 640/*641 * Check if the process is stopped.642 */643static int proc_check_stopped(int pid)644{645	char buf[PAGE_SIZE];646	int len;647 648	len = proc_read_text(pid, 0, "stat", buf, sizeof(buf));649	if (len == -1) {650		debug("Can't get %d stat\n", pid);651		return -1;652	}653 654	if (strstr(buf, "(test_freezer) T ") == NULL) {655		debug("Process %d in the unexpected state: %s\n", pid, buf);656		return -1;657	}658 659	return 0;660}661 662/*663 * Test that it's possible to freeze a cgroup with a stopped process.664 */665static int test_cgfreezer_stopped(const char *root)666{667	int pid, ret = KSFT_FAIL;668	char *cgroup = NULL;669 670	cgroup = cg_name(root, "cg_test_stopped");671	if (!cgroup)672		goto cleanup;673 674	if (cg_create(cgroup))675		goto cleanup;676 677	pid = cg_run_nowait(cgroup, child_fn, NULL);678 679	if (cg_wait_for_proc_count(cgroup, 1))680		goto cleanup;681 682	if (kill(pid, SIGSTOP))683		goto cleanup;684 685	if (cg_check_frozen(cgroup, false))686		goto cleanup;687 688	if (cg_freeze_wait(cgroup, true))689		goto cleanup;690 691	if (cg_freeze_wait(cgroup, false))692		goto cleanup;693 694	if (proc_check_stopped(pid))695		goto cleanup;696 697	ret = KSFT_PASS;698 699cleanup:700	if (cgroup)701		cg_destroy(cgroup);702	free(cgroup);703	return ret;704}705 706/*707 * Test that it's possible to freeze a cgroup with a ptraced process.708 */709static int test_cgfreezer_ptraced(const char *root)710{711	int pid, ret = KSFT_FAIL;712	char *cgroup = NULL;713	siginfo_t siginfo;714 715	cgroup = cg_name(root, "cg_test_ptraced");716	if (!cgroup)717		goto cleanup;718 719	if (cg_create(cgroup))720		goto cleanup;721 722	pid = cg_run_nowait(cgroup, child_fn, NULL);723 724	if (cg_wait_for_proc_count(cgroup, 1))725		goto cleanup;726 727	if (ptrace(PTRACE_SEIZE, pid, NULL, NULL))728		goto cleanup;729 730	if (ptrace(PTRACE_INTERRUPT, pid, NULL, NULL))731		goto cleanup;732 733	waitpid(pid, NULL, 0);734 735	if (cg_check_frozen(cgroup, false))736		goto cleanup;737 738	if (cg_freeze_wait(cgroup, true))739		goto cleanup;740 741	/*742	 * cg_check_frozen(cgroup, true) will fail here,743	 * because the task is in the TRACEd state.744	 */745	if (cg_freeze_wait(cgroup, false))746		goto cleanup;747 748	if (ptrace(PTRACE_GETSIGINFO, pid, NULL, &siginfo))749		goto cleanup;750 751	if (ptrace(PTRACE_DETACH, pid, NULL, NULL))752		goto cleanup;753 754	ret = KSFT_PASS;755 756cleanup:757	if (cgroup)758		cg_destroy(cgroup);759	free(cgroup);760	return ret;761}762 763static int vfork_fn(const char *cgroup, void *arg)764{765	int pid = vfork();766 767	if (pid == 0)768		while (true)769			sleep(1);770 771	return pid;772}773 774/*775 * Test that it's possible to freeze a cgroup with a process,776 * which called vfork() and is waiting for a child.777 */778static int test_cgfreezer_vfork(const char *root)779{780	int ret = KSFT_FAIL;781	char *cgroup = NULL;782 783	cgroup = cg_name(root, "cg_test_vfork");784	if (!cgroup)785		goto cleanup;786 787	if (cg_create(cgroup))788		goto cleanup;789 790	cg_run_nowait(cgroup, vfork_fn, NULL);791 792	if (cg_wait_for_proc_count(cgroup, 2))793		goto cleanup;794 795	if (cg_freeze_wait(cgroup, true))796		goto cleanup;797 798	ret = KSFT_PASS;799 800cleanup:801	if (cgroup)802		cg_destroy(cgroup);803	free(cgroup);804	return ret;805}806 807#define T(x) { x, #x }808struct cgfreezer_test {809	int (*fn)(const char *root);810	const char *name;811} tests[] = {812	T(test_cgfreezer_simple),813	T(test_cgfreezer_tree),814	T(test_cgfreezer_forkbomb),815	T(test_cgfreezer_mkdir),816	T(test_cgfreezer_rmdir),817	T(test_cgfreezer_migrate),818	T(test_cgfreezer_ptrace),819	T(test_cgfreezer_stopped),820	T(test_cgfreezer_ptraced),821	T(test_cgfreezer_vfork),822};823#undef T824 825int main(int argc, char *argv[])826{827	char root[PATH_MAX];828	int i, ret = EXIT_SUCCESS;829 830	if (cg_find_unified_root(root, sizeof(root), NULL))831		ksft_exit_skip("cgroup v2 isn't mounted\n");832	for (i = 0; i < ARRAY_SIZE(tests); i++) {833		switch (tests[i].fn(root)) {834		case KSFT_PASS:835			ksft_test_result_pass("%s\n", tests[i].name);836			break;837		case KSFT_SKIP:838			ksft_test_result_skip("%s\n", tests[i].name);839			break;840		default:841			ret = EXIT_FAILURE;842			ksft_test_result_fail("%s\n", tests[i].name);843			break;844		}845	}846 847	return ret;848}849