1f08c3bdfSopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
2f08c3bdfSopenharmony_ci/*
3f08c3bdfSopenharmony_ci * Copyright (c) 2021 SUSE LLC <rpalethorpe@suse.com>
4f08c3bdfSopenharmony_ci * Copyright (c) Linux Test Project, 2021-2023
5f08c3bdfSopenharmony_ci */
6f08c3bdfSopenharmony_ci
7f08c3bdfSopenharmony_ci/*\
8f08c3bdfSopenharmony_ci * [Description]
9f08c3bdfSopenharmony_ci *
10f08c3bdfSopenharmony_ci * Creates a multi-level CGroup hierarchy with the cpu controller
11f08c3bdfSopenharmony_ci * enabled. The leaf groups are populated with "busy" processes which
12f08c3bdfSopenharmony_ci * simulate intermittent cpu load. They spin for some time then sleep
13f08c3bdfSopenharmony_ci * then repeat.
14f08c3bdfSopenharmony_ci *
15f08c3bdfSopenharmony_ci * Both the trunk and leaf groups are set cpu bandwidth limits. The
16f08c3bdfSopenharmony_ci * busy processes will intermittently exceed these limits. Causing
17f08c3bdfSopenharmony_ci * them to be throttled. When they begin sleeping this will then cause
18f08c3bdfSopenharmony_ci * them to be unthrottle.
19f08c3bdfSopenharmony_ci *
20f08c3bdfSopenharmony_ci * The test is known to reproduce an issue with an update to
21f08c3bdfSopenharmony_ci * SLE-15-SP1 (kernel 4.12.14-197.64,
22f08c3bdfSopenharmony_ci * https://bugzilla.suse.com/show_bug.cgi?id=1179093).
23f08c3bdfSopenharmony_ci *
24f08c3bdfSopenharmony_ci * Also as an reproducer for another bug:
25f08c3bdfSopenharmony_ci *
26f08c3bdfSopenharmony_ci *    commit fdaba61ef8a268d4136d0a113d153f7a89eb9984
27f08c3bdfSopenharmony_ci *    Author: Rik van Riel <riel@surriel.com>
28f08c3bdfSopenharmony_ci *    Date:   Mon Jun 21 19:43:30 2021 +0200
29f08c3bdfSopenharmony_ci *
30f08c3bdfSopenharmony_ci *    sched/fair: Ensure that the CFS parent is added after unthrottling
31f08c3bdfSopenharmony_ci */
32f08c3bdfSopenharmony_ci
33f08c3bdfSopenharmony_ci#include <stdlib.h>
34f08c3bdfSopenharmony_ci
35f08c3bdfSopenharmony_ci#include "tst_test.h"
36f08c3bdfSopenharmony_ci#include "tst_timer.h"
37f08c3bdfSopenharmony_ci
38f08c3bdfSopenharmony_cistatic struct tst_cg_group *cg_level2, *cg_level3a, *cg_level3b;
39f08c3bdfSopenharmony_cistatic struct tst_cg_group *cg_workers[3];
40f08c3bdfSopenharmony_cistatic int may_have_waiters = 0;
41f08c3bdfSopenharmony_ci
42f08c3bdfSopenharmony_cistatic void set_cpu_quota(const struct tst_cg_group *const cg,
43f08c3bdfSopenharmony_ci			  const float quota_percent)
44f08c3bdfSopenharmony_ci{
45f08c3bdfSopenharmony_ci	const unsigned int period_us = 10000;
46f08c3bdfSopenharmony_ci	const unsigned int quota_us = (quota_percent / 100) * (float)period_us;
47f08c3bdfSopenharmony_ci
48f08c3bdfSopenharmony_ci	if (!TST_CG_VER_IS_V1(cg, "cpu")) {
49f08c3bdfSopenharmony_ci		SAFE_CG_PRINTF(cg, "cpu.max",
50f08c3bdfSopenharmony_ci				   "%u %u", quota_us, period_us);
51f08c3bdfSopenharmony_ci	} else {
52f08c3bdfSopenharmony_ci		SAFE_CG_PRINTF(cg, "cpu.cfs_period_us",
53f08c3bdfSopenharmony_ci				  "%u", period_us);
54f08c3bdfSopenharmony_ci		SAFE_CG_PRINTF(cg, "cpu.max",
55f08c3bdfSopenharmony_ci				   "%u", quota_us);
56f08c3bdfSopenharmony_ci	}
57f08c3bdfSopenharmony_ci
58f08c3bdfSopenharmony_ci	tst_res(TINFO, "Set '%s/cpu.max' = '%d %d'",
59f08c3bdfSopenharmony_ci		tst_cg_group_name(cg), quota_us, period_us);
60f08c3bdfSopenharmony_ci}
61f08c3bdfSopenharmony_ci
62f08c3bdfSopenharmony_cistatic void mk_cpu_cgroup(struct tst_cg_group **cg,
63f08c3bdfSopenharmony_ci			  const struct tst_cg_group *const cg_parent,
64f08c3bdfSopenharmony_ci			  const char *const cg_child_name,
65f08c3bdfSopenharmony_ci			  const float quota_percent)
66f08c3bdfSopenharmony_ci
67f08c3bdfSopenharmony_ci{
68f08c3bdfSopenharmony_ci	*cg = tst_cg_group_mk(cg_parent, "%s", cg_child_name);
69f08c3bdfSopenharmony_ci
70f08c3bdfSopenharmony_ci	set_cpu_quota(*cg, quota_percent);
71f08c3bdfSopenharmony_ci}
72f08c3bdfSopenharmony_ci
73f08c3bdfSopenharmony_cistatic void busy_loop(const unsigned int sleep_ms)
74f08c3bdfSopenharmony_ci{
75f08c3bdfSopenharmony_ci	for (;;) {
76f08c3bdfSopenharmony_ci		tst_timer_start(CLOCK_MONOTONIC_RAW);
77f08c3bdfSopenharmony_ci		while (!tst_timer_expired_ms(20))
78f08c3bdfSopenharmony_ci			;
79f08c3bdfSopenharmony_ci
80f08c3bdfSopenharmony_ci		const int ret = tst_checkpoint_wait(0, sleep_ms);
81f08c3bdfSopenharmony_ci
82f08c3bdfSopenharmony_ci		if (!ret)
83f08c3bdfSopenharmony_ci			exit(0);
84f08c3bdfSopenharmony_ci
85f08c3bdfSopenharmony_ci		if (errno != ETIMEDOUT)
86f08c3bdfSopenharmony_ci			tst_brk(TBROK | TERRNO, "tst_checkpoint_wait");
87f08c3bdfSopenharmony_ci	}
88f08c3bdfSopenharmony_ci}
89f08c3bdfSopenharmony_ci
90f08c3bdfSopenharmony_cistatic void fork_busy_procs_in_cgroup(const struct tst_cg_group *const cg)
91f08c3bdfSopenharmony_ci{
92f08c3bdfSopenharmony_ci	const unsigned int sleeps_ms[] = {3000, 1000, 10};
93f08c3bdfSopenharmony_ci	const pid_t worker_pid = SAFE_FORK();
94f08c3bdfSopenharmony_ci	size_t i;
95f08c3bdfSopenharmony_ci
96f08c3bdfSopenharmony_ci	if (worker_pid)
97f08c3bdfSopenharmony_ci		return;
98f08c3bdfSopenharmony_ci
99f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(sleeps_ms); i++) {
100f08c3bdfSopenharmony_ci		const pid_t busy_pid = SAFE_FORK();
101f08c3bdfSopenharmony_ci
102f08c3bdfSopenharmony_ci		if (!busy_pid)
103f08c3bdfSopenharmony_ci			busy_loop(sleeps_ms[i]);
104f08c3bdfSopenharmony_ci
105f08c3bdfSopenharmony_ci		SAFE_CG_PRINTF(cg, "cgroup.procs", "%d", busy_pid);
106f08c3bdfSopenharmony_ci	}
107f08c3bdfSopenharmony_ci
108f08c3bdfSopenharmony_ci	tst_reap_children();
109f08c3bdfSopenharmony_ci
110f08c3bdfSopenharmony_ci	exit(0);
111f08c3bdfSopenharmony_ci}
112f08c3bdfSopenharmony_ci
113f08c3bdfSopenharmony_cistatic void do_test(void)
114f08c3bdfSopenharmony_ci{
115f08c3bdfSopenharmony_ci	size_t i;
116f08c3bdfSopenharmony_ci
117f08c3bdfSopenharmony_ci	may_have_waiters = 1;
118f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cg_workers); i++)
119f08c3bdfSopenharmony_ci		fork_busy_procs_in_cgroup(cg_workers[i]);
120f08c3bdfSopenharmony_ci
121f08c3bdfSopenharmony_ci	tst_res(TPASS, "Scheduled bandwidth constrained workers");
122f08c3bdfSopenharmony_ci
123f08c3bdfSopenharmony_ci	sleep(1);
124f08c3bdfSopenharmony_ci
125f08c3bdfSopenharmony_ci	set_cpu_quota(cg_level2, 50);
126f08c3bdfSopenharmony_ci
127f08c3bdfSopenharmony_ci	sleep(2);
128f08c3bdfSopenharmony_ci
129f08c3bdfSopenharmony_ci	TST_CHECKPOINT_WAKE2(0, 3 * 3);
130f08c3bdfSopenharmony_ci	tst_reap_children();
131f08c3bdfSopenharmony_ci	may_have_waiters = 0;
132f08c3bdfSopenharmony_ci
133f08c3bdfSopenharmony_ci	tst_res(TPASS, "Workers exited");
134f08c3bdfSopenharmony_ci}
135f08c3bdfSopenharmony_ci
136f08c3bdfSopenharmony_cistatic void setup(void)
137f08c3bdfSopenharmony_ci{
138f08c3bdfSopenharmony_ci	cg_level2 = tst_cg_group_mk(tst_cg, "level2");
139f08c3bdfSopenharmony_ci
140f08c3bdfSopenharmony_ci	cg_level3a = tst_cg_group_mk(cg_level2, "level3a");
141f08c3bdfSopenharmony_ci	mk_cpu_cgroup(&cg_workers[0], cg_level3a, "worker1", 30);
142f08c3bdfSopenharmony_ci	mk_cpu_cgroup(&cg_workers[1], cg_level3a, "worker2", 20);
143f08c3bdfSopenharmony_ci
144f08c3bdfSopenharmony_ci	cg_level3b = tst_cg_group_mk(cg_level2, "level3b");
145f08c3bdfSopenharmony_ci	mk_cpu_cgroup(&cg_workers[2], cg_level3b, "worker3", 30);
146f08c3bdfSopenharmony_ci}
147f08c3bdfSopenharmony_ci
148f08c3bdfSopenharmony_cistatic void cleanup(void)
149f08c3bdfSopenharmony_ci{
150f08c3bdfSopenharmony_ci	size_t i;
151f08c3bdfSopenharmony_ci
152f08c3bdfSopenharmony_ci	if (may_have_waiters) {
153f08c3bdfSopenharmony_ci		TST_CHECKPOINT_WAKE2(0, 3 * 3);
154f08c3bdfSopenharmony_ci		tst_reap_children();
155f08c3bdfSopenharmony_ci		may_have_waiters = 0;
156f08c3bdfSopenharmony_ci	}
157f08c3bdfSopenharmony_ci
158f08c3bdfSopenharmony_ci	for (i = 0; i < ARRAY_SIZE(cg_workers); i++) {
159f08c3bdfSopenharmony_ci		if (cg_workers[i])
160f08c3bdfSopenharmony_ci			cg_workers[i] = tst_cg_group_rm(cg_workers[i]);
161f08c3bdfSopenharmony_ci	}
162f08c3bdfSopenharmony_ci
163f08c3bdfSopenharmony_ci	if (cg_level3a)
164f08c3bdfSopenharmony_ci		cg_level3a = tst_cg_group_rm(cg_level3a);
165f08c3bdfSopenharmony_ci	if (cg_level3b)
166f08c3bdfSopenharmony_ci		cg_level3b = tst_cg_group_rm(cg_level3b);
167f08c3bdfSopenharmony_ci	if (cg_level2)
168f08c3bdfSopenharmony_ci		cg_level2 = tst_cg_group_rm(cg_level2);
169f08c3bdfSopenharmony_ci}
170f08c3bdfSopenharmony_ci
171f08c3bdfSopenharmony_cistatic struct tst_test test = {
172f08c3bdfSopenharmony_ci	.test_all = do_test,
173f08c3bdfSopenharmony_ci	.setup = setup,
174f08c3bdfSopenharmony_ci	.cleanup = cleanup,
175f08c3bdfSopenharmony_ci	.forks_child = 1,
176f08c3bdfSopenharmony_ci	.needs_checkpoints = 1,
177f08c3bdfSopenharmony_ci	.max_runtime = 20,
178f08c3bdfSopenharmony_ci	.taint_check = TST_TAINT_W | TST_TAINT_D,
179f08c3bdfSopenharmony_ci	.needs_kconfigs = (const char *[]) {
180f08c3bdfSopenharmony_ci		"CONFIG_CFS_BANDWIDTH",
181f08c3bdfSopenharmony_ci		NULL
182f08c3bdfSopenharmony_ci	},
183f08c3bdfSopenharmony_ci	.needs_cgroup_ctrls = (const char *const []){"cpu", NULL},
184f08c3bdfSopenharmony_ci	.tags = (const struct tst_tag[]) {
185f08c3bdfSopenharmony_ci		{"linux-git", "39f23ce07b93"},
186f08c3bdfSopenharmony_ci		{"linux-git", "b34cb07dde7c"},
187f08c3bdfSopenharmony_ci		{"linux-git", "fe61468b2cbc"},
188f08c3bdfSopenharmony_ci		{"linux-git", "5ab297bab984"},
189f08c3bdfSopenharmony_ci		{"linux-git", "6d4d22468dae"},
190f08c3bdfSopenharmony_ci		{"linux-git", "fdaba61ef8a2"},
191f08c3bdfSopenharmony_ci		{ }
192f08c3bdfSopenharmony_ci	}
193f08c3bdfSopenharmony_ci};
194