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