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