-
Notifications
You must be signed in to change notification settings - Fork 1k
/
Copy pathtst_cgroup.h
259 lines (221 loc) · 9.63 KB
/
tst_cgroup.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2020 Red Hat, Inc.
* Copyright (c) 2020 Li Wang <[email protected]>
* Copyright (c) 2020-2021 SUSE LLC <[email protected]>
*/
/*\
* The LTP CGroups API tries to present a consistent interface to the
* many possible CGroup configurations a system could have.
*
* You may ask; "Why don't you just mount a simple CGroup hierarchy,
* instead of scanning the current setup?". The short answer is that
* it is not possible unless no CGroups are currently active and
* almost all of our users will have CGroups active. Even if
* unmounting the current CGroup hierarchy is a reasonable thing to do
* to the sytem manager, it is highly unlikely the CGroup hierarchy
* will be destroyed. So users would be forced to remove their CGroup
* configuration and reboot the system.
*
* The core library tries to ensure an LTP CGroup exists on each
* hierarchy root. Inside the LTP group it ensures a 'drain' group
* exists and creats a test group for the current test. In the worst
* case we end up with a set of hierarchies like the follwoing. Where
* existing system-manager-created CGroups have been omitted.
*
* (V2 Root) (V1 Root 1) ... (V1 Root N)
* | | |
* (ltp) (ltp) ... (ltp)
* / \ / \ / \
* (drain) (test-n) (drain) (test-n) ... (drain) (test-n)
*
* V2 CGroup controllers use a single unified hierarchy on a single
* root. Two or more V1 controllers may share a root or have their own
* root. However there may exist only one instance of a controller.
* So you can not have the same V1 controller on multiple roots.
*
* It is possible to have both a V2 hierarchy and V1 hierarchies
* active at the same time. Which is what is shown above. Any
* controllers attached to V1 hierarchies will not be available in the
* V2 hierarchy. The reverse is also true.
*
* Note that a single hierarchy may be mounted multiple
* times. Allowing it to be accessed at different locations. However
* subsequent mount operations will fail if the mount options are
* different from the first.
*
* The user may pre-create the CGroup hierarchies and the ltp CGroup,
* otherwise the library will try to create them. If the ltp group
* already exists and has appropriate permissions, then admin
* privileges will not be required to run the tests.
*
* Because the test may not have access to the CGroup root(s), the
* drain CGroup is created. This can be used to store processes which
* would otherwise block the destruction of the individual test CGroup
* or one of its descendants.
*
* The test author may create child CGroups within the test CGroup
* using the CGroup Item API. The library will create the new CGroup
* in all the relevant hierarchies.
*
* There are many differences between the V1 and V2 CGroup APIs. If a
* controller is on both V1 and V2, it may have different parameters
* and control files. Some of these control files have a different
* name, but similar functionality. In this case the Item API uses
* the V2 names and aliases them to the V1 name when appropriate.
*
* Some control files only exist on one of the versions or they can be
* missing due to other reasons. The Item API allows the user to check
* if the file exists before trying to use it.
*
* Often a control file has almost the same functionality between V1
* and V2. Which means it can be used in the same way most of the
* time, but not all. For now this is handled by exposing the API
* version a controller is using to allow the test author to handle
* edge cases. (e.g. V2 memory.swap.max accepts "max", but V1
* memory.memsw.limit_in_bytes does not).
*/
#ifndef TST_CGROUP_H
#define TST_CGROUP_H
#include <sys/types.h>
/* CGroups Kernel API version */
enum tst_cg_ver {
TST_CG_V1 = 1,
TST_CG_V2 = 2,
};
/* This value is greater than ROOTS_MAX in tst_cgroup.c. */
#define TST_CG_ROOTS_MAX 32
/* Used to specify CGroup hierarchy configuration options, allowing a
* test to request a particular CGroup structure.
*/
struct tst_cg_opts {
/* Call tst_brk with TCONF if the controller is not on this
* version. Defautls to zero to accept any version.
*/
enum tst_cg_ver needs_ver;
/* Pass in a specific pid to create and identify the test
* directory as opposed to the default pid of the calling process.
*/
int test_pid;
int needs_nsdelegate;
};
/* A Control Group in LTP's aggregated hierarchy */
struct tst_cg_group;
/* Populated with a reference to this tests's CGroup */
extern const struct tst_cg_group *const tst_cg;
extern const struct tst_cg_group *const tst_cg_drain;
/* Search the system for mounted cgroups and available
* controllers. Called automatically by tst_cg_require.
*/
void tst_cg_scan(void);
/* Print the config detected by tst_cg_scan and print the internal
* state associated with each controller. Output can be passed to
* tst_cg_load_config to configure the internal state to that of the
* config between program invocations.
*/
void tst_cg_print_config(void);
/* Load the config printed out by tst_cg_print_config and configure the internal
* libary state to match the config. Used to allow tst_cg_cleanup to properly
* cleanup mounts and directories created by tst_cg_require between program
* invocations.
*/
void tst_cg_load_config(const char *const config);
/* Ensure the specified controller is available in the test's default
* CGroup, mounting/enabling it if necessary. Usually this is not
* necessary use tst_test.needs_cgroup_ctrls instead.
*/
void tst_cg_require(const char *const ctrl_name,
const struct tst_cg_opts *const options)
__attribute__ ((nonnull));
/* Tear down any CGroups created by calls to tst_cg_require */
void tst_cg_cleanup(void);
/* Call this in setup after you call tst_cg_require and want to
* initialize tst_cg and tst_cg_drain. See tst_cg_require.
*/
void tst_cg_init(void);
/* Create a descendant CGroup */
struct tst_cg_group *
tst_cg_group_mk(const struct tst_cg_group *const parent,
const char *const group_name_fmt, ...)
__attribute__ ((nonnull, warn_unused_result, format (printf, 2, 3)));
const char *
tst_cg_group_name(const struct tst_cg_group *const cg)
__attribute__ ((nonnull, warn_unused_result));
/* This call returns a fd pointing to a v2 directory */
int tst_cg_group_unified_dir_fd(const struct tst_cg_group *const cg)
__attribute__ ((nonnull, warn_unused_result));
/* Remove a descendant CGroup */
struct tst_cg_group *
tst_cg_group_rm(struct tst_cg_group *const cg)
__attribute__ ((nonnull, warn_unused_result));
#define TST_CG_VER(cg, ctrl_name) \
tst_cg_ver(__FILE__, __LINE__, (cg), (ctrl_name))
enum tst_cg_ver tst_cg_ver(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const ctrl_name)
__attribute__ ((nonnull, warn_unused_result));
#define TST_CG_VER_IS_V1(cg, ctrl_name) \
(TST_CG_VER((cg), (ctrl_name)) == TST_CG_V1)
#define SAFE_CG_HAS(cg, file_name) \
safe_cg_has(__FILE__, __LINE__, (cg), (file_name))
int safe_cg_has(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name)
__attribute__ ((nonnull, warn_unused_result));
#define SAFE_CG_READ(cg, file_name, out, len) \
safe_cg_read(__FILE__, __LINE__, \
(cg), (file_name), (out), (len))
ssize_t safe_cg_read(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
char *const out, const size_t len)
__attribute__ ((nonnull));
#define SAFE_CG_PRINTF(cg, file_name, fmt, ...) \
safe_cg_printf(__FILE__, __LINE__, \
(cg), (file_name), (fmt), __VA_ARGS__)
#define SAFE_CG_PRINT(cg, file_name, str) \
safe_cg_printf(__FILE__, __LINE__, (cg), (file_name), "%s", (str))
void safe_cg_printf(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
const char *const fmt, ...)
__attribute__ ((format (printf, 5, 6), nonnull));
#define SAFE_CG_OPEN(cg, file_name, flags, fds) \
safe_cg_open(__FILE__, __LINE__, (cg), (file_name), (flags), (fds))
int safe_cg_open(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
int flags, int *fds)
__attribute__ ((nonnull));
#define SAFE_CG_FCHOWN(cg, file_name, owner, group) \
safe_cg_fchown(__FILE__, __LINE__, (cg), (file_name), (owner), (group))
void safe_cg_fchown(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
uid_t owner, gid_t group)
__attribute__ ((nonnull));
#define SAFE_CG_SCANF(cg, file_name, fmt, ...) \
safe_cg_scanf(__FILE__, __LINE__, \
(cg), (file_name), (fmt), __VA_ARGS__)
void safe_cg_scanf(const char *file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
const char *const fmt, ...)
__attribute__ ((format (scanf, 5, 6), nonnull));
#define SAFE_CG_LINES_SCANF(cg, file_name, fmt, ...) \
safe_cg_lines_scanf(__FILE__, __LINE__, \
(cg), (file_name), (fmt), __VA_ARGS__)
void safe_cg_lines_scanf(const char *const file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
const char *const fmt, ...)
__attribute__ ((format (scanf, 5, 6), nonnull));
#define SAFE_CG_OCCURSIN(cg, file_name, needle) \
safe_cg_occursin(__FILE__, __LINE__, \
(cg), (file_name), (needle))
int safe_cg_occursin(const char *file, const int lineno,
const struct tst_cg_group *const cg,
const char *const file_name,
const char *const needle);
int tst_cg_memory_recursiveprot(struct tst_cg_group *cg);
#endif /* TST_CGROUP_H */