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