Commit | Line | Data |
---|---|---|
591a6e85 SPP |
1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* | |
3 | * Basic resctrl file system operations | |
4 | * | |
5 | * Copyright (C) 2018 Intel Corporation | |
6 | * | |
7 | * Authors: | |
8 | * Sai Praneeth Prakhya <sai.praneeth.prakhya@intel.com>, | |
9 | * Fenghua Yu <fenghua.yu@intel.com> | |
10 | */ | |
20d96b25 | 11 | #include <fcntl.h> |
d56e5da0 IJ |
12 | #include <limits.h> |
13 | ||
591a6e85 SPP |
14 | #include "resctrl.h" |
15 | ||
591a6e85 SPP |
16 | static int find_resctrl_mount(char *buffer) |
17 | { | |
18 | FILE *mounts; | |
19 | char line[256], *fs, *mntpoint; | |
20 | ||
21 | mounts = fopen("/proc/mounts", "r"); | |
22 | if (!mounts) { | |
cc8ff7f5 | 23 | ksft_perror("/proc/mounts"); |
591a6e85 SPP |
24 | return -ENXIO; |
25 | } | |
26 | while (!feof(mounts)) { | |
27 | if (!fgets(line, 256, mounts)) | |
28 | break; | |
29 | fs = strtok(line, " \t"); | |
30 | if (!fs) | |
31 | continue; | |
32 | mntpoint = strtok(NULL, " \t"); | |
33 | if (!mntpoint) | |
34 | continue; | |
35 | fs = strtok(NULL, " \t"); | |
36 | if (!fs) | |
37 | continue; | |
38 | if (strcmp(fs, "resctrl")) | |
39 | continue; | |
40 | ||
41 | fclose(mounts); | |
42 | if (buffer) | |
43 | strncpy(buffer, mntpoint, 256); | |
44 | ||
45 | return 0; | |
46 | } | |
47 | ||
48 | fclose(mounts); | |
49 | ||
50 | return -ENOENT; | |
51 | } | |
52 | ||
53 | /* | |
6383851a | 54 | * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl |
591a6e85 | 55 | * |
6383851a IJ |
56 | * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid |
57 | * pre-existing settings interfering with the test results. | |
591a6e85 | 58 | * |
34813938 | 59 | * Return: 0 on success, < 0 on error. |
591a6e85 | 60 | */ |
6383851a | 61 | int mount_resctrlfs(void) |
591a6e85 | 62 | { |
591a6e85 SPP |
63 | int ret; |
64 | ||
6383851a IJ |
65 | ret = find_resctrl_mount(NULL); |
66 | if (ret != -ENOENT) | |
67 | return -1; | |
591a6e85 | 68 | |
ca2f4214 | 69 | ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH); |
591a6e85 | 70 | ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL); |
591a6e85 | 71 | if (ret) |
cc8ff7f5 | 72 | ksft_perror("mount"); |
591a6e85 | 73 | |
591a6e85 SPP |
74 | return ret; |
75 | } | |
76 | ||
77 | int umount_resctrlfs(void) | |
78 | { | |
ede13008 IJ |
79 | char mountpoint[256]; |
80 | int ret; | |
81 | ||
82 | ret = find_resctrl_mount(mountpoint); | |
83 | if (ret == -ENOENT) | |
4e5cb354 | 84 | return 0; |
ede13008 IJ |
85 | if (ret) |
86 | return ret; | |
4e5cb354 | 87 | |
ede13008 | 88 | if (umount(mountpoint)) { |
cc8ff7f5 | 89 | ksft_perror("Unable to umount resctrl"); |
591a6e85 | 90 | |
c90fba60 | 91 | return -1; |
591a6e85 SPP |
92 | } |
93 | ||
94 | return 0; | |
95 | } | |
96 | ||
e73dda7f IJ |
97 | /* |
98 | * get_cache_level - Convert cache level from string to integer | |
99 | * @cache_type: Cache level as string | |
100 | * | |
101 | * Return: cache level as integer or -1 if @cache_type is invalid. | |
102 | */ | |
103 | static int get_cache_level(const char *cache_type) | |
104 | { | |
105 | if (!strcmp(cache_type, "L3")) | |
106 | return 3; | |
107 | if (!strcmp(cache_type, "L2")) | |
108 | return 2; | |
109 | ||
110 | ksft_print_msg("Invalid cache level\n"); | |
111 | return -1; | |
112 | } | |
113 | ||
591a6e85 | 114 | /* |
6874f6ed | 115 | * get_domain_id - Get resctrl domain ID for a specified CPU |
591a6e85 | 116 | * @cpu_no: CPU number |
6874f6ed | 117 | * @domain_id: domain ID (cache ID; for MB, L3 cache ID) |
591a6e85 SPP |
118 | * |
119 | * Return: >= 0 on success, < 0 on failure. | |
120 | */ | |
6874f6ed | 121 | int get_domain_id(int cpu_no, int *domain_id) |
591a6e85 SPP |
122 | { |
123 | char phys_pkg_path[1024]; | |
124 | FILE *fp; | |
125 | ||
6220f69e | 126 | if (get_vendor() == ARCH_AMD) |
c0327e1d BM |
127 | sprintf(phys_pkg_path, "%s%d/cache/index3/id", |
128 | PHYS_ID_PATH, cpu_no); | |
129 | else | |
130 | sprintf(phys_pkg_path, "%s%d/topology/physical_package_id", | |
131 | PHYS_ID_PATH, cpu_no); | |
132 | ||
591a6e85 SPP |
133 | fp = fopen(phys_pkg_path, "r"); |
134 | if (!fp) { | |
cc8ff7f5 | 135 | ksft_perror("Failed to open physical_package_id"); |
591a6e85 SPP |
136 | |
137 | return -1; | |
138 | } | |
6874f6ed IJ |
139 | if (fscanf(fp, "%d", domain_id) <= 0) { |
140 | ksft_perror("Could not get domain ID"); | |
591a6e85 SPP |
141 | fclose(fp); |
142 | ||
143 | return -1; | |
144 | } | |
145 | fclose(fp); | |
146 | ||
147 | return 0; | |
148 | } | |
149 | ||
78941183 FY |
150 | /* |
151 | * get_cache_size - Get cache size for a specified CPU | |
152 | * @cpu_no: CPU number | |
153 | * @cache_type: Cache level L2/L3 | |
154 | * @cache_size: pointer to cache_size | |
155 | * | |
156 | * Return: = 0 on success, < 0 on failure. | |
157 | */ | |
60c2a692 | 158 | int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size) |
78941183 FY |
159 | { |
160 | char cache_path[1024], cache_str[64]; | |
161 | int length, i, cache_num; | |
162 | FILE *fp; | |
163 | ||
e73dda7f IJ |
164 | cache_num = get_cache_level(cache_type); |
165 | if (cache_num < 0) | |
166 | return cache_num; | |
78941183 FY |
167 | |
168 | sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", | |
169 | cpu_no, cache_num); | |
170 | fp = fopen(cache_path, "r"); | |
171 | if (!fp) { | |
cc8ff7f5 | 172 | ksft_perror("Failed to open cache size"); |
78941183 FY |
173 | |
174 | return -1; | |
175 | } | |
176 | if (fscanf(fp, "%s", cache_str) <= 0) { | |
cc8ff7f5 | 177 | ksft_perror("Could not get cache_size"); |
78941183 FY |
178 | fclose(fp); |
179 | ||
180 | return -1; | |
181 | } | |
182 | fclose(fp); | |
183 | ||
184 | length = (int)strlen(cache_str); | |
185 | ||
186 | *cache_size = 0; | |
187 | ||
188 | for (i = 0; i < length; i++) { | |
189 | if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) | |
190 | ||
191 | *cache_size = *cache_size * 10 + (cache_str[i] - '0'); | |
192 | ||
193 | else if (cache_str[i] == 'K') | |
194 | ||
195 | *cache_size = *cache_size * 1024; | |
196 | ||
197 | else if (cache_str[i] == 'M') | |
198 | ||
199 | *cache_size = *cache_size * 1024 * 1024; | |
200 | ||
201 | else | |
202 | break; | |
203 | } | |
204 | ||
205 | return 0; | |
206 | } | |
207 | ||
208 | #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" | |
209 | ||
210 | /* | |
4b357e2a IJ |
211 | * get_bit_mask - Get bit mask from given file |
212 | * @filename: File containing the mask | |
213 | * @mask: The bit mask returned as unsigned long | |
78941183 FY |
214 | * |
215 | * Return: = 0 on success, < 0 on failure. | |
216 | */ | |
4b357e2a | 217 | static int get_bit_mask(const char *filename, unsigned long *mask) |
78941183 | 218 | { |
78941183 FY |
219 | FILE *fp; |
220 | ||
4b357e2a | 221 | if (!filename || !mask) |
8236c51d FY |
222 | return -1; |
223 | ||
4b357e2a | 224 | fp = fopen(filename, "r"); |
78941183 | 225 | if (!fp) { |
4b357e2a IJ |
226 | ksft_print_msg("Failed to open bit mask file '%s': %s\n", |
227 | filename, strerror(errno)); | |
78941183 FY |
228 | return -1; |
229 | } | |
4b357e2a IJ |
230 | |
231 | if (fscanf(fp, "%lx", mask) <= 0) { | |
232 | ksft_print_msg("Could not read bit mask file '%s': %s\n", | |
233 | filename, strerror(errno)); | |
78941183 FY |
234 | fclose(fp); |
235 | ||
236 | return -1; | |
237 | } | |
238 | fclose(fp); | |
239 | ||
240 | return 0; | |
241 | } | |
242 | ||
b6dfac94 IJ |
243 | /* |
244 | * create_bit_mask- Create bit mask from start, len pair | |
245 | * @start: LSB of the mask | |
246 | * @len Number of bits in the mask | |
247 | */ | |
248 | unsigned long create_bit_mask(unsigned int start, unsigned int len) | |
249 | { | |
250 | return ((1UL << len) - 1UL) << start; | |
251 | } | |
252 | ||
253 | /* | |
254 | * count_contiguous_bits - Returns the longest train of bits in a bit mask | |
255 | * @val A bit mask | |
256 | * @start The location of the least-significant bit of the longest train | |
257 | * | |
258 | * Return: The length of the contiguous bits in the longest train of bits | |
259 | */ | |
205de6dd | 260 | unsigned int count_contiguous_bits(unsigned long val, unsigned int *start) |
b6dfac94 IJ |
261 | { |
262 | unsigned long last_val; | |
263 | unsigned int count = 0; | |
264 | ||
265 | while (val) { | |
266 | last_val = val; | |
267 | val &= (val >> 1); | |
268 | count++; | |
269 | } | |
270 | ||
271 | if (start) { | |
272 | if (count) | |
273 | *start = ffsl(last_val) - 1; | |
274 | else | |
275 | *start = 0; | |
276 | } | |
277 | ||
278 | return count; | |
279 | } | |
280 | ||
4b357e2a IJ |
281 | /* |
282 | * get_full_cbm - Get full Cache Bit Mask (CBM) | |
283 | * @cache_type: Cache type as "L2" or "L3" | |
284 | * @mask: Full cache bit mask representing the maximal portion of cache | |
285 | * available for allocation, returned as unsigned long. | |
286 | * | |
287 | * Return: = 0 on success, < 0 on failure. | |
288 | */ | |
289 | int get_full_cbm(const char *cache_type, unsigned long *mask) | |
290 | { | |
291 | char cbm_path[PATH_MAX]; | |
292 | int ret; | |
293 | ||
294 | if (!cache_type) | |
295 | return -1; | |
296 | ||
297 | snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask", | |
298 | INFO_PATH, cache_type); | |
299 | ||
300 | ret = get_bit_mask(cbm_path, mask); | |
301 | if (ret || !*mask) | |
302 | return -1; | |
303 | ||
304 | return 0; | |
305 | } | |
306 | ||
b6dfac94 IJ |
307 | /* |
308 | * get_shareable_mask - Get shareable mask from shareable_bits | |
309 | * @cache_type: Cache type as "L2" or "L3" | |
310 | * @shareable_mask: Shareable mask returned as unsigned long | |
311 | * | |
312 | * Return: = 0 on success, < 0 on failure. | |
313 | */ | |
314 | static int get_shareable_mask(const char *cache_type, unsigned long *shareable_mask) | |
315 | { | |
316 | char mask_path[PATH_MAX]; | |
317 | ||
318 | if (!cache_type) | |
319 | return -1; | |
320 | ||
321 | snprintf(mask_path, sizeof(mask_path), "%s/%s/shareable_bits", | |
322 | INFO_PATH, cache_type); | |
323 | ||
324 | return get_bit_mask(mask_path, shareable_mask); | |
325 | } | |
326 | ||
327 | /* | |
328 | * get_mask_no_shareable - Get Cache Bit Mask (CBM) without shareable bits | |
329 | * @cache_type: Cache type as "L2" or "L3" | |
330 | * @mask: The largest exclusive portion of the cache out of the | |
331 | * full CBM, returned as unsigned long | |
332 | * | |
333 | * Parts of a cache may be shared with other devices such as GPU. This function | |
334 | * calculates the largest exclusive portion of the cache where no other devices | |
335 | * besides CPU have access to the cache portion. | |
336 | * | |
337 | * Return: = 0 on success, < 0 on failure. | |
338 | */ | |
339 | int get_mask_no_shareable(const char *cache_type, unsigned long *mask) | |
340 | { | |
341 | unsigned long full_mask, shareable_mask; | |
342 | unsigned int start, len; | |
343 | ||
344 | if (get_full_cbm(cache_type, &full_mask) < 0) | |
345 | return -1; | |
346 | if (get_shareable_mask(cache_type, &shareable_mask) < 0) | |
347 | return -1; | |
348 | ||
349 | len = count_contiguous_bits(full_mask & ~shareable_mask, &start); | |
350 | if (!len) | |
351 | return -1; | |
352 | ||
353 | *mask = create_bit_mask(start, len); | |
354 | ||
355 | return 0; | |
356 | } | |
357 | ||
591a6e85 SPP |
358 | /* |
359 | * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu | |
6c8cb747 IJ |
360 | * @bm_pid: PID that should be binded |
361 | * @cpu_no: CPU number at which the PID would be binded | |
362 | * @old_affinity: When not NULL, set to old CPU affinity | |
591a6e85 | 363 | * |
34813938 | 364 | * Return: 0 on success, < 0 on error. |
591a6e85 | 365 | */ |
6c8cb747 | 366 | int taskset_benchmark(pid_t bm_pid, int cpu_no, cpu_set_t *old_affinity) |
591a6e85 SPP |
367 | { |
368 | cpu_set_t my_set; | |
369 | ||
6c8cb747 IJ |
370 | if (old_affinity) { |
371 | CPU_ZERO(old_affinity); | |
372 | if (sched_getaffinity(bm_pid, sizeof(*old_affinity), | |
373 | old_affinity)) { | |
374 | ksft_perror("Unable to read CPU affinity"); | |
375 | return -1; | |
376 | } | |
377 | } | |
378 | ||
591a6e85 SPP |
379 | CPU_ZERO(&my_set); |
380 | CPU_SET(cpu_no, &my_set); | |
381 | ||
382 | if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) { | |
cc8ff7f5 | 383 | ksft_perror("Unable to taskset benchmark"); |
591a6e85 SPP |
384 | |
385 | return -1; | |
386 | } | |
387 | ||
388 | return 0; | |
389 | } | |
390 | ||
6c8cb747 IJ |
391 | /* |
392 | * taskset_restore - Taskset PID to the earlier CPU affinity | |
393 | * @bm_pid: PID that should be reset | |
394 | * @old_affinity: The old CPU affinity to restore | |
395 | * | |
396 | * Return: 0 on success, < 0 on error. | |
397 | */ | |
398 | int taskset_restore(pid_t bm_pid, cpu_set_t *old_affinity) | |
399 | { | |
400 | if (sched_setaffinity(bm_pid, sizeof(*old_affinity), old_affinity)) { | |
401 | ksft_perror("Unable to restore CPU affinity"); | |
402 | return -1; | |
403 | } | |
404 | ||
405 | return 0; | |
406 | } | |
407 | ||
591a6e85 SPP |
408 | /* |
409 | * create_grp - Create a group only if one doesn't exist | |
410 | * @grp_name: Name of the group | |
411 | * @grp: Full path and name of the group | |
412 | * @parent_grp: Full path and name of the parent group | |
413 | * | |
34813938 | 414 | * Return: 0 on success, < 0 on error. |
591a6e85 SPP |
415 | */ |
416 | static int create_grp(const char *grp_name, char *grp, const char *parent_grp) | |
417 | { | |
418 | int found_grp = 0; | |
419 | struct dirent *ep; | |
420 | DIR *dp; | |
421 | ||
78941183 FY |
422 | /* |
423 | * At this point, we are guaranteed to have resctrl FS mounted and if | |
424 | * length of grp_name == 0, it means, user wants to use root con_mon | |
425 | * grp, so do nothing | |
426 | */ | |
427 | if (strlen(grp_name) == 0) | |
428 | return 0; | |
429 | ||
591a6e85 SPP |
430 | /* Check if requested grp exists or not */ |
431 | dp = opendir(parent_grp); | |
432 | if (dp) { | |
433 | while ((ep = readdir(dp)) != NULL) { | |
434 | if (strcmp(ep->d_name, grp_name) == 0) | |
435 | found_grp = 1; | |
436 | } | |
437 | closedir(dp); | |
438 | } else { | |
cc8ff7f5 | 439 | ksft_perror("Unable to open resctrl for group"); |
591a6e85 SPP |
440 | |
441 | return -1; | |
442 | } | |
443 | ||
444 | /* Requested grp doesn't exist, hence create it */ | |
445 | if (found_grp == 0) { | |
446 | if (mkdir(grp, 0) == -1) { | |
cc8ff7f5 | 447 | ksft_perror("Unable to create group"); |
591a6e85 SPP |
448 | |
449 | return -1; | |
450 | } | |
451 | } | |
452 | ||
453 | return 0; | |
454 | } | |
455 | ||
456 | static int write_pid_to_tasks(char *tasks, pid_t pid) | |
457 | { | |
458 | FILE *fp; | |
459 | ||
460 | fp = fopen(tasks, "w"); | |
461 | if (!fp) { | |
cc8ff7f5 | 462 | ksft_perror("Failed to open tasks file"); |
591a6e85 SPP |
463 | |
464 | return -1; | |
465 | } | |
466 | if (fprintf(fp, "%d\n", pid) < 0) { | |
cc8ff7f5 | 467 | ksft_print_msg("Failed to write pid to tasks file\n"); |
591a6e85 SPP |
468 | fclose(fp); |
469 | ||
470 | return -1; | |
471 | } | |
472 | fclose(fp); | |
473 | ||
474 | return 0; | |
475 | } | |
476 | ||
477 | /* | |
478 | * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS | |
479 | * @bm_pid: PID that should be written | |
480 | * @ctrlgrp: Name of the control monitor group (con_mon grp) | |
481 | * @mongrp: Name of the monitor group (mon grp) | |
482 | * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) | |
483 | * | |
484 | * If a con_mon grp is requested, create it and write pid to it, otherwise | |
485 | * write pid to root con_mon grp. | |
486 | * If a mon grp is requested, create it and write pid to it, otherwise | |
487 | * pid is not written, this means that pid is in con_mon grp and hence | |
488 | * should consult con_mon grp's mon_data directory for results. | |
489 | * | |
34813938 | 490 | * Return: 0 on success, < 0 on error. |
591a6e85 SPP |
491 | */ |
492 | int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, | |
493 | char *resctrl_val) | |
494 | { | |
495 | char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; | |
496 | char tasks[1024]; | |
497 | int ret = 0; | |
498 | ||
499 | if (strlen(ctrlgrp)) | |
500 | sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); | |
501 | else | |
502 | sprintf(controlgroup, "%s", RESCTRL_PATH); | |
503 | ||
504 | /* Create control and monitoring group and write pid into it */ | |
505 | ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); | |
506 | if (ret) | |
507 | goto out; | |
508 | sprintf(tasks, "%s/tasks", controlgroup); | |
509 | ret = write_pid_to_tasks(tasks, bm_pid); | |
510 | if (ret) | |
511 | goto out; | |
512 | ||
2f320911 FY |
513 | /* Create mon grp and write pid into it for "mbm" and "cmt" test */ |
514 | if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || | |
24286736 | 515 | !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { |
78941183 | 516 | if (strlen(mongrp)) { |
591a6e85 SPP |
517 | sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); |
518 | sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); | |
519 | ret = create_grp(mongrp, monitorgroup, monitorgroup_p); | |
520 | if (ret) | |
521 | goto out; | |
522 | ||
523 | sprintf(tasks, "%s/mon_groups/%s/tasks", | |
524 | controlgroup, mongrp); | |
525 | ret = write_pid_to_tasks(tasks, bm_pid); | |
526 | if (ret) | |
527 | goto out; | |
528 | } | |
529 | } | |
530 | ||
531 | out: | |
ca2f4214 | 532 | ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); |
591a6e85 | 533 | if (ret) |
cc8ff7f5 | 534 | ksft_print_msg("Failed writing to resctrlfs\n"); |
591a6e85 | 535 | |
591a6e85 SPP |
536 | return ret; |
537 | } | |
538 | ||
539 | /* | |
540 | * write_schemata - Update schemata of a con_mon grp | |
541 | * @ctrlgrp: Name of the con_mon grp | |
542 | * @schemata: Schemata that should be updated to | |
543 | * @cpu_no: CPU number that the benchmark PID is binded to | |
ca160887 | 544 | * @resource: Resctrl resource (Eg: MB, L3, L2, etc.) |
591a6e85 | 545 | * |
ca160887 | 546 | * Update schemata of a con_mon grp *only* if requested resctrl resource is |
591a6e85 SPP |
547 | * allocation type |
548 | * | |
34813938 | 549 | * Return: 0 on success, < 0 on error. |
591a6e85 | 550 | */ |
ca160887 | 551 | int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, const char *resource) |
591a6e85 | 552 | { |
20d96b25 | 553 | char controlgroup[1024], reason[128], schema[1024] = {}; |
6874f6ed | 554 | int domain_id, fd, schema_len, ret = 0; |
591a6e85 SPP |
555 | |
556 | if (!schemata) { | |
ca2f4214 | 557 | ksft_print_msg("Skipping empty schemata update\n"); |
591a6e85 SPP |
558 | |
559 | return -1; | |
560 | } | |
561 | ||
6874f6ed IJ |
562 | if (get_domain_id(cpu_no, &domain_id) < 0) { |
563 | sprintf(reason, "Failed to get domain ID"); | |
591a6e85 SPP |
564 | ret = -1; |
565 | ||
566 | goto out; | |
567 | } | |
568 | ||
569 | if (strlen(ctrlgrp) != 0) | |
570 | sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); | |
571 | else | |
572 | sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); | |
573 | ||
ca160887 | 574 | schema_len = snprintf(schema, sizeof(schema), "%s:%d=%s\n", |
6874f6ed | 575 | resource, domain_id, schemata); |
20d96b25 MWR |
576 | if (schema_len < 0 || schema_len >= sizeof(schema)) { |
577 | snprintf(reason, sizeof(reason), | |
578 | "snprintf() failed with return value : %d", schema_len); | |
591a6e85 | 579 | ret = -1; |
591a6e85 SPP |
580 | goto out; |
581 | } | |
582 | ||
20d96b25 MWR |
583 | fd = open(controlgroup, O_WRONLY); |
584 | if (fd < 0) { | |
585 | snprintf(reason, sizeof(reason), | |
586 | "open() failed : %s", strerror(errno)); | |
591a6e85 SPP |
587 | ret = -1; |
588 | ||
20d96b25 | 589 | goto err_schema_not_empty; |
591a6e85 | 590 | } |
20d96b25 MWR |
591 | if (write(fd, schema, schema_len) < 0) { |
592 | snprintf(reason, sizeof(reason), | |
593 | "write() failed : %s", strerror(errno)); | |
594 | close(fd); | |
595 | ret = -1; | |
596 | ||
597 | goto err_schema_not_empty; | |
598 | } | |
599 | close(fd); | |
591a6e85 | 600 | |
20d96b25 MWR |
601 | err_schema_not_empty: |
602 | schema[schema_len - 1] = 0; | |
591a6e85 | 603 | out: |
ca2f4214 FY |
604 | ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n", |
605 | schema, ret ? " # " : "", | |
606 | ret ? reason : ""); | |
591a6e85 SPP |
607 | |
608 | return ret; | |
609 | } | |
610 | ||
ecdbb911 FY |
611 | bool check_resctrlfs_support(void) |
612 | { | |
613 | FILE *inf = fopen("/proc/filesystems", "r"); | |
614 | DIR *dp; | |
615 | char *res; | |
616 | bool ret = false; | |
617 | ||
618 | if (!inf) | |
619 | return false; | |
620 | ||
621 | res = fgrep(inf, "nodev\tresctrl\n"); | |
622 | ||
623 | if (res) { | |
624 | ret = true; | |
625 | free(res); | |
626 | } | |
627 | ||
628 | fclose(inf); | |
629 | ||
e7507478 | 630 | ksft_print_msg("%s Check kernel supports resctrl filesystem\n", |
ca2f4214 | 631 | ret ? "Pass:" : "Fail:"); |
ecdbb911 | 632 | |
a3611fbc FY |
633 | if (!ret) |
634 | return ret; | |
635 | ||
ecdbb911 | 636 | dp = opendir(RESCTRL_PATH); |
e7507478 | 637 | ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n", |
ca2f4214 | 638 | dp ? "Pass:" : "Fail:", RESCTRL_PATH); |
ecdbb911 FY |
639 | if (dp) |
640 | closedir(dp); | |
ecdbb911 | 641 | |
ca2f4214 FY |
642 | ksft_print_msg("resctrl filesystem %s mounted\n", |
643 | find_resctrl_mount(NULL) ? "not" : "is"); | |
ecdbb911 FY |
644 | |
645 | return ret; | |
646 | } | |
647 | ||
591a6e85 SPP |
648 | char *fgrep(FILE *inf, const char *str) |
649 | { | |
650 | char line[256]; | |
651 | int slen = strlen(str); | |
652 | ||
653 | while (!feof(inf)) { | |
654 | if (!fgets(line, 256, inf)) | |
655 | break; | |
656 | if (strncmp(line, str, slen)) | |
657 | continue; | |
658 | ||
659 | return strdup(line); | |
660 | } | |
661 | ||
662 | return NULL; | |
663 | } | |
664 | ||
665 | /* | |
666 | * validate_resctrl_feature_request - Check if requested feature is valid. | |
d56e5da0 IJ |
667 | * @resource: Required resource (e.g., MB, L3, L2, L3_MON, etc.) |
668 | * @feature: Required monitor feature (in mon_features file). Can only be | |
669 | * set for L3_MON. Must be NULL for all other resources. | |
591a6e85 | 670 | * |
d56e5da0 IJ |
671 | * Return: True if the resource/feature is supported, else false. False is |
672 | * also returned if resctrl FS is not mounted. | |
591a6e85 | 673 | */ |
d56e5da0 | 674 | bool validate_resctrl_feature_request(const char *resource, const char *feature) |
591a6e85 | 675 | { |
d56e5da0 | 676 | char res_path[PATH_MAX]; |
ee041568 | 677 | struct stat statbuf; |
591a6e85 | 678 | char *res; |
ee041568 | 679 | FILE *inf; |
caddc0fb | 680 | int ret; |
591a6e85 | 681 | |
d56e5da0 | 682 | if (!resource) |
591a6e85 SPP |
683 | return false; |
684 | ||
caddc0fb IJ |
685 | ret = find_resctrl_mount(NULL); |
686 | if (ret) | |
ee041568 | 687 | return false; |
591a6e85 | 688 | |
d56e5da0 IJ |
689 | snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource); |
690 | ||
691 | if (stat(res_path, &statbuf)) | |
692 | return false; | |
693 | ||
694 | if (!feature) | |
695 | return true; | |
696 | ||
697 | snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource); | |
698 | inf = fopen(res_path, "r"); | |
699 | if (!inf) | |
700 | return false; | |
701 | ||
702 | res = fgrep(inf, feature); | |
703 | free(res); | |
704 | fclose(inf); | |
591a6e85 | 705 | |
d56e5da0 | 706 | return !!res; |
591a6e85 SPP |
707 | } |
708 | ||
c603ff5b IJ |
709 | bool test_resource_feature_check(const struct resctrl_test *test) |
710 | { | |
711 | return validate_resctrl_feature_request(test->resource, NULL); | |
712 | } | |
713 | ||
ecdbb911 FY |
714 | int filter_dmesg(void) |
715 | { | |
716 | char line[1024]; | |
717 | FILE *fp; | |
718 | int pipefds[2]; | |
719 | pid_t pid; | |
720 | int ret; | |
721 | ||
722 | ret = pipe(pipefds); | |
723 | if (ret) { | |
cc8ff7f5 | 724 | ksft_perror("pipe"); |
ecdbb911 FY |
725 | return ret; |
726 | } | |
a080b6e7 | 727 | fflush(stdout); |
ecdbb911 FY |
728 | pid = fork(); |
729 | if (pid == 0) { | |
730 | close(pipefds[0]); | |
731 | dup2(pipefds[1], STDOUT_FILENO); | |
732 | execlp("dmesg", "dmesg", NULL); | |
cc8ff7f5 | 733 | ksft_perror("Executing dmesg"); |
ecdbb911 FY |
734 | exit(1); |
735 | } | |
736 | close(pipefds[1]); | |
737 | fp = fdopen(pipefds[0], "r"); | |
738 | if (!fp) { | |
cc8ff7f5 | 739 | ksft_perror("fdopen(pipe)"); |
ecdbb911 FY |
740 | kill(pid, SIGTERM); |
741 | ||
742 | return -1; | |
743 | } | |
744 | ||
745 | while (fgets(line, 1024, fp)) { | |
746 | if (strstr(line, "intel_rdt:")) | |
ca2f4214 | 747 | ksft_print_msg("dmesg: %s", line); |
ecdbb911 | 748 | if (strstr(line, "resctrl:")) |
ca2f4214 | 749 | ksft_print_msg("dmesg: %s", line); |
ecdbb911 FY |
750 | } |
751 | fclose(fp); | |
752 | waitpid(pid, NULL, 0); | |
753 | ||
754 | return 0; | |
755 | } | |
756 | ||
591a6e85 SPP |
757 | int validate_bw_report_request(char *bw_report) |
758 | { | |
759 | if (strcmp(bw_report, "reads") == 0) | |
760 | return 0; | |
761 | if (strcmp(bw_report, "writes") == 0) | |
762 | return 0; | |
763 | if (strcmp(bw_report, "nt-writes") == 0) { | |
764 | strcpy(bw_report, "writes"); | |
765 | return 0; | |
766 | } | |
767 | if (strcmp(bw_report, "total") == 0) | |
768 | return 0; | |
769 | ||
770 | fprintf(stderr, "Requested iMC B/W report type unavailable\n"); | |
771 | ||
772 | return -1; | |
773 | } | |
774 | ||
775 | int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, | |
776 | int group_fd, unsigned long flags) | |
777 | { | |
778 | int ret; | |
779 | ||
780 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, | |
781 | group_fd, flags); | |
782 | return ret; | |
783 | } | |
78941183 FY |
784 | |
785 | unsigned int count_bits(unsigned long n) | |
786 | { | |
787 | unsigned int count = 0; | |
788 | ||
789 | while (n) { | |
790 | count += n & 1; | |
791 | n >>= 1; | |
792 | } | |
793 | ||
794 | return count; | |
795 | } |