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