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_timerlat_align_us - read and save the original "timerlat_align_us" 428 */ 429 static long long 430 osnoise_get_timerlat_align_us(struct osnoise_context *context) 431 { 432 long long timerlat_align_us; 433 434 if (context->timerlat_align_us != OSNOISE_OPTION_INIT_VAL) 435 return context->timerlat_align_us; 436 437 if (context->orig_timerlat_align_us != OSNOISE_OPTION_INIT_VAL) 438 return context->orig_timerlat_align_us; 439 440 timerlat_align_us = osnoise_read_ll_config("osnoise/timerlat_align_us"); 441 if (timerlat_align_us < 0) 442 goto out_err; 443 444 context->orig_timerlat_align_us = timerlat_align_us; 445 return timerlat_align_us; 446 447 out_err: 448 return OSNOISE_OPTION_INIT_VAL; 449 } 450 451 /* 452 * osnoise_set_timerlat_align_us - set "timerlat_align_us" 453 */ 454 int osnoise_set_timerlat_align_us(struct osnoise_context *context, long long timerlat_align_us) 455 { 456 long long curr_timerlat_align_us = osnoise_get_timerlat_align_us(context); 457 int retval; 458 459 if (curr_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) 460 return -1; 461 462 retval = osnoise_write_ll_config("osnoise/timerlat_align_us", timerlat_align_us); 463 if (retval < 0) 464 return -1; 465 466 context->timerlat_align_us = timerlat_align_us; 467 468 return 0; 469 } 470 471 /* 472 * osnoise_restore_timerlat_align_us - restore "timerlat_align_us" 473 */ 474 void osnoise_restore_timerlat_align_us(struct osnoise_context *context) 475 { 476 int retval; 477 478 if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) 479 return; 480 481 if (context->orig_timerlat_align_us == context->timerlat_align_us) 482 goto out_done; 483 484 retval = osnoise_write_ll_config("osnoise/timerlat_align_us", 485 context->orig_timerlat_align_us); 486 if (retval < 0) 487 err_msg("Could not restore original osnoise timerlat_align_us\n"); 488 489 out_done: 490 context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL; 491 } 492 493 /* 494 * osnoise_put_timerlat_align_us - restore original values and cleanup data 495 */ 496 void osnoise_put_timerlat_align_us(struct osnoise_context *context) 497 { 498 osnoise_restore_timerlat_align_us(context); 499 500 if (context->orig_timerlat_align_us == OSNOISE_OPTION_INIT_VAL) 501 return; 502 503 context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL; 504 } 505 506 /* 507 * osnoise_get_stop_us - read and save the original "stop_tracing_us" 508 */ 509 static long long 510 osnoise_get_stop_us(struct osnoise_context *context) 511 { 512 long long stop_us; 513 514 if (context->stop_us != OSNOISE_OPTION_INIT_VAL) 515 return context->stop_us; 516 517 if (context->orig_stop_us != OSNOISE_OPTION_INIT_VAL) 518 return context->orig_stop_us; 519 520 stop_us = osnoise_read_ll_config("osnoise/stop_tracing_us"); 521 if (stop_us < 0) 522 goto out_err; 523 524 context->orig_stop_us = stop_us; 525 return stop_us; 526 527 out_err: 528 return OSNOISE_OPTION_INIT_VAL; 529 } 530 531 /* 532 * osnoise_set_stop_us - set "stop_tracing_us" 533 */ 534 int osnoise_set_stop_us(struct osnoise_context *context, long long stop_us) 535 { 536 long long curr_stop_us = osnoise_get_stop_us(context); 537 int retval; 538 539 if (curr_stop_us == OSNOISE_OPTION_INIT_VAL) 540 return -1; 541 542 retval = osnoise_write_ll_config("osnoise/stop_tracing_us", stop_us); 543 if (retval < 0) 544 return -1; 545 546 context->stop_us = stop_us; 547 548 return 0; 549 } 550 551 /* 552 * osnoise_restore_stop_us - restore the original "stop_tracing_us" 553 */ 554 void osnoise_restore_stop_us(struct osnoise_context *context) 555 { 556 int retval; 557 558 if (context->orig_stop_us == OSNOISE_OPTION_INIT_VAL) 559 return; 560 561 if (context->orig_stop_us == context->stop_us) 562 goto out_done; 563 564 retval = osnoise_write_ll_config("osnoise/stop_tracing_us", context->orig_stop_us); 565 if (retval < 0) 566 err_msg("Could not restore original osnoise stop_us\n"); 567 568 out_done: 569 context->stop_us = OSNOISE_OPTION_INIT_VAL; 570 } 571 572 /* 573 * osnoise_put_stop_us - restore original values and cleanup data 574 */ 575 void osnoise_put_stop_us(struct osnoise_context *context) 576 { 577 osnoise_restore_stop_us(context); 578 579 if (context->orig_stop_us == OSNOISE_OPTION_INIT_VAL) 580 return; 581 582 context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; 583 } 584 585 /* 586 * osnoise_get_stop_total_us - read and save the original "stop_tracing_total_us" 587 */ 588 static long long 589 osnoise_get_stop_total_us(struct osnoise_context *context) 590 { 591 long long stop_total_us; 592 593 if (context->stop_total_us != OSNOISE_OPTION_INIT_VAL) 594 return context->stop_total_us; 595 596 if (context->orig_stop_total_us != OSNOISE_OPTION_INIT_VAL) 597 return context->orig_stop_total_us; 598 599 stop_total_us = osnoise_read_ll_config("osnoise/stop_tracing_total_us"); 600 if (stop_total_us < 0) 601 goto out_err; 602 603 context->orig_stop_total_us = stop_total_us; 604 return stop_total_us; 605 606 out_err: 607 return OSNOISE_OPTION_INIT_VAL; 608 } 609 610 /* 611 * osnoise_set_stop_total_us - set "stop_tracing_total_us" 612 */ 613 int osnoise_set_stop_total_us(struct osnoise_context *context, long long stop_total_us) 614 { 615 long long curr_stop_total_us = osnoise_get_stop_total_us(context); 616 int retval; 617 618 if (curr_stop_total_us == OSNOISE_OPTION_INIT_VAL) 619 return -1; 620 621 retval = osnoise_write_ll_config("osnoise/stop_tracing_total_us", stop_total_us); 622 if (retval < 0) 623 return -1; 624 625 context->stop_total_us = stop_total_us; 626 627 return 0; 628 } 629 630 /* 631 * osnoise_restore_stop_total_us - restore the original "stop_tracing_total_us" 632 */ 633 void osnoise_restore_stop_total_us(struct osnoise_context *context) 634 { 635 int retval; 636 637 if (context->orig_stop_total_us == OSNOISE_OPTION_INIT_VAL) 638 return; 639 640 if (context->orig_stop_total_us == context->stop_total_us) 641 goto out_done; 642 643 retval = osnoise_write_ll_config("osnoise/stop_tracing_total_us", 644 context->orig_stop_total_us); 645 if (retval < 0) 646 err_msg("Could not restore original osnoise stop_total_us\n"); 647 648 out_done: 649 context->stop_total_us = OSNOISE_OPTION_INIT_VAL; 650 } 651 652 /* 653 * osnoise_put_stop_total_us - restore original values and cleanup data 654 */ 655 void osnoise_put_stop_total_us(struct osnoise_context *context) 656 { 657 osnoise_restore_stop_total_us(context); 658 659 if (context->orig_stop_total_us == OSNOISE_OPTION_INIT_VAL) 660 return; 661 662 context->orig_stop_total_us = OSNOISE_OPTION_INIT_VAL; 663 } 664 665 /* 666 * osnoise_get_print_stack - read and save the original "print_stack" 667 */ 668 static long long 669 osnoise_get_print_stack(struct osnoise_context *context) 670 { 671 long long print_stack; 672 673 if (context->print_stack != OSNOISE_OPTION_INIT_VAL) 674 return context->print_stack; 675 676 if (context->orig_print_stack != OSNOISE_OPTION_INIT_VAL) 677 return context->orig_print_stack; 678 679 print_stack = osnoise_read_ll_config("osnoise/print_stack"); 680 if (print_stack < 0) 681 goto out_err; 682 683 context->orig_print_stack = print_stack; 684 return print_stack; 685 686 out_err: 687 return OSNOISE_OPTION_INIT_VAL; 688 } 689 690 /* 691 * osnoise_set_print_stack - set "print_stack" 692 */ 693 int osnoise_set_print_stack(struct osnoise_context *context, long long print_stack) 694 { 695 long long curr_print_stack = osnoise_get_print_stack(context); 696 int retval; 697 698 if (curr_print_stack == OSNOISE_OPTION_INIT_VAL) 699 return -1; 700 701 retval = osnoise_write_ll_config("osnoise/print_stack", print_stack); 702 if (retval < 0) 703 return -1; 704 705 context->print_stack = print_stack; 706 707 return 0; 708 } 709 710 /* 711 * osnoise_restore_print_stack - restore the original "print_stack" 712 */ 713 void osnoise_restore_print_stack(struct osnoise_context *context) 714 { 715 int retval; 716 717 if (context->orig_print_stack == OSNOISE_OPTION_INIT_VAL) 718 return; 719 720 if (context->orig_print_stack == context->print_stack) 721 goto out_done; 722 723 retval = osnoise_write_ll_config("osnoise/print_stack", context->orig_print_stack); 724 if (retval < 0) 725 err_msg("Could not restore original osnoise print_stack\n"); 726 727 out_done: 728 context->print_stack = OSNOISE_OPTION_INIT_VAL; 729 } 730 731 /* 732 * osnoise_put_print_stack - restore original values and cleanup data 733 */ 734 void osnoise_put_print_stack(struct osnoise_context *context) 735 { 736 osnoise_restore_print_stack(context); 737 738 if (context->orig_print_stack == OSNOISE_OPTION_INIT_VAL) 739 return; 740 741 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL; 742 } 743 744 /* 745 * osnoise_get_tracing_thresh - read and save the original "tracing_thresh" 746 */ 747 static long long 748 osnoise_get_tracing_thresh(struct osnoise_context *context) 749 { 750 long long tracing_thresh; 751 752 if (context->tracing_thresh != OSNOISE_OPTION_INIT_VAL) 753 return context->tracing_thresh; 754 755 if (context->orig_tracing_thresh != OSNOISE_OPTION_INIT_VAL) 756 return context->orig_tracing_thresh; 757 758 tracing_thresh = osnoise_read_ll_config("tracing_thresh"); 759 if (tracing_thresh < 0) 760 goto out_err; 761 762 context->orig_tracing_thresh = tracing_thresh; 763 return tracing_thresh; 764 765 out_err: 766 return OSNOISE_OPTION_INIT_VAL; 767 } 768 769 /* 770 * osnoise_set_tracing_thresh - set "tracing_thresh" 771 */ 772 int osnoise_set_tracing_thresh(struct osnoise_context *context, long long tracing_thresh) 773 { 774 long long curr_tracing_thresh = osnoise_get_tracing_thresh(context); 775 int retval; 776 777 if (curr_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 778 return -1; 779 780 retval = osnoise_write_ll_config("tracing_thresh", tracing_thresh); 781 if (retval < 0) 782 return -1; 783 784 context->tracing_thresh = tracing_thresh; 785 786 return 0; 787 } 788 789 /* 790 * osnoise_restore_tracing_thresh - restore the original "tracing_thresh" 791 */ 792 void osnoise_restore_tracing_thresh(struct osnoise_context *context) 793 { 794 int retval; 795 796 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 797 return; 798 799 if (context->orig_tracing_thresh == context->tracing_thresh) 800 goto out_done; 801 802 retval = osnoise_write_ll_config("tracing_thresh", context->orig_tracing_thresh); 803 if (retval < 0) 804 err_msg("Could not restore original tracing_thresh\n"); 805 806 out_done: 807 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 808 } 809 810 /* 811 * osnoise_put_tracing_thresh - restore original values and cleanup data 812 */ 813 void osnoise_put_tracing_thresh(struct osnoise_context *context) 814 { 815 osnoise_restore_tracing_thresh(context); 816 817 if (context->orig_tracing_thresh == OSNOISE_OPTION_INIT_VAL) 818 return; 819 820 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 821 } 822 823 static int osnoise_options_get_option(char *option) 824 { 825 char *options = tracefs_instance_file_read(NULL, "osnoise/options", NULL); 826 char no_option[128]; 827 int retval = 0; 828 char *opt; 829 830 if (!options) 831 return OSNOISE_OPTION_INIT_VAL; 832 833 /* 834 * Check first if the option is disabled. 835 */ 836 snprintf(no_option, sizeof(no_option), "NO_%s", option); 837 838 opt = strstr(options, no_option); 839 if (opt) 840 goto out_free; 841 842 /* 843 * Now that it is not disabled, if the string is there, it is 844 * enabled. If the string is not there, the option does not exist. 845 */ 846 opt = strstr(options, option); 847 if (opt) 848 retval = 1; 849 else 850 retval = OSNOISE_OPTION_INIT_VAL; 851 852 out_free: 853 free(options); 854 return retval; 855 } 856 857 static int osnoise_options_set_option(char *option, bool onoff) 858 { 859 char no_option[128]; 860 861 if (onoff) 862 return tracefs_instance_file_write(NULL, "osnoise/options", option); 863 864 snprintf(no_option, sizeof(no_option), "NO_%s", option); 865 866 return tracefs_instance_file_write(NULL, "osnoise/options", no_option); 867 } 868 869 static int osnoise_get_irq_disable(struct osnoise_context *context) 870 { 871 if (context->opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 872 return context->opt_irq_disable; 873 874 if (context->orig_opt_irq_disable != OSNOISE_OPTION_INIT_VAL) 875 return context->orig_opt_irq_disable; 876 877 context->orig_opt_irq_disable = osnoise_options_get_option("OSNOISE_IRQ_DISABLE"); 878 879 return context->orig_opt_irq_disable; 880 } 881 882 int osnoise_set_irq_disable(struct osnoise_context *context, bool onoff) 883 { 884 int opt_irq_disable = osnoise_get_irq_disable(context); 885 int retval; 886 887 if (opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 888 return -1; 889 890 if (opt_irq_disable == onoff) 891 return 0; 892 893 retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", onoff); 894 if (retval < 0) 895 return -1; 896 897 context->opt_irq_disable = onoff; 898 899 return 0; 900 } 901 902 static void osnoise_restore_irq_disable(struct osnoise_context *context) 903 { 904 int retval; 905 906 if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 907 return; 908 909 if (context->orig_opt_irq_disable == context->opt_irq_disable) 910 goto out_done; 911 912 retval = osnoise_options_set_option("OSNOISE_IRQ_DISABLE", context->orig_opt_irq_disable); 913 if (retval < 0) 914 err_msg("Could not restore original OSNOISE_IRQ_DISABLE option\n"); 915 916 out_done: 917 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 918 } 919 920 static void osnoise_put_irq_disable(struct osnoise_context *context) 921 { 922 osnoise_restore_irq_disable(context); 923 924 if (context->orig_opt_irq_disable == OSNOISE_OPTION_INIT_VAL) 925 return; 926 927 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 928 } 929 930 static int osnoise_get_workload(struct osnoise_context *context) 931 { 932 if (context->opt_workload != OSNOISE_OPTION_INIT_VAL) 933 return context->opt_workload; 934 935 if (context->orig_opt_workload != OSNOISE_OPTION_INIT_VAL) 936 return context->orig_opt_workload; 937 938 context->orig_opt_workload = osnoise_options_get_option("OSNOISE_WORKLOAD"); 939 940 return context->orig_opt_workload; 941 } 942 943 int osnoise_set_workload(struct osnoise_context *context, bool onoff) 944 { 945 int opt_workload = osnoise_get_workload(context); 946 int retval; 947 948 if (opt_workload == OSNOISE_OPTION_INIT_VAL) 949 return -1; 950 951 if (opt_workload == onoff) 952 return 0; 953 954 retval = osnoise_options_set_option("OSNOISE_WORKLOAD", onoff); 955 if (retval < 0) 956 return -2; 957 958 context->opt_workload = onoff; 959 960 return 0; 961 } 962 963 static void osnoise_restore_workload(struct osnoise_context *context) 964 { 965 int retval; 966 967 if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL) 968 return; 969 970 if (context->orig_opt_workload == context->opt_workload) 971 goto out_done; 972 973 retval = osnoise_options_set_option("OSNOISE_WORKLOAD", context->orig_opt_workload); 974 if (retval < 0) 975 err_msg("Could not restore original OSNOISE_WORKLOAD option\n"); 976 977 out_done: 978 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 979 } 980 981 static void osnoise_put_workload(struct osnoise_context *context) 982 { 983 osnoise_restore_workload(context); 984 985 if (context->orig_opt_workload == OSNOISE_OPTION_INIT_VAL) 986 return; 987 988 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 989 } 990 991 static int osnoise_get_timerlat_align(struct osnoise_context *context) 992 { 993 if (context->opt_timerlat_align != OSNOISE_OPTION_INIT_VAL) 994 return context->opt_timerlat_align; 995 996 if (context->orig_opt_timerlat_align != OSNOISE_OPTION_INIT_VAL) 997 return context->orig_opt_timerlat_align; 998 999 context->orig_opt_timerlat_align = osnoise_options_get_option("TIMERLAT_ALIGN"); 1000 1001 return context->orig_opt_timerlat_align; 1002 } 1003 1004 int osnoise_set_timerlat_align(struct osnoise_context *context, bool onoff) 1005 { 1006 int opt_timerlat_align = osnoise_get_timerlat_align(context); 1007 int retval; 1008 1009 if (opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) 1010 return -1; 1011 1012 if (opt_timerlat_align == onoff) 1013 return 0; 1014 1015 retval = osnoise_options_set_option("TIMERLAT_ALIGN", onoff); 1016 if (retval < 0) 1017 return -2; 1018 1019 context->opt_timerlat_align = onoff; 1020 1021 return 0; 1022 } 1023 1024 static void osnoise_restore_timerlat_align(struct osnoise_context *context) 1025 { 1026 int retval; 1027 1028 if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) 1029 return; 1030 1031 if (context->orig_opt_timerlat_align == context->opt_timerlat_align) 1032 goto out_done; 1033 1034 retval = osnoise_options_set_option("TIMERLAT_ALIGN", context->orig_opt_timerlat_align); 1035 if (retval < 0) 1036 err_msg("Could not restore original TIMERLAT_ALIGN option\n"); 1037 1038 out_done: 1039 context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; 1040 } 1041 1042 static void osnoise_put_timerlat_align(struct osnoise_context *context) 1043 { 1044 osnoise_restore_timerlat_align(context); 1045 1046 if (context->orig_opt_timerlat_align == OSNOISE_OPTION_INIT_VAL) 1047 return; 1048 1049 context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; 1050 } 1051 1052 enum { 1053 FLAG_CONTEXT_NEWLY_CREATED = (1 << 0), 1054 FLAG_CONTEXT_DELETED = (1 << 1), 1055 }; 1056 1057 /* 1058 * osnoise_get_context - increase the usage of a context and return it 1059 */ 1060 int osnoise_get_context(struct osnoise_context *context) 1061 { 1062 int ret; 1063 1064 if (context->flags & FLAG_CONTEXT_DELETED) { 1065 ret = -1; 1066 } else { 1067 context->ref++; 1068 ret = 0; 1069 } 1070 1071 return ret; 1072 } 1073 1074 /* 1075 * osnoise_context_alloc - alloc an osnoise_context 1076 * 1077 * The osnoise context contains the information of the "osnoise/" configs. 1078 * It is used to set and restore the config. 1079 */ 1080 struct osnoise_context *osnoise_context_alloc(void) 1081 { 1082 struct osnoise_context *context; 1083 1084 context = calloc_fatal(1, sizeof(*context)); 1085 1086 context->orig_stop_us = OSNOISE_OPTION_INIT_VAL; 1087 context->stop_us = OSNOISE_OPTION_INIT_VAL; 1088 1089 context->orig_stop_total_us = OSNOISE_OPTION_INIT_VAL; 1090 context->stop_total_us = OSNOISE_OPTION_INIT_VAL; 1091 1092 context->orig_print_stack = OSNOISE_OPTION_INIT_VAL; 1093 context->print_stack = OSNOISE_OPTION_INIT_VAL; 1094 1095 context->orig_tracing_thresh = OSNOISE_OPTION_INIT_VAL; 1096 context->tracing_thresh = OSNOISE_OPTION_INIT_VAL; 1097 1098 context->orig_opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 1099 context->opt_irq_disable = OSNOISE_OPTION_INIT_VAL; 1100 1101 context->orig_opt_workload = OSNOISE_OPTION_INIT_VAL; 1102 context->opt_workload = OSNOISE_OPTION_INIT_VAL; 1103 1104 context->orig_opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; 1105 context->opt_timerlat_align = OSNOISE_OPTION_INIT_VAL; 1106 1107 context->orig_timerlat_align_us = OSNOISE_OPTION_INIT_VAL; 1108 context->timerlat_align_us = OSNOISE_OPTION_INIT_VAL; 1109 1110 osnoise_get_context(context); 1111 1112 return context; 1113 } 1114 1115 /* 1116 * osnoise_put_context - put the osnoise_put_context 1117 * 1118 * If there is no other user for the context, the original data 1119 * is restored. 1120 */ 1121 void osnoise_put_context(struct osnoise_context *context) 1122 { 1123 if (--context->ref < 1) 1124 context->flags |= FLAG_CONTEXT_DELETED; 1125 1126 if (!(context->flags & FLAG_CONTEXT_DELETED)) 1127 return; 1128 1129 osnoise_put_cpus(context); 1130 osnoise_put_runtime_period(context); 1131 osnoise_put_stop_us(context); 1132 osnoise_put_stop_total_us(context); 1133 osnoise_put_timerlat_period_us(context); 1134 osnoise_put_print_stack(context); 1135 osnoise_put_tracing_thresh(context); 1136 osnoise_put_irq_disable(context); 1137 osnoise_put_workload(context); 1138 osnoise_put_timerlat_align(context); 1139 osnoise_put_timerlat_align_us(context); 1140 1141 free(context); 1142 } 1143 1144 /* 1145 * osnoise_destroy_tool - disable trace, restore configs and free data 1146 */ 1147 void osnoise_destroy_tool(struct osnoise_tool *top) 1148 { 1149 if (!top) 1150 return; 1151 1152 trace_instance_destroy(&top->trace); 1153 1154 if (top->context) 1155 osnoise_put_context(top->context); 1156 1157 free(top); 1158 } 1159 1160 /* 1161 * osnoise_init_tool - init an osnoise tool 1162 * 1163 * It allocs data, create a context to store data and 1164 * creates a new trace instance for the tool. 1165 */ 1166 struct osnoise_tool *osnoise_init_tool(char *tool_name) 1167 { 1168 struct osnoise_tool *top; 1169 1170 top = calloc_fatal(1, sizeof(*top)); 1171 top->context = osnoise_context_alloc(); 1172 1173 if (trace_instance_init(&top->trace, tool_name)) { 1174 osnoise_destroy_tool(top); 1175 return NULL; 1176 } 1177 1178 return top; 1179 } 1180 1181 /* 1182 * osnoise_init_trace_tool - init a tracer instance to trace osnoise events 1183 */ 1184 struct osnoise_tool *osnoise_init_trace_tool(const char *tracer) 1185 { 1186 struct osnoise_tool *trace; 1187 int retval; 1188 1189 trace = osnoise_init_tool("osnoise_trace"); 1190 if (!trace) 1191 return NULL; 1192 1193 retval = tracefs_event_enable(trace->trace.inst, "osnoise", NULL); 1194 if (retval < 0 && !errno) { 1195 err_msg("Could not find osnoise events\n"); 1196 goto out_err; 1197 } 1198 1199 retval = enable_tracer_by_name(trace->trace.inst, tracer); 1200 if (retval) { 1201 err_msg("Could not enable %s tracer for tracing\n", tracer); 1202 goto out_err; 1203 } 1204 1205 return trace; 1206 out_err: 1207 osnoise_destroy_tool(trace); 1208 return NULL; 1209 } 1210 1211 bool osnoise_trace_is_off(struct osnoise_tool *tool, struct osnoise_tool *record) 1212 { 1213 /* 1214 * The tool instance is always present, it is the one used to collect 1215 * data. 1216 */ 1217 if (!tracefs_trace_is_on(tool->trace.inst)) 1218 return true; 1219 1220 /* 1221 * The trace record instance is only enabled when -t is set. IOW, when the system 1222 * is tracing. 1223 */ 1224 return record && !tracefs_trace_is_on(record->trace.inst); 1225 } 1226 1227 /* 1228 * osnoise_report_missed_events - report number of events dropped by trace 1229 * buffer 1230 */ 1231 void 1232 osnoise_report_missed_events(struct osnoise_tool *tool) 1233 { 1234 unsigned long long total_events; 1235 1236 if (tool->trace.missed_events == UINT64_MAX) 1237 printf("unknown number of events missed, results might not be accurate\n"); 1238 else if (tool->trace.missed_events > 0) { 1239 total_events = tool->trace.processed_events + tool->trace.missed_events; 1240 1241 printf("%lld (%.2f%%) events missed, results might not be accurate\n", 1242 tool->trace.missed_events, 1243 (double) tool->trace.missed_events / total_events * 100.0); 1244 } 1245 } 1246 1247 /* 1248 * osnoise_apply_config - apply osnoise configs to the initialized tool 1249 */ 1250 int 1251 osnoise_apply_config(struct osnoise_tool *tool, struct osnoise_params *params) 1252 { 1253 int retval; 1254 1255 params->common.kernel_workload = true; 1256 1257 if (params->runtime || params->period) { 1258 retval = osnoise_set_runtime_period(tool->context, 1259 params->runtime, 1260 params->period); 1261 } else { 1262 retval = osnoise_set_runtime_period(tool->context, 1263 DEFAULT_SAMPLE_PERIOD, 1264 DEFAULT_SAMPLE_RUNTIME); 1265 } 1266 1267 if (retval) { 1268 err_msg("Failed to set runtime and/or period\n"); 1269 goto out_err; 1270 } 1271 1272 retval = osnoise_set_tracing_thresh(tool->context, params->threshold); 1273 if (retval) { 1274 err_msg("Failed to set tracing_thresh\n"); 1275 goto out_err; 1276 } 1277 1278 return common_apply_config(tool, ¶ms->common); 1279 1280 out_err: 1281 return -1; 1282 } 1283 1284 int osnoise_enable(struct osnoise_tool *tool) 1285 { 1286 struct osnoise_params *params = to_osnoise_params(tool->params); 1287 int retval; 1288 1289 /* 1290 * Start the tracer here, after having set all instances. 1291 * 1292 * Let the trace instance start first for the case of hitting a stop 1293 * tracing while enabling other instances. The trace instance is the 1294 * one with most valuable information. 1295 */ 1296 if (tool->record) 1297 trace_instance_start(&tool->record->trace); 1298 trace_instance_start(&tool->trace); 1299 1300 if (params->common.warmup > 0) { 1301 debug_msg("Warming up for %d seconds\n", params->common.warmup); 1302 sleep(params->common.warmup); 1303 if (stop_tracing) 1304 return -1; 1305 1306 /* 1307 * Clean up the buffer. The osnoise workload do not run 1308 * with tracing off to avoid creating a performance penalty 1309 * when not needed. 1310 */ 1311 retval = tracefs_instance_file_write(tool->trace.inst, "trace", ""); 1312 if (retval < 0) { 1313 debug_msg("Error cleaning up the buffer"); 1314 return retval; 1315 } 1316 } 1317 1318 retval = osn_set_stop(tool); 1319 if (retval) 1320 return retval; 1321 1322 return 0; 1323 } 1324 1325 __noreturn static void osnoise_usage(int err) 1326 { 1327 int i; 1328 1329 static const char *msg[] = { 1330 "", 1331 "osnoise version " VERSION, 1332 "", 1333 " usage: [rtla] osnoise [MODE] ...", 1334 "", 1335 " modes:", 1336 " top - prints the summary from osnoise tracer", 1337 " hist - prints a histogram of osnoise samples", 1338 "", 1339 "if no MODE is given, the top mode is called, passing the arguments", 1340 NULL, 1341 }; 1342 1343 for (i = 0; msg[i]; i++) 1344 fprintf(stderr, "%s\n", msg[i]); 1345 exit(err); 1346 } 1347 1348 int osnoise_main(int argc, char *argv[]) 1349 { 1350 if (argc == 0) 1351 goto usage; 1352 1353 /* 1354 * if osnoise was called without any argument, run the 1355 * default cmdline. 1356 */ 1357 if (argc == 1) { 1358 run_tool(&osnoise_top_ops, argc, argv); 1359 exit(0); 1360 } 1361 1362 if ((strcmp(argv[1], "-h") == 0) || (strcmp(argv[1], "--help") == 0)) { 1363 osnoise_usage(129); 1364 } else if (str_has_prefix(argv[1], "-")) { 1365 /* the user skipped the tool, call the default one */ 1366 run_tool(&osnoise_top_ops, argc, argv); 1367 exit(0); 1368 } else if (strcmp(argv[1], "top") == 0) { 1369 run_tool(&osnoise_top_ops, argc-1, &argv[1]); 1370 exit(0); 1371 } else if (strcmp(argv[1], "hist") == 0) { 1372 run_tool(&osnoise_hist_ops, argc-1, &argv[1]); 1373 exit(0); 1374 } 1375 1376 usage: 1377 osnoise_usage(129); 1378 } 1379 1380 int hwnoise_main(int argc, char *argv[]) 1381 { 1382 run_tool(&osnoise_top_ops, argc, argv); 1383 exit(0); 1384 } 1385