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