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