1 /**
2 * collectd - src/utils_config_cores.c
3 *
4 * Copyright(c) 2018 Intel Corporation. All rights reserved.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 *
24 * Authors:
25 * Kamil Wiatrowski <kamilx.wiatrowski@intel.com>
26 **/
27
28 #include "collectd.h"
29
30 #include "utils/common/common.h"
31
32 #include "utils/config_cores/config_cores.h"
33
34 #define UTIL_NAME "utils_config_cores"
35
36 #define MAX_SOCKETS 8
37 #define MAX_SOCKET_CORES 64
38 #define MAX_CORES (MAX_SOCKET_CORES * MAX_SOCKETS)
39
is_in_list(unsigned val,const unsigned * list,size_t len)40 static inline _Bool is_in_list(unsigned val, const unsigned *list, size_t len) {
41 for (size_t i = 0; i < len; i++)
42 if (list[i] == val)
43 return 1;
44 return 0;
45 }
46
str_to_uint(const char * s,unsigned * n)47 static int str_to_uint(const char *s, unsigned *n) {
48 if (s == NULL || n == NULL)
49 return -EINVAL;
50 char *endptr = NULL;
51
52 *n = (unsigned)strtoul(s, &endptr, 0);
53 if (*s == '\0' || *endptr != '\0') {
54 ERROR(UTIL_NAME ": Failed to parse '%s' into unsigned number", s);
55 return -EINVAL;
56 }
57
58 return 0;
59 }
60
61 /*
62 * NAME
63 * str_list_to_nums
64 *
65 * DESCRIPTION
66 * Converts string of characters representing list of numbers into array of
67 * numbers. Allowed formats are:
68 * 0,1,2,3
69 * 0-10,20-18
70 * 1,3,5-8,10,0x10-12
71 *
72 * Numbers can be in decimal or hexadecimal format.
73 *
74 * PARAMETERS
75 * `s' String representing list of unsigned numbers.
76 * `nums' Array to put converted numeric values into.
77 * `nums_len' Maximum number of elements that nums can accommodate.
78 *
79 * RETURN VALUE
80 * Number of elements placed into nums.
81 */
str_list_to_nums(char * s,unsigned * nums,size_t nums_len)82 static size_t str_list_to_nums(char *s, unsigned *nums, size_t nums_len) {
83 char *saveptr = NULL;
84 char *token;
85 size_t idx = 0;
86
87 while ((token = strtok_r(s, ",", &saveptr))) {
88 char *pos;
89 unsigned start, end = 0;
90 s = NULL;
91
92 while (isspace(*token))
93 token++;
94 if (*token == '\0')
95 continue;
96
97 pos = strchr(token, '-');
98 if (pos) {
99 *pos = '\0';
100 }
101
102 if (str_to_uint(token, &start))
103 return 0;
104
105 if (pos) {
106 if (str_to_uint(pos + 1, &end))
107 return 0;
108 } else {
109 end = start;
110 }
111
112 if (start > end) {
113 unsigned swap = start;
114 start = end;
115 end = swap;
116 }
117
118 for (unsigned i = start; i <= end; i++) {
119 if (is_in_list(i, nums, idx))
120 continue;
121 if (idx >= nums_len) {
122 WARNING(UTIL_NAME ": exceeded the cores number limit: %" PRIsz,
123 nums_len);
124 return idx;
125 }
126 nums[idx] = i;
127 idx++;
128 }
129 }
130 return idx;
131 }
132
133 /*
134 * NAME
135 * check_core_grouping
136 *
137 * DESCRIPTION
138 * Look for [...] brackets in *in string and if found copy the
139 * part between brackets into *out string and set grouped to 0.
140 * Otherwise grouped is set to 1 and input is copied without leading
141 * whitespaces.
142 *
143 * PARAMETERS
144 * `out' Output string to store result.
145 * `in' Input string to be parsed and copied.
146 * `out_size' Maximum number of elements that out can accommodate.
147 * `grouped' Set by function depending if cores should be grouped or not.
148 *
149 * RETURN VALUE
150 * Zero upon success or non-zero if an error occurred.
151 */
check_core_grouping(char * out,const char * in,size_t out_size,_Bool * grouped)152 static int check_core_grouping(char *out, const char *in, size_t out_size,
153 _Bool *grouped) {
154 const char *start = in;
155 char *end;
156 while (isspace(*start))
157 ++start;
158 if (start[0] == '[') {
159 *grouped = 0;
160 ++start;
161 end = strchr(start, ']');
162 if (end == NULL) {
163 ERROR(UTIL_NAME ": Missing closing bracket ] in option %s.", in);
164 return -EINVAL;
165 }
166 if ((size_t)(end - start) >= out_size) {
167 ERROR(UTIL_NAME ": Out buffer is too small.");
168 return -EINVAL;
169 }
170 sstrncpy(out, start, end - start + 1);
171 DEBUG(UTIL_NAME ": Mask for individual (not aggregated) cores: %s", out);
172 } else {
173 *grouped = 1;
174 sstrncpy(out, start, out_size);
175 }
176 return 0;
177 }
178
config_cores_parse(const oconfig_item_t * ci,core_groups_list_t * cgl)179 int config_cores_parse(const oconfig_item_t *ci, core_groups_list_t *cgl) {
180 if (ci == NULL || cgl == NULL)
181 return -EINVAL;
182 if (ci->values_num == 0 || ci->values_num > MAX_CORES)
183 return -EINVAL;
184 core_group_t cgroups[MAX_CORES] = {{0}};
185 size_t cg_idx = 0; /* index for cgroups array */
186 int ret = 0;
187
188 for (int i = 0; i < ci->values_num; i++) {
189 if (ci->values[i].type != OCONFIG_TYPE_STRING) {
190 WARNING(UTIL_NAME ": The %s option requires string arguments.", ci->key);
191 return -EINVAL;
192 }
193 }
194
195 if (ci->values_num == 1 && ci->values[0].value.string &&
196 strlen(ci->values[0].value.string) == 0)
197 return 0;
198
199 for (int i = 0; i < ci->values_num; i++) {
200 size_t n;
201 _Bool grouped = 1;
202 char str[DATA_MAX_NAME_LEN];
203 unsigned cores[MAX_CORES] = {0};
204
205 if (cg_idx >= STATIC_ARRAY_SIZE(cgroups)) {
206 ERROR(UTIL_NAME
207 ": Configuration exceeds maximum number of cores: %" PRIsz,
208 STATIC_ARRAY_SIZE(cgroups));
209 ret = -EINVAL;
210 goto parse_error;
211 }
212 if ((ci->values[i].value.string == NULL) ||
213 (strlen(ci->values[i].value.string) == 0)) {
214 ERROR(UTIL_NAME ": Failed to parse parameters for %s option.", ci->key);
215 ret = -EINVAL;
216 goto parse_error;
217 }
218
219 ret = check_core_grouping(str, ci->values[i].value.string, sizeof(str),
220 &grouped);
221 if (ret != 0) {
222 ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
223 ci->values[i].value.string);
224 goto parse_error;
225 }
226 n = str_list_to_nums(str, cores, STATIC_ARRAY_SIZE(cores));
227 if (n == 0) {
228 ERROR(UTIL_NAME ": Failed to parse config option [%d] %s.", i,
229 ci->values[i].value.string);
230 ret = -EINVAL;
231 goto parse_error;
232 }
233
234 if (grouped) {
235 cgroups[cg_idx].desc = strdup(ci->values[i].value.string);
236 if (cgroups[cg_idx].desc == NULL) {
237 ERROR(UTIL_NAME ": Failed to allocate description.");
238 ret = -ENOMEM;
239 goto parse_error;
240 }
241
242 cgroups[cg_idx].cores = calloc(n, sizeof(*cgroups[cg_idx].cores));
243 if (cgroups[cg_idx].cores == NULL) {
244 ERROR(UTIL_NAME ": Failed to allocate cores for cgroup.");
245 ret = -ENOMEM;
246 goto parse_error;
247 }
248
249 for (size_t j = 0; j < n; j++)
250 cgroups[cg_idx].cores[j] = cores[j];
251
252 cgroups[cg_idx].num_cores = n;
253 cg_idx++;
254 } else {
255 for (size_t j = 0; j < n && cg_idx < STATIC_ARRAY_SIZE(cgroups); j++) {
256 char desc[DATA_MAX_NAME_LEN];
257 ssnprintf(desc, sizeof(desc), "%u", cores[j]);
258
259 cgroups[cg_idx].desc = strdup(desc);
260 if (cgroups[cg_idx].desc == NULL) {
261 ERROR(UTIL_NAME ": Failed to allocate desc for core %u.", cores[j]);
262 ret = -ENOMEM;
263 goto parse_error;
264 }
265
266 cgroups[cg_idx].cores = calloc(1, sizeof(*(cgroups[cg_idx].cores)));
267 if (cgroups[cg_idx].cores == NULL) {
268 ERROR(UTIL_NAME ": Failed to allocate cgroup for core %u.", cores[j]);
269 ret = -ENOMEM;
270 goto parse_error;
271 }
272 cgroups[cg_idx].num_cores = 1;
273 cgroups[cg_idx].cores[0] = cores[j];
274 cg_idx++;
275 }
276 }
277 }
278
279 cgl->cgroups = calloc(cg_idx, sizeof(*cgl->cgroups));
280 if (cgl->cgroups == NULL) {
281 ERROR(UTIL_NAME ": Failed to allocate core groups.");
282 ret = -ENOMEM;
283 goto parse_error;
284 }
285
286 cgl->num_cgroups = cg_idx;
287 for (size_t i = 0; i < cg_idx; i++)
288 cgl->cgroups[i] = cgroups[i];
289
290 return 0;
291
292 parse_error:
293
294 cg_idx = 0;
295 while (cg_idx < STATIC_ARRAY_SIZE(cgroups) && cgroups[cg_idx].desc != NULL) {
296 sfree(cgroups[cg_idx].desc);
297 sfree(cgroups[cg_idx].cores);
298 cg_idx++;
299 }
300 return ret;
301 }
302
config_cores_default(int num_cores,core_groups_list_t * cgl)303 int config_cores_default(int num_cores, core_groups_list_t *cgl) {
304 if (cgl == NULL || num_cores < 0 || num_cores > MAX_CORES)
305 return -EINVAL;
306
307 cgl->cgroups = calloc(num_cores, sizeof(*(cgl->cgroups)));
308 if (cgl->cgroups == NULL) {
309 ERROR(UTIL_NAME ": Failed to allocate memory for core groups.");
310 return -ENOMEM;
311 }
312 cgl->num_cgroups = num_cores;
313
314 for (int i = 0; i < num_cores; i++) {
315 char desc[DATA_MAX_NAME_LEN];
316 ssnprintf(desc, sizeof(desc), "%d", i);
317
318 cgl->cgroups[i].cores = calloc(1, sizeof(*(cgl->cgroups[i].cores)));
319 if (cgl->cgroups[i].cores == NULL) {
320 ERROR(UTIL_NAME ": Failed to allocate default cores for cgroup %d.", i);
321 config_cores_cleanup(cgl);
322 return -ENOMEM;
323 }
324 cgl->cgroups[i].num_cores = 1;
325 cgl->cgroups[i].cores[0] = i;
326
327 cgl->cgroups[i].desc = strdup(desc);
328 if (cgl->cgroups[i].desc == NULL) {
329 ERROR(UTIL_NAME ": Failed to allocate description for cgroup %d.", i);
330 config_cores_cleanup(cgl);
331 return -ENOMEM;
332 }
333 }
334 return 0;
335 }
336
config_cores_cleanup(core_groups_list_t * cgl)337 void config_cores_cleanup(core_groups_list_t *cgl) {
338 if (cgl == NULL)
339 return;
340 for (size_t i = 0; i < cgl->num_cgroups; i++) {
341 sfree(cgl->cgroups[i].desc);
342 sfree(cgl->cgroups[i].cores);
343 }
344 sfree(cgl->cgroups);
345 cgl->num_cgroups = 0;
346 }
347
config_cores_cmp_cgroups(const core_group_t * cg_a,const core_group_t * cg_b)348 int config_cores_cmp_cgroups(const core_group_t *cg_a,
349 const core_group_t *cg_b) {
350 size_t found = 0;
351
352 assert(cg_a != NULL);
353 assert(cg_b != NULL);
354
355 const size_t sz_a = cg_a->num_cores;
356 const size_t sz_b = cg_b->num_cores;
357 const unsigned *tab_a = cg_a->cores;
358 const unsigned *tab_b = cg_b->cores;
359
360 for (size_t i = 0; i < sz_a; i++)
361 if (is_in_list(tab_a[i], tab_b, sz_b))
362 found++;
363
364 /* if no cores are the same */
365 if (!found)
366 return 0;
367 /* if group contains same cores */
368 if (sz_a == sz_b && sz_b == found)
369 return 1;
370 /* if not all cores are the same */
371 return -1;
372 }
373