KVM: SVM: Rename vmplX_ssp -> plX_ssp
[linux-2.6-block.git] / tools / tracing / rtla / src / utils.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Copyright (C) 2021 Red Hat Inc, Daniel Bristot de Oliveira <bristot@kernel.org>
4  */
5
6 #define _GNU_SOURCE
7 #include <dirent.h>
8 #include <stdarg.h>
9 #include <stdlib.h>
10 #include <string.h>
11 #include <unistd.h>
12 #include <ctype.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <sched.h>
16 #include <stdio.h>
17
18 #include "utils.h"
19
20 #define MAX_MSG_LENGTH  1024
21 int config_debug;
22
23 /*
24  * err_msg - print an error message to the stderr
25  */
26 void err_msg(const char *fmt, ...)
27 {
28         char message[MAX_MSG_LENGTH];
29         va_list ap;
30
31         va_start(ap, fmt);
32         vsnprintf(message, sizeof(message), fmt, ap);
33         va_end(ap);
34
35         fprintf(stderr, "%s", message);
36 }
37
38 /*
39  * debug_msg - print a debug message to stderr if debug is set
40  */
41 void debug_msg(const char *fmt, ...)
42 {
43         char message[MAX_MSG_LENGTH];
44         va_list ap;
45
46         if (!config_debug)
47                 return;
48
49         va_start(ap, fmt);
50         vsnprintf(message, sizeof(message), fmt, ap);
51         va_end(ap);
52
53         fprintf(stderr, "%s", message);
54 }
55
56 /*
57  * get_llong_from_str - get a long long int from a string
58  */
59 long long get_llong_from_str(char *start)
60 {
61         long long value;
62         char *end;
63
64         errno = 0;
65         value = strtoll(start, &end, 10);
66         if (errno || start == end)
67                 return -1;
68
69         return value;
70 }
71
72 /*
73  * get_duration - fill output with a human readable duration since start_time
74  */
75 void get_duration(time_t start_time, char *output, int output_size)
76 {
77         time_t now = time(NULL);
78         struct tm *tm_info;
79         time_t duration;
80
81         duration = difftime(now, start_time);
82         tm_info = gmtime(&duration);
83
84         snprintf(output, output_size, "%3d %02d:%02d:%02d",
85                         tm_info->tm_yday,
86                         tm_info->tm_hour,
87                         tm_info->tm_min,
88                         tm_info->tm_sec);
89 }
90
91 /*
92  * parse_cpu_set - parse a cpu_list filling cpu_set_t argument
93  *
94  * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set
95  * filling cpu_set_t argument.
96  *
97  * Returns 1 on success, 0 otherwise.
98  */
99 int parse_cpu_set(char *cpu_list, cpu_set_t *set)
100 {
101         const char *p;
102         int end_cpu;
103         int nr_cpus;
104         int cpu;
105         int i;
106
107         CPU_ZERO(set);
108
109         nr_cpus = sysconf(_SC_NPROCESSORS_CONF);
110
111         for (p = cpu_list; *p; ) {
112                 cpu = atoi(p);
113                 if (cpu < 0 || (!cpu && *p != '0') || cpu >= nr_cpus)
114                         goto err;
115
116                 while (isdigit(*p))
117                         p++;
118                 if (*p == '-') {
119                         p++;
120                         end_cpu = atoi(p);
121                         if (end_cpu < cpu || (!end_cpu && *p != '0') || end_cpu >= nr_cpus)
122                                 goto err;
123                         while (isdigit(*p))
124                                 p++;
125                 } else
126                         end_cpu = cpu;
127
128                 if (cpu == end_cpu) {
129                         debug_msg("cpu_set: adding cpu %d\n", cpu);
130                         CPU_SET(cpu, set);
131                 } else {
132                         for (i = cpu; i <= end_cpu; i++) {
133                                 debug_msg("cpu_set: adding cpu %d\n", i);
134                                 CPU_SET(i, set);
135                         }
136                 }
137
138                 if (*p == ',')
139                         p++;
140         }
141
142         return 0;
143 err:
144         debug_msg("Error parsing the cpu set %s\n", cpu_list);
145         return 1;
146 }
147
148 /*
149  * parse_duration - parse duration with s/m/h/d suffix converting it to seconds
150  */
151 long parse_seconds_duration(char *val)
152 {
153         char *end;
154         long t;
155
156         t = strtol(val, &end, 10);
157
158         if (end) {
159                 switch (*end) {
160                 case 's':
161                 case 'S':
162                         break;
163                 case 'm':
164                 case 'M':
165                         t *= 60;
166                         break;
167                 case 'h':
168                 case 'H':
169                         t *= 60 * 60;
170                         break;
171
172                 case 'd':
173                 case 'D':
174                         t *= 24 * 60 * 60;
175                         break;
176                 }
177         }
178
179         return t;
180 }
181
182 /*
183  * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds
184  */
185 long parse_ns_duration(char *val)
186 {
187         char *end;
188         long t;
189
190         t = strtol(val, &end, 10);
191
192         if (end) {
193                 if (!strncmp(end, "ns", 2)) {
194                         return t;
195                 } else if (!strncmp(end, "us", 2)) {
196                         t *= 1000;
197                         return t;
198                 } else if (!strncmp(end, "ms", 2)) {
199                         t *= 1000 * 1000;
200                         return t;
201                 } else if (!strncmp(end, "s", 1)) {
202                         t *= 1000 * 1000 * 1000;
203                         return t;
204                 }
205                 return -1;
206         }
207
208         return t;
209 }
210
211 /*
212  * This is a set of helper functions to use SCHED_DEADLINE.
213  */
214 #ifdef __x86_64__
215 # define __NR_sched_setattr     314
216 # define __NR_sched_getattr     315
217 #elif __i386__
218 # define __NR_sched_setattr     351
219 # define __NR_sched_getattr     352
220 #elif __arm__
221 # define __NR_sched_setattr     380
222 # define __NR_sched_getattr     381
223 #elif __aarch64__ || __riscv
224 # define __NR_sched_setattr     274
225 # define __NR_sched_getattr     275
226 #elif __powerpc__
227 # define __NR_sched_setattr     355
228 # define __NR_sched_getattr     356
229 #elif __s390x__
230 # define __NR_sched_setattr     345
231 # define __NR_sched_getattr     346
232 #endif
233
234 #define SCHED_DEADLINE          6
235
236 static inline int sched_setattr(pid_t pid, const struct sched_attr *attr,
237                                 unsigned int flags) {
238         return syscall(__NR_sched_setattr, pid, attr, flags);
239 }
240
241 static inline int sched_getattr(pid_t pid, struct sched_attr *attr,
242                                 unsigned int size, unsigned int flags)
243 {
244         return syscall(__NR_sched_getattr, pid, attr, size, flags);
245 }
246
247 int __set_sched_attr(int pid, struct sched_attr *attr)
248 {
249         int flags = 0;
250         int retval;
251
252         retval = sched_setattr(pid, attr, flags);
253         if (retval < 0) {
254                 err_msg("Failed to set sched attributes to the pid %d: %s\n",
255                         pid, strerror(errno));
256                 return 1;
257         }
258
259         return 0;
260 }
261
262 /*
263  * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm
264  *
265  * Check if the procfs entry is a directory of a process, and then check if the
266  * process has a comm with the prefix set in char *comm_prefix. As the
267  * current users of this function only check for kernel threads, there is no
268  * need to check for the threads for the process.
269  *
270  * Return: True if the proc_entry contains a comm file with comm_prefix*.
271  * Otherwise returns false.
272  */
273 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry)
274 {
275         char buffer[MAX_PATH];
276         int comm_fd, retval;
277         char *t_name;
278
279         if (proc_entry->d_type != DT_DIR)
280                 return 0;
281
282         if (*proc_entry->d_name == '.')
283                 return 0;
284
285         /* check if the string is a pid */
286         for (t_name = proc_entry->d_name; t_name; t_name++) {
287                 if (!isdigit(*t_name))
288                         break;
289         }
290
291         if (*t_name != '\0')
292                 return 0;
293
294         snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name);
295         comm_fd = open(buffer, O_RDONLY);
296         if (comm_fd < 0)
297                 return 0;
298
299         memset(buffer, 0, MAX_PATH);
300         retval = read(comm_fd, buffer, MAX_PATH);
301
302         close(comm_fd);
303
304         if (retval <= 0)
305                 return 0;
306
307         retval = strncmp(comm_prefix, buffer, strlen(comm_prefix));
308         if (retval)
309                 return 0;
310
311         /* comm already have \n */
312         debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer);
313
314         return 1;
315 }
316
317 /*
318  * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix
319  *
320  * This function uses procfs to list the currently running threads and then set the
321  * sched_attr *attr to the threads that start with char *comm_prefix. It is
322  * mainly used to set the priority to the kernel threads created by the
323  * tracers.
324  */
325 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr)
326 {
327         struct dirent *proc_entry;
328         DIR *procfs;
329         int retval;
330
331         if (strlen(comm_prefix) >= MAX_PATH) {
332                 err_msg("Command prefix is too long: %d < strlen(%s)\n",
333                         MAX_PATH, comm_prefix);
334                 return 1;
335         }
336
337         procfs = opendir("/proc");
338         if (!procfs) {
339                 err_msg("Could not open procfs\n");
340                 return 1;
341         }
342
343         while ((proc_entry = readdir(procfs))) {
344
345                 retval = procfs_is_workload_pid(comm_prefix, proc_entry);
346                 if (!retval)
347                         continue;
348
349                 /* procfs_is_workload_pid confirmed it is a pid */
350                 retval = __set_sched_attr(atoi(proc_entry->d_name), attr);
351                 if (retval) {
352                         err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name);
353                         goto out_err;
354                 }
355
356                 debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name);
357         }
358         return 0;
359
360 out_err:
361         closedir(procfs);
362         return 1;
363 }
364
365 #define INVALID_VAL     (~0L)
366 static long get_long_ns_after_colon(char *start)
367 {
368         long val = INVALID_VAL;
369
370         /* find the ":" */
371         start = strstr(start, ":");
372         if (!start)
373                 return -1;
374
375         /* skip ":" */
376         start++;
377         val = parse_ns_duration(start);
378
379         return val;
380 }
381
382 static long get_long_after_colon(char *start)
383 {
384         long val = INVALID_VAL;
385
386         /* find the ":" */
387         start = strstr(start, ":");
388         if (!start)
389                 return -1;
390
391         /* skip ":" */
392         start++;
393         val = get_llong_from_str(start);
394
395         return val;
396 }
397
398 /*
399  * parse priority in the format:
400  * SCHED_OTHER:
401  *              o:<prio>
402  *              O:<prio>
403  * SCHED_RR:
404  *              r:<prio>
405  *              R:<prio>
406  * SCHED_FIFO:
407  *              f:<prio>
408  *              F:<prio>
409  * SCHED_DEADLINE:
410  *              d:runtime:period
411  *              D:runtime:period
412  */
413 int parse_prio(char *arg, struct sched_attr *sched_param)
414 {
415         long prio;
416         long runtime;
417         long period;
418
419         memset(sched_param, 0, sizeof(*sched_param));
420         sched_param->size = sizeof(*sched_param);
421
422         switch (arg[0]) {
423         case 'd':
424         case 'D':
425                 /* d:runtime:period */
426                 if (strlen(arg) < 4)
427                         return -1;
428
429                 runtime = get_long_ns_after_colon(arg);
430                 if (runtime == INVALID_VAL)
431                         return -1;
432
433                 period = get_long_ns_after_colon(&arg[2]);
434                 if (period == INVALID_VAL)
435                         return -1;
436
437                 if (runtime > period)
438                         return -1;
439
440                 sched_param->sched_policy   = SCHED_DEADLINE;
441                 sched_param->sched_runtime  = runtime;
442                 sched_param->sched_deadline = period;
443                 sched_param->sched_period   = period;
444                 break;
445         case 'f':
446         case 'F':
447                 /* f:prio */
448                 prio = get_long_after_colon(arg);
449                 if (prio == INVALID_VAL)
450                         return -1;
451
452                 if (prio < sched_get_priority_min(SCHED_FIFO))
453                         return -1;
454                 if (prio > sched_get_priority_max(SCHED_FIFO))
455                         return -1;
456
457                 sched_param->sched_policy   = SCHED_FIFO;
458                 sched_param->sched_priority = prio;
459                 break;
460         case 'r':
461         case 'R':
462                 /* r:prio */
463                 prio = get_long_after_colon(arg);
464                 if (prio == INVALID_VAL)
465                         return -1;
466
467                 if (prio < sched_get_priority_min(SCHED_RR))
468                         return -1;
469                 if (prio > sched_get_priority_max(SCHED_RR))
470                         return -1;
471
472                 sched_param->sched_policy   = SCHED_RR;
473                 sched_param->sched_priority = prio;
474                 break;
475         case 'o':
476         case 'O':
477                 /* o:prio */
478                 prio = get_long_after_colon(arg);
479                 if (prio == INVALID_VAL)
480                         return -1;
481
482                 if (prio < sched_get_priority_min(SCHED_OTHER))
483                         return -1;
484                 if (prio > sched_get_priority_max(SCHED_OTHER))
485                         return -1;
486
487                 sched_param->sched_policy   = SCHED_OTHER;
488                 sched_param->sched_priority = prio;
489                 break;
490         default:
491                 return -1;
492         }
493         return 0;
494 }
495
496 /*
497  * set_cpu_dma_latency - set the /dev/cpu_dma_latecy
498  *
499  * This is used to reduce the exit from idle latency. The value
500  * will be reset once the file descriptor of /dev/cpu_dma_latecy
501  * is closed.
502  *
503  * Return: the /dev/cpu_dma_latecy file descriptor
504  */
505 int set_cpu_dma_latency(int32_t latency)
506 {
507         int retval;
508         int fd;
509
510         fd = open("/dev/cpu_dma_latency", O_RDWR);
511         if (fd < 0) {
512                 err_msg("Error opening /dev/cpu_dma_latency\n");
513                 return -1;
514         }
515
516         retval = write(fd, &latency, 4);
517         if (retval < 1) {
518                 err_msg("Error setting /dev/cpu_dma_latency\n");
519                 close(fd);
520                 return -1;
521         }
522
523         debug_msg("Set /dev/cpu_dma_latency to %d\n", latency);
524
525         return fd;
526 }
527
528 #define _STR(x) #x
529 #define STR(x) _STR(x)
530
531 /*
532  * find_mount - find a the mount point of a given fs
533  *
534  * Returns 0 if mount is not found, otherwise return 1 and fill mp
535  * with the mount point.
536  */
537 static const int find_mount(const char *fs, char *mp, int sizeof_mp)
538 {
539         char mount_point[MAX_PATH];
540         char type[100];
541         int found = 0;
542         FILE *fp;
543
544         fp = fopen("/proc/mounts", "r");
545         if (!fp)
546                 return 0;
547
548         while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", mount_point, type) == 2) {
549                 if (strcmp(type, fs) == 0) {
550                         found = 1;
551                         break;
552                 }
553         }
554         fclose(fp);
555
556         if (!found)
557                 return 0;
558
559         memset(mp, 0, sizeof_mp);
560         strncpy(mp, mount_point, sizeof_mp - 1);
561
562         debug_msg("Fs %s found at %s\n", fs, mp);
563         return 1;
564 }
565
566 /*
567  * get_self_cgroup - get the current thread cgroup path
568  *
569  * Parse /proc/$$/cgroup file to get the thread's cgroup. As an example of line to parse:
570  *
571  * 0::/user.slice/user-0.slice/session-3.scope'\n'
572  *
573  * This function is interested in the content after the second : and before the '\n'.
574  *
575  * Returns 1 if a string was found, 0 otherwise.
576  */
577 static int get_self_cgroup(char *self_cg, int sizeof_self_cg)
578 {
579         char path[MAX_PATH], *start;
580         int fd, retval;
581
582         snprintf(path, MAX_PATH, "/proc/%d/cgroup", getpid());
583
584         fd = open(path, O_RDONLY);
585         if (fd < 0)
586                 return 0;
587
588         retval = read(fd, path, MAX_PATH);
589
590         close(fd);
591
592         if (retval <= 0)
593                 return 0;
594
595         start = path;
596
597         start = strstr(start, ":");
598         if (!start)
599                 return 0;
600
601         /* skip ":" */
602         start++;
603
604         start = strstr(start, ":");
605         if (!start)
606                 return 0;
607
608         /* skip ":" */
609         start++;
610
611         if (strlen(start) >= sizeof_self_cg)
612                 return 0;
613
614         snprintf(self_cg, sizeof_self_cg, "%s", start);
615
616         /* Swap '\n' with '\0' */
617         start = strstr(self_cg, "\n");
618
619         /* there must be '\n' */
620         if (!start)
621                 return 0;
622
623         /* ok, it found a string after the second : and before the \n */
624         *start = '\0';
625
626         return 1;
627 }
628
629 /*
630  * set_comm_cgroup - Set cgroup to pid_t pid
631  *
632  * If cgroup argument is not NULL, the threads will move to the given cgroup.
633  * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
634  *
635  * Supports cgroup v2.
636  *
637  * Returns 1 on success, 0 otherwise.
638  */
639 int set_pid_cgroup(pid_t pid, const char *cgroup)
640 {
641         char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
642         char cgroup_procs[MAX_PATH];
643         char pid_str[24];
644         int retval;
645         int cg_fd;
646
647         retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
648         if (!retval) {
649                 err_msg("Did not find cgroupv2 mount point\n");
650                 return 0;
651         }
652
653         if (!cgroup) {
654                 retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
655                                 sizeof(cgroup_path) - strlen(cgroup_path));
656                 if (!retval) {
657                         err_msg("Did not find self cgroup\n");
658                         return 0;
659                 }
660         } else {
661                 snprintf(&cgroup_path[strlen(cgroup_path)],
662                                 sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
663         }
664
665         snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
666
667         debug_msg("Using cgroup path at: %s\n", cgroup_procs);
668
669         cg_fd = open(cgroup_procs, O_RDWR);
670         if (cg_fd < 0)
671                 return 0;
672
673         snprintf(pid_str, sizeof(pid_str), "%d\n", pid);
674
675         retval = write(cg_fd, pid_str, strlen(pid_str));
676         if (retval < 0)
677                 err_msg("Error setting cgroup attributes for pid:%s - %s\n",
678                                 pid_str, strerror(errno));
679         else
680                 debug_msg("Set cgroup attributes for pid:%s\n", pid_str);
681
682         close(cg_fd);
683
684         return (retval >= 0);
685 }
686
687 /**
688  * set_comm_cgroup - Set cgroup to threads starting with char *comm_prefix
689  *
690  * If cgroup argument is not NULL, the threads will move to the given cgroup.
691  * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used.
692  *
693  * Supports cgroup v2.
694  *
695  * Returns 1 on success, 0 otherwise.
696  */
697 int set_comm_cgroup(const char *comm_prefix, const char *cgroup)
698 {
699         char cgroup_path[MAX_PATH - strlen("/cgroup.procs")];
700         char cgroup_procs[MAX_PATH];
701         struct dirent *proc_entry;
702         DIR *procfs;
703         int retval;
704         int cg_fd;
705
706         if (strlen(comm_prefix) >= MAX_PATH) {
707                 err_msg("Command prefix is too long: %d < strlen(%s)\n",
708                         MAX_PATH, comm_prefix);
709                 return 0;
710         }
711
712         retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path));
713         if (!retval) {
714                 err_msg("Did not find cgroupv2 mount point\n");
715                 return 0;
716         }
717
718         if (!cgroup) {
719                 retval = get_self_cgroup(&cgroup_path[strlen(cgroup_path)],
720                                 sizeof(cgroup_path) - strlen(cgroup_path));
721                 if (!retval) {
722                         err_msg("Did not find self cgroup\n");
723                         return 0;
724                 }
725         } else {
726                 snprintf(&cgroup_path[strlen(cgroup_path)],
727                                 sizeof(cgroup_path) - strlen(cgroup_path), "%s/", cgroup);
728         }
729
730         snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path);
731
732         debug_msg("Using cgroup path at: %s\n", cgroup_procs);
733
734         cg_fd = open(cgroup_procs, O_RDWR);
735         if (cg_fd < 0)
736                 return 0;
737
738         procfs = opendir("/proc");
739         if (!procfs) {
740                 err_msg("Could not open procfs\n");
741                 goto out_cg;
742         }
743
744         while ((proc_entry = readdir(procfs))) {
745
746                 retval = procfs_is_workload_pid(comm_prefix, proc_entry);
747                 if (!retval)
748                         continue;
749
750                 retval = write(cg_fd, proc_entry->d_name, strlen(proc_entry->d_name));
751                 if (retval < 0) {
752                         err_msg("Error setting cgroup attributes for pid:%s - %s\n",
753                                 proc_entry->d_name, strerror(errno));
754                         goto out_procfs;
755                 }
756
757                 debug_msg("Set cgroup attributes for pid:%s\n", proc_entry->d_name);
758         }
759
760         closedir(procfs);
761         close(cg_fd);
762         return 1;
763
764 out_procfs:
765         closedir(procfs);
766 out_cg:
767         close(cg_fd);
768         return 0;
769 }
770
771 /**
772  * auto_house_keeping - Automatically move rtla out of measurement threads
773  *
774  * Try to move rtla away from the tracer, if possible.
775  *
776  * Returns 1 on success, 0 otherwise.
777  */
778 int auto_house_keeping(cpu_set_t *monitored_cpus)
779 {
780         cpu_set_t rtla_cpus, house_keeping_cpus;
781         int retval;
782
783         /* first get the CPUs in which rtla can actually run. */
784         retval = sched_getaffinity(getpid(), sizeof(rtla_cpus), &rtla_cpus);
785         if (retval == -1) {
786                 debug_msg("Could not get rtla affinity, rtla might run with the threads!\n");
787                 return 0;
788         }
789
790         /* then check if the existing setup is already good. */
791         CPU_AND(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
792         if (!CPU_COUNT(&house_keeping_cpus)) {
793                 debug_msg("rtla and the monitored CPUs do not share CPUs.");
794                 debug_msg("Skipping auto house-keeping\n");
795                 return 1;
796         }
797
798         /* remove the intersection */
799         CPU_XOR(&house_keeping_cpus, &rtla_cpus, monitored_cpus);
800
801         /* get only those that rtla can run */
802         CPU_AND(&house_keeping_cpus, &house_keeping_cpus, &rtla_cpus);
803
804         /* is there any cpu left? */
805         if (!CPU_COUNT(&house_keeping_cpus)) {
806                 debug_msg("Could not find any CPU for auto house-keeping\n");
807                 return 0;
808         }
809
810         retval = sched_setaffinity(getpid(), sizeof(house_keeping_cpus), &house_keeping_cpus);
811         if (retval == -1) {
812                 debug_msg("Could not set affinity for auto house-keeping\n");
813                 return 0;
814         }
815
816         debug_msg("rtla automatically moved to an auto house-keeping cpu set\n");
817
818         return 1;
819 }