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