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