• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // SPDX-License-Identifier: GPL-2.0-or-later
2 /*
3  * Copyright (c) 2020 Red Hat, Inc.
4  * Copyright (c) 2020 Li Wang <liwang@redhat.com>
5  * Copyright (c) 2020-2021 SUSE LLC <rpalethorpe@suse.com>
6  */
7 /*\
8  * [Description]
9  *
10  * The LTP CGroups API tries to present a consistent interface to the
11  * many possible CGroup configurations a system could have.
12  *
13  * You may ask; "Why don't you just mount a simple CGroup hierarchy,
14  * instead of scanning the current setup?". The short answer is that
15  * it is not possible unless no CGroups are currently active and
16  * almost all of our users will have CGroups active. Even if
17  * unmounting the current CGroup hierarchy is a reasonable thing to do
18  * to the sytem manager, it is highly unlikely the CGroup hierarchy
19  * will be destroyed. So users would be forced to remove their CGroup
20  * configuration and reboot the system.
21  *
22  * The core library tries to ensure an LTP CGroup exists on each
23  * hierarchy root. Inside the LTP group it ensures a 'drain' group
24  * exists and creats a test group for the current test. In the worst
25  * case we end up with a set of hierarchies like the follwoing. Where
26  * existing system-manager-created CGroups have been omitted.
27  *
28  *	(V2 Root)	(V1 Root 1)	...	(V1 Root N)
29  *	    |		     |			     |
30  *	  (ltp)		   (ltp)	...	   (ltp)
31  *	 /     \	  /	\		  /	\
32  *  (drain) (test-n) (drain)  (test-n)  ...  (drain)  (test-n)
33  *
34  * V2 CGroup controllers use a single unified hierarchy on a single
35  * root. Two or more V1 controllers may share a root or have their own
36  * root. However there may exist only one instance of a controller.
37  * So you can not have the same V1 controller on multiple roots.
38  *
39  * It is possible to have both a V2 hierarchy and V1 hierarchies
40  * active at the same time. Which is what is shown above. Any
41  * controllers attached to V1 hierarchies will not be available in the
42  * V2 hierarchy. The reverse is also true.
43  *
44  * Note that a single hierarchy may be mounted multiple
45  * times. Allowing it to be accessed at different locations. However
46  * subsequent mount operations will fail if the mount options are
47  * different from the first.
48  *
49  * The user may pre-create the CGroup hierarchies and the ltp CGroup,
50  * otherwise the library will try to create them. If the ltp group
51  * already exists and has appropriate permissions, then admin
52  * privileges will not be required to run the tests.
53  *
54  * Because the test may not have access to the CGroup root(s), the
55  * drain CGroup is created. This can be used to store processes which
56  * would otherwise block the destruction of the individual test CGroup
57  * or one of its descendants.
58  *
59  * The test author may create child CGroups within the test CGroup
60  * using the CGroup Item API. The library will create the new CGroup
61  * in all the relevant hierarchies.
62  *
63  * There are many differences between the V1 and V2 CGroup APIs. If a
64  * controller is on both V1 and V2, it may have different parameters
65  * and control files. Some of these control files have a different
66  * name, but similar functionality. In this case the Item API uses
67  * the V2 names and aliases them to the V1 name when appropriate.
68  *
69  * Some control files only exist on one of the versions or they can be
70  * missing due to other reasons. The Item API allows the user to check
71  * if the file exists before trying to use it.
72  *
73  * Often a control file has almost the same functionality between V1
74  * and V2. Which means it can be used in the same way most of the
75  * time, but not all. For now this is handled by exposing the API
76  * version a controller is using to allow the test author to handle
77  * edge cases. (e.g. V2 memory.swap.max accepts "max", but V1
78  * memory.memsw.limit_in_bytes does not).
79  */
80 
81 #ifndef TST_CGROUP_H
82 #define TST_CGROUP_H
83 
84 #include <sys/types.h>
85 
86 /* CGroups Kernel API version */
87 enum tst_cgroup_ver {
88 	TST_CGROUP_V1 = 1,
89 	TST_CGROUP_V2 = 2,
90 };
91 
92 /* Used to specify CGroup hierarchy configuration options, allowing a
93  * test to request a particular CGroup structure.
94  */
95 struct tst_cgroup_opts {
96 	/* Only try to mount V1 CGroup controllers. This will not
97 	 * prevent V2 from being used if it is already mounted, it
98 	 * only indicates that we should mount V1 controllers if
99 	 * nothing is present. By default we try to mount V2 first. */
100 	int only_mount_v1:1;
101 };
102 
103 /* A Control Group in LTP's aggregated hierarchy */
104 struct tst_cgroup_group;
105 
106 /* Search the system for mounted cgroups and available
107  * controllers. Called automatically by tst_cgroup_require.
108  */
109 void tst_cgroup_scan(void);
110 /* Print the config detected by tst_cgroup_scan */
111 void tst_cgroup_print_config(void);
112 
113 /* Ensure the specified controller is available in the test's default
114  * CGroup, mounting/enabling it if necessary */
115 void tst_cgroup_require(const char *const ctrl_name,
116 			const struct tst_cgroup_opts *const options)
117 			__attribute__ ((nonnull (1)));
118 
119 /* Tear down any CGroups created by calls to tst_cgroup_require */
120 void tst_cgroup_cleanup(void);
121 
122 /* Get the default CGroup for the test. It allocates memory (in a
123  * guarded buffer) so should always be called from setup
124  */
125 const struct tst_cgroup_group *tst_cgroup_get_test_group(void)
126 	__attribute__ ((warn_unused_result));
127 /* Get the shared drain group. Also should be called from setup */
128 const struct tst_cgroup_group *tst_cgroup_get_drain_group(void)
129 	__attribute__ ((warn_unused_result));
130 
131 /* Create a descendant CGroup */
132 struct tst_cgroup_group *
133 tst_cgroup_group_mk(const struct tst_cgroup_group *const parent,
134 		    const char *const group_name)
135 		    __attribute__ ((nonnull, warn_unused_result));
136 const char *
137 tst_cgroup_group_name(const struct tst_cgroup_group *const cg)
138 		      __attribute__ ((nonnull, warn_unused_result));
139 
140 /* Remove a descendant CGroup */
141 struct tst_cgroup_group *
142 tst_cgroup_group_rm(struct tst_cgroup_group *const cg)
143 		    __attribute__ ((nonnull, warn_unused_result));
144 
145 #define TST_CGROUP_VER(cg, ctrl_name) \
146 	tst_cgroup_ver(__FILE__, __LINE__, (cg), (ctrl_name))
147 
148 enum tst_cgroup_ver tst_cgroup_ver(const char *const file, const int lineno,
149 				   const struct tst_cgroup_group *const cg,
150 				   const char *const ctrl_name)
151 				   __attribute__ ((nonnull, warn_unused_result));
152 
153 #define TST_CGROUP_VER_IS_V1(cg, ctrl_name) \
154 	(TST_CGROUP_VER((cg), (ctrl_name)) == TST_CGROUP_V1)
155 
156 #define SAFE_CGROUP_HAS(cg, file_name) \
157 	safe_cgroup_has(__FILE__, __LINE__, (cg), (file_name))
158 
159 int safe_cgroup_has(const char *const file, const int lineno,
160 		    const struct tst_cgroup_group *const cg,
161 		    const char *const file_name)
162 		    __attribute__ ((nonnull, warn_unused_result));
163 
164 #define SAFE_CGROUP_READ(cg, file_name, out, len)			\
165 	safe_cgroup_read(__FILE__, __LINE__,				\
166 			 (cg), (file_name), (out), (len))
167 
168 ssize_t safe_cgroup_read(const char *const file, const int lineno,
169 			 const struct tst_cgroup_group *const cg,
170 			 const char *const file_name,
171 			 char *const out, const size_t len)
172 			 __attribute__ ((nonnull));
173 
174 #define SAFE_CGROUP_PRINTF(cg, file_name, fmt, ...)			\
175 	safe_cgroup_printf(__FILE__, __LINE__,				\
176 			   (cg), (file_name), (fmt), __VA_ARGS__)
177 
178 #define SAFE_CGROUP_PRINT(cg, file_name, str)				\
179 	safe_cgroup_printf(__FILE__, __LINE__, (cg), (file_name), "%s", (str))
180 
181 void safe_cgroup_printf(const char *const file, const int lineno,
182 			const struct tst_cgroup_group *const cg,
183 			const char *const file_name,
184 			const char *const fmt, ...)
185 			__attribute__ ((format (printf, 5, 6), nonnull));
186 
187 #define SAFE_CGROUP_SCANF(cg, file_name, fmt, ...)			\
188 	safe_cgroup_scanf(__FILE__, __LINE__,				\
189 			  (cg), (file_name), (fmt), __VA_ARGS__)
190 
191 void safe_cgroup_scanf(const char *file, const int lineno,
192 		       const struct tst_cgroup_group *const cg,
193 		       const char *const file_name,
194 		       const char *const fmt, ...)
195 		       __attribute__ ((format (scanf, 5, 6), nonnull));
196 
197 #define SAFE_CGROUP_LINES_SCANF(cg, file_name, fmt, ...)		\
198 	safe_cgroup_lines_scanf(__FILE__, __LINE__,			\
199 				(cg), (file_name), (fmt), __VA_ARGS__)
200 
201 void safe_cgroup_lines_scanf(const char *const file, const int lineno,
202 			     const struct tst_cgroup_group *const cg,
203 			     const char *const file_name,
204 			     const char *const fmt, ...)
205 			__attribute__ ((format (scanf, 5, 6), nonnull));
206 
207 #define SAFE_CGROUP_OCCURSIN(cg, file_name, needle)		\
208 	safe_cgroup_occursin(__FILE__, __LINE__,		\
209 			     (cg), (file_name), (needle))
210 
211 int safe_cgroup_occursin(const char *file, const int lineno,
212 			 const struct tst_cgroup_group *const cg,
213 			 const char *const file_name,
214 			 const char *const needle);
215 
216 #endif /* TST_CGROUP_H */
217