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