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 "osnoise.h" 19 20 #define DEFAULT_SAMPLE_PERIOD 1000000 /* 1s */ 21 #define DEFAULT_SAMPLE_RUNTIME 1000000 /* 1s */ 22 23 /* 24 * osnoise_get_cpus - return the original "osnoise/cpus" content 25 * 26 * It also saves the value to be restored. 27 */ 28 char *osnoise_get_cpus(struct osnoise_context *context) 29 { 30 if (context->curr_cpus) 31 return context->curr_cpus; 32 33 if (context->orig_cpus) 34 return context->orig_cpus; 35 36 context->orig_cpus = tracefs_instance_file_read(NULL, "osnoise/cpus", NULL); 37 38 /* 39 * The error value (NULL) is the same for tracefs_instance_file_read() 40 * and this functions, so: 41 */ 42 return context->orig_cpus; 43 } 44 45 /* 46 * osnoise_set_cpus - configure osnoise to run on *cpus 47 * 48 * "osnoise/cpus" file is used to set the cpus in which osnoise/timerlat 49 * will run. This function opens this file, saves the current value, 50 * and set the cpus passed as argument. 51 */ 52 int osnoise_set_cpus(struct osnoise_context *context, char *cpus) 53 { 54 char *orig_cpus = osnoise_get_cpus(context); 55 char buffer[1024]; 56 int retval; 57 58 if (!orig_cpus) 59 return -1; 60 61 context->curr_cpus = strdup(cpus); 62 if (!context->curr_cpus) 63 return -1; 64 65 snprintf(buffer, 1024, "%s\n", cpus); 66 67 debug_msg("setting cpus to %s from %s", cpus, context->orig_cpus); 68 69 retval = tracefs_instance_file_write(NULL, "osnoise/cpus", buffer); 70 if (retval < 0) { 71 free(context->curr_cpus); 72 context->curr_cpus = NULL; 73 return -1; 74 } 75 76 return 0; 77 } 78 79 /* 80 * osnoise_restore_cpus - restore the original "osnoise/cpus" 81 * 82 * osnoise_set_cpus() saves the original data for the "osnoise/cpus" 83 * file. This function restore the original config it was previously 84 * modified. 85 */ 86 void osnoise_restore_cpus(struct osnoise_context *context) 87 { 88 int retval; 89 90 if (!context->orig_cpus) 91 return; 92 93 if (!context->curr_cpus) 94 return; 95 96 /* nothing to do? */ 97 if (!strcmp(context->orig_cpus, context->curr_cpus)) 98 goto out_done; 99 100 debug_msg("restoring cpus to %s", context->orig_cpus); 101 102 retval = tracefs_instance_file_write(NULL, "osnoise/cpus", context->orig_cpus); 103 if (retval < 0) 104 err_msg("could not restore original osnoise cpus\n"); 105 106 out_done: 107 free(context->curr_cpus); 108 context->curr_cpus = NULL; 109 } 110 111 /* 112 * osnoise_put_cpus - restore cpus config and cleanup data 113 */ 114 void osnoise_put_cpus(struct osnoise_context *context) 115 { 116 osnoise_restore_cpus(context); 117 118 if (!context->orig_cpus) 119 return; 120 121 free(context->orig_cpus); 122 context->orig_cpus = NULL; 123 } 124 125 /* 126 * osnoise_read_ll_config - read a long long value from a config 127 * 128 * returns -1 on error. 129 */ 130 static long long osnoise_read_ll_config(char *rel_path) 131 { 132 long long retval; 133 char *buffer; 134 135 buffer = tracefs_instance_file_read(NULL, rel_path, NULL); 136 if (!buffer) 137 return -1; 138 139 /* get_llong_from_str returns -1 on error */ 140 retval = get_llong_from_str(buffer); 141 142 debug_msg("reading %s returned %lld\n", rel_path, retval); 143 144 free(buffer); 145 146 return retval; 147 } 148 149 /* 150 * osnoise_write_ll_config - write a long long value to a config in rel_path 151 * 152 * returns -1 on error. 153 */ 154 static long long osnoise_write_ll_config(char *rel_path, long long value) 155 { 156 char buffer[BUFF_U64_STR_SIZE]; 157 long long retval; 158 159 snprintf(buffer, sizeof(buffer), "%lld\n", value); 160 161 debug_msg("setting %s to %lld\n", rel_path, value); 162 163 retval = tracefs_instance_file_write(NULL, rel_path, buffer); 164 return retval; 165 } 166 167 /* 168 * osnoise_get_runtime - return the original "osnoise/runtime_us" value 169 * 170 * It also saves the value to be restored. 171 */ 172 unsigned long long osnoise_get_runtime(struct osnoise_context *context) 173 { 174 long long runtime_us; 175 176 if (context->runtime_us != OSNOISE_TIME_INIT_VAL) 177 return context->runtime_us; 178 179 if (context->orig_runtime_us != OSNOISE_TIME_INIT_VAL) 180 return context->orig_runtime_us; 181 182 runtime_us = osnoise_read_ll_config("osnoise/runtime_us"); 183 if (runtime_us < 0) 184 goto out_err; 185 186 context->orig_runtime_us = runtime_us; 187 return runtime_us; 188 189 out_err: 190 return OSNOISE_TIME_INIT_VAL; 191 } 192 193 /* 194 * osnoise_get_period - return the original "osnoise/period_us" value 195 * 196 * It also saves the value to be restored. 197 */ 198 unsigned long long osnoise_get_period(struct osnoise_context *context) 199 { 200 long long period_us; 201 202 if (context->period_us != OSNOISE_TIME_INIT_VAL) 203 return context->period_us; 204 205 if (context->orig_period_us != OSNOISE_TIME_INIT_VAL) 206 return context->orig_period_us; 207 208 period_us = osnoise_read_ll_config("osnoise/period_us"); 209 if (period_us < 0) 210 goto out_err; 211 212 context->orig_period_us = period_us; 213 return period_us; 214 215 out_err: 216 return OSNOISE_TIME_INIT_VAL; 217 } 218 219 static int __osnoise_write_runtime(struct osnoise_context *context, 220 unsigned long long runtime) 221 { 222 int retval; 223 224 if (context->orig_runtime_us == OSNOISE_TIME_INIT_VAL) 225 return -1; 226 227 retval = osnoise_write_ll_config("osnoise/runtime_us", runtime); 228 if (retval < 0) 229 return -1; 230 231 context->runtime_us = runtime; 232 return 0; 233 } 234 235 static int __osnoise_write_period(struct osnoise_context *context, 236 unsigned long long period) 237 { 238 int retval; 239 240 if (context->orig_period_us == OSNOISE_TIME_INIT_VAL) 241 return -1; 242 243 retval = osnoise_write_ll_config("osnoise/period_us", period); 244 if (retval < 0) 245 return -1; 246 247 context->period_us = period; 248 return 0; 249 } 250 251 /* 252 * osnoise_set_runtime_period - set osnoise runtime and period 253 * 254 * Osnoise's runtime and period are related as runtime <= period. 255 * Thus, this function saves the original values, and then tries 256 * to set the runtime and period if they are != 0. 257 */ 258 int osnoise_set_runtime_period(struct osnoise_context *context, 259 unsigned long long runtime, 260 unsigned long long period) 261 { 262 unsigned long long curr_runtime_us; 263 unsigned long long curr_period_us; 264 int retval; 265 266 if (!period && !runtime) 267 return 0; 268 269 curr_runtime_us = osnoise_get_runtime(context); 270 curr_period_us = osnoise_get_period(context); 271 272 /* error getting any value? */ 273 if (curr_period_us == OSNOISE_TIME_INIT_VAL || curr_runtime_us == OSNOISE_TIME_INIT_VAL) 274 return -1; 275 276 if (!period) { 277 if (runtime > curr_period_us) 278 return -1; 279 return __osnoise_write_runtime(context, runtime); 280 } else if (!runtime) { 281 if (period < curr_runtime_us) 282 return -1; 283 return __osnoise_write_period(context, period); 284 } 285 286 if (runtime > curr_period_us) { 287 retval = __osnoise_write_period(context, period); 288 if (retval) 289 return -1; 290 retval = __osnoise_write_runtime(context, runtime); 291 if (retval) 292 return -1; 293 } else { 294 retval = __osnoise_write_runtime(context, runtime); 295 if (retval) 296 return -1; 297 retval = __osnoise_write_period(context, period); 298 if (retval) 299 return -1; 300 } 301 302 return 0; 303 } 304 305 /* 306 * osnoise_restore_runtime_period - restore the original runtime and period 307 */ 308 void osnoise_restore_runtime_period(struct osnoise_context *context) 309 { 310 unsigned long long orig_runtime = context->orig_runtime_us; 311 unsigned long long orig_period = context->orig_period_us; 312 unsigned long long curr_runtime = context->runtime_us; 313 unsigned long long curr_period = context->period_us; 314 int retval; 315 316 if ((orig_runtime == OSNOISE_TIME_INIT_VAL) && (orig_period == OSNOISE_TIME_INIT_VAL)) 317 return; 318 319 if ((orig_period == curr_period) && (orig_runtime == curr_runtime)) 320 goto out_done; 321 322 retval = osnoise_set_runtime_period(context, orig_runtime, orig_period); 323 if (retval) 324 err_msg("Could not restore original osnoise runtime/period\n"); 325 326 out_done: 327 context->runtime_us = OSNOISE_TIME_INIT_VAL; 328 context->period_us = OSNOISE_TIME_INIT_VAL; 329 } 330 331 /* 332 * osnoise_put_runtime_period - restore original values and cleanup data 333 */ 334 void osnoise_put_runtime_period(struct osnoise_context *context) 335 { 336 osnoise_restore_runtime_period(context); 337 338 if (context->orig_runtime_us != OSNOISE_TIME_INIT_VAL) 339 context->orig_runtime_us = OSNOISE_TIME_INIT_VAL; 340 341 if (context->orig_period_us != OSNOISE_TIME_INIT_VAL) 342 context->orig_period_us = OSNOISE_TIME_INIT_VAL; 343 } 344 345 /* 346 * osnoise_get_timerlat_period_us - read and save the original "timerlat_period_us" 347 */ 348 static long long 349 osnoise_get_timerlat_period_us(struct osnoise_context *context) 350 { 351 long long timerlat_period_us; 352 353 if (context->timerlat_period_us != OSNOISE_TIME_INIT_VAL) 354 return context->timerlat_period_us; 355 356 if (context->orig_timerlat_period_us != OSNOISE_TIME_INIT_VAL) 357 return context->orig_timerlat_period_us; 358 359 timerlat_period_us = osnoise_read_ll_config("osnoise/timerlat_period_us"); 360 if (timerlat_period_us < 0) 361 goto out_err; 362 363 context->orig_timerlat_period_us = timerlat_period_us; 364 return timerlat_period_us; 365 366 out_err: 367 return OSNOISE_TIME_INIT_VAL; 368 } 369 370 /* 371 * osnoise_set_timerlat_period_us - set "timerlat_period_us" 372 */ 373 int osnoise_set_timerlat_period_us(struct osnoise_context *context, long long timerlat_period_us) 374 { 375 long long curr_timerlat_period_us = osnoise_get_timerlat_period_us(context); 376 int retval; 377 378 if (curr_timerlat_period_us == OSNOISE_TIME_INIT_VAL) 379 return -1; 380 381 retval = osnoise_write_ll_config("osnoise/timerlat_period_us", timerlat_period_us); 382 if (retval < 0) 383 return -1; 384 385 context->timerlat_period_us = timerlat_period_us; 386 387 return 0; 388 } 389 390 /* 391 * osnoise_restore_timerlat_period_us - restore "timerlat_period_us" 392 */ 393 void osnoise_restore_timerlat_period_us(struct osnoise_context *context) 394 { 395 int retval; 396 397 if (context->orig_timerlat_period_us == OSNOISE_TIME_INIT_VAL) 398 return; 399 400 if (context->orig_timerlat_period_us == context->timerlat_period_us) 401 goto out_done; 402 403 retval = osnoise_write_ll_config("osnoise/timerlat_period_us", context->orig_timerlat_period_us); 404 if (retval < 0) 405 err_msg("Could not restore original osnoise timerlat_period_us\n"); 406 407 out_done: 408 context->timerlat_period_us = OSNOISE_TIME_INIT_VAL; 409 } 410 411 /* 412 * osnoise_put_timerlat_period_us - restore original values and cleanup data 413 */ 414 void osnoise_put_timerlat_period_us(struct osnoise_context *context) 415 { 416 osnoise_restore_timerlat_period_us(context); 417 418 if (context->orig_timerlat_period_us == OSNOISE_TIME_INIT_VAL) 419 return; 420 421 context->orig_timerlat_period_us = OSNOISE_TIME_INIT_VAL; 422 } 423 424 /* 425 * osnoise_get_stop_us - read and save the original "stop_tracing_us" 426 */ 427 static long long 428 osnoise_get_stop_us(struct osnoise_context *context) 429 { 430 long long stop_us; 431 432 if (context->stop_us != OSNOISE_OPTION_INIT_VAL) 433 return context->stop_us; 434 435 if (context->orig_stop_us != OSNOISE_OPTION_INIT_VAL) 436 return context->orig_stop_us; 437 438 stop_us = osnoise_read_ll_config("osnoise/stop_tracing_us"); 439 if (stop_us < 0) 440 goto out_err; 441 442 context->orig_stop_us = stop_us; 443 return stop_us; 444 445 out_err: 446 return OSNOISE_OPTION_INIT_VAL; 447 } 448 449 /* 450 * osnoise_set_stop_us - set "stop_tracing_us" 451 */ 452 int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us) 453 { 454 long long curr_stop_us = osnoise_get_stop_us(context); 455 int retval; 456 457 if (curr_stop_us == OSNOISE_OPTION_INIT_VAL) 458 return -1; 459 460 retval = osnoise_write_ll_config("osnoise/stop_tracing_us", stop_us); 461 if (retval < 0) 462 return -1; 463 464 context->stop_us = stop_us; 465 466 return 0; 467 } 468 469 /* 470 * osnoise_restore_stop_us - restore the original "stop_tracing_us" 471 */ 472 void osnoise_restore_stop_us(struct osnoise_context *context) 473 { 474 int retval; 475 476 if (context->orig_stop_us == OSNOISE_OPTION_INIT_VAL) 477 return; 478 479 if (context->orig_stop_us == context->stop_us) 480 goto out_done; 481 482 retval = osnoise_write_ll_config("osnoise/stop_tracing_us", context->orig_stop_us); 483 if (retval < 0) 484 err_msg("Could not restore original osnoise stop_us\n"); 485 486 out_done: 487 context->stop_us = OSNOISE_OPTION_INIT_VAL; 488 } 489 490 /* 491 * osnoise_put_stop_us - restore original values and cleanup data 492 */ 493 void osnoise_put_stop_us(struct osnoise_context *context) 494 { 495 osnoise_restore_stop_us(context); 496 497 if (context->orig_stop_us == OSNOISE_OPTION_INIT_VAL) 498 return; 499 500 context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; 501 } 502 503 /* 504 * osnoise_get_stop_total_us - read and save the original "stop_tracing_total_us" 505 */ 506 static long long 507 osnoise_get_stop_total_us(struct osnoise_context *context) 508 { 509 long long stop_total_us; 510 511 if (context->stop_total_us != OSNOISE_OPTION_INIT_VAL) 512 return context->stop_total_us; 513 514 if (context->orig_stop_total_us != OSNOISE_OPTION_INIT_VAL) 515 return context->orig_stop_total_us; 516 517 stop_total_us = osnoise_read_ll_config("osnoise/stop_tracing_total_us"); 518 if (stop_total_us < 0) 519 goto out_err; 520 521 context->orig_stop_total_us = stop_total_us; 522 return stop_total_us; 523 524 out_err: 525 return OSNOISE_OPTION_INIT_VAL; 526 } 527 528 /* 529 * osnoise_set_stop_total_us - set "stop_tracing_total_us" 530 */ 531 int osnoise_set_stop_total_us(struct osnoise_context *context, long long stop_total_us) 532 { 533 long long curr_stop_total_us = osnoise_get_stop_total_us(context); 534 int retval; 535 536 if (curr_stop_total_us == OSNOISE_OPTION_INIT_VAL) 537 return -1; 538 539 retval = osnoise_write_ll_config("osnoise/stop_tracing_total_us", stop_total_us); 540 if (retval < 0) 541 return -1; 542 543 context->stop_total_us = stop_total_us; 544 545 return 0; 546 } 547 548 /* 549 * osnoise_restore_stop_total_us - restore the original "stop_tracing_total_us" 550 */ 551 void osnoise_restore_stop_total_us(struct osnoise_context *context) 552 { 553 int retval; 554 555 if (context->orig_stop_total_us == OSNOISE_OPTION_INIT_VAL) 556 return; 557 558 if (context->orig_stop_total_us == context->stop_total_us) 559 goto out_done; 560 561 retval = osnoise_write_ll_config("osnoise/stop_tracing_total_us", 562 context->orig_stop_total_us); 563 if (retval < 0) 564 err_msg("Could not restore original osnoise stop_total_us\n"); 565 566 out_done: 567 context->stop_total_us = OSNOISE_OPTION_INIT_VAL; 568 } 569 570 /* 571 * osnoise_put_stop_total_us - restore original values and cleanup data 572 */ 573 void osnoise_put_stop_total_us(struct osnoise_context *context) 574 { 575 osnoise_restore_stop_total_us(context); 576 577 if (context->orig_stop_total_us == OSNOISE_OPTION_INIT_VAL) 578 return; 579 580 context->orig_stop_total_us = OSNOISE_OPTION_INIT_VAL; 581 } 582 583 /* 584 * osnoise_get_print_stack - read and save the original "print_stack" 585 */ 586 static long long 587 osnoise_get_print_stack(struct osnoise_context *context) 588 { 589 long long print_stack; 590 591 if (context->print_stack != OSNOISE_OPTION_INIT_VAL) 592 return context->print_stack; 593 594 if (context->orig_print_stack != OSNOISE_OPTION_INIT_VAL) 595 return context->orig_print_stack; 596 597 print_stack = osnoise_read_ll_config("osnoise/print_stack"); 598 if (print_stack < 0) 599 goto out_err; 600 601 context->orig_print_stack = print_stack; 602 return print_stack; 603 604 out_err: 605 return OSNOISE_OPTION_INIT_VAL; 606 } 607 608 /* 609 * osnoise_set_print_stack - set "print_stack" 610 */ 611 int osnoise_set_print_stack(struct osnoise_context *context, long long print_stack) 612 { 613 long long curr_print_stack = osnoise_get_print_stack(context); 614 int retval; 615 616 if (curr_print_stack == OSNOISE_OPTION_INIT_VAL) 617 return -1; 618 619 retval = osnoise_write_ll_config("osnoise/print_stack", print_stack); 620 if (retval < 0) 621 return -1; 622 623 context->print_stack = print_stack; 624 625 return 0; 626 } 627 628 /* 629 * osnoise_restore_print_stack - restore the original "print_stack" 630 */ 631 void osnoise_restore_print_stack(struct osnoise_context *context) 632 { 633 int retval; 634 635 if (context->orig_print_stack == OSNOISE_OPTION_INIT_VAL) 636 return; 637 638 if (context->orig_print_stack == context->print_stack) 639 goto out_done; 640 641 retval = osnoise_write_ll_config("osnoise/print_stack", context->orig_print_stack); 642 if (retval < 0) 643 err_msg("Could not restore original osnoise print_stack\n"); 644 645 out_done: 646 context->print_stack = OSNOISE_OPTION_INIT_VAL; 647 } 648 649 /* 650 * osnoise_put_print_stack - restore original values and cleanup data 651 */ 652 void osnoise_put_print_stack(struct osnoise_context *context) 653 { 654 osnoise_restore_print_stack(context); 655 656 if (context->orig_print_stack == OSNOISE_OPTION_INIT_VAL) 657 return; 658 659 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL; 660 } 661 662 /* 663 * osnoise_get_tracing_thresh - read and save the original "tracing_thresh" 664 */ 665 static long long 666 osnoise_get_tracing_thresh(struct osnoise_context *context) 667 { 668 long long tracing_thresh; 669 670 if (context->tracing_thresh != OSNOISE_OPTION_INIT_VAL) 671 return context->tracing_thresh; 672 673 if (context->orig_tracing_thresh != OSNOISE_OPTION_INIT_VAL) 674 return context->orig_tracing_thresh; 675 676 tracing_thresh = osnoise_read_ll_config("tracing_thresh"); 677 if (tracing_thresh < 0) 678 goto out_err; 679 680 context->orig_tracing_thresh = tracing_thresh; 681 return tracing_thresh; 682 683 out_err: 684 return OSNOISE_OPTION_INIT_VAL; 685 } 686 687 /* 688 * osnoise_set_tracing_thresh - set "tracing_thresh" 689 */ 690 int osnoise_set_tracing_thresh(struct osnoise_context *context, long long tracing_thresh) 691 { 692 long long curr_tracing_thresh = osnoise_get_tracing_thresh(context); 693 int retval; 694 695 if (curr_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 696 return -1; 697 698 retval = osnoise_write_ll_config("tracing_thresh", tracing_thresh); 699 if (retval < 0) 700 return -1; 701 702 context->tracing_thresh = tracing_thresh; 703 704 return 0; 705 } 706 707 /* 708 * osnoise_restore_tracing_thresh - restore the original "tracing_thresh" 709 */ 710 void osnoise_restore_tracing_thresh(struct osnoise_context *context) 711 { 712 int retval; 713 714 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 715 return; 716 717 if (context->orig_tracing_thresh == context->tracing_thresh) 718 goto out_done; 719 720 retval = osnoise_write_ll_config("tracing_thresh", context->orig_tracing_thresh); 721 if (retval < 0) 722 err_msg("Could not restore original tracing_thresh\n"); 723 724 out_done: 725 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 726 } 727 728 /* 729 * osnoise_put_tracing_thresh - restore original values and cleanup data 730 */ 731 void osnoise_put_tracing_thresh(struct osnoise_context *context) 732 { 733 osnoise_restore_tracing_thresh(context); 734 735 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 736 return; 737 738 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 739 } 740 741 static int osnoise_options_get_option(char *option) 742 { 743 char *options = tracefs_instance_file_read(NULL, "osnoise/options", NULL); 744 char no_option[128]; 745 int retval = 0; 746 char *opt; 747 748 if (!options) 749 return OSNOISE_OPTION_INIT_VAL; 750 751 /* 752 * Check first if the option is disabled. 753 */ 754 snprintf(no_option, sizeof(no_option), "NO_%s", option); 755 756 opt = strstr(options, no_option); 757 if (opt) 758 goto out_free; 759 760 /* 761 * Now that it is not disabled, if the string is there, it is 762 * enabled. If the string is not there, the option does not exist. 763 */ 764 opt = strstr(options, option); 765 if (opt) 766 retval = 1; 767 else 768 retval = OSNOISE_OPTION_INIT_VAL; 769 770 out_free: 771 free(options); 772 return retval; 773 } 774 775 static int osnoise_options_set_option(char *option, bool onoff) 776 { 777 char no_option[128]; 778 779 if (onoff) 780 return tracefs_instance_file_write(NULL, "osnoise/options", option); 781 782 snprintf(no_option, sizeof(no_option), "NO_%s", option); 783 784 return tracefs_instance_file_write(NULL, "osnoise/options", no_option); 785 } 786 787 static int osnoise_get_irq_disable(struct osnoise_context *context) 788 { 789 if (context->opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 790 return context->opt_irq_disable; 791 792 if (context->orig_opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 793 return context->orig_opt_irq_disable; 794 795 context->orig_opt_irq_disable = osnoise_options_get_option("OSNOISE_IRQ_DISABLE"); 796 797 return context->orig_opt_irq_disable; 798 } 799 800 int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff) 801 { 802 int opt_irq_disable = osnoise_get_irq_disable(context); 803 int retval; 804 805 if (opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 806 return -1; 807 808 if (opt_irq_disable == onoff) 809 return 0; 810 811 retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", onoff); 812 if (retval < 0) 813 return -1; 814 815 context->opt_irq_disable = onoff; 816 817 return 0; 818 } 819 820 static void osnoise_restore_irq_disable(struct osnoise_context *context) 821 { 822 int retval; 823 824 if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 825 return; 826 827 if (context->orig_opt_irq_disable == context->opt_irq_disable) 828 goto out_done; 829 830 retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", context->orig_opt_irq_disable); 831 if (retval < 0) 832 err_msg("Could not restore original OSNOISE_IRQ_DISABLE option\n"); 833 834 out_done: 835 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 836 } 837 838 static void osnoise_put_irq_disable(struct osnoise_context *context) 839 { 840 osnoise_restore_irq_disable(context); 841 842 if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 843 return; 844 845 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 846 } 847 848 static int osnoise_get_workload(struct osnoise_context *context) 849 { 850 if (context->opt_workload != OSNOISE_OPTION_INIT_VAL) 851 return context->opt_workload; 852 853 if (context->orig_opt_workload != OSNOISE_OPTION_INIT_VAL) 854 return context->orig_opt_workload; 855 856 context->orig_opt_workload = osnoise_options_get_option("OSNOISE_WORKLOAD"); 857 858 return context->orig_opt_workload; 859 } 860 861 int osnoise_set_workload(struct osnoise_context *context, bool onoff) 862 { 863 int opt_workload = osnoise_get_workload(context); 864 int retval; 865 866 if (opt_workload == OSNOISE_OPTION_INIT_VAL) 867 return -1; 868 869 if (opt_workload == onoff) 870 return 0; 871 872 retval = osnoise_options_set_option("OSNOISE_WORKLOAD", onoff); 873 if (retval < 0) 874 return -2; 875 876 context->opt_workload = onoff; 877 878 return 0; 879 } 880 881 static void osnoise_restore_workload(struct osnoise_context *context) 882 { 883 int retval; 884 885 if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL) 886 return; 887 888 if (context->orig_opt_workload == context->opt_workload) 889 goto out_done; 890 891 retval = osnoise_options_set_option("OSNOISE_WORKLOAD", context->orig_opt_workload); 892 if (retval < 0) 893 err_msg("Could not restore original OSNOISE_WORKLOAD option\n"); 894 895 out_done: 896 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 897 } 898 899 static void osnoise_put_workload(struct osnoise_context *context) 900 { 901 osnoise_restore_workload(context); 902 903 if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL) 904 return; 905 906 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 907 } 908 909 /* 910 * enable_osnoise - enable osnoise tracer in the trace_instance 911 */ 912 int enable_osnoise(struct trace_instance *trace) 913 { 914 return enable_tracer_by_name(trace->inst, "osnoise"); 915 } 916 917 /* 918 * enable_timerlat - enable timerlat tracer in the trace_instance 919 */ 920 int enable_timerlat(struct trace_instance *trace) 921 { 922 return enable_tracer_by_name(trace->inst, "timerlat"); 923 } 924 925 enum { 926 FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), 927 FLAG_CONTEXT_DELETED = (1 << 1), 928 }; 929 930 /* 931 * osnoise_get_context - increase the usage of a context and return it 932 */ 933 int osnoise_get_context(struct osnoise_context *context) 934 { 935 int ret; 936 937 if (context->flags & FLAG_CONTEXT_DELETED) { 938 ret = -1; 939 } else { 940 context->ref++; 941 ret = 0; 942 } 943 944 return ret; 945 } 946 947 /* 948 * osnoise_context_alloc - alloc an osnoise_context 949 * 950 * The osnoise context contains the information of the "osnoise/" configs. 951 * It is used to set and restore the config. 952 */ 953 struct osnoise_context *osnoise_context_alloc(void) 954 { 955 struct osnoise_context *context; 956 957 context = calloc(1, sizeof(*context)); 958 if (!context) 959 return NULL; 960 961 context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; 962 context->stop_us = OSNOISE_OPTION_INIT_VAL; 963 964 context->orig_stop_total_us = OSNOISE_OPTION_INIT_VAL; 965 context->stop_total_us = OSNOISE_OPTION_INIT_VAL; 966 967 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL; 968 context->print_stack = OSNOISE_OPTION_INIT_VAL; 969 970 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 971 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 972 973 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 974 context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 975 976 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 977 context->opt_workload = OSNOISE_OPTION_INIT_VAL; 978 979 osnoise_get_context(context); 980 981 return context; 982 } 983 984 /* 985 * osnoise_put_context - put the osnoise_put_context 986 * 987 * If there is no other user for the context, the original data 988 * is restored. 989 */ 990 void osnoise_put_context(struct osnoise_context *context) 991 { 992 if (--context->ref < 1) 993 context->flags |= FLAG_CONTEXT_DELETED; 994 995 if (!(context->flags & FLAG_CONTEXT_DELETED)) 996 return; 997 998 osnoise_put_cpus(context); 999 osnoise_put_runtime_period(context); 1000 osnoise_put_stop_us(context); 1001 osnoise_put_stop_total_us(context); 1002 osnoise_put_timerlat_period_us(context); 1003 osnoise_put_print_stack(context); 1004 osnoise_put_tracing_thresh(context); 1005 osnoise_put_irq_disable(context); 1006 osnoise_put_workload(context); 1007 1008 free(context); 1009 } 1010 1011 /* 1012 * osnoise_destroy_tool - disable trace, restore configs and free data 1013 */ 1014 void osnoise_destroy_tool(struct osnoise_tool *top) 1015 { 1016 if (!top) 1017 return; 1018 1019 trace_instance_destroy(&top->trace); 1020 1021 if (top->context) 1022 osnoise_put_context(top->context); 1023 1024 free(top); 1025 } 1026 1027 /* 1028 * osnoise_init_tool - init an osnoise tool 1029 * 1030 * It allocs data, create a context to store data and 1031 * creates a new trace instance for the tool. 1032 */ 1033 struct osnoise_tool *osnoise_init_tool(char *tool_name) 1034 { 1035 struct osnoise_tool *top; 1036 int retval; 1037 1038 top = calloc(1, sizeof(*top)); 1039 if (!top) 1040 return NULL; 1041 1042 top->context = osnoise_context_alloc(); 1043 if (!top->context) 1044 goto out_err; 1045 1046 retval = trace_instance_init(&top->trace, tool_name); 1047 if (retval) 1048 goto out_err; 1049 1050 return top; 1051 out_err: 1052 osnoise_destroy_tool(top); 1053 return NULL; 1054 } 1055 1056 /* 1057 * osnoise_init_trace_tool - init a tracer instance to trace osnoise events 1058 */ 1059 struct osnoise_tool *osnoise_init_trace_tool(char *tracer) 1060 { 1061 struct osnoise_tool *trace; 1062 int retval; 1063 1064 trace = osnoise_init_tool("osnoise_trace"); 1065 if (!trace) 1066 return NULL; 1067 1068 retval = tracefs_event_enable(trace->trace.inst, "osnoise", NULL); 1069 if (retval < 0 && !errno) { 1070 err_msg("Could not find osnoise events\n"); 1071 goto out_err; 1072 } 1073 1074 retval = enable_tracer_by_name(trace->trace.inst, tracer); 1075 if (retval) { 1076 err_msg("Could not enable %s tracer for tracing\n", tracer); 1077 goto out_err; 1078 } 1079 1080 return trace; 1081 out_err: 1082 osnoise_destroy_tool(trace); 1083 return NULL; 1084 } 1085 1086 bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record) 1087 { 1088 /* 1089 * The tool instance is always present, it is the one used to collect 1090 * data. 1091 */ 1092 if (!tracefs_trace_is_on(tool->trace.inst)) 1093 return true; 1094 1095 /* 1096 * The trace record instance is only enabled when -t is set. IOW, when the system 1097 * is tracing. 1098 */ 1099 return record && !tracefs_trace_is_on(record->trace.inst); 1100 } 1101 1102 /* 1103 * osnoise_report_missed_events - report number of events dropped by trace 1104 * buffer 1105 */ 1106 void 1107 osnoise_report_missed_events(struct osnoise_tool *tool) 1108 { 1109 unsigned long long total_events; 1110 1111 if (tool->trace.missed_events == UINT64_MAX) 1112 printf("unknown number of events missed, results might not be accurate\n"); 1113 else if (tool->trace.missed_events > 0) { 1114 total_events = tool->trace.processed_events + tool->trace.missed_events; 1115 1116 printf("%lld (%.2f%%) events missed, results might not be accurate\n", 1117 tool->trace.missed_events, 1118 (double) tool->trace.missed_events / total_events * 100.0); 1119 } 1120 } 1121 1122 /* 1123 * osnoise_apply_config - apply common configs to the initialized tool 1124 */ 1125 int 1126 osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) 1127 { 1128 int retval; 1129 1130 if (!params->sleep_time) 1131 params->sleep_time = 1; 1132 1133 retval = osnoise_set_cpus(tool->context, params->cpus ? params->cpus : "all"); 1134 if (retval) { 1135 err_msg("Failed to apply CPUs config\n"); 1136 goto out_err; 1137 } 1138 1139 if (params->runtime || params->period) { 1140 retval = osnoise_set_runtime_period(tool->context, 1141 params->runtime, 1142 params->period); 1143 } else { 1144 retval = osnoise_set_runtime_period(tool->context, 1145 DEFAULT_SAMPLE_PERIOD, 1146 DEFAULT_SAMPLE_RUNTIME); 1147 } 1148 1149 if (retval) { 1150 err_msg("Failed to set runtime and/or period\n"); 1151 goto out_err; 1152 } 1153 1154 retval = osnoise_set_stop_us(tool->context, params->stop_us); 1155 if (retval) { 1156 err_msg("Failed to set stop us\n"); 1157 goto out_err; 1158 } 1159 1160 retval = osnoise_set_stop_total_us(tool->context, params->stop_total_us); 1161 if (retval) { 1162 err_msg("Failed to set stop total us\n"); 1163 goto out_err; 1164 } 1165 1166 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 1167 if (retval) { 1168 err_msg("Failed to set tracing_thresh\n"); 1169 goto out_err; 1170 } 1171 1172 if (params->hk_cpus) { 1173 retval = sched_setaffinity(getpid(), sizeof(params->hk_cpu_set), 1174 ¶ms->hk_cpu_set); 1175 if (retval == -1) { 1176 err_msg("Failed to set rtla to the house keeping CPUs\n"); 1177 goto out_err; 1178 } 1179 } else if (params->cpus) { 1180 /* 1181 * Even if the user do not set a house-keeping CPU, try to 1182 * move rtla to a CPU set different to the one where the user 1183 * set the workload to run. 1184 * 1185 * No need to check results as this is an automatic attempt. 1186 */ 1187 auto_house_keeping(¶ms->monitored_cpus); 1188 } 1189 1190 retval = osnoise_set_workload(tool->context, true); 1191 if (retval < -1) { 1192 err_msg("Failed to set OSNOISE_WORKLOAD option\n"); 1193 goto out_err; 1194 } 1195 1196 return 0; 1197 1198 out_err: 1199 return -1; 1200 } 1201 1202 static void osnoise_usage(int err) 1203 { 1204 int i; 1205 1206 static const char *msg[] = { 1207 "", 1208 "osnoise version " VERSION, 1209 "", 1210 " usage: [rtla] osnoise [MODE] ...", 1211 "", 1212 " modes:", 1213 " top - prints the summary from osnoise tracer", 1214 " hist - prints a histogram of osnoise samples", 1215 "", 1216 "if no MODE is given, the top mode is called, passing the arguments", 1217 NULL, 1218 }; 1219 1220 for (i = 0; msg[i]; i++) 1221 fprintf(stderr, "%s\n", msg[i]); 1222 exit(err); 1223 } 1224 1225 int osnoise_main(int argc, char *argv[]) 1226 { 1227 if (argc == 0) 1228 goto usage; 1229 1230 /* 1231 * if osnoise was called without any argument, run the 1232 * default cmdline. 1233 */ 1234 if (argc == 1) { 1235 osnoise_top_main(argc, argv); 1236 exit(0); 1237 } 1238 1239 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 1240 osnoise_usage(0); 1241 } else if (strncmp(argv[1], "-", 1) == 0) { 1242 /* the user skipped the tool, call the default one */ 1243 osnoise_top_main(argc, argv); 1244 exit(0); 1245 } else if (strcmp(argv[1], "top") == 0) { 1246 osnoise_top_main(argc-1, &argv[1]); 1247 exit(0); 1248 } else if (strcmp(argv[1], "hist") == 0) { 1249 osnoise_hist_main(argc-1, &argv[1]); 1250 exit(0); 1251 } 1252 1253 usage: 1254 osnoise_usage(1); 1255 exit(1); 1256 } 1257 1258 int hwnoise_main(int argc, char *argv[]) 1259 { 1260 osnoise_top_main(argc, argv); 1261 exit(0); 1262 } 1263