selftests/resctrl: Mark get_cache_size() cache_type const
[linux-2.6-block.git] / tools / testing / selftests / resctrl / resctrlfs.c
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  */
11 #include <fcntl.h>
12 #include <limits.h>
13
14 #include "resctrl.h"
15
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) {
23                 ksft_perror("/proc/mounts");
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 /*
54  * mount_resctrlfs - Mount resctrl FS at /sys/fs/resctrl
55  *
56  * Mounts resctrl FS. Fails if resctrl FS is already mounted to avoid
57  * pre-existing settings interfering with the test results.
58  *
59  * Return: 0 on success, < 0 on error.
60  */
61 int mount_resctrlfs(void)
62 {
63         int ret;
64
65         ret = find_resctrl_mount(NULL);
66         if (ret != -ENOENT)
67                 return -1;
68
69         ksft_print_msg("Mounting resctrl to \"%s\"\n", RESCTRL_PATH);
70         ret = mount("resctrl", RESCTRL_PATH, "resctrl", 0, NULL);
71         if (ret)
72                 ksft_perror("mount");
73
74         return ret;
75 }
76
77 int umount_resctrlfs(void)
78 {
79         char mountpoint[256];
80         int ret;
81
82         ret = find_resctrl_mount(mountpoint);
83         if (ret == -ENOENT)
84                 return 0;
85         if (ret)
86                 return ret;
87
88         if (umount(mountpoint)) {
89                 ksft_perror("Unable to umount resctrl");
90
91                 return -1;
92         }
93
94         return 0;
95 }
96
97 /*
98  * get_resource_id - Get socket number/l3 id for a specified CPU
99  * @cpu_no:     CPU number
100  * @resource_id: Socket number or l3_id
101  *
102  * Return: >= 0 on success, < 0 on failure.
103  */
104 int get_resource_id(int cpu_no, int *resource_id)
105 {
106         char phys_pkg_path[1024];
107         FILE *fp;
108
109         if (get_vendor() == ARCH_AMD)
110                 sprintf(phys_pkg_path, "%s%d/cache/index3/id",
111                         PHYS_ID_PATH, cpu_no);
112         else
113                 sprintf(phys_pkg_path, "%s%d/topology/physical_package_id",
114                         PHYS_ID_PATH, cpu_no);
115
116         fp = fopen(phys_pkg_path, "r");
117         if (!fp) {
118                 ksft_perror("Failed to open physical_package_id");
119
120                 return -1;
121         }
122         if (fscanf(fp, "%d", resource_id) <= 0) {
123                 ksft_perror("Could not get socket number or l3 id");
124                 fclose(fp);
125
126                 return -1;
127         }
128         fclose(fp);
129
130         return 0;
131 }
132
133 /*
134  * get_cache_size - Get cache size for a specified CPU
135  * @cpu_no:     CPU number
136  * @cache_type: Cache level L2/L3
137  * @cache_size: pointer to cache_size
138  *
139  * Return: = 0 on success, < 0 on failure.
140  */
141 int get_cache_size(int cpu_no, const char *cache_type, unsigned long *cache_size)
142 {
143         char cache_path[1024], cache_str[64];
144         int length, i, cache_num;
145         FILE *fp;
146
147         if (!strcmp(cache_type, "L3")) {
148                 cache_num = 3;
149         } else if (!strcmp(cache_type, "L2")) {
150                 cache_num = 2;
151         } else {
152                 ksft_print_msg("Invalid cache level\n");
153                 return -1;
154         }
155
156         sprintf(cache_path, "/sys/bus/cpu/devices/cpu%d/cache/index%d/size",
157                 cpu_no, cache_num);
158         fp = fopen(cache_path, "r");
159         if (!fp) {
160                 ksft_perror("Failed to open cache size");
161
162                 return -1;
163         }
164         if (fscanf(fp, "%s", cache_str) <= 0) {
165                 ksft_perror("Could not get cache_size");
166                 fclose(fp);
167
168                 return -1;
169         }
170         fclose(fp);
171
172         length = (int)strlen(cache_str);
173
174         *cache_size = 0;
175
176         for (i = 0; i < length; i++) {
177                 if ((cache_str[i] >= '0') && (cache_str[i] <= '9'))
178
179                         *cache_size = *cache_size * 10 + (cache_str[i] - '0');
180
181                 else if (cache_str[i] == 'K')
182
183                         *cache_size = *cache_size * 1024;
184
185                 else if (cache_str[i] == 'M')
186
187                         *cache_size = *cache_size * 1024 * 1024;
188
189                 else
190                         break;
191         }
192
193         return 0;
194 }
195
196 #define CORE_SIBLINGS_PATH      "/sys/bus/cpu/devices/cpu"
197
198 /*
199  * get_bit_mask - Get bit mask from given file
200  * @filename:   File containing the mask
201  * @mask:       The bit mask returned as unsigned long
202  *
203  * Return: = 0 on success, < 0 on failure.
204  */
205 static int get_bit_mask(const char *filename, unsigned long *mask)
206 {
207         FILE *fp;
208
209         if (!filename || !mask)
210                 return -1;
211
212         fp = fopen(filename, "r");
213         if (!fp) {
214                 ksft_print_msg("Failed to open bit mask file '%s': %s\n",
215                                filename, strerror(errno));
216                 return -1;
217         }
218
219         if (fscanf(fp, "%lx", mask) <= 0) {
220                 ksft_print_msg("Could not read bit mask file '%s': %s\n",
221                                filename, strerror(errno));
222                 fclose(fp);
223
224                 return -1;
225         }
226         fclose(fp);
227
228         return 0;
229 }
230
231 /*
232  * get_full_cbm - Get full Cache Bit Mask (CBM)
233  * @cache_type: Cache type as "L2" or "L3"
234  * @mask:       Full cache bit mask representing the maximal portion of cache
235  *              available for allocation, returned as unsigned long.
236  *
237  * Return: = 0 on success, < 0 on failure.
238  */
239 int get_full_cbm(const char *cache_type, unsigned long *mask)
240 {
241         char cbm_path[PATH_MAX];
242         int ret;
243
244         if (!cache_type)
245                 return -1;
246
247         snprintf(cbm_path, sizeof(cbm_path), "%s/%s/cbm_mask",
248                  INFO_PATH, cache_type);
249
250         ret = get_bit_mask(cbm_path, mask);
251         if (ret || !*mask)
252                 return -1;
253
254         return 0;
255 }
256
257 /*
258  * get_core_sibling - Get sibling core id from the same socket for given CPU
259  * @cpu_no:     CPU number
260  *
261  * Return:      > 0 on success, < 0 on failure.
262  */
263 int get_core_sibling(int cpu_no)
264 {
265         char core_siblings_path[1024], cpu_list_str[64];
266         int sibling_cpu_no = -1;
267         FILE *fp;
268
269         sprintf(core_siblings_path, "%s%d/topology/core_siblings_list",
270                 CORE_SIBLINGS_PATH, cpu_no);
271
272         fp = fopen(core_siblings_path, "r");
273         if (!fp) {
274                 ksft_perror("Failed to open core siblings path");
275
276                 return -1;
277         }
278         if (fscanf(fp, "%s", cpu_list_str) <= 0) {
279                 ksft_perror("Could not get core_siblings list");
280                 fclose(fp);
281
282                 return -1;
283         }
284         fclose(fp);
285
286         char *token = strtok(cpu_list_str, "-,");
287
288         while (token) {
289                 sibling_cpu_no = atoi(token);
290                 /* Skipping core 0 as we don't want to run test on core 0 */
291                 if (sibling_cpu_no != 0 && sibling_cpu_no != cpu_no)
292                         break;
293                 token = strtok(NULL, "-,");
294         }
295
296         return sibling_cpu_no;
297 }
298
299 /*
300  * taskset_benchmark - Taskset PID (i.e. benchmark) to a specified cpu
301  * @bm_pid:     PID that should be binded
302  * @cpu_no:     CPU number at which the PID would be binded
303  *
304  * Return: 0 on success, < 0 on error.
305  */
306 int taskset_benchmark(pid_t bm_pid, int cpu_no)
307 {
308         cpu_set_t my_set;
309
310         CPU_ZERO(&my_set);
311         CPU_SET(cpu_no, &my_set);
312
313         if (sched_setaffinity(bm_pid, sizeof(cpu_set_t), &my_set)) {
314                 ksft_perror("Unable to taskset benchmark");
315
316                 return -1;
317         }
318
319         return 0;
320 }
321
322 /*
323  * create_grp - Create a group only if one doesn't exist
324  * @grp_name:   Name of the group
325  * @grp:        Full path and name of the group
326  * @parent_grp: Full path and name of the parent group
327  *
328  * Return: 0 on success, < 0 on error.
329  */
330 static int create_grp(const char *grp_name, char *grp, const char *parent_grp)
331 {
332         int found_grp = 0;
333         struct dirent *ep;
334         DIR *dp;
335
336         /*
337          * At this point, we are guaranteed to have resctrl FS mounted and if
338          * length of grp_name == 0, it means, user wants to use root con_mon
339          * grp, so do nothing
340          */
341         if (strlen(grp_name) == 0)
342                 return 0;
343
344         /* Check if requested grp exists or not */
345         dp = opendir(parent_grp);
346         if (dp) {
347                 while ((ep = readdir(dp)) != NULL) {
348                         if (strcmp(ep->d_name, grp_name) == 0)
349                                 found_grp = 1;
350                 }
351                 closedir(dp);
352         } else {
353                 ksft_perror("Unable to open resctrl for group");
354
355                 return -1;
356         }
357
358         /* Requested grp doesn't exist, hence create it */
359         if (found_grp == 0) {
360                 if (mkdir(grp, 0) == -1) {
361                         ksft_perror("Unable to create group");
362
363                         return -1;
364                 }
365         }
366
367         return 0;
368 }
369
370 static int write_pid_to_tasks(char *tasks, pid_t pid)
371 {
372         FILE *fp;
373
374         fp = fopen(tasks, "w");
375         if (!fp) {
376                 ksft_perror("Failed to open tasks file");
377
378                 return -1;
379         }
380         if (fprintf(fp, "%d\n", pid) < 0) {
381                 ksft_print_msg("Failed to write pid to tasks file\n");
382                 fclose(fp);
383
384                 return -1;
385         }
386         fclose(fp);
387
388         return 0;
389 }
390
391 /*
392  * write_bm_pid_to_resctrl - Write a PID (i.e. benchmark) to resctrl FS
393  * @bm_pid:             PID that should be written
394  * @ctrlgrp:            Name of the control monitor group (con_mon grp)
395  * @mongrp:             Name of the monitor group (mon grp)
396  * @resctrl_val:        Resctrl feature (Eg: mbm, mba.. etc)
397  *
398  * If a con_mon grp is requested, create it and write pid to it, otherwise
399  * write pid to root con_mon grp.
400  * If a mon grp is requested, create it and write pid to it, otherwise
401  * pid is not written, this means that pid is in con_mon grp and hence
402  * should consult con_mon grp's mon_data directory for results.
403  *
404  * Return: 0 on success, < 0 on error.
405  */
406 int write_bm_pid_to_resctrl(pid_t bm_pid, char *ctrlgrp, char *mongrp,
407                             char *resctrl_val)
408 {
409         char controlgroup[128], monitorgroup[512], monitorgroup_p[256];
410         char tasks[1024];
411         int ret = 0;
412
413         if (strlen(ctrlgrp))
414                 sprintf(controlgroup, "%s/%s", RESCTRL_PATH, ctrlgrp);
415         else
416                 sprintf(controlgroup, "%s", RESCTRL_PATH);
417
418         /* Create control and monitoring group and write pid into it */
419         ret = create_grp(ctrlgrp, controlgroup, RESCTRL_PATH);
420         if (ret)
421                 goto out;
422         sprintf(tasks, "%s/tasks", controlgroup);
423         ret = write_pid_to_tasks(tasks, bm_pid);
424         if (ret)
425                 goto out;
426
427         /* Create mon grp and write pid into it for "mbm" and "cmt" test */
428         if (!strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)) ||
429             !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR))) {
430                 if (strlen(mongrp)) {
431                         sprintf(monitorgroup_p, "%s/mon_groups", controlgroup);
432                         sprintf(monitorgroup, "%s/%s", monitorgroup_p, mongrp);
433                         ret = create_grp(mongrp, monitorgroup, monitorgroup_p);
434                         if (ret)
435                                 goto out;
436
437                         sprintf(tasks, "%s/mon_groups/%s/tasks",
438                                 controlgroup, mongrp);
439                         ret = write_pid_to_tasks(tasks, bm_pid);
440                         if (ret)
441                                 goto out;
442                 }
443         }
444
445 out:
446         ksft_print_msg("Writing benchmark parameters to resctrl FS\n");
447         if (ret)
448                 ksft_print_msg("Failed writing to resctrlfs\n");
449
450         return ret;
451 }
452
453 /*
454  * write_schemata - Update schemata of a con_mon grp
455  * @ctrlgrp:            Name of the con_mon grp
456  * @schemata:           Schemata that should be updated to
457  * @cpu_no:             CPU number that the benchmark PID is binded to
458  * @resctrl_val:        Resctrl feature (Eg: mbm, mba.. etc)
459  *
460  * Update schemata of a con_mon grp *only* if requested resctrl feature is
461  * allocation type
462  *
463  * Return: 0 on success, < 0 on error.
464  */
465 int write_schemata(char *ctrlgrp, char *schemata, int cpu_no, char *resctrl_val)
466 {
467         char controlgroup[1024], reason[128], schema[1024] = {};
468         int resource_id, fd, schema_len = -1, ret = 0;
469
470         if (strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) &&
471             strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)) &&
472             strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) &&
473             strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
474                 return -ENOENT;
475
476         if (!schemata) {
477                 ksft_print_msg("Skipping empty schemata update\n");
478
479                 return -1;
480         }
481
482         if (get_resource_id(cpu_no, &resource_id) < 0) {
483                 sprintf(reason, "Failed to get resource id");
484                 ret = -1;
485
486                 goto out;
487         }
488
489         if (strlen(ctrlgrp) != 0)
490                 sprintf(controlgroup, "%s/%s/schemata", RESCTRL_PATH, ctrlgrp);
491         else
492                 sprintf(controlgroup, "%s/schemata", RESCTRL_PATH);
493
494         if (!strncmp(resctrl_val, CAT_STR, sizeof(CAT_STR)) ||
495             !strncmp(resctrl_val, CMT_STR, sizeof(CMT_STR)))
496                 schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n",
497                                       "L3:", resource_id, '=', schemata);
498         if (!strncmp(resctrl_val, MBA_STR, sizeof(MBA_STR)) ||
499             !strncmp(resctrl_val, MBM_STR, sizeof(MBM_STR)))
500                 schema_len = snprintf(schema, sizeof(schema), "%s%d%c%s\n",
501                                       "MB:", resource_id, '=', schemata);
502         if (schema_len < 0 || schema_len >= sizeof(schema)) {
503                 snprintf(reason, sizeof(reason),
504                          "snprintf() failed with return value : %d", schema_len);
505                 ret = -1;
506                 goto out;
507         }
508
509         fd = open(controlgroup, O_WRONLY);
510         if (fd < 0) {
511                 snprintf(reason, sizeof(reason),
512                          "open() failed : %s", strerror(errno));
513                 ret = -1;
514
515                 goto err_schema_not_empty;
516         }
517         if (write(fd, schema, schema_len) < 0) {
518                 snprintf(reason, sizeof(reason),
519                          "write() failed : %s", strerror(errno));
520                 close(fd);
521                 ret = -1;
522
523                 goto err_schema_not_empty;
524         }
525         close(fd);
526
527 err_schema_not_empty:
528         schema[schema_len - 1] = 0;
529 out:
530         ksft_print_msg("Write schema \"%s\" to resctrl FS%s%s\n",
531                        schema, ret ? " # " : "",
532                        ret ? reason : "");
533
534         return ret;
535 }
536
537 bool check_resctrlfs_support(void)
538 {
539         FILE *inf = fopen("/proc/filesystems", "r");
540         DIR *dp;
541         char *res;
542         bool ret = false;
543
544         if (!inf)
545                 return false;
546
547         res = fgrep(inf, "nodev\tresctrl\n");
548
549         if (res) {
550                 ret = true;
551                 free(res);
552         }
553
554         fclose(inf);
555
556         ksft_print_msg("%s Check kernel supports resctrl filesystem\n",
557                        ret ? "Pass:" : "Fail:");
558
559         if (!ret)
560                 return ret;
561
562         dp = opendir(RESCTRL_PATH);
563         ksft_print_msg("%s Check resctrl mountpoint \"%s\" exists\n",
564                        dp ? "Pass:" : "Fail:", RESCTRL_PATH);
565         if (dp)
566                 closedir(dp);
567
568         ksft_print_msg("resctrl filesystem %s mounted\n",
569                        find_resctrl_mount(NULL) ? "not" : "is");
570
571         return ret;
572 }
573
574 char *fgrep(FILE *inf, const char *str)
575 {
576         char line[256];
577         int slen = strlen(str);
578
579         while (!feof(inf)) {
580                 if (!fgets(line, 256, inf))
581                         break;
582                 if (strncmp(line, str, slen))
583                         continue;
584
585                 return strdup(line);
586         }
587
588         return NULL;
589 }
590
591 /*
592  * validate_resctrl_feature_request - Check if requested feature is valid.
593  * @resource:   Required resource (e.g., MB, L3, L2, L3_MON, etc.)
594  * @feature:    Required monitor feature (in mon_features file). Can only be
595  *              set for L3_MON. Must be NULL for all other resources.
596  *
597  * Return: True if the resource/feature is supported, else false. False is
598  *         also returned if resctrl FS is not mounted.
599  */
600 bool validate_resctrl_feature_request(const char *resource, const char *feature)
601 {
602         char res_path[PATH_MAX];
603         struct stat statbuf;
604         char *res;
605         FILE *inf;
606         int ret;
607
608         if (!resource)
609                 return false;
610
611         ret = find_resctrl_mount(NULL);
612         if (ret)
613                 return false;
614
615         snprintf(res_path, sizeof(res_path), "%s/%s", INFO_PATH, resource);
616
617         if (stat(res_path, &statbuf))
618                 return false;
619
620         if (!feature)
621                 return true;
622
623         snprintf(res_path, sizeof(res_path), "%s/%s/mon_features", INFO_PATH, resource);
624         inf = fopen(res_path, "r");
625         if (!inf)
626                 return false;
627
628         res = fgrep(inf, feature);
629         free(res);
630         fclose(inf);
631
632         return !!res;
633 }
634
635 int filter_dmesg(void)
636 {
637         char line[1024];
638         FILE *fp;
639         int pipefds[2];
640         pid_t pid;
641         int ret;
642
643         ret = pipe(pipefds);
644         if (ret) {
645                 ksft_perror("pipe");
646                 return ret;
647         }
648         fflush(stdout);
649         pid = fork();
650         if (pid == 0) {
651                 close(pipefds[0]);
652                 dup2(pipefds[1], STDOUT_FILENO);
653                 execlp("dmesg", "dmesg", NULL);
654                 ksft_perror("Executing dmesg");
655                 exit(1);
656         }
657         close(pipefds[1]);
658         fp = fdopen(pipefds[0], "r");
659         if (!fp) {
660                 ksft_perror("fdopen(pipe)");
661                 kill(pid, SIGTERM);
662
663                 return -1;
664         }
665
666         while (fgets(line, 1024, fp)) {
667                 if (strstr(line, "intel_rdt:"))
668                         ksft_print_msg("dmesg: %s", line);
669                 if (strstr(line, "resctrl:"))
670                         ksft_print_msg("dmesg: %s", line);
671         }
672         fclose(fp);
673         waitpid(pid, NULL, 0);
674
675         return 0;
676 }
677
678 int validate_bw_report_request(char *bw_report)
679 {
680         if (strcmp(bw_report, "reads") == 0)
681                 return 0;
682         if (strcmp(bw_report, "writes") == 0)
683                 return 0;
684         if (strcmp(bw_report, "nt-writes") == 0) {
685                 strcpy(bw_report, "writes");
686                 return 0;
687         }
688         if (strcmp(bw_report, "total") == 0)
689                 return 0;
690
691         fprintf(stderr, "Requested iMC B/W report type unavailable\n");
692
693         return -1;
694 }
695
696 int perf_event_open(struct perf_event_attr *hw_event, pid_t pid, int cpu,
697                     int group_fd, unsigned long flags)
698 {
699         int ret;
700
701         ret = syscall(__NR_perf_event_open, hw_event, pid, cpu,
702                       group_fd, flags);
703         return ret;
704 }
705
706 unsigned int count_bits(unsigned long n)
707 {
708         unsigned int count = 0;
709
710         while (n) {
711                 count += n & 1;
712                 n >>= 1;
713         }
714
715         return count;
716 }