xref: /linux/tools/testing/selftests/cgroup/test_pids.c (revision 07fdad3a93756b872da7b53647715c48d0f4a2d0)
1 // SPDX-License-Identifier: GPL-2.0
2 #define _GNU_SOURCE
3 
4 #include <errno.h>
5 #include <linux/limits.h>
6 #include <signal.h>
7 #include <string.h>
8 #include <sys/stat.h>
9 #include <sys/types.h>
10 #include <unistd.h>
11 
12 #include "../kselftest.h"
13 #include "cgroup_util.h"
14 
15 static int run_success(const char *cgroup, void *arg)
16 {
17 	return 0;
18 }
19 
20 static int run_pause(const char *cgroup, void *arg)
21 {
22 	return pause();
23 }
24 
25 /*
26  * This test checks that pids.max prevents forking new children above the
27  * specified limit in the cgroup.
28  */
29 static int test_pids_max(const char *root)
30 {
31 	int ret = KSFT_FAIL;
32 	char *cg_pids;
33 	int pid;
34 
35 	cg_pids = cg_name(root, "pids_test");
36 	if (!cg_pids)
37 		goto cleanup;
38 
39 	if (cg_create(cg_pids))
40 		goto cleanup;
41 
42 	if (cg_read_strcmp(cg_pids, "pids.max", "max\n"))
43 		goto cleanup;
44 
45 	if (cg_write(cg_pids, "pids.max", "2"))
46 		goto cleanup;
47 
48 	if (cg_enter_current(cg_pids))
49 		goto cleanup;
50 
51 	pid = cg_run_nowait(cg_pids, run_pause, NULL);
52 	if (pid < 0)
53 		goto cleanup;
54 
55 	if (cg_run_nowait(cg_pids, run_success, NULL) != -1 || errno != EAGAIN)
56 		goto cleanup;
57 
58 	if (kill(pid, SIGINT))
59 		goto cleanup;
60 
61 	ret = KSFT_PASS;
62 
63 cleanup:
64 	cg_enter_current(root);
65 	cg_destroy(cg_pids);
66 	free(cg_pids);
67 
68 	return ret;
69 }
70 
71 /*
72  * This test checks that pids.events are counted in cgroup associated with pids.max
73  */
74 static int test_pids_events(const char *root)
75 {
76 	int ret = KSFT_FAIL;
77 	char *cg_parent = NULL, *cg_child = NULL;
78 	int pid;
79 
80 	if (cgroup_feature("pids_localevents") <= 0)
81 		return KSFT_SKIP;
82 
83 	cg_parent = cg_name(root, "pids_parent");
84 	cg_child = cg_name(cg_parent, "pids_child");
85 	if (!cg_parent || !cg_child)
86 		goto cleanup;
87 
88 	if (cg_create(cg_parent))
89 		goto cleanup;
90 	if (cg_write(cg_parent, "cgroup.subtree_control", "+pids"))
91 		goto cleanup;
92 	if (cg_create(cg_child))
93 		goto cleanup;
94 
95 	if (cg_write(cg_parent, "pids.max", "2"))
96 		goto cleanup;
97 
98 	if (cg_read_strcmp(cg_child, "pids.max", "max\n"))
99 		goto cleanup;
100 
101 	if (cg_enter_current(cg_child))
102 		goto cleanup;
103 
104 	pid = cg_run_nowait(cg_child, run_pause, NULL);
105 	if (pid < 0)
106 		goto cleanup;
107 
108 	if (cg_run_nowait(cg_child, run_success, NULL) != -1 || errno != EAGAIN)
109 		goto cleanup;
110 
111 	if (kill(pid, SIGINT))
112 		goto cleanup;
113 
114 	if (cg_read_key_long(cg_child, "pids.events", "max ") != 0)
115 		goto cleanup;
116 	if (cg_read_key_long(cg_parent, "pids.events", "max ") != 1)
117 		goto cleanup;
118 
119 
120 	ret = KSFT_PASS;
121 
122 cleanup:
123 	cg_enter_current(root);
124 	if (cg_child)
125 		cg_destroy(cg_child);
126 	if (cg_parent)
127 		cg_destroy(cg_parent);
128 	free(cg_child);
129 	free(cg_parent);
130 
131 	return ret;
132 }
133 
134 
135 
136 #define T(x) { x, #x }
137 struct pids_test {
138 	int (*fn)(const char *root);
139 	const char *name;
140 } tests[] = {
141 	T(test_pids_max),
142 	T(test_pids_events),
143 };
144 #undef T
145 
146 int main(int argc, char **argv)
147 {
148 	char root[PATH_MAX];
149 
150 	ksft_print_header();
151 	ksft_set_plan(ARRAY_SIZE(tests));
152 	if (cg_find_unified_root(root, sizeof(root), NULL))
153 		ksft_exit_skip("cgroup v2 isn't mounted\n");
154 
155 	/*
156 	 * Check that pids controller is available:
157 	 * pids is listed in cgroup.controllers
158 	 */
159 	if (cg_read_strstr(root, "cgroup.controllers", "pids"))
160 		ksft_exit_skip("pids controller isn't available\n");
161 
162 	if (cg_read_strstr(root, "cgroup.subtree_control", "pids"))
163 		if (cg_write(root, "cgroup.subtree_control", "+pids"))
164 			ksft_exit_skip("Failed to set pids controller\n");
165 
166 	for (int i = 0; i < ARRAY_SIZE(tests); i++) {
167 		switch (tests[i].fn(root)) {
168 		case KSFT_PASS:
169 			ksft_test_result_pass("%s\n", tests[i].name);
170 			break;
171 		case KSFT_SKIP:
172 			ksft_test_result_skip("%s\n", tests[i].name);
173 			break;
174 		default:
175 			ksft_test_result_fail("%s\n", tests[i].name);
176 			break;
177 		}
178 	}
179 
180 	ksft_finished();
181 }
182