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 #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 #include <limits.h> 21 22 #include "common.h" 23 24 #define MAX_MSG_LENGTH 1024 25 bool config_debug; 26 27 /* 28 * err_msg - print an error message to the stderr 29 */ 30 void err_msg(const char *fmt, ...) 31 { 32 char message[MAX_MSG_LENGTH]; 33 va_list ap; 34 35 va_start(ap, fmt); 36 vsnprintf(message, sizeof(message), fmt, ap); 37 va_end(ap); 38 39 fprintf(stderr, "%s", message); 40 } 41 42 /* 43 * debug_msg - print a debug message to stderr if debug is set 44 */ 45 void debug_msg(const char *fmt, ...) 46 { 47 char message[MAX_MSG_LENGTH]; 48 va_list ap; 49 50 if (!config_debug) 51 return; 52 53 va_start(ap, fmt); 54 vsnprintf(message, sizeof(message), fmt, ap); 55 va_end(ap); 56 57 fprintf(stderr, "%s", message); 58 } 59 60 /* 61 * fatal - print an error message and EOL to stderr and exit with ERROR 62 */ 63 void fatal(const char *fmt, ...) 64 { 65 va_list ap; 66 67 va_start(ap, fmt); 68 vfprintf(stderr, fmt, ap); 69 va_end(ap); 70 fprintf(stderr, "\n"); 71 72 exit(ERROR); 73 } 74 75 /* 76 * get_llong_from_str - get a long long int from a string 77 */ 78 long long get_llong_from_str(char *start) 79 { 80 long long value; 81 char *end; 82 83 errno = 0; 84 value = strtoll(start, &end, 10); 85 if (errno || start == end) 86 return -1; 87 88 return value; 89 } 90 91 /* 92 * get_duration - fill output with a human readable duration since start_time 93 */ 94 void get_duration(time_t start_time, char *output, int output_size) 95 { 96 time_t now = time(NULL); 97 struct tm *tm_info; 98 time_t duration; 99 100 duration = difftime(now, start_time); 101 tm_info = gmtime(&duration); 102 103 snprintf(output, output_size, "%3d %02d:%02d:%02d", 104 tm_info->tm_yday, 105 tm_info->tm_hour, 106 tm_info->tm_min, 107 tm_info->tm_sec); 108 } 109 110 /* 111 * cpu_list_iterate - parse a cpu list and call a function on each element 112 * 113 * If callback returns a non-zero value, the iteration is stopped. 114 * 115 * Returns the number of cpus in the list (including duplicates) on success, 116 * callback return value on break, and -1 on error. 117 */ 118 int cpu_list_iterate(const char *cpu_list, int (*callback)(int, void *), void *data) 119 { 120 const char *p; 121 int i, cpu, end_cpu, count = 0, retval; 122 123 for (p = cpu_list; *p && *p != '\n'; ) { 124 cpu = atoi(p); 125 if (cpu < 0 || (!cpu && *p != '0')) 126 return -1; 127 128 while (isdigit(*p)) 129 p++; 130 if (*p == '-') { 131 p++; 132 end_cpu = atoi(p); 133 if (end_cpu < cpu || (!end_cpu && *p != '0')) 134 return -1; 135 while (isdigit(*p)) 136 p++; 137 } else 138 end_cpu = cpu; 139 140 for (i = cpu; i <= end_cpu; i++) { 141 retval = callback(i, data); 142 if (retval) 143 return retval; 144 ++count; 145 } 146 147 if (*p == ',') 148 p++; 149 } 150 151 return count; 152 } 153 154 static int max_cpu_callback(int i, void *data) 155 { 156 int *max_cpu = data; 157 158 if (i > *max_cpu) 159 *max_cpu = i; 160 161 return 0; 162 } 163 164 /* 165 * get_max_cpu_from_list - get the maximum CPU in a CPU list 166 * 167 * Returns maximum CPU number, or -1 on error 168 */ 169 int get_max_cpu_from_list(const char *cpu_list) 170 { 171 int max_cpu = -1; 172 173 if (cpu_list_iterate(cpu_list, max_cpu_callback, &max_cpu) < 0) 174 return -1; 175 176 return max_cpu; 177 } 178 179 180 static int tmp_cpu_set_callback(int i, void *data) 181 { 182 bool *cpu_set = data; 183 184 cpu_set[i] = true; 185 186 return 0; 187 } 188 189 /* 190 * get_possible_cpus - get the number of possible CPUs from sysfs 191 * 192 * Parse /sys/devices/system/cpu/possible to determine the number of 193 * possible CPUs. Only contiguous zero-based CPUs lists are accepted. 194 * 195 * Returns the number of possible CPUs, or a negative value on error: 196 * - -1 if the file is unreadable, 197 * - -2 if parsing failed, 198 * - -3 if the cpu list is non-zero-based or non-contiguous. 199 */ 200 int get_possible_cpus(void) 201 { 202 char *str = NULL; 203 size_t len = 0; 204 int nr_cpus = 0, max_cpu = -1, i; 205 FILE *fp; 206 bool *cpu_set; 207 208 fp = fopen("/sys/devices/system/cpu/possible", "r"); 209 if (!fp) 210 return -1; 211 212 if (getline(&str, &len, fp) < 1) { 213 /* cpu string should be at least 1 character */ 214 if (str) 215 free(str); 216 fclose(fp); 217 return -1; 218 } 219 220 fclose(fp); 221 222 /* get maximum cpu number */ 223 if (cpu_list_iterate(str, max_cpu_callback, &max_cpu) < 0) { 224 free(str); 225 return -2; 226 } 227 228 if (max_cpu < 0 || max_cpu == INT_MAX) { 229 /* empty or bogus cpu list */ 230 free(str); 231 return -2; 232 } 233 234 /* get max cpu using dynamic array, as nr_cpus might be > 1024 */ 235 cpu_set = calloc(max_cpu + 1, sizeof(bool)); 236 if (!cpu_set) { 237 free(str); 238 return -2; 239 } 240 if (cpu_list_iterate(str, tmp_cpu_set_callback, cpu_set) < 0) { 241 free(str); 242 free(cpu_set); 243 return -2; 244 } 245 for (i = 0; i <= max_cpu; i++) { 246 if (cpu_set[i]) 247 ++nr_cpus; 248 } 249 free(cpu_set); 250 251 free(str); 252 253 if (max_cpu >= nr_cpus) 254 /* rtla assumes cpu < nr_cpus for all cpus */ 255 return -3; 256 257 return nr_cpus; 258 } 259 260 static int cpu_set_callback(int i, void *data) 261 { 262 cpu_set_t *set = data; 263 264 if (i >= nr_cpus || i >= CPU_SETSIZE) 265 return -1; 266 267 debug_msg("cpu_set: adding cpu %d\n", i); 268 CPU_SET(i, set); 269 270 return 0; 271 } 272 273 /* 274 * parse_cpu_set - parse a cpu_list filling cpu_set_t argument 275 * 276 * Receives a cpu list, like 1-3,5 (cpus 1, 2, 3, 5), and then set 277 * filling cpu_set_t argument. 278 * 279 * Returns 0 on success, 1 otherwise. 280 */ 281 int parse_cpu_set(char *cpu_list, cpu_set_t *set) 282 { 283 CPU_ZERO(set); 284 285 if (cpu_list_iterate(cpu_list, cpu_set_callback, set) < 0) { 286 debug_msg("Error parsing the cpu set %s\n", cpu_list); 287 return 1; 288 } 289 290 return 0; 291 } 292 293 /* 294 * parse_stack_format - parse the stack format 295 * 296 * Return: the stack format on success, -1 otherwise. 297 */ 298 int parse_stack_format(char *arg) 299 { 300 if (!strcmp(arg, "truncate")) 301 return STACK_FORMAT_TRUNCATE; 302 if (!strcmp(arg, "skip")) 303 return STACK_FORMAT_SKIP; 304 if (!strcmp(arg, "full")) 305 return STACK_FORMAT_FULL; 306 307 debug_msg("Error parsing the stack format %s\n", arg); 308 return -1; 309 } 310 311 /* 312 * parse_duration - parse duration with s/m/h/d suffix converting it to seconds 313 */ 314 long parse_seconds_duration(char *val) 315 { 316 char *end; 317 long t; 318 319 t = strtol(val, &end, 10); 320 321 if (end) { 322 switch (*end) { 323 case 's': 324 case 'S': 325 break; 326 case 'm': 327 case 'M': 328 t *= 60; 329 break; 330 case 'h': 331 case 'H': 332 t *= 60 * 60; 333 break; 334 335 case 'd': 336 case 'D': 337 t *= 24 * 60 * 60; 338 break; 339 } 340 } 341 342 return t; 343 } 344 345 /* 346 * match_time_unit - check if str starts with unit followed by end-of-string or ':' 347 * 348 * This allows the time unit parser to work both in standalone duration strings 349 * like "100ms" and in colon-delimited SCHED_DEADLINE specifications like 350 * "d:10ms:100ms", while still rejecting malformed input like "100msx". 351 */ 352 static bool match_time_unit(const char *str, const char *unit) 353 { 354 size_t len = strlen(unit); 355 356 return strncmp(str, unit, len) == 0 && 357 (str[len] == '\0' || str[len] == ':'); 358 } 359 360 /* 361 * parse_ns_duration - parse duration with ns/us/ms/s converting it to nanoseconds 362 */ 363 long parse_ns_duration(char *val) 364 { 365 char *end; 366 long t; 367 368 t = strtol(val, &end, 10); 369 370 if (end) { 371 if (match_time_unit(end, "ns")) { 372 return t; 373 } else if (match_time_unit(end, "us")) { 374 t *= 1000; 375 return t; 376 } else if (match_time_unit(end, "ms")) { 377 t *= 1000 * 1000; 378 return t; 379 } else if (match_time_unit(end, "s")) { 380 t *= 1000 * 1000 * 1000; 381 return t; 382 } 383 return -1; 384 } 385 386 return t; 387 } 388 389 /* 390 * This is a set of helper functions to use SCHED_DEADLINE. 391 */ 392 #ifndef __NR_sched_setattr 393 # ifdef __x86_64__ 394 # define __NR_sched_setattr 314 395 # elif __i386__ 396 # define __NR_sched_setattr 351 397 # elif __arm__ 398 # define __NR_sched_setattr 380 399 # elif __aarch64__ || __riscv 400 # define __NR_sched_setattr 274 401 # elif __powerpc__ 402 # define __NR_sched_setattr 355 403 # elif __s390x__ 404 # define __NR_sched_setattr 345 405 # elif __loongarch__ 406 # define __NR_sched_setattr 274 407 # endif 408 #endif 409 410 #define SCHED_DEADLINE 6 411 412 static inline int syscall_sched_setattr(pid_t pid, const struct sched_attr *attr, 413 unsigned int flags) { 414 return syscall(__NR_sched_setattr, pid, attr, flags); 415 } 416 417 int __set_sched_attr(int pid, struct sched_attr *attr) 418 { 419 int flags = 0; 420 int retval; 421 422 retval = syscall_sched_setattr(pid, attr, flags); 423 if (retval < 0) { 424 err_msg("Failed to set sched attributes to the pid %d: %s\n", 425 pid, strerror(errno)); 426 return 1; 427 } 428 429 return 0; 430 } 431 432 /* 433 * procfs_is_workload_pid - check if a procfs entry contains a comm_prefix* comm 434 * 435 * Check if the procfs entry is a directory of a process, and then check if the 436 * process has a comm with the prefix set in char *comm_prefix. As the 437 * current users of this function only check for kernel threads, there is no 438 * need to check for the threads for the process. 439 * 440 * Return: True if the proc_entry contains a comm file with comm_prefix*. 441 * Otherwise returns false. 442 */ 443 static int procfs_is_workload_pid(const char *comm_prefix, struct dirent *proc_entry) 444 { 445 char buffer[MAX_PATH]; 446 int comm_fd, retval; 447 char *t_name; 448 449 if (proc_entry->d_type != DT_DIR) 450 return 0; 451 452 if (*proc_entry->d_name == '.') 453 return 0; 454 455 /* check if the string is a pid */ 456 for (t_name = proc_entry->d_name; *t_name; t_name++) { 457 if (!isdigit(*t_name)) 458 break; 459 } 460 461 if (*t_name != '\0') 462 return 0; 463 464 snprintf(buffer, MAX_PATH, "/proc/%s/comm", proc_entry->d_name); 465 comm_fd = open(buffer, O_RDONLY); 466 if (comm_fd < 0) 467 return 0; 468 469 memset(buffer, 0, MAX_PATH); 470 retval = read(comm_fd, buffer, MAX_PATH); 471 472 close(comm_fd); 473 474 if (retval <= 0) 475 return 0; 476 477 buffer[MAX_PATH-1] = '\0'; 478 if (!str_has_prefix(buffer, comm_prefix)) 479 return 0; 480 481 /* comm already have \n */ 482 debug_msg("Found workload pid:%s comm:%s", proc_entry->d_name, buffer); 483 484 return 1; 485 } 486 487 /* 488 * set_comm_sched_attr - set sched params to threads starting with char *comm_prefix 489 * 490 * This function uses procfs to list the currently running threads and then set the 491 * sched_attr *attr to the threads that start with char *comm_prefix. It is 492 * mainly used to set the priority to the kernel threads created by the 493 * tracers. 494 */ 495 int set_comm_sched_attr(const char *comm_prefix, struct sched_attr *attr) 496 { 497 struct dirent *proc_entry; 498 DIR *procfs; 499 int retval; 500 int pid; 501 502 if (strlen(comm_prefix) >= MAX_PATH) { 503 err_msg("Command prefix is too long: %d < strlen(%s)\n", 504 MAX_PATH, comm_prefix); 505 return 1; 506 } 507 508 procfs = opendir("/proc"); 509 if (!procfs) { 510 err_msg("Could not open procfs\n"); 511 return 1; 512 } 513 514 while ((proc_entry = readdir(procfs))) { 515 516 retval = procfs_is_workload_pid(comm_prefix, proc_entry); 517 if (!retval) 518 continue; 519 520 if (strtoi(proc_entry->d_name, &pid)) { 521 err_msg("'%s' is not a valid pid", proc_entry->d_name); 522 retval = 1; 523 goto out; 524 } 525 /* procfs_is_workload_pid confirmed it is a pid */ 526 retval = __set_sched_attr(pid, attr); 527 if (retval) { 528 err_msg("Error setting sched attributes for pid:%s\n", proc_entry->d_name); 529 goto out; 530 } 531 532 debug_msg("Set sched attributes for pid:%s\n", proc_entry->d_name); 533 } 534 535 retval = 0; 536 out: 537 closedir(procfs); 538 return retval; 539 } 540 541 #define INVALID_VAL (~0L) 542 static long get_long_ns_after_colon(char *start) 543 { 544 long val = INVALID_VAL; 545 546 /* find the ":" */ 547 start = strstr(start, ":"); 548 if (!start) 549 return -1; 550 551 /* skip ":" */ 552 start++; 553 val = parse_ns_duration(start); 554 555 return val; 556 } 557 558 static long get_long_after_colon(char *start) 559 { 560 long val = INVALID_VAL; 561 562 /* find the ":" */ 563 start = strstr(start, ":"); 564 if (!start) 565 return -1; 566 567 /* skip ":" */ 568 start++; 569 val = get_llong_from_str(start); 570 571 return val; 572 } 573 574 /* 575 * parse priority in the format: 576 * SCHED_OTHER: 577 * o:<prio> 578 * O:<prio> 579 * SCHED_RR: 580 * r:<prio> 581 * R:<prio> 582 * SCHED_FIFO: 583 * f:<prio> 584 * F:<prio> 585 * SCHED_DEADLINE: 586 * d:runtime:period 587 * D:runtime:period 588 */ 589 int parse_prio(char *arg, struct sched_attr *sched_param) 590 { 591 long prio; 592 long runtime; 593 long period; 594 595 memset(sched_param, 0, sizeof(*sched_param)); 596 sched_param->size = sizeof(*sched_param); 597 598 switch (arg[0]) { 599 case 'd': 600 case 'D': 601 /* d:runtime:period */ 602 if (strlen(arg) < 4) 603 return -1; 604 605 runtime = get_long_ns_after_colon(arg); 606 if (runtime == INVALID_VAL) 607 return -1; 608 609 period = get_long_ns_after_colon(&arg[2]); 610 if (period == INVALID_VAL) 611 return -1; 612 613 if (runtime > period) 614 return -1; 615 616 sched_param->sched_policy = SCHED_DEADLINE; 617 sched_param->sched_runtime = runtime; 618 sched_param->sched_deadline = period; 619 sched_param->sched_period = period; 620 break; 621 case 'f': 622 case 'F': 623 /* f:prio */ 624 prio = get_long_after_colon(arg); 625 if (prio == INVALID_VAL) 626 return -1; 627 628 if (prio < sched_get_priority_min(SCHED_FIFO)) 629 return -1; 630 if (prio > sched_get_priority_max(SCHED_FIFO)) 631 return -1; 632 633 sched_param->sched_policy = SCHED_FIFO; 634 sched_param->sched_priority = prio; 635 break; 636 case 'r': 637 case 'R': 638 /* r:prio */ 639 prio = get_long_after_colon(arg); 640 if (prio == INVALID_VAL) 641 return -1; 642 643 if (prio < sched_get_priority_min(SCHED_RR)) 644 return -1; 645 if (prio > sched_get_priority_max(SCHED_RR)) 646 return -1; 647 648 sched_param->sched_policy = SCHED_RR; 649 sched_param->sched_priority = prio; 650 break; 651 case 'o': 652 case 'O': 653 /* o:prio */ 654 prio = get_long_after_colon(arg); 655 if (prio == INVALID_VAL) 656 return -1; 657 658 if (prio < MIN_NICE) 659 return -1; 660 if (prio > MAX_NICE) 661 return -1; 662 663 sched_param->sched_policy = SCHED_OTHER; 664 sched_param->sched_nice = prio; 665 break; 666 default: 667 return -1; 668 } 669 return 0; 670 } 671 672 /* 673 * set_cpu_dma_latency - set the /dev/cpu_dma_latecy 674 * 675 * This is used to reduce the exit from idle latency. The value 676 * will be reset once the file descriptor of /dev/cpu_dma_latecy 677 * is closed. 678 * 679 * Return: the /dev/cpu_dma_latecy file descriptor 680 */ 681 int set_cpu_dma_latency(int32_t latency) 682 { 683 int retval; 684 int fd; 685 686 fd = open("/dev/cpu_dma_latency", O_RDWR); 687 if (fd < 0) { 688 err_msg("Error opening /dev/cpu_dma_latency\n"); 689 return -1; 690 } 691 692 retval = write(fd, &latency, 4); 693 if (retval < 1) { 694 err_msg("Error setting /dev/cpu_dma_latency\n"); 695 close(fd); 696 return -1; 697 } 698 699 debug_msg("Set /dev/cpu_dma_latency to %d\n", latency); 700 701 return fd; 702 } 703 704 #ifdef HAVE_LIBCPUPOWER_SUPPORT 705 static unsigned int **saved_cpu_idle_disable_state; 706 static size_t saved_cpu_idle_disable_state_alloc_ctr; 707 708 /* 709 * save_cpu_idle_state_disable - save disable for all idle states of a cpu 710 * 711 * Saves the current disable of all idle states of a cpu, to be subsequently 712 * restored via restore_cpu_idle_disable_state. 713 * 714 * Return: idle state count on success, negative on error 715 */ 716 int save_cpu_idle_disable_state(unsigned int cpu) 717 { 718 unsigned int nr_states; 719 unsigned int state; 720 int disabled; 721 722 nr_states = cpuidle_state_count(cpu); 723 724 if (nr_states == 0) 725 return 0; 726 727 if (saved_cpu_idle_disable_state == NULL) { 728 saved_cpu_idle_disable_state = calloc(nr_cpus, sizeof(unsigned int *)); 729 if (!saved_cpu_idle_disable_state) 730 return -1; 731 } 732 733 saved_cpu_idle_disable_state[cpu] = calloc(nr_states, sizeof(unsigned int)); 734 if (!saved_cpu_idle_disable_state[cpu]) 735 return -1; 736 saved_cpu_idle_disable_state_alloc_ctr++; 737 738 for (state = 0; state < nr_states; state++) { 739 disabled = cpuidle_is_state_disabled(cpu, state); 740 if (disabled < 0) 741 return disabled; 742 saved_cpu_idle_disable_state[cpu][state] = disabled; 743 } 744 745 return nr_states; 746 } 747 748 /* 749 * restore_cpu_idle_disable_state - restore disable for all idle states of a cpu 750 * 751 * Restores the current disable state of all idle states of a cpu that was 752 * previously saved by save_cpu_idle_disable_state. 753 * 754 * Return: idle state count on success, negative on error 755 */ 756 int restore_cpu_idle_disable_state(unsigned int cpu) 757 { 758 unsigned int nr_states; 759 unsigned int state; 760 int disabled; 761 int result; 762 763 nr_states = cpuidle_state_count(cpu); 764 765 if (nr_states == 0) 766 return 0; 767 768 if (!saved_cpu_idle_disable_state) 769 return -1; 770 771 for (state = 0; state < nr_states; state++) { 772 if (!saved_cpu_idle_disable_state[cpu]) 773 return -1; 774 disabled = saved_cpu_idle_disable_state[cpu][state]; 775 result = cpuidle_state_disable(cpu, state, disabled); 776 if (result < 0) 777 return result; 778 } 779 780 free(saved_cpu_idle_disable_state[cpu]); 781 saved_cpu_idle_disable_state[cpu] = NULL; 782 saved_cpu_idle_disable_state_alloc_ctr--; 783 if (saved_cpu_idle_disable_state_alloc_ctr == 0) { 784 free(saved_cpu_idle_disable_state); 785 saved_cpu_idle_disable_state = NULL; 786 } 787 788 return nr_states; 789 } 790 791 /* 792 * free_cpu_idle_disable_states - free saved idle state disable for all cpus 793 * 794 * Frees the memory used for storing cpu idle state disable for all cpus 795 * and states. 796 * 797 * Normally, the memory is freed automatically in 798 * restore_cpu_idle_disable_state; this is mostly for cleaning up after an 799 * error. 800 */ 801 void free_cpu_idle_disable_states(void) 802 { 803 int cpu; 804 805 if (!saved_cpu_idle_disable_state) 806 return; 807 808 for (cpu = 0; cpu < nr_cpus; cpu++) { 809 free(saved_cpu_idle_disable_state[cpu]); 810 saved_cpu_idle_disable_state[cpu] = NULL; 811 } 812 813 free(saved_cpu_idle_disable_state); 814 saved_cpu_idle_disable_state = NULL; 815 } 816 817 /* 818 * set_deepest_cpu_idle_state - limit idle state of cpu 819 * 820 * Disables all idle states deeper than the one given in 821 * deepest_state (assuming states with higher number are deeper). 822 * 823 * This is used to reduce the exit from idle latency. Unlike 824 * set_cpu_dma_latency, it can disable idle states per cpu. 825 * 826 * Return: idle state count on success, negative on error 827 */ 828 int set_deepest_cpu_idle_state(unsigned int cpu, unsigned int deepest_state) 829 { 830 unsigned int nr_states; 831 unsigned int state; 832 int result; 833 834 nr_states = cpuidle_state_count(cpu); 835 836 for (state = deepest_state + 1; state < nr_states; state++) { 837 result = cpuidle_state_disable(cpu, state, 1); 838 if (result < 0) 839 return result; 840 } 841 842 return nr_states; 843 } 844 #endif /* HAVE_LIBCPUPOWER_SUPPORT */ 845 846 #define _STR(x) #x 847 #define STR(x) _STR(x) 848 849 /* 850 * find_mount - find a the mount point of a given fs 851 * 852 * Returns 0 if mount is not found, otherwise return 1 and fill mp 853 * with the mount point. 854 */ 855 static const int find_mount(const char *fs, char *mp, int sizeof_mp) 856 { 857 char mount_point[MAX_PATH+1]; 858 char type[100]; 859 int found = 0; 860 FILE *fp; 861 862 fp = fopen("/proc/mounts", "r"); 863 if (!fp) 864 return 0; 865 866 while (fscanf(fp, "%*s %" STR(MAX_PATH) "s %99s %*s %*d %*d\n", mount_point, type) == 2) { 867 if (strcmp(type, fs) == 0) { 868 found = 1; 869 break; 870 } 871 } 872 fclose(fp); 873 874 if (!found) 875 return 0; 876 877 memset(mp, 0, sizeof_mp); 878 strncpy(mp, mount_point, sizeof_mp - 1); 879 880 debug_msg("Fs %s found at %s\n", fs, mp); 881 return 1; 882 } 883 884 /* 885 * get_self_cgroup - get the current thread cgroup path 886 * 887 * Parse /proc/$$/cgroup file to get the thread's cgroup. As an example of line to parse: 888 * 889 * 0::/user.slice/user-0.slice/session-3.scope'\n' 890 * 891 * This function is interested in the content after the second : and before the '\n'. 892 * 893 * Returns 1 if a string was found, 0 otherwise. 894 */ 895 static int get_self_cgroup(char *self_cg, int sizeof_self_cg) 896 { 897 char path[MAX_PATH], *start; 898 int fd, retval; 899 900 snprintf(path, MAX_PATH, "/proc/%d/cgroup", getpid()); 901 902 fd = open(path, O_RDONLY); 903 if (fd < 0) 904 return 0; 905 906 memset(path, 0, sizeof(path)); 907 retval = read(fd, path, MAX_PATH); 908 909 close(fd); 910 911 if (retval <= 0) 912 return 0; 913 914 path[MAX_PATH-1] = '\0'; 915 start = path; 916 917 start = strstr(start, ":"); 918 if (!start) 919 return 0; 920 921 /* skip ":" */ 922 start++; 923 924 start = strstr(start, ":"); 925 if (!start) 926 return 0; 927 928 /* skip ":" */ 929 start++; 930 931 if (strlen(start) >= sizeof_self_cg) 932 return 0; 933 934 snprintf(self_cg, sizeof_self_cg, "%s", start); 935 936 /* Swap '\n' with '\0' */ 937 start = strstr(self_cg, "\n"); 938 939 /* there must be '\n' */ 940 if (!start) 941 return 0; 942 943 /* ok, it found a string after the second : and before the \n */ 944 *start = '\0'; 945 946 return 1; 947 } 948 949 /* 950 * open_cgroup_procs - Open the cgroup.procs file for the given cgroup 951 * 952 * If cgroup argument is not NULL, the cgroup.procs file for that cgroup 953 * will be opened. Otherwise, the cgroup of the calling, i.e., rtla, thread 954 * will be used. 955 * 956 * Supports cgroup v2. 957 * 958 * Returns the file descriptor on success, -1 otherwise. 959 */ 960 static int open_cgroup_procs(const char *cgroup) 961 { 962 char cgroup_path[MAX_PATH - strlen("/cgroup.procs")]; 963 char cgroup_procs[MAX_PATH]; 964 int retval; 965 int cg_fd; 966 size_t cg_path_len; 967 968 retval = find_mount("cgroup2", cgroup_path, sizeof(cgroup_path)); 969 if (!retval) { 970 err_msg("Did not find cgroupv2 mount point\n"); 971 return -1; 972 } 973 974 cg_path_len = strlen(cgroup_path); 975 976 if (!cgroup) { 977 retval = get_self_cgroup(&cgroup_path[cg_path_len], 978 sizeof(cgroup_path) - cg_path_len); 979 if (!retval) { 980 err_msg("Did not find self cgroup\n"); 981 return -1; 982 } 983 } else { 984 snprintf(&cgroup_path[cg_path_len], 985 sizeof(cgroup_path) - cg_path_len, "%s/", cgroup); 986 } 987 988 snprintf(cgroup_procs, MAX_PATH, "%s/cgroup.procs", cgroup_path); 989 990 debug_msg("Using cgroup path at: %s\n", cgroup_procs); 991 992 cg_fd = open(cgroup_procs, O_RDWR); 993 if (cg_fd < 0) 994 return -1; 995 996 return cg_fd; 997 } 998 999 /* 1000 * set_pid_cgroup - Set cgroup to pid_t pid 1001 * 1002 * If cgroup argument is not NULL, the threads will move to the given cgroup. 1003 * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. 1004 * 1005 * Supports cgroup v2. 1006 * 1007 * Returns 1 on success, 0 otherwise. 1008 */ 1009 int set_pid_cgroup(pid_t pid, const char *cgroup) 1010 { 1011 char pid_str[24]; 1012 int retval; 1013 int cg_fd; 1014 1015 cg_fd = open_cgroup_procs(cgroup); 1016 if (cg_fd < 0) 1017 return 0; 1018 1019 snprintf(pid_str, sizeof(pid_str), "%d\n", pid); 1020 1021 retval = write(cg_fd, pid_str, strlen(pid_str)); 1022 if (retval < 0) 1023 err_msg("Error setting cgroup attributes for pid:%s - %s\n", 1024 pid_str, strerror(errno)); 1025 else 1026 debug_msg("Set cgroup attributes for pid:%s\n", pid_str); 1027 1028 close(cg_fd); 1029 1030 return (retval >= 0); 1031 } 1032 1033 /** 1034 * set_comm_cgroup - Set cgroup to threads starting with char *comm_prefix 1035 * 1036 * If cgroup argument is not NULL, the threads will move to the given cgroup. 1037 * Otherwise, the cgroup of the calling, i.e., rtla, thread will be used. 1038 * 1039 * Supports cgroup v2. 1040 * 1041 * Returns 1 on success, 0 otherwise. 1042 */ 1043 int set_comm_cgroup(const char *comm_prefix, const char *cgroup) 1044 { 1045 struct dirent *proc_entry; 1046 DIR *procfs; 1047 int retval; 1048 int cg_fd; 1049 1050 if (strlen(comm_prefix) >= MAX_PATH) { 1051 err_msg("Command prefix is too long: %d < strlen(%s)\n", 1052 MAX_PATH, comm_prefix); 1053 return 0; 1054 } 1055 1056 cg_fd = open_cgroup_procs(cgroup); 1057 if (cg_fd < 0) 1058 return 0; 1059 1060 procfs = opendir("/proc"); 1061 if (!procfs) { 1062 err_msg("Could not open procfs\n"); 1063 goto out_cg; 1064 } 1065 1066 while ((proc_entry = readdir(procfs))) { 1067 1068 retval = procfs_is_workload_pid(comm_prefix, proc_entry); 1069 if (!retval) 1070 continue; 1071 1072 retval = write(cg_fd, proc_entry->d_name, strlen(proc_entry->d_name)); 1073 if (retval < 0) { 1074 err_msg("Error setting cgroup attributes for pid:%s - %s\n", 1075 proc_entry->d_name, strerror(errno)); 1076 goto out_procfs; 1077 } 1078 1079 debug_msg("Set cgroup attributes for pid:%s\n", proc_entry->d_name); 1080 } 1081 1082 closedir(procfs); 1083 close(cg_fd); 1084 return 1; 1085 1086 out_procfs: 1087 closedir(procfs); 1088 out_cg: 1089 close(cg_fd); 1090 return 0; 1091 } 1092 1093 /** 1094 * auto_house_keeping - Automatically move rtla out of measurement threads 1095 * 1096 * Try to move rtla away from the tracer, if possible. 1097 * 1098 * Returns 1 on success, 0 otherwise. 1099 */ 1100 int auto_house_keeping(cpu_set_t *monitored_cpus) 1101 { 1102 cpu_set_t rtla_cpus, house_keeping_cpus; 1103 int retval; 1104 1105 /* first get the CPUs in which rtla can actually run. */ 1106 retval = sched_getaffinity(getpid(), sizeof(rtla_cpus), &rtla_cpus); 1107 if (retval == -1) { 1108 debug_msg("Could not get rtla affinity, rtla might run with the threads!\n"); 1109 return 0; 1110 } 1111 1112 /* then check if the existing setup is already good. */ 1113 CPU_AND(&house_keeping_cpus, &rtla_cpus, monitored_cpus); 1114 if (!CPU_COUNT(&house_keeping_cpus)) { 1115 debug_msg("rtla and the monitored CPUs do not share CPUs."); 1116 debug_msg("Skipping auto house-keeping\n"); 1117 return 1; 1118 } 1119 1120 /* remove the intersection */ 1121 CPU_XOR(&house_keeping_cpus, &rtla_cpus, monitored_cpus); 1122 1123 /* get only those that rtla can run */ 1124 CPU_AND(&house_keeping_cpus, &house_keeping_cpus, &rtla_cpus); 1125 1126 /* is there any cpu left? */ 1127 if (!CPU_COUNT(&house_keeping_cpus)) { 1128 debug_msg("Could not find any CPU for auto house-keeping\n"); 1129 return 0; 1130 } 1131 1132 retval = sched_setaffinity(getpid(), sizeof(house_keeping_cpus), &house_keeping_cpus); 1133 if (retval == -1) { 1134 debug_msg("Could not set affinity for auto house-keeping\n"); 1135 return 0; 1136 } 1137 1138 debug_msg("rtla automatically moved to an auto house-keeping cpu set\n"); 1139 1140 return 1; 1141 } 1142 1143 /* 1144 * strtoi - convert string to integer with error checking 1145 * 1146 * Returns 0 on success, -1 if conversion fails or result is out of int range. 1147 */ 1148 int strtoi(const char *s, int *res) 1149 { 1150 char *end_ptr; 1151 long lres; 1152 1153 if (!*s) 1154 return -1; 1155 1156 errno = 0; 1157 lres = strtol(s, &end_ptr, 0); 1158 if (errno || *end_ptr || lres > INT_MAX || lres < INT_MIN) 1159 return -1; 1160 1161 *res = (int) lres; 1162 return 0; 1163 } 1164 1165 static inline void fatal_alloc(void) 1166 { 1167 fatal("Error allocating memory\n"); 1168 } 1169 1170 void *calloc_fatal(size_t n, size_t size) 1171 { 1172 void *p = calloc(n, size); 1173 1174 if (!p) 1175 fatal_alloc(); 1176 1177 return p; 1178 } 1179 1180 void *reallocarray_fatal(void *p, size_t n, size_t size) 1181 { 1182 p = reallocarray(p, n, size); 1183 1184 if (!p) 1185 fatal_alloc(); 1186 1187 return p; 1188 } 1189 1190 char *strdup_fatal(const char *s) 1191 { 1192 char *p = strdup(s); 1193 1194 if (!p) 1195 fatal_alloc(); 1196 1197 return p; 1198 } 1199