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 <sys/types.h> 8 #include <sys/stat.h> 9 #include <pthread.h> 10 #include <stdlib.h> 11 #include <string.h> 12 #include <unistd.h> 13 #include <errno.h> 14 #include <fcntl.h> 15 #include <stdio.h> 16 #include <sched.h> 17 18 #include <linux/compiler.h> 19 20 #include "osnoise.h" 21 22 #define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */ 23 #define DEFAULT_SAMPLE_RUNTIME 1000000 /* 1s */ 24 25 /* 26 * osnoise_get_cpus - return the original "osnoise/cpus" content 27 * 28 * It also saves the value to be restored. 29 */ 30 char *osnoise_get_cpus(struct osnoise_context *context) 31 { 32 if (context->curr_cpus) 33 return context->curr_cpus; 34 35 if (context->orig_cpus) 36 return context->orig_cpus; 37 38 context->orig_cpus = tracefs_instance_file_read(NULL, "osnoise/cpus", NULL); 39 40 /* 41 * The error value (NULL) is the same for tracefs_instance_file_read() 42 * and this functions, so: 43 */ 44 return context->orig_cpus; 45 } 46 47 /* 48 * osnoise_set_cpus - configure osnoise to run on *cpus 49 * 50 * "osnoise/cpus" file is used to set the cpus in which osnoise/timerlat 51 * will run. This function opens this file, saves the current value, 52 * and set the cpus passed as argument. 53 */ 54 int osnoise_set_cpus(struct osnoise_context *context, char *cpus) 55 { 56 char *orig_cpus = osnoise_get_cpus(context); 57 char buffer[1024]; 58 int retval; 59 60 if (!orig_cpus) 61 return -1; 62 63 context->curr_cpus = strdup(cpus); 64 if (!context->curr_cpus) 65 return -1; 66 67 snprintf(buffer, ARRAY_SIZE(buffer), "%s\n", cpus); 68 69 debug_msg("setting cpus to %s from %s", cpus, context->orig_cpus); 70 71 retval = tracefs_instance_file_write(NULL, "osnoise/cpus", buffer); 72 if (retval < 0) { 73 free(context->curr_cpus); 74 context->curr_cpus = NULL; 75 return -1; 76 } 77 78 return 0; 79 } 80 81 /* 82 * osnoise_restore_cpus - restore the original "osnoise/cpus" 83 * 84 * osnoise_set_cpus() saves the original data for the "osnoise/cpus" 85 * file. This function restore the original config it was previously 86 * modified. 87 */ 88 void osnoise_restore_cpus(struct osnoise_context *context) 89 { 90 int retval; 91 92 if (!context->orig_cpus) 93 return; 94 95 if (!context->curr_cpus) 96 return; 97 98 /* nothing to do? */ 99 if (!strcmp(context->orig_cpus, context->curr_cpus)) 100 goto out_done; 101 102 debug_msg("restoring cpus to %s", context->orig_cpus); 103 104 retval = tracefs_instance_file_write(NULL, "osnoise/cpus", context->orig_cpus); 105 if (retval < 0) 106 err_msg("could not restore original osnoise cpus\n"); 107 108 out_done: 109 free(context->curr_cpus); 110 context->curr_cpus = NULL; 111 } 112 113 /* 114 * osnoise_put_cpus - restore cpus config and cleanup data 115 */ 116 void osnoise_put_cpus(struct osnoise_context *context) 117 { 118 osnoise_restore_cpus(context); 119 120 if (!context->orig_cpus) 121 return; 122 123 free(context->orig_cpus); 124 context->orig_cpus = NULL; 125 } 126 127 /* 128 * osnoise_get_cpu_count - get the number of CPUs as seen by the osnoise tracer 129 * 130 * Write "all" to osnoise/cpus and read back the real kernel-side list 131 * of all possible cpus. Restore the original value afterwards. 132 * 133 * Returns the number of CPUs, or -1 on error. 134 */ 135 int osnoise_get_cpu_count(void) 136 { 137 char *orig, *readback; 138 int max_cpu; 139 140 orig = tracefs_instance_file_read(NULL, "osnoise/cpus", NULL); 141 if (!orig) 142 return -1; 143 144 if (tracefs_instance_file_write(NULL, "osnoise/cpus", "all\n") < 0) { 145 free(orig); 146 return -1; 147 } 148 149 readback = tracefs_instance_file_read(NULL, "osnoise/cpus", NULL); 150 if (!readback) { 151 tracefs_instance_file_write(NULL, "osnoise/cpus", orig); 152 free(orig); 153 return -1; 154 } 155 156 max_cpu = get_max_cpu_from_list(readback); 157 free(readback); 158 159 if (tracefs_instance_file_write(NULL, "osnoise/cpus", orig) < 0) { 160 free(orig); 161 return -1; 162 } 163 free(orig); 164 165 if (max_cpu < 0) 166 return -1; 167 168 return max_cpu + 1; 169 } 170 171 /** 172 * osnoise_read_ll_config - read a long long value from a tracefs config 173 * @rel_path: tracefs-relative path to the config file 174 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 175 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 176 * 177 * Returns the current effective value for the config at @rel_path. If RTLA has 178 * already written a value (@set_value != -1), that value is returned. If the 179 * original has already been read (@orig_value != -1), that cached value is 180 * returned. Otherwise, reads the value from tracefs, caches it in @orig_value 181 * (so it can later be restored), and returns it. 182 * 183 * This is the shared read primitive used by both the manually implemented 184 * get functions (osnoise_get_runtime, osnoise_get_period) and the 185 * OSNOISE_LL_OPTION-generated set/restore/put functions. 186 * 187 * Returns the config value on success, -1 on error. 188 */ 189 static long long osnoise_read_ll_config(char *rel_path, 190 long long *set_value, 191 long long *orig_value) 192 { 193 long long retval; 194 char *buffer; 195 196 if (*set_value != -1) 197 /* option has been set by RTLA already */ 198 return *set_value; 199 200 if (*orig_value != -1) 201 /* RTLA has already read the option */ 202 return *orig_value; 203 204 /* current value is not known to RTLA yet, read it from tracefs */ 205 buffer = tracefs_instance_file_read(NULL, rel_path, NULL); 206 if (!buffer) 207 return -1; 208 209 /* get_llong_from_str returns -1 on error */ 210 retval = get_llong_from_str(buffer); 211 212 debug_msg("reading %s returned %lld\n", rel_path, retval); 213 214 free(buffer); 215 216 if (retval < 0) 217 goto out_err; 218 219 /* save the value and return it */ 220 *orig_value = retval; 221 return retval; 222 223 out_err: 224 return -1; 225 } 226 227 /** 228 * osnoise_write_ll_config - write a long long value to a tracefs config 229 * @rel_path: tracefs-relative path to the config file 230 * @value: the value to write 231 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 232 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 233 * 234 * Writes @value to the config at @rel_path. Before writing, calls 235 * osnoise_read_ll_config() to ensure the original value is cached in 236 * @orig_value (enabling later restoration). On successful write, records 237 * the new value in @set_value. 238 * 239 * This is the shared write primitive used by both the manually implemented 240 * write functions (__osnoise_write_runtime, __osnoise_write_period) and the 241 * OSNOISE_LL_OPTION-generated set/restore/put functions. 242 * 243 * Returns 0 on success, -1 on read error (option likely unknown to kernel), 244 * or -2 on write error. 245 */ 246 static int osnoise_write_ll_config(char *rel_path, 247 long long value, 248 long long *set_value, 249 long long *orig_value) 250 { 251 long long curr = osnoise_read_ll_config(rel_path, set_value, orig_value); 252 long long retval; 253 char buffer[BUFF_U64_STR_SIZE]; 254 255 if (curr == -1) 256 /* read failed, option likely unknown to kernel */ 257 return -1; 258 259 snprintf(buffer, sizeof(buffer), "%lld\n", value); 260 261 debug_msg("setting %s to %lld\n", rel_path, value); 262 263 retval = tracefs_instance_file_write(NULL, rel_path, buffer); 264 265 if (retval < 0) 266 /* write failed, hard error */ 267 return -2; 268 269 /* record the set value and return success */ 270 *set_value = value; 271 return 0; 272 } 273 274 /** 275 * osnoise_restore_ll_config - restore a long long config to its original value 276 * @rel_path: tracefs-relative path to the config file 277 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 278 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 279 * 280 * Restores the config at @rel_path to the value cached in @orig_value (which 281 * was saved by a prior osnoise_read_ll_config() or osnoise_write_ll_config() 282 * call). If the original was never read, or if the current set value already 283 * matches the original, no write is performed. After restoring, clears 284 * @set_value to -1 to indicate RTLA no longer overrides this config. 285 * 286 * This is the shared restore primitive used by both the manually implemented 287 * restore function (osnoise_restore_runtime_period) and the 288 * OSNOISE_LL_OPTION-generated restore/put functions. 289 */ 290 static void osnoise_restore_ll_config(char *rel_path, 291 long long *set_value, 292 long long *orig_value) 293 { 294 int retval; 295 296 if (*orig_value == -1) 297 return; 298 299 if (*orig_value == *set_value) 300 goto out_done; 301 302 retval = osnoise_write_ll_config(rel_path, *orig_value, set_value, orig_value); 303 if (retval < 0) 304 err_msg("Could not restore original value for %s\n", rel_path); 305 306 out_done: 307 *set_value = -1; 308 } 309 310 /* 311 * osnoise_get_runtime - return the original "osnoise/runtime_us" value 312 * 313 * It also saves the value to be restored. 314 */ 315 long long osnoise_get_runtime(struct osnoise_context *context) 316 { 317 return osnoise_read_ll_config("osnoise/runtime_us", 318 &context->runtime_us, 319 &context->orig_runtime_us); 320 } 321 322 /* 323 * osnoise_get_period - return the original "osnoise/period_us" value 324 * 325 * It also saves the value to be restored. 326 */ 327 long long osnoise_get_period(struct osnoise_context *context) 328 { 329 return osnoise_read_ll_config("osnoise/period_us", 330 &context->period_us, 331 &context->orig_period_us); 332 } 333 334 static int __osnoise_write_runtime(struct osnoise_context *context, 335 long long runtime) 336 { 337 return osnoise_write_ll_config("osnoise/runtime_us", 338 runtime, 339 &context->runtime_us, 340 &context->orig_runtime_us); 341 } 342 343 static int __osnoise_write_period(struct osnoise_context *context, 344 long long period) 345 { 346 return osnoise_write_ll_config("osnoise/period_us", 347 period, 348 &context->period_us, 349 &context->orig_period_us); 350 } 351 352 /* 353 * osnoise_set_runtime_period - set osnoise runtime and period 354 * 355 * Osnoise's runtime and period are related as runtime <= period. 356 * Thus, this function saves the original values, and then tries 357 * to set the runtime and period if they are != 0. 358 */ 359 int osnoise_set_runtime_period(struct osnoise_context *context, 360 long long runtime, 361 long long period) 362 { 363 long long curr_runtime_us; 364 long long curr_period_us; 365 int retval; 366 367 if (!period && !runtime) 368 return 0; 369 370 curr_runtime_us = osnoise_get_runtime(context); 371 curr_period_us = osnoise_get_period(context); 372 373 /* error getting any value? */ 374 if (curr_period_us == -1 || curr_runtime_us == -1) 375 return -1; 376 377 if (!period) { 378 if (runtime > curr_period_us) 379 return -1; 380 return __osnoise_write_runtime(context, runtime); 381 } else if (!runtime) { 382 if (period < curr_runtime_us) 383 return -1; 384 return __osnoise_write_period(context, period); 385 } 386 387 if (runtime > curr_period_us) { 388 retval = __osnoise_write_period(context, period); 389 if (retval) 390 return -1; 391 retval = __osnoise_write_runtime(context, runtime); 392 if (retval) 393 return -1; 394 } else { 395 retval = __osnoise_write_runtime(context, runtime); 396 if (retval) 397 return -1; 398 retval = __osnoise_write_period(context, period); 399 if (retval) 400 return -1; 401 } 402 403 return 0; 404 } 405 406 /* 407 * osnoise_restore_runtime_period - restore the original runtime and period 408 */ 409 void osnoise_restore_runtime_period(struct osnoise_context *context) 410 { 411 long long orig_runtime = context->orig_runtime_us; 412 long long orig_period = context->orig_period_us; 413 long long curr_runtime = context->runtime_us; 414 long long curr_period = context->period_us; 415 int retval; 416 417 if ((orig_runtime == -1) && (orig_period == -1)) 418 return; 419 420 if ((orig_period == curr_period) && (orig_runtime == curr_runtime)) 421 goto out_done; 422 423 retval = osnoise_set_runtime_period(context, orig_runtime, orig_period); 424 if (retval) 425 err_msg("Could not restore original osnoise runtime/period\n"); 426 427 out_done: 428 context->runtime_us = -1; 429 context->period_us = -1; 430 } 431 432 /* 433 * osnoise_put_runtime_period - restore original values and cleanup data 434 */ 435 void osnoise_put_runtime_period(struct osnoise_context *context) 436 { 437 osnoise_restore_runtime_period(context); 438 439 if (context->orig_runtime_us != -1) 440 context->orig_runtime_us = -1; 441 442 if (context->orig_period_us != -1) 443 context->orig_period_us = -1; 444 } 445 446 /* 447 * Long long option set/restore/put functions, generated from OSNOISE_LL_OPTIONS. 448 */ 449 #define OSNOISE_LL_OPTION(name, path) \ 450 int osnoise_set_##name(struct osnoise_context *context, long long value) \ 451 { \ 452 return osnoise_write_ll_config(path, \ 453 value, \ 454 &context->name, \ 455 &context->orig_##name); \ 456 } \ 457 \ 458 void osnoise_restore_##name(struct osnoise_context *context) \ 459 { \ 460 osnoise_restore_ll_config(path, &context->name, &context->orig_##name); \ 461 } \ 462 \ 463 static void osnoise_put_##name(struct osnoise_context *context) \ 464 { \ 465 osnoise_restore_##name(context); \ 466 \ 467 if (context->orig_##name == -1) \ 468 return; \ 469 \ 470 context->orig_##name = -1; \ 471 } 472 OSNOISE_LL_OPTIONS 473 #undef OSNOISE_LL_OPTION 474 475 /** 476 * osnoise_get_option - read a boolean flag from osnoise/options 477 * @option: the option name string to look for (e.g. "OSNOISE_IRQ_DISABLE") 478 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 479 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 480 * 481 * Returns the current state of the flag @option. If RTLA has already written a 482 * value (@set_value != -1), that value is returned. If the original has already 483 * been read (@orig_value != -1), that cached value is returned. Otherwise, reads 484 * the "osnoise/options" file from tracefs, checks whether @option appears with or 485 * without a "NO_" prefix, caches the result in @orig_value (so it can later be 486 * restored), and returns it. 487 * 488 * This is the shared read primitive used by the OSNOISE_FLAG_OPTION-generated 489 * set/restore/put functions. 490 * 491 * Returns 1 if enabled, 0 if disabled, or -1 on error (option unknown to kernel). 492 */ 493 static int osnoise_get_option(char *option, 494 int *set_value, 495 int *orig_value) 496 { 497 char *options; 498 char no_option[128]; 499 int retval = 0; 500 char *opt; 501 502 if (*set_value != -1) 503 /* option has been set by RTLA already */ 504 return *set_value; 505 506 if (*orig_value != -1) 507 /* RTLA has already read the option */ 508 return *orig_value; 509 510 /* current value is not known to RTLA yet, read it from tracefs */ 511 options = tracefs_instance_file_read(NULL, "osnoise/options", NULL); 512 if (!options) 513 return -1; 514 515 /* 516 * Check first if the option is disabled. 517 */ 518 snprintf(no_option, sizeof(no_option), "NO_%s", option); 519 520 opt = strstr(options, no_option); 521 if (opt) 522 goto out_free; 523 524 /* 525 * Now that it is not disabled, if the string is there, it is 526 * enabled. If the string is not there, the option does not exist. 527 */ 528 opt = strstr(options, option); 529 if (opt) 530 retval = 1; 531 else 532 retval = -1; 533 534 out_free: 535 free(options); 536 537 if (retval < 0) 538 goto out_err; 539 540 /* save the value and return it */ 541 *orig_value = retval; 542 return retval; 543 544 out_err: 545 return -1; 546 } 547 548 /** 549 * osnoise_set_option - write a boolean flag to osnoise/options 550 * @option: the option name string (e.g. "OSNOISE_IRQ_DISABLE") 551 * @onoff: the desired state (true to enable, false to disable) 552 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 553 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 554 * 555 * Sets the flag @option to the state @onoff. Before writing, calls 556 * osnoise_get_option() to ensure the original value is cached in @orig_value 557 * (enabling later restoration). If the current value already matches @onoff, 558 * no write is performed. On successful write, records the new state in 559 * @set_value. 560 * 561 * This is the shared write primitive used by the OSNOISE_FLAG_OPTION-generated 562 * set/restore/put functions. 563 * 564 * Returns 0 on success, -1 on read error (option likely unknown to kernel), 565 * or -2 on write error. 566 */ 567 static int osnoise_set_option(char *option, 568 bool onoff, 569 int *set_value, 570 int *orig_value) 571 { 572 int curr = osnoise_get_option(option, set_value, orig_value); 573 char no_option[128]; 574 int retval; 575 576 if (curr == -1) 577 /* read failed, option likely unknown to kernel */ 578 return -1; 579 580 if (curr == onoff) 581 return 0; 582 583 if (onoff) { 584 retval = tracefs_instance_file_write(NULL, "osnoise/options", option); 585 } else { 586 snprintf(no_option, sizeof(no_option), "NO_%s", option); 587 retval = tracefs_instance_file_write(NULL, "osnoise/options", no_option); 588 } 589 590 if (retval < 0) 591 /* write failed, hard error */ 592 return -2; 593 594 /* record the set value and return success */ 595 *set_value = onoff; 596 return 0; 597 } 598 599 /** 600 * osnoise_restore_option - restore a boolean flag to its original value 601 * @option: the option name string (e.g. "OSNOISE_IRQ_DISABLE") 602 * @set_value: pointer to the cached value set by RTLA, or -1 if unset 603 * @orig_value: pointer to the cached original value read from tracefs, or -1 if unread 604 * 605 * Restores the flag @option to the state cached in @orig_value (which was saved 606 * by a prior osnoise_get_option() or osnoise_set_option() call). If the original 607 * was never read, or if the current set value already matches the original, no 608 * write is performed. After restoring, clears @set_value to -1 to indicate RTLA 609 * no longer overrides this option. 610 * 611 * This is the shared restore primitive used by the OSNOISE_FLAG_OPTION-generated 612 * restore/put functions. 613 */ 614 static void osnoise_restore_option(char *option, 615 int *set_value, 616 int *orig_value) 617 { 618 int retval; 619 620 if (*orig_value == -1) 621 return; 622 623 if (*orig_value == *set_value) 624 goto out_done; 625 626 retval = osnoise_set_option(option, *orig_value, set_value, orig_value); 627 if (retval < 0) 628 err_msg("Could not restore original %s option\n", option); 629 630 out_done: 631 *set_value = -1; 632 } 633 634 /* 635 * Flag option set/restore/put functions, generated from OSNOISE_FLAG_OPTIONS. 636 */ 637 #define OSNOISE_FLAG_OPTION(name, option_str) \ 638 int osnoise_set_##name(struct osnoise_context *context, bool onoff) \ 639 { \ 640 return osnoise_set_option(option_str, \ 641 onoff, \ 642 &context->opt_##name, \ 643 &context->orig_opt_##name); \ 644 } \ 645 \ 646 void osnoise_restore_##name(struct osnoise_context *context) \ 647 { \ 648 osnoise_restore_option(option_str, \ 649 &context->opt_##name, \ 650 &context->orig_opt_##name); \ 651 } \ 652 \ 653 static void osnoise_put_##name(struct osnoise_context *context) \ 654 { \ 655 osnoise_restore_##name(context); \ 656 \ 657 if (context->orig_opt_##name == -1) \ 658 return; \ 659 \ 660 context->orig_opt_##name = -1; \ 661 } 662 OSNOISE_FLAG_OPTIONS 663 #undef OSNOISE_FLAG_OPTION 664 665 enum { 666 FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), 667 FLAG_CONTEXT_DELETED = (1 << 1), 668 }; 669 670 /* 671 * osnoise_get_context - increase the usage of a context and return it 672 */ 673 int osnoise_get_context(struct osnoise_context *context) 674 { 675 int ret; 676 677 if (context->flags & FLAG_CONTEXT_DELETED) { 678 ret = -1; 679 } else { 680 context->ref++; 681 ret = 0; 682 } 683 684 return ret; 685 } 686 687 /* 688 * osnoise_context_alloc - alloc an osnoise_context 689 * 690 * The osnoise context contains the information of the "osnoise/" configs. 691 * It is used to set and restore the config. 692 */ 693 struct osnoise_context *osnoise_context_alloc(void) 694 { 695 struct osnoise_context *context; 696 697 context = calloc_fatal(1, sizeof(*context)); 698 699 /* First allocate manually implemented options... */ 700 context->orig_runtime_us = -1; 701 context->runtime_us = -1; 702 context->orig_period_us = -1; 703 context->period_us = -1; 704 705 /* ...then the automatically generated ones. */ 706 #define OSNOISE_LL_OPTION(name, path) \ 707 context->orig_##name = -1; \ 708 context->name = -1; 709 #define OSNOISE_FLAG_OPTION(name, option_str) \ 710 context->orig_opt_##name = -1; \ 711 context->opt_##name = -1; 712 OSNOISE_LL_OPTIONS 713 OSNOISE_FLAG_OPTIONS 714 #undef OSNOISE_LL_OPTION 715 #undef OSNOISE_FLAG_OPTION 716 717 osnoise_get_context(context); 718 719 return context; 720 } 721 722 /* 723 * osnoise_put_context - put the osnoise_put_context 724 * 725 * If there is no other user for the context, the original data 726 * is restored. 727 */ 728 void osnoise_put_context(struct osnoise_context *context) 729 { 730 if (--context->ref < 1) 731 context->flags |= FLAG_CONTEXT_DELETED; 732 733 if (!(context->flags & FLAG_CONTEXT_DELETED)) 734 return; 735 736 /* First restore the original values of the options... */ 737 osnoise_put_cpus(context); 738 osnoise_put_runtime_period(context); 739 740 /* ...then the automatically generated ones. */ 741 #define OSNOISE_LL_OPTION(name, path) osnoise_put_##name(context); 742 #define OSNOISE_FLAG_OPTION(name, option_str) osnoise_put_##name(context); 743 OSNOISE_LL_OPTIONS 744 OSNOISE_FLAG_OPTIONS 745 #undef OSNOISE_LL_OPTION 746 #undef OSNOISE_FLAG_OPTION 747 748 free(context); 749 } 750 751 /* 752 * osnoise_destroy_tool - disable trace, restore configs and free data 753 */ 754 void osnoise_destroy_tool(struct osnoise_tool *top) 755 { 756 if (!top) 757 return; 758 759 trace_instance_destroy(&top->trace); 760 761 if (top->context) 762 osnoise_put_context(top->context); 763 764 free(top); 765 } 766 767 /* 768 * osnoise_init_tool - init an osnoise tool 769 * 770 * It allocs data, create a context to store data and 771 * creates a new trace instance for the tool. 772 */ 773 struct osnoise_tool *osnoise_init_tool(char *tool_name) 774 { 775 struct osnoise_tool *top; 776 777 top = calloc_fatal(1, sizeof(*top)); 778 top->context = osnoise_context_alloc(); 779 780 if (trace_instance_init(&top->trace, tool_name)) { 781 osnoise_destroy_tool(top); 782 return NULL; 783 } 784 785 return top; 786 } 787 788 /* 789 * osnoise_init_ipi_filters - Initialize event filtering for IPI events 790 */ 791 int osnoise_init_ipi_filters(struct osnoise_tool *tool, 792 struct common_params *params, 793 bool *filters_enabled) 794 { 795 char filter[MAX_PATH]; 796 int retval; 797 /* 798 * If tracing on a subset of possible CPUs, leverage the kernel filtering 799 * infrastructure to only generate events on traced CPUs. 800 * Older kernels (pre v6.6) may have the IPI events but not the ability 801 * to filter them, so allow that to fail gracefully. 802 */ 803 804 snprintf(filter, ARRAY_SIZE(filter), "cpu & CPUS{%s}\n", params->cpus); 805 retval = tracefs_event_file_write(tool->trace.inst, 806 "ipi", "ipi_send_cpu", "filter", 807 filter); 808 if (retval < 0) { 809 debug_msg("Could not set ipi_send_cpu CPU filter\n"); 810 *filters_enabled = false; 811 return 0; 812 } 813 814 815 snprintf(filter, ARRAY_SIZE(filter), "cpumask & CPUS{%s}\n", params->cpus); 816 retval = tracefs_event_file_write(tool->trace.inst, 817 "ipi", "ipi_send_cpumask", "filter", 818 filter); 819 if (retval < 0) { 820 /* 821 * If we managed to set up the previous filter but not 822 * this one, something's really wrong 823 */ 824 err_msg("Could not set ipi_send_cpumask CPU filter\n"); 825 *filters_enabled = false; 826 return -1; 827 } 828 829 *filters_enabled = true; 830 return 0; 831 } 832 833 /* 834 * osnoise_init_trace_tool - init a tracer instance to trace osnoise events 835 */ 836 struct osnoise_tool *osnoise_init_trace_tool(struct common_params *params, 837 const char *tracer) 838 { 839 struct osnoise_tool *trace; 840 int retval; 841 842 trace = osnoise_init_tool("osnoise_trace"); 843 if (!trace) 844 return NULL; 845 846 retval = tracefs_event_enable(trace->trace.inst, "osnoise", NULL); 847 if (retval < 0 && !errno) { 848 err_msg("Could not find osnoise events\n"); 849 goto out_err; 850 } 851 852 if (!params->ipi) 853 goto done; 854 855 retval = tracefs_event_enable(trace->trace.inst, "ipi", "ipi_send_cpu"); 856 if (retval < 0 && !errno) { 857 err_msg("Could not find ipi_send_cpu event\n"); 858 goto out_err; 859 } 860 861 retval = tracefs_event_enable(trace->trace.inst, "ipi", "ipi_send_cpumask"); 862 if (retval < 0 && !errno) { 863 err_msg("Could not find ipi_send_cpumask event\n"); 864 goto out_err; 865 } 866 867 if (params->cpus) { 868 bool unused; 869 870 retval = osnoise_init_ipi_filters(trace, params, &unused); 871 if (retval < 0) 872 goto out_err; 873 } 874 875 done: 876 retval = enable_tracer_by_name(trace->trace.inst, tracer); 877 if (retval) { 878 err_msg("Could not enable %s tracer for tracing\n", tracer); 879 goto out_err; 880 } 881 882 return trace; 883 out_err: 884 osnoise_destroy_tool(trace); 885 return NULL; 886 } 887 888 bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record) 889 { 890 /* 891 * The tool instance is always present, it is the one used to collect 892 * data. 893 */ 894 if (!tracefs_trace_is_on(tool->trace.inst)) 895 return true; 896 897 /* 898 * The trace record instance is only enabled when -t is set. IOW, when the system 899 * is tracing. 900 */ 901 return record && !tracefs_trace_is_on(record->trace.inst); 902 } 903 904 /* 905 * osnoise_report_invalid_events - report number of invalid events 906 */ 907 void 908 osnoise_report_invalid_events(struct osnoise_tool *tool) 909 { 910 unsigned long long total_events; 911 912 if (tool->trace.invalid_events > 0) { 913 if (tool->trace.missed_events != UINT64_MAX) { 914 total_events = tool->trace.processed_events + tool->trace.invalid_events + 915 tool->trace.missed_events; 916 917 printf("%lld (%.2f%%) invalid events, results might not be accurate\n", 918 tool->trace.invalid_events, 919 (double) tool->trace.invalid_events / total_events * 100.0); 920 } else { 921 printf("%lld invalid events, results might not be accurate\n", 922 tool->trace.invalid_events); 923 } 924 } 925 } 926 927 /* 928 * osnoise_report_missed_events - report number of events dropped by trace 929 * buffer 930 */ 931 void 932 osnoise_report_missed_events(struct osnoise_tool *tool) 933 { 934 unsigned long long total_events; 935 936 if (tool->trace.missed_events == UINT64_MAX) 937 printf("unknown number of events missed, results might not be accurate\n"); 938 else if (tool->trace.missed_events > 0) { 939 total_events = tool->trace.processed_events + tool->trace.invalid_events + 940 tool->trace.missed_events; 941 942 printf("%lld (%.2f%%) events missed, results might not be accurate\n", 943 tool->trace.missed_events, 944 (double) tool->trace.missed_events / total_events * 100.0); 945 } 946 } 947 948 /* 949 * osnoise_apply_config - apply osnoise configs to the initialized tool 950 */ 951 int 952 osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) 953 { 954 int retval; 955 956 params->common.kernel_workload = true; 957 958 if (params->runtime || params->period) { 959 retval = osnoise_set_runtime_period(tool->context, 960 params->runtime, 961 params->period); 962 } else { 963 retval = osnoise_set_runtime_period(tool->context, 964 DEFAULT_SAMPLE_PERIOD, 965 DEFAULT_SAMPLE_RUNTIME); 966 } 967 968 if (retval) { 969 err_msg("Failed to set runtime and/or period\n"); 970 goto out_err; 971 } 972 973 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 974 if (retval) { 975 err_msg("Failed to set tracing_thresh\n"); 976 goto out_err; 977 } 978 979 return common_apply_config(tool, ¶ms->common); 980 981 out_err: 982 return -1; 983 } 984 985 int osnoise_enable(struct osnoise_tool *tool) 986 { 987 struct osnoise_params *params = to_osnoise_params(tool->params); 988 int retval; 989 990 /* 991 * Start the tracer here, after having set all instances. 992 * 993 * Let the trace instance start first for the case of hitting a stop 994 * tracing while enabling other instances. The trace instance is the 995 * one with most valuable information. 996 */ 997 if (tool->record) 998 trace_instance_start(&tool->record->trace); 999 trace_instance_start(&tool->trace); 1000 1001 if (params->common.warmup > 0) { 1002 debug_msg("Warming up for %d seconds\n", params->common.warmup); 1003 sleep(params->common.warmup); 1004 if (stop_tracing) 1005 return -1; 1006 1007 /* 1008 * Clean up the buffer. The osnoise workload do not run 1009 * with tracing off to avoid creating a performance penalty 1010 * when not needed. 1011 */ 1012 retval = tracefs_instance_file_write(tool->trace.inst, "trace", ""); 1013 if (retval < 0) { 1014 debug_msg("Error cleaning up the buffer"); 1015 return retval; 1016 } 1017 } 1018 1019 retval = osn_set_stop(tool); 1020 if (retval) 1021 return retval; 1022 1023 return 0; 1024 } 1025 1026 __noreturn static void osnoise_usage(int err) 1027 { 1028 int i; 1029 1030 static const char *msg[] = { 1031 "", 1032 "osnoise version " VERSION, 1033 "", 1034 " usage: [rtla] osnoise [MODE] ...", 1035 "", 1036 " modes:", 1037 " top - prints the summary from osnoise tracer", 1038 " hist - prints a histogram of osnoise samples", 1039 "", 1040 "if no MODE is given, the top mode is called, passing the arguments", 1041 NULL, 1042 }; 1043 1044 for (i = 0; msg[i]; i++) 1045 fprintf(stderr, "%s\n", msg[i]); 1046 exit(err); 1047 } 1048 1049 int osnoise_main(int argc, char *argv[]) 1050 { 1051 if (argc == 0) 1052 goto usage; 1053 1054 /* 1055 * if osnoise was called without any argument, run the 1056 * default cmdline. 1057 */ 1058 if (argc == 1) { 1059 run_tool(&osnoise_top_ops, argc, argv); 1060 exit(0); 1061 } 1062 1063 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 1064 osnoise_usage(129); 1065 } else if (str_has_prefix(argv[1], "-")) { 1066 /* the user skipped the tool, call the default one */ 1067 run_tool(&osnoise_top_ops, argc, argv); 1068 exit(0); 1069 } else if (strcmp(argv[1], "top") == 0) { 1070 run_tool(&osnoise_top_ops, argc-1, &argv[1]); 1071 exit(0); 1072 } else if (strcmp(argv[1], "hist") == 0) { 1073 run_tool(&osnoise_hist_ops, argc-1, &argv[1]); 1074 exit(0); 1075 } 1076 1077 usage: 1078 osnoise_usage(129); 1079 } 1080 1081 int hwnoise_main(int argc, char *argv[]) 1082 { 1083 run_tool(&osnoise_top_ops, argc, argv); 1084 exit(0); 1085 } 1086