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