1 // SPDX-License-Identifier: GPL-2.0-or-later 2 /* 3 * Copyright (c) 2020 Red Hat, Inc. 4 * Copyright (c) 2020 Li Wang <[email protected]> 5 * Copyright (c) 2020-2021 SUSE LLC <[email protected]> 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_cg_ver { 88 TST_CG_V1 = 1, 89 TST_CG_V2 = 2, 90 }; 91 92 /* This value is greater than ROOTS_MAX in tst_cgroup.c. */ 93 #define TST_CG_ROOTS_MAX 32 94 95 /* Used to specify CGroup hierarchy configuration options, allowing a 96 * test to request a particular CGroup structure. 97 */ 98 struct tst_cg_opts { 99 /* Call tst_brk with TCONF if the controller is not on this 100 * version. Defautls to zero to accept any version. 101 */ 102 enum tst_cg_ver needs_ver; 103 /* Pass in a specific pid to create and identify the test 104 * directory as opposed to the default pid of the calling process. 105 */ 106 int test_pid; 107 int needs_nsdelegate; 108 }; 109 110 /* A Control Group in LTP's aggregated hierarchy */ 111 struct tst_cg_group; 112 113 /* Populated with a reference to this tests's CGroup */ 114 extern const struct tst_cg_group *const tst_cg; 115 extern const struct tst_cg_group *const tst_cg_drain; 116 117 /* Search the system for mounted cgroups and available 118 * controllers. Called automatically by tst_cg_require. 119 */ 120 void tst_cg_scan(void); 121 /* Print the config detected by tst_cg_scan and print the internal 122 * state associated with each controller. Output can be passed to 123 * tst_cg_load_config to configure the internal state to that of the 124 * config between program invocations. 125 */ 126 void tst_cg_print_config(void); 127 128 /* Load the config printed out by tst_cg_print_config and configure the internal 129 * libary state to match the config. Used to allow tst_cg_cleanup to properly 130 * cleanup mounts and directories created by tst_cg_require between program 131 * invocations. 132 */ 133 void tst_cg_load_config(const char *const config); 134 135 /* Ensure the specified controller is available in the test's default 136 * CGroup, mounting/enabling it if necessary. Usually this is not 137 * necessary use tst_test.needs_cgroup_ctrls instead. 138 */ 139 void tst_cg_require(const char *const ctrl_name, 140 const struct tst_cg_opts *const options) 141 __attribute__ ((nonnull)); 142 143 /* Tear down any CGroups created by calls to tst_cg_require */ 144 void tst_cg_cleanup(void); 145 146 /* Call this in setup after you call tst_cg_require and want to 147 * initialize tst_cg and tst_cg_drain. See tst_cg_require. 148 */ 149 void tst_cg_init(void); 150 151 /* Create a descendant CGroup */ 152 struct tst_cg_group * 153 tst_cg_group_mk(const struct tst_cg_group *const parent, 154 const char *const group_name_fmt, ...) 155 __attribute__ ((nonnull, warn_unused_result, format (printf, 2, 3))); 156 157 const char * 158 tst_cg_group_name(const struct tst_cg_group *const cg) 159 __attribute__ ((nonnull, warn_unused_result)); 160 161 /* This call returns a fd pointing to a v2 directory */ 162 int tst_cg_group_unified_dir_fd(const struct tst_cg_group *const cg) 163 __attribute__ ((nonnull, warn_unused_result)); 164 165 /* Remove a descendant CGroup */ 166 struct tst_cg_group * 167 tst_cg_group_rm(struct tst_cg_group *const cg) 168 __attribute__ ((nonnull, warn_unused_result)); 169 170 #define TST_CG_VER(cg, ctrl_name) \ 171 tst_cg_ver(__FILE__, __LINE__, (cg), (ctrl_name)) 172 173 enum tst_cg_ver tst_cg_ver(const char *const file, const int lineno, 174 const struct tst_cg_group *const cg, 175 const char *const ctrl_name) 176 __attribute__ ((nonnull, warn_unused_result)); 177 178 #define TST_CG_VER_IS_V1(cg, ctrl_name) \ 179 (TST_CG_VER((cg), (ctrl_name)) == TST_CG_V1) 180 181 #define SAFE_CG_HAS(cg, file_name) \ 182 safe_cg_has(__FILE__, __LINE__, (cg), (file_name)) 183 184 int safe_cg_has(const char *const file, const int lineno, 185 const struct tst_cg_group *const cg, 186 const char *const file_name) 187 __attribute__ ((nonnull, warn_unused_result)); 188 189 #define SAFE_CG_READ(cg, file_name, out, len) \ 190 safe_cg_read(__FILE__, __LINE__, \ 191 (cg), (file_name), (out), (len)) 192 193 ssize_t safe_cg_read(const char *const file, const int lineno, 194 const struct tst_cg_group *const cg, 195 const char *const file_name, 196 char *const out, const size_t len) 197 __attribute__ ((nonnull)); 198 199 #define SAFE_CG_PRINTF(cg, file_name, fmt, ...) \ 200 safe_cg_printf(__FILE__, __LINE__, \ 201 (cg), (file_name), (fmt), __VA_ARGS__) 202 203 #define SAFE_CG_PRINT(cg, file_name, str) \ 204 safe_cg_printf(__FILE__, __LINE__, (cg), (file_name), "%s", (str)) 205 206 void safe_cg_printf(const char *const file, const int lineno, 207 const struct tst_cg_group *const cg, 208 const char *const file_name, 209 const char *const fmt, ...) 210 __attribute__ ((format (printf, 5, 6), nonnull)); 211 212 #define SAFE_CG_OPEN(cg, file_name, flags, fds) \ 213 safe_cg_open(__FILE__, __LINE__, (cg), (file_name), (flags), (fds)) 214 215 int safe_cg_open(const char *const file, const int lineno, 216 const struct tst_cg_group *const cg, 217 const char *const file_name, 218 int flags, int *fds) 219 __attribute__ ((nonnull)); 220 221 #define SAFE_CG_FCHOWN(cg, file_name, owner, group) \ 222 safe_cg_fchown(__FILE__, __LINE__, (cg), (file_name), (owner), (group)) 223 224 void safe_cg_fchown(const char *const file, const int lineno, 225 const struct tst_cg_group *const cg, 226 const char *const file_name, 227 uid_t owner, gid_t group) 228 __attribute__ ((nonnull)); 229 230 #define SAFE_CG_SCANF(cg, file_name, fmt, ...) \ 231 safe_cg_scanf(__FILE__, __LINE__, \ 232 (cg), (file_name), (fmt), __VA_ARGS__) 233 234 void safe_cg_scanf(const char *file, const int lineno, 235 const struct tst_cg_group *const cg, 236 const char *const file_name, 237 const char *const fmt, ...) 238 __attribute__ ((format (scanf, 5, 6), nonnull)); 239 240 #define SAFE_CG_LINES_SCANF(cg, file_name, fmt, ...) \ 241 safe_cg_lines_scanf(__FILE__, __LINE__, \ 242 (cg), (file_name), (fmt), __VA_ARGS__) 243 244 void safe_cg_lines_scanf(const char *const file, const int lineno, 245 const struct tst_cg_group *const cg, 246 const char *const file_name, 247 const char *const fmt, ...) 248 __attribute__ ((format (scanf, 5, 6), nonnull)); 249 250 #define SAFE_CG_OCCURSIN(cg, file_name, needle) \ 251 safe_cg_occursin(__FILE__, __LINE__, \ 252 (cg), (file_name), (needle)) 253 254 int safe_cg_occursin(const char *file, const int lineno, 255 const struct tst_cg_group *const cg, 256 const char *const file_name, 257 const char *const needle); 258 259 #endif /* TST_CGROUP_H */ 260