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 enum { 910 FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), 911 FLAG_CONTEXT_DELETED = (1 << 1), 912 }; 913 914 /* 915 * osnoise_get_context - increase the usage of a context and return it 916 */ 917 int osnoise_get_context(struct osnoise_context *context) 918 { 919 int ret; 920 921 if (context->flags & FLAG_CONTEXT_DELETED) { 922 ret = -1; 923 } else { 924 context->ref++; 925 ret = 0; 926 } 927 928 return ret; 929 } 930 931 /* 932 * osnoise_context_alloc - alloc an osnoise_context 933 * 934 * The osnoise context contains the information of the "osnoise/" configs. 935 * It is used to set and restore the config. 936 */ 937 struct osnoise_context *osnoise_context_alloc(void) 938 { 939 struct osnoise_context *context; 940 941 context = calloc(1, sizeof(*context)); 942 if (!context) 943 return NULL; 944 945 context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; 946 context->stop_us = OSNOISE_OPTION_INIT_VAL; 947 948 context->orig_stop_total_us = OSNOISE_OPTION_INIT_VAL; 949 context->stop_total_us = OSNOISE_OPTION_INIT_VAL; 950 951 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL; 952 context->print_stack = OSNOISE_OPTION_INIT_VAL; 953 954 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 955 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 956 957 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 958 context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 959 960 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 961 context->opt_workload = OSNOISE_OPTION_INIT_VAL; 962 963 osnoise_get_context(context); 964 965 return context; 966 } 967 968 /* 969 * osnoise_put_context - put the osnoise_put_context 970 * 971 * If there is no other user for the context, the original data 972 * is restored. 973 */ 974 void osnoise_put_context(struct osnoise_context *context) 975 { 976 if (--context->ref < 1) 977 context->flags |= FLAG_CONTEXT_DELETED; 978 979 if (!(context->flags & FLAG_CONTEXT_DELETED)) 980 return; 981 982 osnoise_put_cpus(context); 983 osnoise_put_runtime_period(context); 984 osnoise_put_stop_us(context); 985 osnoise_put_stop_total_us(context); 986 osnoise_put_timerlat_period_us(context); 987 osnoise_put_print_stack(context); 988 osnoise_put_tracing_thresh(context); 989 osnoise_put_irq_disable(context); 990 osnoise_put_workload(context); 991 992 free(context); 993 } 994 995 /* 996 * osnoise_destroy_tool - disable trace, restore configs and free data 997 */ 998 void osnoise_destroy_tool(struct osnoise_tool *top) 999 { 1000 if (!top) 1001 return; 1002 1003 trace_instance_destroy(&top->trace); 1004 1005 if (top->context) 1006 osnoise_put_context(top->context); 1007 1008 free(top); 1009 } 1010 1011 /* 1012 * osnoise_init_tool - init an osnoise tool 1013 * 1014 * It allocs data, create a context to store data and 1015 * creates a new trace instance for the tool. 1016 */ 1017 struct osnoise_tool *osnoise_init_tool(char *tool_name) 1018 { 1019 struct osnoise_tool *top; 1020 int retval; 1021 1022 top = calloc(1, sizeof(*top)); 1023 if (!top) 1024 return NULL; 1025 1026 top->context = osnoise_context_alloc(); 1027 if (!top->context) 1028 goto out_err; 1029 1030 retval = trace_instance_init(&top->trace, tool_name); 1031 if (retval) 1032 goto out_err; 1033 1034 return top; 1035 out_err: 1036 osnoise_destroy_tool(top); 1037 return NULL; 1038 } 1039 1040 /* 1041 * osnoise_init_trace_tool - init a tracer instance to trace osnoise events 1042 */ 1043 struct osnoise_tool *osnoise_init_trace_tool(const char *tracer) 1044 { 1045 struct osnoise_tool *trace; 1046 int retval; 1047 1048 trace = osnoise_init_tool("osnoise_trace"); 1049 if (!trace) 1050 return NULL; 1051 1052 retval = tracefs_event_enable(trace->trace.inst, "osnoise", NULL); 1053 if (retval < 0 && !errno) { 1054 err_msg("Could not find osnoise events\n"); 1055 goto out_err; 1056 } 1057 1058 retval = enable_tracer_by_name(trace->trace.inst, tracer); 1059 if (retval) { 1060 err_msg("Could not enable %s tracer for tracing\n", tracer); 1061 goto out_err; 1062 } 1063 1064 return trace; 1065 out_err: 1066 osnoise_destroy_tool(trace); 1067 return NULL; 1068 } 1069 1070 bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record) 1071 { 1072 /* 1073 * The tool instance is always present, it is the one used to collect 1074 * data. 1075 */ 1076 if (!tracefs_trace_is_on(tool->trace.inst)) 1077 return true; 1078 1079 /* 1080 * The trace record instance is only enabled when -t is set. IOW, when the system 1081 * is tracing. 1082 */ 1083 return record && !tracefs_trace_is_on(record->trace.inst); 1084 } 1085 1086 /* 1087 * osnoise_report_missed_events - report number of events dropped by trace 1088 * buffer 1089 */ 1090 void 1091 osnoise_report_missed_events(struct osnoise_tool *tool) 1092 { 1093 unsigned long long total_events; 1094 1095 if (tool->trace.missed_events == UINT64_MAX) 1096 printf("unknown number of events missed, results might not be accurate\n"); 1097 else if (tool->trace.missed_events > 0) { 1098 total_events = tool->trace.processed_events + tool->trace.missed_events; 1099 1100 printf("%lld (%.2f%%) events missed, results might not be accurate\n", 1101 tool->trace.missed_events, 1102 (double) tool->trace.missed_events / total_events * 100.0); 1103 } 1104 } 1105 1106 /* 1107 * osnoise_apply_config - apply osnoise configs to the initialized tool 1108 */ 1109 int 1110 osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) 1111 { 1112 int retval; 1113 1114 params->common.kernel_workload = true; 1115 1116 if (params->runtime || params->period) { 1117 retval = osnoise_set_runtime_period(tool->context, 1118 params->runtime, 1119 params->period); 1120 } else { 1121 retval = osnoise_set_runtime_period(tool->context, 1122 DEFAULT_SAMPLE_PERIOD, 1123 DEFAULT_SAMPLE_RUNTIME); 1124 } 1125 1126 if (retval) { 1127 err_msg("Failed to set runtime and/or period\n"); 1128 goto out_err; 1129 } 1130 1131 retval = osnoise_set_stop_us(tool->context, params->common.stop_us); 1132 if (retval) { 1133 err_msg("Failed to set stop us\n"); 1134 goto out_err; 1135 } 1136 1137 retval = osnoise_set_stop_total_us(tool->context, params->common.stop_total_us); 1138 if (retval) { 1139 err_msg("Failed to set stop total us\n"); 1140 goto out_err; 1141 } 1142 1143 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 1144 if (retval) { 1145 err_msg("Failed to set tracing_thresh\n"); 1146 goto out_err; 1147 } 1148 1149 return common_apply_config(tool, ¶ms->common); 1150 1151 out_err: 1152 return -1; 1153 } 1154 1155 int osnoise_enable(struct osnoise_tool *tool) 1156 { 1157 struct osnoise_params *params = to_osnoise_params(tool->params); 1158 int retval; 1159 1160 /* 1161 * Start the tracer here, after having set all instances. 1162 * 1163 * Let the trace instance start first for the case of hitting a stop 1164 * tracing while enabling other instances. The trace instance is the 1165 * one with most valuable information. 1166 */ 1167 if (tool->record) 1168 trace_instance_start(&tool->record->trace); 1169 trace_instance_start(&tool->trace); 1170 1171 if (params->common.warmup > 0) { 1172 debug_msg("Warming up for %d seconds\n", params->common.warmup); 1173 sleep(params->common.warmup); 1174 if (stop_tracing) 1175 return -1; 1176 1177 /* 1178 * Clean up the buffer. The osnoise workload do not run 1179 * with tracing off to avoid creating a performance penalty 1180 * when not needed. 1181 */ 1182 retval = tracefs_instance_file_write(tool->trace.inst, "trace", ""); 1183 if (retval < 0) { 1184 debug_msg("Error cleaning up the buffer"); 1185 return retval; 1186 } 1187 1188 } 1189 1190 return 0; 1191 } 1192 1193 static void osnoise_usage(int err) 1194 { 1195 int i; 1196 1197 static const char *msg[] = { 1198 "", 1199 "osnoise version " VERSION, 1200 "", 1201 " usage: [rtla] osnoise [MODE] ...", 1202 "", 1203 " modes:", 1204 " top - prints the summary from osnoise tracer", 1205 " hist - prints a histogram of osnoise samples", 1206 "", 1207 "if no MODE is given, the top mode is called, passing the arguments", 1208 NULL, 1209 }; 1210 1211 for (i = 0; msg[i]; i++) 1212 fprintf(stderr, "%s\n", msg[i]); 1213 exit(err); 1214 } 1215 1216 int osnoise_main(int argc, char *argv[]) 1217 { 1218 if (argc == 0) 1219 goto usage; 1220 1221 /* 1222 * if osnoise was called without any argument, run the 1223 * default cmdline. 1224 */ 1225 if (argc == 1) { 1226 run_tool(&osnoise_top_ops, argc, argv); 1227 exit(0); 1228 } 1229 1230 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 1231 osnoise_usage(0); 1232 } else if (strncmp(argv[1], "-", 1) == 0) { 1233 /* the user skipped the tool, call the default one */ 1234 run_tool(&osnoise_top_ops, argc, argv); 1235 exit(0); 1236 } else if (strcmp(argv[1], "top") == 0) { 1237 run_tool(&osnoise_top_ops, argc-1, &argv[1]); 1238 exit(0); 1239 } else if (strcmp(argv[1], "hist") == 0) { 1240 run_tool(&osnoise_hist_ops, argc-1, &argv[1]); 1241 exit(0); 1242 } 1243 1244 usage: 1245 osnoise_usage(1); 1246 exit(1); 1247 } 1248 1249 int hwnoise_main(int argc, char *argv[]) 1250 { 1251 run_tool(&osnoise_top_ops, argc, argv); 1252 exit(0); 1253 } 1254