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 | ||
345e8abe IJ |
114 | static int get_resource_cache_level(const char *resource) |
115 | { | |
116 | /* "MB" use L3 (LLC) as resource */ | |
117 | if (!strcmp(resource, "MB")) | |
118 | return 3; | |
119 | return get_cache_level(resource); | |
120 | } | |
121 | ||
591a6e85 | 122 | /* |
6874f6ed | 123 | * get_domain_id - Get resctrl domain ID for a specified CPU |
345e8abe | 124 | * @resource: resource name |
591a6e85 | 125 | * @cpu_no: CPU number |
6874f6ed | 126 | * @domain_id: domain ID (cache ID; for MB, L3 cache ID) |
591a6e85 SPP |
127 | * |
128 | * Return: >= 0 on success, < 0 on failure. | |
129 | */ | |
345e8abe | 130 | int get_domain_id(const char *resource, int cpu_no, int *domain_id) |
591a6e85 SPP |
131 | { |
132 | char phys_pkg_path[1024]; | |
345e8abe | 133 | int cache_num; |
591a6e85 SPP |
134 | FILE *fp; |
135 | ||
345e8abe IJ |
136 | cache_num = get_resource_cache_level(resource); |
137 | if (cache_num < 0) | |
138 | return cache_num; | |
139 | ||
140 | sprintf(phys_pkg_path, "%s%d/cache/index%d/id", PHYS_ID_PATH, cpu_no, cache_num); | |
c0327e1d | 141 | |
591a6e85 SPP |
142 | fp = fopen(phys_pkg_path, "r"); |
143 | if (!fp) { | |
345e8abe | 144 | ksft_perror("Failed to open cache id file"); |
591a6e85 SPP |
145 | |
146 | return -1; | |
147 | } | |
6874f6ed IJ |
148 | if (fscanf(fp, "%d", domain_id) <= 0) { |
149 | ksft_perror("Could not get domain ID"); | |
591a6e85 SPP |
150 | fclose(fp); |
151 | ||
152 | return -1; | |
153 | } | |
154 | fclose(fp); | |
155 | ||
156 | return 0; | |
157 | } | |
158 | ||
78941183 FY |
159 | /* |
160 | * get_cache_size - Get cache size for a specified CPU | |
161 | * @cpu_no: CPU number | |
162 | * @cache_type: Cache level L2/L3 | |
163 | * @cache_size: pointer to cache_size | |
164 | * | |
165 | * Return: = 0 on success, < 0 on failure. | |
166 | */ | |
60c2a692 | 167 | int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size) |
78941183 FY |
168 | { |
169 | char cache_path[1024], cache_str[64]; | |
170 | int length, i, cache_num; | |
171 | FILE *fp; | |
172 | ||
e73dda7f IJ |
173 | cache_num = get_cache_level(cache_type); |
174 | if (cache_num < 0) | |
175 | return cache_num; | |
78941183 FY |
176 | |
177 | sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size", | |
178 | cpu_no, cache_num); | |
179 | fp = fopen(cache_path, "r"); | |
180 | if (!fp) { | |
cc8ff7f5 | 181 | ksft_perror("Failed to open cache size"); |
78941183 FY |
182 | |
183 | return -1; | |
184 | } | |
185 | if (fscanf(fp, "%s", cache_str) <= 0) { | |
cc8ff7f5 | 186 | ksft_perror("Could not get cache_size"); |
78941183 FY |
187 | fclose(fp); |
188 | ||
189 | return -1; | |
190 | } | |
191 | fclose(fp); | |
192 | ||
193 | length = (int)strlen(cache_str); | |
194 | ||
195 | *cache_size = 0; | |
196 | ||
197 | for (i = 0; i < length; i++) { | |
198 | if ((cache_str[i] >= '0') && (cache_str[i] <= '9')) | |
199 | ||
200 | *cache_size = *cache_size * 10 + (cache_str[i] - '0'); | |
201 | ||
202 | else if (cache_str[i] == 'K') | |
203 | ||
204 | *cache_size = *cache_size * 1024; | |
205 | ||
206 | else if (cache_str[i] == 'M') | |
207 | ||
208 | *cache_size = *cache_size * 1024 * 1024; | |
209 | ||
210 | else | |
211 | break; | |
212 | } | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | #define CORE_SIBLINGS_PATH "/sys/bus/cpu/devices/cpu" | |
218 | ||
219 | /* | |
4b357e2a IJ |
220 | * get_bit_mask - Get bit mask from given file |
221 | * @filename: File containing the mask | |
222 | * @mask: The bit mask returned as unsigned long | |
78941183 FY |
223 | * |
224 | * Return: = 0 on success, < 0 on failure. | |
225 | */ | |
4b357e2a | 226 | static int get_bit_mask(const char *filename, unsigned long *mask) |
78941183 | 227 | { |
78941183 FY |
228 | FILE *fp; |
229 | ||
4b357e2a | 230 | if (!filename || !mask) |
8236c51d FY |
231 | return -1; |
232 | ||
4b357e2a | 233 | fp = fopen(filename, "r"); |
78941183 | 234 | if (!fp) { |
4b357e2a IJ |
235 | ksft_print_msg("Failed to open bit mask file '%s': %s\n", |
236 | filename, strerror(errno)); | |
78941183 FY |
237 | return -1; |
238 | } | |
4b357e2a IJ |
239 | |
240 | if (fscanf(fp, "%lx", mask) <= 0) { | |
241 | ksft_print_msg("Could not read bit mask file '%s': %s\n", | |
242 | filename, strerror(errno)); | |
78941183 FY |
243 | fclose(fp); |
244 | ||
245 | return -1; | |
246 | } | |
247 | fclose(fp); | |
248 | ||
249 | return 0; | |
250 | } | |
251 | ||
e331ac14 MWR |
252 | /* |
253 | * resource_info_unsigned_get - Read an unsigned value from | |
254 | * /sys/fs/resctrl/info/@resource/@filename | |
255 | * @resource: Resource name that matches directory name in | |
256 | * /sys/fs/resctrl/info | |
257 | * @filename: File in /sys/fs/resctrl/info/@resource | |
258 | * @val: Contains read value on success. | |
259 | * | |
260 | * Return: = 0 on success, < 0 on failure. On success the read | |
261 | * value is saved into @val. | |
262 | */ | |
263 | int resource_info_unsigned_get(const char *resource, const char *filename, | |
264 | unsigned int *val) | |
265 | { | |
266 | char file_path[PATH_MAX]; | |
267 | FILE *fp; | |
268 | ||
269 | snprintf(file_path, sizeof(file_path), "%s/%s/%s", INFO_PATH, resource, | |
270 | filename); | |
271 | ||
272 | fp = fopen(file_path, "r"); | |
273 | if (!fp) { | |
274 | ksft_print_msg("Error opening %s: %m\n", file_path); | |
275 | return -1; | |
276 | } | |
277 | ||
278 | if (fscanf(fp, "%u", val) <= 0) { | |
279 | ksft_print_msg("Could not get contents of %s: %m\n", file_path); | |
280 | fclose(fp); | |
281 | return -1; | |
282 | } | |
283 | ||
284 | fclose(fp); | |
285 | return 0; | |
286 | } | |
287 | ||
b6dfac94 IJ |
288 | /* |
289 | * create_bit_mask- Create bit mask from start, len pair | |
290 | * @start: LSB of the mask | |
291 | * @len Number of bits in the mask | |
292 | */ | |
293 | unsigned long create_bit_mask(unsigned int start, unsigned int len) | |
294 | { | |
295 | return ((1UL << len) - 1UL) << start; | |
296 | } | |
297 | ||
298 | /* | |
299 | * count_contiguous_bits - Returns the longest train of bits in a bit mask | |
300 | * @val A bit mask | |
301 | * @start The location of the least-significant bit of the longest train | |
302 | * | |
303 | * Return: The length of the contiguous bits in the longest train of bits | |
304 | */ | |
205de6dd | 305 | unsigned int count_contiguous_bits(unsigned long val, unsigned int *start) |
b6dfac94 IJ |
306 | { |
307 | unsigned long last_val; | |
308 | unsigned int count = 0; | |
309 | ||
310 | while (val) { | |
311 | last_val = val; | |
312 | val &= (val >> 1); | |
313 | count++; | |
314 | } | |
315 | ||
316 | if (start) { | |
317 | if (count) | |
318 | *start = ffsl(last_val) - 1; | |
319 | else | |
320 | *start = 0; | |
321 | } | |
322 | ||
323 | return count; | |
324 | } | |
325 | ||
4b357e2a IJ |
326 | /* |
327 | * get_full_cbm - Get full Cache Bit Mask (CBM) | |
328 | * @cache_type: Cache type as "L2" or "L3" | |
329 | * @mask: Full cache bit mask representing the maximal portion of cache | |
330 | * available for allocation, returned as unsigned long. | |
331 | * | |
332 | * Return: = 0 on success, < 0 on failure. | |
333 | */ | |
334 | int get_full_cbm(const char *cache_type, unsigned long *mask) | |
335 | { | |
336 | char cbm_path[PATH_MAX]; | |
337 | int ret; | |
338 | ||
339 | if (!cache_type) | |
340 | return -1; | |
341 | ||
342 | snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask", | |
343 | INFO_PATH, cache_type); | |
344 | ||
345 | ret = get_bit_mask(cbm_path, mask); | |
346 | if (ret || !*mask) | |
347 | return -1; | |
348 | ||
349 | return 0; | |
350 | } | |
351 | ||
b6dfac94 IJ |
352 | /* |
353 | * get_shareable_mask - Get shareable mask from shareable_bits | |
354 | * @cache_type: Cache type as "L2" or "L3" | |
355 | * @shareable_mask: Shareable mask returned as unsigned long | |
356 | * | |
357 | * Return: = 0 on success, < 0 on failure. | |
358 | */ | |
359 | static int get_shareable_mask(const char *cache_type, unsigned long *shareable_mask) | |
360 | { | |
361 | char mask_path[PATH_MAX]; | |
362 | ||
363 | if (!cache_type) | |
364 | return -1; | |
365 | ||
366 | snprintf(mask_path, sizeof(mask_path), "%s/%s/shareable_bits", | |
367 | INFO_PATH, cache_type); | |
368 | ||
369 | return get_bit_mask(mask_path, shareable_mask); | |
370 | } | |
371 | ||
372 | /* | |
373 | * get_mask_no_shareable - Get Cache Bit Mask (CBM) without shareable bits | |
374 | * @cache_type: Cache type as "L2" or "L3" | |
375 | * @mask: The largest exclusive portion of the cache out of the | |
376 | * full CBM, returned as unsigned long | |
377 | * | |
378 | * Parts of a cache may be shared with other devices such as GPU. This function | |
379 | * calculates the largest exclusive portion of the cache where no other devices | |
380 | * besides CPU have access to the cache portion. | |
381 | * | |
382 | * Return: = 0 on success, < 0 on failure. | |
383 | */ | |
384 | int get_mask_no_shareable(const char *cache_type, unsigned long *mask) | |
385 | { | |
386 | unsigned long full_mask, shareable_mask; | |
387 | unsigned int start, len; | |
388 | ||
389 | if (get_full_cbm(cache_type, &full_mask) < 0) | |
390 | return -1; | |
391 | if (get_shareable_mask(cache_type, &shareable_mask) < 0) | |
392 | return -1; | |
393 | ||
394 | len = count_contiguous_bits(full_mask & ~shareable_mask, &start); | |
395 | if (!len) | |
396 | return -1; | |
397 | ||
398 | *mask = create_bit_mask(start, len); | |
399 | ||
400 | return 0; | |
401 | } | |
402 | ||
591a6e85 SPP |
403 | /* |
404 | * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu | |
6c8cb747 IJ |
405 | * @bm_pid: PID that should be binded |
406 | * @cpu_no: CPU number at which the PID would be binded | |
407 | * @old_affinity: When not NULL, set to old CPU affinity | |
591a6e85 | 408 | * |
34813938 | 409 | * Return: 0 on success, < 0 on error. |
591a6e85 | 410 | */ |
6c8cb747 | 411 | int taskset_benchmark(pid_t bm_pid, int cpu_no, cpu_set_t *old_affinity) |
591a6e85 SPP |
412 | { |
413 | cpu_set_t my_set; | |
414 | ||
6c8cb747 IJ |
415 | if (old_affinity) { |
416 | CPU_ZERO(old_affinity); | |
417 | if (sched_getaffinity(bm_pid, sizeof(*old_affinity), | |
418 | old_affinity)) { | |
419 | ksft_perror("Unable to read CPU affinity"); | |
420 | return -1; | |
421 | } | |
422 | } | |
423 | ||
591a6e85 SPP |
424 | CPU_ZERO(&my_set); |
425 | CPU_SET(cpu_no, &my_set); | |
426 | ||
427 | if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) { | |
cc8ff7f5 | 428 | ksft_perror("Unable to taskset benchmark"); |
591a6e85 SPP |
429 | |
430 | return -1; | |
431 | } | |
432 | ||
433 | return 0; | |
434 | } | |
435 | ||
6c8cb747 IJ |
436 | /* |
437 | * taskset_restore - Taskset PID to the earlier CPU affinity | |
438 | * @bm_pid: PID that should be reset | |
439 | * @old_affinity: The old CPU affinity to restore | |
440 | * | |
441 | * Return: 0 on success, < 0 on error. | |
442 | */ | |
443 | int taskset_restore(pid_t bm_pid, cpu_set_t *old_affinity) | |
444 | { | |
445 | if (sched_setaffinity(bm_pid, sizeof(*old_affinity), old_affinity)) { | |
446 | ksft_perror("Unable to restore CPU affinity"); | |
447 | return -1; | |
448 | } | |
449 | ||
450 | return 0; | |
451 | } | |
452 | ||
591a6e85 SPP |
453 | /* |
454 | * create_grp - Create a group only if one doesn't exist | |
455 | * @grp_name: Name of the group | |
456 | * @grp: Full path and name of the group | |
457 | * @parent_grp: Full path and name of the parent group | |
458 | * | |
34813938 | 459 | * Return: 0 on success, < 0 on error. |
591a6e85 SPP |
460 | */ |
461 | static int create_grp(const char *grp_name, char *grp, const char *parent_grp) | |
462 | { | |
463 | int found_grp = 0; | |
464 | struct dirent *ep; | |
465 | DIR *dp; | |
466 | ||
78941183 FY |
467 | /* |
468 | * At this point, we are guaranteed to have resctrl FS mounted and if | |
469 | * length of grp_name == 0, it means, user wants to use root con_mon | |
470 | * grp, so do nothing | |
471 | */ | |
472 | if (strlen(grp_name) == 0) | |
473 | return 0; | |
474 | ||
591a6e85 SPP |
475 | /* Check if requested grp exists or not */ |
476 | dp = opendir(parent_grp); | |
477 | if (dp) { | |
478 | while ((ep = readdir(dp)) != NULL) { | |
479 | if (strcmp(ep->d_name, grp_name) == 0) | |
480 | found_grp = 1; | |
481 | } | |
482 | closedir(dp); | |
483 | } else { | |
cc8ff7f5 | 484 | ksft_perror("Unable to open resctrl for group"); |
591a6e85 SPP |
485 | |
486 | return -1; | |
487 | } | |
488 | ||
489 | /* Requested grp doesn't exist, hence create it */ | |
490 | if (found_grp == 0) { | |
491 | if (mkdir(grp, 0) == -1) { | |
cc8ff7f5 | 492 | ksft_perror("Unable to create group"); |
591a6e85 SPP |
493 | |
494 | return -1; | |
495 | } | |
496 | } | |
497 | ||
498 | return 0; | |
499 | } | |
500 | ||
501 | static int write_pid_to_tasks(char *tasks, pid_t pid) | |
502 | { | |
503 | FILE *fp; | |
504 | ||
505 | fp = fopen(tasks, "w"); | |
506 | if (!fp) { | |
cc8ff7f5 | 507 | ksft_perror("Failed to open tasks file"); |
591a6e85 SPP |
508 | |
509 | return -1; | |
510 | } | |
511 | if (fprintf(fp, "%d\n", pid) < 0) { | |
cc8ff7f5 | 512 | ksft_print_msg("Failed to write pid to tasks file\n"); |
591a6e85 SPP |
513 | fclose(fp); |
514 | ||
515 | return -1; | |
516 | } | |
517 | fclose(fp); | |
518 | ||
519 | return 0; | |
520 | } | |
521 | ||
522 | /* | |
523 | * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS | |
524 | * @bm_pid: PID that should be written | |
525 | * @ctrlgrp: Name of the control monitor group (con_mon grp) | |
526 | * @mongrp: Name of the monitor group (mon grp) | |
527 | * @resctrl_val: Resctrl feature (Eg: mbm, mba.. etc) | |
528 | * | |
529 | * If a con_mon grp is requested, create it and write pid to it, otherwise | |
530 | * write pid to root con_mon grp. | |
531 | * If a mon grp is requested, create it and write pid to it, otherwise | |
532 | * pid is not written, this means that pid is in con_mon grp and hence | |
533 | * should consult con_mon grp's mon_data directory for results. | |
534 | * | |
34813938 | 535 | * Return: 0 on success, < 0 on error. |
591a6e85 SPP |
536 | */ |
537 | int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp, | |
538 | char *resctrl_val) | |
539 | { | |
540 | char controlgroup[128], monitorgroup[512], monitorgroup_p[256]; | |
541 | char tasks[1024]; | |
542 | int ret = 0; | |
543 | ||
544 | if (strlen(ctrlgrp)) | |
545 | sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp); | |
546 | else | |
547 | sprintf(controlgroup, "%s", RESCTRL_PATH); | |
548 | ||
549 | /* Create control and monitoring group and write pid into it */ | |
550 | ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH); | |
551 | if (ret) | |
552 | goto out; | |
553 | sprintf(tasks, "%s/tasks", controlgroup); | |
554 | ret = write_pid_to_tasks(tasks, bm_pid); | |
555 | if (ret) | |
556 | goto out; | |
557 | ||
2f320911 FY |
558 | /* Create mon grp and write pid into it for "mbm" and "cmt" test */ |
559 | if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) || | |
24286736 | 560 | !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) { |
78941183 | 561 | if (strlen(mongrp)) { |
591a6e85 SPP |
562 | sprintf(monitorgroup_p, "%s/mon_groups", controlgroup); |
563 | sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp); | |
564 | ret = create_grp(mongrp, monitorgroup, monitorgroup_p); | |
565 | if (ret) | |
566 | goto out; | |
567 | ||
568 | sprintf(tasks, "%s/mon_groups/%s/tasks", | |
569 | controlgroup, mongrp); | |
570 | ret = write_pid_to_tasks(tasks, bm_pid); | |
571 | if (ret) | |
572 | goto out; | |
573 | } | |
574 | } | |
575 | ||
576 | out: | |
ca2f4214 | 577 | ksft_print_msg("Writing benchmark parameters to resctrl FS\n"); |
591a6e85 | 578 | if (ret) |
cc8ff7f5 | 579 | ksft_print_msg("Failed writing to resctrlfs\n"); |
591a6e85 | 580 | |
591a6e85 SPP |
581 | return ret; |
582 | } | |
583 | ||
584 | /* | |
585 | * write_schemata - Update schemata of a con_mon grp | |
586 | * @ctrlgrp: Name of the con_mon grp | |
587 | * @schemata: Schemata that should be updated to | |
588 | * @cpu_no: CPU number that the benchmark PID is binded to | |
ca160887 | 589 | * @resource: Resctrl resource (Eg: MB, L3, L2, etc.) |
591a6e85 | 590 | * |
ca160887 | 591 | * Update schemata of a con_mon grp *only* if requested resctrl resource is |
591a6e85 SPP |
592 | * allocation type |
593 | * | |
34813938 | 594 | * Return: 0 on success, < 0 on error. |
591a6e85 | 595 | */ |
ca160887 | 596 | int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, const char *resource) |
591a6e85 | 597 | { |
20d96b25 | 598 | char controlgroup[1024], reason[128], schema[1024] = {}; |
6874f6ed | 599 | int domain_id, fd, schema_len, ret = 0; |
591a6e85 SPP |
600 | |
601 | if (!schemata) { | |
ca2f4214 | 602 | ksft_print_msg("Skipping empty schemata update\n"); |
591a6e85 SPP |
603 | |
604 | return -1; | |
605 | } | |
606 | ||
345e8abe | 607 | if (get_domain_id(resource, cpu_no, &domain_id) < 0) { |
6874f6ed | 608 | sprintf(reason, "Failed to get domain ID"); |
591a6e85 SPP |
609 | ret = -1; |
610 | ||
611 | goto out; | |
612 | } | |
613 | ||
614 | if (strlen(ctrlgrp) != 0) | |
615 | sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp); | |
616 | else | |
617 | sprintf(controlgroup, "%s/schemata", RESCTRL_PATH); | |
618 | ||
ca160887 | 619 | schema_len = snprintf(schema, sizeof(schema), "%s:%d=%s\n", |
6874f6ed | 620 | resource, domain_id, schemata); |
20d96b25 MWR |
621 | if (schema_len < 0 || schema_len >= sizeof(schema)) { |
622 | snprintf(reason, sizeof(reason), | |
623 | "snprintf() failed with return value : %d", schema_len); | |
591a6e85 | 624 | ret = -1; |
591a6e85 SPP |
625 | goto out; |
626 | } | |
627 | ||
20d96b25 MWR |
628 | fd = open(controlgroup, O_WRONLY); |
629 | if (fd < 0) { | |
630 | snprintf(reason, sizeof(reason), | |
631 | "open() failed : %s", strerror(errno)); | |
591a6e85 SPP |
632 | ret = -1; |
633 | ||
20d96b25 | 634 | goto err_schema_not_empty; |
591a6e85 | 635 | } |
20d96b25 MWR |
636 | if (write(fd, schema, schema_len) < 0) { |
637 | snprintf(reason, sizeof(reason), | |
638 | "write() failed : %s", strerror(errno)); | |
639 | close(fd); | |
640 | ret = -1; | |
641 | ||
642 | goto err_schema_not_empty; | |
643 | } | |
644 | close(fd); | |
591a6e85 | 645 | |
20d96b25 MWR |
646 | err_schema_not_empty: |
647 | schema[schema_len - 1] = 0; | |
591a6e85 | 648 | out: |
ca2f4214 FY |
649 | ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n", |
650 | schema, ret ? " # " : "", | |
651 | ret ? reason : ""); | |
591a6e85 SPP |
652 | |
653 | return ret; | |
654 | } | |
655 | ||
ecdbb911 FY |
656 | bool check_resctrlfs_support(void) |
657 | { | |
658 | FILE *inf = fopen("/proc/filesystems", "r"); | |
659 | DIR *dp; | |
660 | char *res; | |
661 | bool ret = false; | |
662 | ||
663 | if (!inf) | |
664 | return false; | |
665 | ||
666 | res = fgrep(inf, "nodev\tresctrl\n"); | |
667 | ||
668 | if (res) { | |
669 | ret = true; | |
670 | free(res); | |
671 | } | |
672 | ||
673 | fclose(inf); | |
674 | ||
e7507478 | 675 | ksft_print_msg("%s Check kernel supports resctrl filesystem\n", |
ca2f4214 | 676 | ret ? "Pass:" : "Fail:"); |
ecdbb911 | 677 | |
a3611fbc FY |
678 | if (!ret) |
679 | return ret; | |
680 | ||
ecdbb911 | 681 | dp = opendir(RESCTRL_PATH); |
e7507478 | 682 | ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n", |
ca2f4214 | 683 | dp ? "Pass:" : "Fail:", RESCTRL_PATH); |
ecdbb911 FY |
684 | if (dp) |
685 | closedir(dp); | |
ecdbb911 | 686 | |
ca2f4214 FY |
687 | ksft_print_msg("resctrl filesystem %s mounted\n", |
688 | find_resctrl_mount(NULL) ? "not" : "is"); | |
ecdbb911 FY |
689 | |
690 | return ret; | |
691 | } | |
692 | ||
591a6e85 SPP |
693 | char *fgrep(FILE *inf, const char *str) |
694 | { | |
695 | char line[256]; | |
696 | int slen = strlen(str); | |
697 | ||
698 | while (!feof(inf)) { | |
699 | if (!fgets(line, 256, inf)) | |
700 | break; | |
701 | if (strncmp(line, str, slen)) | |
702 | continue; | |
703 | ||
704 | return strdup(line); | |
705 | } | |
706 | ||
707 | return NULL; | |
708 | } | |
709 | ||
710 | /* | |
00616416 MWR |
711 | * resctrl_resource_exists - Check if a resource is supported. |
712 | * @resource: Resctrl resource (e.g., MB, L3, L2, L3_MON, etc.) | |
591a6e85 | 713 | * |
00616416 | 714 | * Return: True if the resource is supported, else false. False is |
d56e5da0 | 715 | * also returned if resctrl FS is not mounted. |
591a6e85 | 716 | */ |
00616416 | 717 | bool resctrl_resource_exists(const char *resource) |
591a6e85 | 718 | { |
d56e5da0 | 719 | char res_path[PATH_MAX]; |
ee041568 | 720 | struct stat statbuf; |
caddc0fb | 721 | int ret; |
591a6e85 | 722 | |
d56e5da0 | 723 | if (!resource) |
591a6e85 SPP |
724 | return false; |
725 | ||
caddc0fb IJ |
726 | ret = find_resctrl_mount(NULL); |
727 | if (ret) | |
ee041568 | 728 | return false; |
591a6e85 | 729 | |
d56e5da0 IJ |
730 | snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource); |
731 | ||
732 | if (stat(res_path, &statbuf)) | |
733 | return false; | |
734 | ||
00616416 MWR |
735 | return true; |
736 | } | |
737 | ||
738 | /* | |
739 | * resctrl_mon_feature_exists - Check if requested monitoring feature is valid. | |
740 | * @resource: Resource that uses the mon_features file. Currently only L3_MON | |
741 | * is valid. | |
742 | * @feature: Required monitor feature (in mon_features file). | |
743 | * | |
744 | * Return: True if the feature is supported, else false. | |
745 | */ | |
746 | bool resctrl_mon_feature_exists(const char *resource, const char *feature) | |
747 | { | |
748 | char res_path[PATH_MAX]; | |
749 | char *res; | |
750 | FILE *inf; | |
751 | ||
752 | if (!feature || !resource) | |
753 | return false; | |
d56e5da0 IJ |
754 | |
755 | snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource); | |
756 | inf = fopen(res_path, "r"); | |
757 | if (!inf) | |
758 | return false; | |
759 | ||
760 | res = fgrep(inf, feature); | |
761 | free(res); | |
762 | fclose(inf); | |
591a6e85 | 763 | |
d56e5da0 | 764 | return !!res; |
591a6e85 SPP |
765 | } |
766 | ||
74e76cba MWR |
767 | /* |
768 | * resource_info_file_exists - Check if a file is present inside | |
769 | * /sys/fs/resctrl/info/@resource. | |
770 | * @resource: Required resource (Eg: MB, L3, L2, etc.) | |
771 | * @file: Required file. | |
772 | * | |
773 | * Return: True if the /sys/fs/resctrl/info/@resource/@file exists, else false. | |
774 | */ | |
775 | bool resource_info_file_exists(const char *resource, const char *file) | |
776 | { | |
777 | char res_path[PATH_MAX]; | |
778 | struct stat statbuf; | |
779 | ||
780 | if (!file || !resource) | |
781 | return false; | |
782 | ||
783 | snprintf(res_path, sizeof(res_path), "%s/%s/%s", INFO_PATH, resource, | |
784 | file); | |
785 | ||
786 | if (stat(res_path, &statbuf)) | |
787 | return false; | |
788 | ||
789 | return true; | |
790 | } | |
791 | ||
c603ff5b IJ |
792 | bool test_resource_feature_check(const struct resctrl_test *test) |
793 | { | |
00616416 | 794 | return resctrl_resource_exists(test->resource); |
c603ff5b IJ |
795 | } |
796 | ||
ecdbb911 FY |
797 | int filter_dmesg(void) |
798 | { | |
799 | char line[1024]; | |
800 | FILE *fp; | |
801 | int pipefds[2]; | |
802 | pid_t pid; | |
803 | int ret; | |
804 | ||
805 | ret = pipe(pipefds); | |
806 | if (ret) { | |
cc8ff7f5 | 807 | ksft_perror("pipe"); |
ecdbb911 FY |
808 | return ret; |
809 | } | |
a080b6e7 | 810 | fflush(stdout); |
ecdbb911 FY |
811 | pid = fork(); |
812 | if (pid == 0) { | |
813 | close(pipefds[0]); | |
814 | dup2(pipefds[1], STDOUT_FILENO); | |
815 | execlp("dmesg", "dmesg", NULL); | |
cc8ff7f5 | 816 | ksft_perror("Executing dmesg"); |
ecdbb911 FY |
817 | exit(1); |
818 | } | |
819 | close(pipefds[1]); | |
820 | fp = fdopen(pipefds[0], "r"); | |
821 | if (!fp) { | |
cc8ff7f5 | 822 | ksft_perror("fdopen(pipe)"); |
ecdbb911 FY |
823 | kill(pid, SIGTERM); |
824 | ||
825 | return -1; | |
826 | } | |
827 | ||
828 | while (fgets(line, 1024, fp)) { | |
829 | if (strstr(line, "intel_rdt:")) | |
ca2f4214 | 830 | ksft_print_msg("dmesg: %s", line); |
ecdbb911 | 831 | if (strstr(line, "resctrl:")) |
ca2f4214 | 832 | ksft_print_msg("dmesg: %s", line); |
ecdbb911 FY |
833 | } |
834 | fclose(fp); | |
835 | waitpid(pid, NULL, 0); | |
836 | ||
837 | return 0; | |
838 | } | |
839 | ||
591a6e85 SPP |
840 | int validate_bw_report_request(char *bw_report) |
841 | { | |
842 | if (strcmp(bw_report, "reads") == 0) | |
843 | return 0; | |
844 | if (strcmp(bw_report, "writes") == 0) | |
845 | return 0; | |
846 | if (strcmp(bw_report, "nt-writes") == 0) { | |
847 | strcpy(bw_report, "writes"); | |
848 | return 0; | |
849 | } | |
850 | if (strcmp(bw_report, "total") == 0) | |
851 | return 0; | |
852 | ||
853 | fprintf(stderr, "Requested iMC B/W report type unavailable\n"); | |
854 | ||
855 | return -1; | |
856 | } | |
857 | ||
858 | int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu, | |
859 | int group_fd, unsigned long flags) | |
860 | { | |
861 | int ret; | |
862 | ||
863 | ret = syscall(__NR_perf_event_open, hw_event, pid, cpu, | |
864 | group_fd, flags); | |
865 | return ret; | |
866 | } | |
78941183 FY |
867 | |
868 | unsigned int count_bits(unsigned long n) | |
869 | { | |
870 | unsigned int count = 0; | |
871 | ||
872 | while (n) { | |
873 | count += n & 1; | |
874 | n >>= 1; | |
875 | } | |
876 | ||
877 | return count; | |
878 | } |