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 /* Remove a descendant CGroup */ 137 struct tst_cgroup_group * 138 tst_cgroup_group_rm(struct tst_cgroup_group *const cg) 139 __attribute__ ((nonnull, warn_unused_result)); 140 141 #define TST_CGROUP_VER(cg, ctrl_name) \ 142 tst_cgroup_ver(__FILE__, __LINE__, (cg), (ctrl_name)) 143 144 enum tst_cgroup_ver tst_cgroup_ver(const char *const file, const int lineno, 145 const struct tst_cgroup_group *const cg, 146 const char *const ctrl_name) 147 __attribute__ ((nonnull, warn_unused_result)); 148 149 #define SAFE_CGROUP_HAS(cg, file_name) \ 150 safe_cgroup_has(__FILE__, __LINE__, (cg), (file_name)) 151 152 int safe_cgroup_has(const char *const file, const int lineno, 153 const struct tst_cgroup_group *const cg, 154 const char *const file_name) 155 __attribute__ ((nonnull, warn_unused_result)); 156 157 #define SAFE_CGROUP_READ(cg, file_name, out, len) \ 158 safe_cgroup_read(__FILE__, __LINE__, \ 159 (cg), (file_name), (out), (len)) 160 161 ssize_t safe_cgroup_read(const char *const file, const int lineno, 162 const struct tst_cgroup_group *const cg, 163 const char *const file_name, 164 char *const out, const size_t len) 165 __attribute__ ((nonnull)); 166 167 #define SAFE_CGROUP_PRINTF(cg, file_name, fmt, ...) \ 168 safe_cgroup_printf(__FILE__, __LINE__, \ 169 (cg), (file_name), (fmt), __VA_ARGS__) 170 171 #define SAFE_CGROUP_PRINT(cg, file_name, str) \ 172 safe_cgroup_printf(__FILE__, __LINE__, (cg), (file_name), "%s", (str)) 173 174 void safe_cgroup_printf(const char *const file, const int lineno, 175 const struct tst_cgroup_group *const cg, 176 const char *const file_name, 177 const char *const fmt, ...) 178 __attribute__ ((format (printf, 5, 6), nonnull)); 179 180 #define SAFE_CGROUP_SCANF(cg, file_name, fmt, ...) \ 181 safe_cgroup_scanf(__FILE__, __LINE__, \ 182 (cg), (file_name), (fmt), __VA_ARGS__) 183 184 void safe_cgroup_scanf(const char *file, const int lineno, 185 const struct tst_cgroup_group *const cg, 186 const char *const file_name, 187 const char *fmt, ...) 188 __attribute__ ((format (scanf, 5, 6), nonnull)); 189 190 191 #endif /* TST_CGROUP_H */ 192