xref: /linux/tools/tracing/rtla/src/osnoise.c (revision 03d745b9843560ab89a796d0d9311bed5c6df6d6)
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, &params->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