tracing/osnoise: Support multiple instances (for RTLA)
Subsystems (2)
v8 cover tracing/osnoise: Support multiple instances (for RTLA)
Currently, osnoise and timerlat run only on a single instance. To reduce this limitation, this series adds support for parallel instances of the same tracer. That is, it is possible to run two instances of osnoise tracer with different configurations. For example, one for the tracer output and another for a set of tracepoints.
This patchset is the kernel dependencies for RTLA. This patchset was being sent along with RTLA [1], but we split the kernel and user-space patch sets.
[1] RTLA: An interface for osnoise/timerlat tracers:
https://lore.kernel.org/lkml/[email protected]/
Changes from v7:
- Fix check for print_stack disabled
- Fix thread/sofitrq filter when TIMERLAT ifdef is enabled Both fixes for the patches added in the v6.
Changes from v6:
- Fix compilation problems for the case in which osnoise enabled, but timerlat disabled (Steven)
- Remove ifdefs from inside functions (Steven)
Changes that happened while in the RTLA patchset:
- Fix comment on start_per_cpu_kthreads() (Steven)
- Fix msg log on patch 1
- Add comments about the barrier need for trace_nmi_enter/exit
- Fix RCU usage in osnoise_unregister_instance() (Steven/Paul)
- Add lockdep checks in osnoise_unregister/unregister_instance() (Steven/Paul)
- Improve the explanation about the multi instances support (Steven)
Daniel Bristot de Oliveira (9):
tracing/osnoise: Do not follow tracing_cpumask tracing/osnoise: Improve comments about barrier need for NMI callbacks tracing/osnoise: Split workload start from the tracer start tracing/osnoise: Use start/stop_per_cpu_kthreads() on
osnoise_cpus_write()
tracing/osnoise: Support a list of trace_array *tr tracing/osnoise: Remove TIMERLAT ifdefs from inside functions tracing/osnoise: Allow multiple instances of the same tracer tracing/osnoise: Remove STACKTRACE ifdefs from inside functions tracing/osnoise: Remove PREEMPT_RT ifdefs from inside functions
kernel/trace/trace_osnoise.c | 615 ++++++++++++++++++++++++----------- 1 file changed, 434 insertions(+), 181 deletions(-)
Signature · 1 line
2.31.1
PATCH v8 1/9 tracing/osnoise: Do not follow tracing_cpumask
In preparation to support multiple instances, decouple the osnoise/timelat workload from instance-specific tracing_cpumask.
Different instances can have conflicting cpumasks, making osnoise workload management needlessly complex. Osnoise already has its global cpumask.
I also thought about using the first instance mask, but the "first" instance could be removed before the others.
This also fixes the problem that changing the tracing_mask was not re-starting the trace.
--- kernel/trace/trace_osnoise.c | 24 ++++++------------------ 1 file changed, 6 insertions(+), 18 deletions(-)
kernel/trace/trace_osnoise.c +6 -18
@@ -1553,13 +1553,9 @@ static int start_per_cpu_kthreads(struct trace_array *tr)cpus_read_lock ();/* -* Run only on CPUs in which trace and osnoise are allowed to run. +* Run only on online CPUs in which osnoise is allowed to run. */ -cpumask_and (current_mask, tr->tracing_cpumask, &osnoise_cpumask); -/* -* And the CPU is online. -*/ -cpumask_and (current_mask, cpu_online_mask, current_mask); +cpumask_and (current_mask, cpu_online_mask, &osnoise_cpumask);for_each_possible_cpu (cpu)per_cpu (per_cpu_osnoise_var, cpu).kthread =NULL ;
@@ -1580,10 +1576,8 @@ static int start_per_cpu_kthreads(struct trace_array *tr)#ifdef CONFIG_HOTPLUG_CPU static void osnoise_hotplug_workfn (struct work_struct *dummy) { -struct trace_array *tr = osnoise_trace;unsigned int cpu =smp_processor_id (); -mutex_lock (&trace_types_lock);if (!osnoise_busy)
@@ -1595,9 +1589,6 @@ static void osnoise_hotplug_workfn(struct work_struct *dummy)if (!cpumask_test_cpu (cpu, &osnoise_cpumask)) goto out_unlock; -if (!cpumask_test_cpu (cpu, tr->tracing_cpumask)) - goto out_unlock; -start_kthread (cpu); out_unlock:
@@ -1700,13 +1691,10 @@ static void osnoise_tracer_stop(struct trace_array *tr);* interface to the osnoise trace. By default, it lists all CPUs, * in this way, allowing osnoise threads to run on any online CPU * of the system. It serves to restrict the execution of osnoise to the -* set of CPUs writing via this interface. Note that osnoise also -* respects the "tracing_cpumask." Hence, osnoise threads will run only -* on the set of CPUs allowed here AND on "tracing_cpumask." Why not -* have just "tracing_cpumask?" Because the user might be interested -* in tracing what is running on other CPUs. For instance, one might -* run osnoise in one HT CPU while observing what is running on the -* sibling HT CPU. +* set of CPUs writing via this interface. Why not use "tracing_cpumask"? +* Because the user might be interested in tracing what is running on +* other CPUs. For instance, one might run osnoise in one HT CPU +* while observing what is running on the sibling HT CPU. */ static ssize_t osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count,
Signature · 1 line
2.31.1
PATCH v8 2/9 tracing/osnoise: Improve comments about barrier need for NMI callbacks
In reply to Daniel Bristot de Oliveiratrace_osnoise_callback_enabled is used by ftrace_nmi_enter/exit() to know when to call the NMI callback. The barrier is used to avoid having callbacks enabled before the resetting date during the start or to touch the values after stopping the tracer.
--- kernel/trace/trace_osnoise.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-)
kernel/trace/trace_osnoise.c +7 -1
@@ -1929,8 +1929,10 @@ static int __osnoise_tracer_start(struct trace_array *tr) retval =osnoise_hook_events ();if (retval)return retval; +/* -* Make sure NMIs see reseted values. +* Make sure that ftrace_nmi_enter/exit() see reset values +* before enabling trace_osnoise_callback_enabled. */ barrier (); trace_osnoise_callback_enabled = true;
@@ -1965,6 +1967,10 @@ static void osnoise_tracer_stop(struct trace_array *tr)return ; trace_osnoise_callback_enabled = false; +/* +* Make sure that ftrace_nmi_enter/exit() see +* trace_osnoise_callback_enabled as false before continuing. +*/ barrier ();stop_per_cpu_kthreads ();
Signature · 1 line
2.31.1
PATCH v8 3/9 tracing/osnoise: Split workload start from the tracer start
In reply to Daniel Bristot de OliveiraIn preparation from supporting multiple trace instances, create workload start/stop specific functions.
No functional change.
--- kernel/trace/trace_osnoise.c | 59 ++++++++++++++++++++++-------------- 1 file changed, 36 insertions(+), 23 deletions(-)
kernel/trace/trace_osnoise.c +36 -23
@@ -1545,7 +1545,7 @@ static int start_kthread(unsigned int cpu)* This starts the kernel thread that will look for osnoise on many * cpus. */ -static int start_per_cpu_kthreads (struct trace_array *tr) +static int start_per_cpu_kthreads (void ) {struct cpumask *current_mask = &save_cpumask;int retval =0 ;
@@ -1677,8 +1677,8 @@ osnoise_cpus_read(struct file *filp, char __user *ubuf, size_t count,return count; } -static void osnoise_tracer_start (struct trace_array *tr); -static void osnoise_tracer_stop (struct trace_array *tr); +static int osnoise_workload_start (void ); +static void osnoise_workload_stop (void );/* * osnoise_cpus_write - Write function for "cpus" entry
@@ -1700,7 +1700,6 @@ static ssize_t osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count, loff_t *ppos) { - struct trace_array *tr = osnoise_trace; cpumask_var_t osnoise_cpumask_new;int running, err;char buf[256 ];
@@ -1725,7 +1724,7 @@ osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count,mutex_lock (&trace_types_lock); running = osnoise_busy;if (running) -osnoise_tracer_stop (tr); +osnoise_workload_stop ();mutex_lock (&interface_lock);/*
@@ -1739,7 +1738,7 @@ osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count,mutex_unlock (&interface_lock);if (running) -osnoise_tracer_start (tr); +osnoise_workload_start ();mutex_unlock (&trace_types_lock);free_cpumask_var (osnoise_cpumask_new);
@@ -1920,7 +1919,10 @@ static int osnoise_hook_events(void)return -EINVAL ; } -static int __osnoise_tracer_start (struct trace_array *tr) +/* +* osnoise_workload_start - start the workload and hook to events +*/ +static int osnoise_workload_start (void ) {int retval;
@@ -1937,7 +1939,7 @@ static int __osnoise_tracer_start(struct trace_array *tr)barrier (); trace_osnoise_callback_enabled = true; - retval =start_per_cpu_kthreads (tr); + retval =start_per_cpu_kthreads ();if (retval) {unhook_irq_events ();return retval;
@@ -1948,20 +1950,10 @@ static int __osnoise_tracer_start(struct trace_array *tr)return 0 ; } -static void osnoise_tracer_start (struct trace_array *tr) -{ -int retval; - -if (osnoise_busy) -return ; - - retval =__osnoise_tracer_start (tr); -if (retval) -pr_err (BANNER "Error starting osnoise tracer\n" ); - -} - -static void osnoise_tracer_stop (struct trace_array *tr) +/* +* osnoise_workload_stop - stop the workload and unhook the events +*/ +static void osnoise_workload_stop (void ) {if (!osnoise_busy)return ;
@@ -1982,6 +1974,27 @@ static void osnoise_tracer_stop(struct trace_array *tr) osnoise_busy = false; } +static void osnoise_tracer_start (struct trace_array *tr) +{ +int retval; + +if (osnoise_busy) +return ; + + retval =osnoise_workload_start (); +if (retval) +pr_err (BANNER "Error starting osnoise tracer\n" ); + +} + +static void osnoise_tracer_stop (struct trace_array *tr) +{ +if (!osnoise_busy) +return ; + +osnoise_workload_stop (); +} +static int osnoise_tracer_init (struct trace_array *tr) {
@@ -2022,7 +2035,7 @@ static void timerlat_tracer_start(struct trace_array *tr) osnoise_data.timerlat_tracer =1 ; - retval =__osnoise_tracer_start (tr); + retval =osnoise_workload_start ();if (retval) goto out_err;
Signature · 1 line
2.31.1
PATCH v8 4/9 tracing/osnoise: Use start/stop_per_cpu_kthreads() on osnoise_cpus_write()
In reply to Daniel Bristot de OliveiraWhen writing a new CPU mask via osnoise/cpus, if the tracer is running, the workload is restarted to follow the new cpumask. The restart is currently done using osnoise_workload_start/stop(), which disables the workload *and* the instrumentation. However, disabling the instrumentation is not necessary.
Calling start/stop_per_cpu_kthreads() is enough to apply the new osnoise/cpus config.
--- kernel/trace/trace_osnoise.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-)
kernel/trace/trace_osnoise.c +2 -5
@@ -1677,9 +1677,6 @@ osnoise_cpus_read(struct file *filp, char __user *ubuf, size_t count,return count; } -static int osnoise_workload_start (void ); -static void osnoise_workload_stop (void ); -/* * osnoise_cpus_write - Write function for "cpus" entry * @filp: The active open file structure
@@ -1724,7 +1721,7 @@ osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count,mutex_lock (&trace_types_lock); running = osnoise_busy;if (running) -osnoise_workload_stop (); +stop_per_cpu_kthreads ();mutex_lock (&interface_lock);/*
@@ -1738,7 +1735,7 @@ osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count,mutex_unlock (&interface_lock);if (running) -osnoise_workload_start (); +start_per_cpu_kthreads ();mutex_unlock (&trace_types_lock);free_cpumask_var (osnoise_cpumask_new);
Signature · 1 line
2.31.1
PATCH v8 5/9 tracing/osnoise: Support a list of trace_array *tr
In reply to Daniel Bristot de Oliveiraosnoise/timerlat were built to run a single instance, and for this, a single variable is enough to store the current struct trace_array *tr with information about the tracing instance. This is done via the *osnoise_trace variable. A trace_array represents a trace instance.
In preparation to support multiple instances, replace the *osnoise_trace variable with an RCU protected list of instances.
The operations that refer to an instance now propagate to all elements of the list (all instances).
Also, replace the osnoise_busy variable with a check if the list has elements (busy).
No functional change is expected with this patch, i.e., only one instance is allowed yet.
--- kernel/trace/trace_osnoise.c | 261 ++++++++++++++++++++++++++--------- 1 file changed, 192 insertions(+), 69 deletions(-)
kernel/trace/trace_osnoise.c +192 -69
@@ -38,8 +38,6 @@#define CREATE_TRACE_POINTS #include <trace/events/osnoise.h> -static struct trace_array *osnoise_trace; -/* * Default values. */
@@ -50,6 +48,81 @@ static struct trace_array *osnoise_trace;#define DEFAULT_TIMERLAT_PERIOD 1000/* 1ms */ #define DEFAULT_TIMERLAT_PRIO 95/* FIFO 95 */ +/* +* trace_array of the enabled osnoise/timerlat instances. +*/ +struct osnoise_instance { +struct list_head list; +struct trace_array *tr; +}; +struct list_head osnoise_instances; + +static bool osnoise_has_registered_instances (void ) +{ +return !!list_first_or_null_rcu (&osnoise_instances, + struct osnoise_instance, + list); +} + +/* +* osnoise_register_instance - register a new trace instance +* +* Register a trace_array *tr in the list of instances running +* osnoise/timerlat tracers. +*/ +static int osnoise_register_instance (struct trace_array *tr) +{ +struct osnoise_instance *inst; + +/* +* register/unregister serialization is provided by trace's +* trace_types_lock. +*/ +lockdep_assert_held (&trace_types_lock); + + inst =kmalloc (sizeof (*inst),GFP_KERNEL ); +if (!inst) +return -ENOMEM ; + +INIT_LIST_HEAD_RCU (&inst->list); + inst->tr = tr; +list_add_tail_rcu (&inst->list, &osnoise_instances); + +return 0 ; +} + +/* +* osnoise_unregister_instance - unregister a registered trace instance +* +* Remove the trace_array *tr from the list of instances running +* osnoise/timerlat tracers. +*/ +static void osnoise_unregister_instance (struct trace_array *tr) +{ +struct osnoise_instance *inst; +int found =0 ; + +/* +* register/unregister serialization is provided by trace's +* trace_types_lock. +*/ +lockdep_assert_held (&trace_types_lock); + +list_for_each_entry_rcu (inst, &osnoise_instances, list) { +if (inst->tr == tr) { +list_del_rcu (&inst->list); + found =1 ; +break ; + } + } + +if (!found) +return ; + +synchronize_rcu (); +kfree (inst); +} +/* * NMI runtime info. */
@@ -248,11 +321,6 @@ static struct osnoise_data {#endif }; -/* -* Boolean variable used to inform that the tracer is currently sampling. -*/ -static bool osnoise_busy; -#ifdef CONFIG_PREEMPT_RT /* * Print the osnoise header info.
@@ -315,19 +383,24 @@ static void print_osnoise_headers(struct seq_file *s)* osnoise_taint - report an osnoise error. */ #define osnoise_taint (msg) ({ \ - struct trace_array *tr = osnoise_trace; \ + struct osnoise_instance *inst; \ + struct trace_buffer *buffer; \ \ - trace_array_printk_buf(tr->array_buffer.buffer, _THIS_IP_, msg); \ + rcu_read_lock(); \ + list_for_each_entry_rcu(inst, &osnoise_instances, list) { \ + buffer = inst->tr->array_buffer.buffer; \ + trace_array_printk_buf(buffer, _THIS_IP_, msg); \ + } \ + rcu_read_unlock(); \ osnoise_data.tainted = true; \ })/* * Record an osnoise_sample into the tracer buffer. */ -static void trace_osnoise_sample (struct osnoise_sample *sample) +static void +__trace_osnoise_sample (struct osnoise_sample *sample,struct trace_buffer *buffer) { -struct trace_array *tr = osnoise_trace; -struct trace_buffer *buffer = tr->array_buffer.buffer;struct trace_event_call *call = &event_osnoise;struct ring_buffer_event *event;struct osnoise_entry *entry;
@@ -350,6 +423,22 @@ static void trace_osnoise_sample(struct osnoise_sample *sample)trace_buffer_unlock_commit_nostack (buffer, event); } +/* +* Record an osnoise_sample on all osnoise instances. +*/ +static void trace_osnoise_sample (struct osnoise_sample *sample) +{ +struct osnoise_instance *inst; +struct trace_buffer *buffer; + +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { + buffer = inst->tr->array_buffer.buffer; +__trace_osnoise_sample (sample, buffer); + } +rcu_read_unlock (); +} +#ifdef CONFIG_TIMERLAT_TRACER /* * Print the timerlat header info.
@@ -387,14 +476,10 @@ static void print_timerlat_headers(struct seq_file *s) }#endif /* CONFIG_PREEMPT_RT */ -/* -* Record an timerlat_sample into the tracer buffer. -*/ -static void trace_timerlat_sample (struct timerlat_sample *sample) +static void +__trace_timerlat_sample (struct timerlat_sample *sample,struct trace_buffer *buffer) { -struct trace_array *tr = osnoise_trace;struct trace_event_call *call = &event_osnoise; -struct trace_buffer *buffer = tr->array_buffer.buffer;struct ring_buffer_event *event;struct timerlat_entry *entry;
@@ -411,6 +496,22 @@ static void trace_timerlat_sample(struct timerlat_sample *sample)trace_buffer_unlock_commit_nostack (buffer, event); } +/* +* Record an timerlat_sample into the tracer buffer. +*/ +static void trace_timerlat_sample (struct timerlat_sample *sample) +{ +struct osnoise_instance *inst; +struct trace_buffer *buffer; + +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { + buffer = inst->tr->array_buffer.buffer; +__trace_timerlat_sample (sample, buffer); + } +rcu_read_unlock (); +} +#ifdef CONFIG_STACKTRACE #define MAX_CALLS 256
@@ -450,29 +551,18 @@ static void timerlat_save_stack(int skip)return ; } -/* -* timerlat_dump_stack - dump a stack trace previously saved -* -* Dump a saved stack trace into the trace buffer. -*/ -static void timerlat_dump_stack (void ) + +static void +__timerlat_dump_stack (struct trace_buffer *buffer,struct trace_stack *fstack,unsigned int size) {struct trace_event_call *call = &event_osnoise; -struct trace_array *tr = osnoise_trace; -struct trace_buffer *buffer = tr->array_buffer.buffer;struct ring_buffer_event *event; -struct trace_stack *fstack;struct stack_entry *entry; -unsigned int size; - -preempt_disable_notrace (); - fstack =this_cpu_ptr (&trace_stack); - size = fstack->stack_size; event =trace_buffer_lock_reserve (buffer,TRACE_STACK ,sizeof (*entry) + size,tracing_gen_ctx ());if (!event) - goto out; +return ; entry =ring_buffer_event_data (event);
@@ -481,8 +571,29 @@ static void timerlat_dump_stack(void)if (!call_filter_check_discard (call, entry, buffer, event)) trace_buffer_unlock_commit_nostack(buffer, event); +} -out: +/* +* timerlat_dump_stack - dump a stack trace previously saved +*/ +static void timerlat_dump_stack (void ) +{ +struct osnoise_instance *inst; +struct trace_buffer *buffer; +struct trace_stack *fstack; +unsigned int size; + +preempt_disable_notrace (); + fstack =this_cpu_ptr (&trace_stack); + size = fstack->stack_size; + +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { + buffer = inst->tr->array_buffer.buffer; +__timerlat_dump_stack (buffer, fstack, size); + + } +rcu_read_unlock ();preempt_enable_notrace (); }#else
@@ -1077,12 +1188,37 @@ diff_osn_sample_stats(struct osnoise_variables *osn_var, struct osnoise_sample **/ static __always_inline void osnoise_stop_tracing(void) { -struct trace_array *tr = osnoise_trace; +struct osnoise_instance *inst; +struct trace_array *tr; + +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { + tr = inst->tr; +trace_array_printk_buf (tr->array_buffer.buffer, _THIS_IP_, +"stop tracing hit on cpu %d\n" ,smp_processor_id ()); + +tracer_tracing_off (tr); + } +rcu_read_unlock (); +} -trace_array_printk_buf (tr->array_buffer.buffer, _THIS_IP_, -"stop tracing hit on cpu %d\n" ,smp_processor_id ()); +/* +* notify_new_max_latency - Notify a new max latency via fsnotify interface. +*/ +void notify_new_max_latency (u64 latency) +{ +struct osnoise_instance *inst; +struct trace_array *tr; -tracer_tracing_off (tr); +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { + tr = inst->tr; +if (tr->max_latency < latency) { + tr->max_latency = latency; +latency_fsnotify (tr); + } + } +rcu_read_unlock (); }/*
@@ -1096,7 +1232,6 @@ static __always_inline void osnoise_stop_tracing(void)static int run_osnoise (void ) {struct osnoise_variables *osn_var =this_cpu_osn_var (); -struct trace_array *tr = osnoise_trace;u64 start, sample, last_sample;u64 last_int_count, int_count;s64 noise =0 , max_noise =0 ;
@@ -1231,11 +1366,7 @@ static int run_osnoise(void)trace_osnoise_sample (&s); -/* Keep a running maximum ever recorded osnoise "latency" */ -if (max_noise > tr->max_latency) { - tr->max_latency = max_noise; -latency_fsnotify (tr); - } +notify_new_max_latency (max_noise);if (osnoise_data.stop_tracing_total)if (s.noise > osnoise_data.stop_tracing_total)
@@ -1293,7 +1424,6 @@ static int osnoise_main(void *data)static enum hrtimer_restart timerlat_irq (struct hrtimer *timer) {struct osnoise_variables *osn_var =this_cpu_osn_var (); -struct trace_array *tr = osnoise_trace;struct timerlat_variables *tlat;struct timerlat_sample s;u64 now;
@@ -1364,11 +1494,7 @@ static enum hrtimer_restart timerlat_irq(struct hrtimer *timer)trace_timerlat_sample (&s); -/* Keep a running maximum ever recorded os noise "latency" */ -if (diff > tr->max_latency) { - tr->max_latency = diff; -latency_fsnotify (tr); - } +notify_new_max_latency (diff);if (osnoise_data.stop_tracing)if (time_to_us (diff) >= osnoise_data.stop_tracing)
@@ -1580,7 +1706,7 @@ static void osnoise_hotplug_workfn(struct work_struct *dummy)mutex_lock (&trace_types_lock); -if (!osnoise_busy) +if (!osnoise_has_registered_instances ()) goto out_unlock_trace;mutex_lock (&interface_lock);
@@ -1715,11 +1841,10 @@ osnoise_cpus_write(struct file *filp, const char __user *ubuf, size_t count, goto err_free;/* -* trace_types_lock is taken to avoid concurrency on start/stop -* and osnoise_busy. +* trace_types_lock is taken to avoid concurrency on start/stop. */ mutex_lock (&trace_types_lock); - running = osnoise_busy; + running =osnoise_has_registered_instances ();if (running)stop_per_cpu_kthreads ();
@@ -1942,8 +2067,6 @@ static int osnoise_workload_start(void)return retval; } - osnoise_busy = true; -return 0 ; }
@@ -1952,7 +2075,7 @@ static int osnoise_workload_start(void)*/ static void osnoise_workload_stop (void ) { -if (!osnoise_busy) +if (osnoise_has_registered_instances ())return ; trace_osnoise_callback_enabled = false;
@@ -1967,28 +2090,28 @@ static void osnoise_workload_stop(void)unhook_irq_events ();unhook_softirq_events ();unhook_thread_events (); - - osnoise_busy = false; }static void osnoise_tracer_start (struct trace_array *tr) {int retval; -if (osnoise_busy) +if (osnoise_has_registered_instances ())return ; retval =osnoise_workload_start ();if (retval)pr_err (BANNER "Error starting osnoise tracer\n" ); +osnoise_register_instance (tr); }static void osnoise_tracer_stop (struct trace_array *tr) { -if (!osnoise_busy) +if (!osnoise_has_registered_instances ())return ; +osnoise_unregister_instance (tr);osnoise_workload_stop (); }
@@ -1996,14 +2119,12 @@ static int osnoise_tracer_init(struct trace_array *tr) {/* Only allow one instance to enable this */ -if (osnoise_busy) +if (osnoise_has_registered_instances ())return -EBUSY ; - osnoise_trace = tr; tr->max_latency =0 ;osnoise_tracer_start (tr); -return 0 ; }
@@ -2027,7 +2148,7 @@ static void timerlat_tracer_start(struct trace_array *tr) {int retval; -if (osnoise_busy) +if (osnoise_has_registered_instances ())return ; osnoise_data.timerlat_tracer =1 ;
@@ -2036,6 +2157,8 @@ static void timerlat_tracer_start(struct trace_array *tr)if (retval) goto out_err; +osnoise_register_instance (tr); +return ; out_err:pr_err (BANNER "Error starting timerlat tracer\n" );
@@ -2045,7 +2168,7 @@ static void timerlat_tracer_stop(struct trace_array *tr) {int cpu; -if (!osnoise_busy) +if (!osnoise_has_registered_instances ())return ;for_each_online_cpu (cpu)
@@ -2059,11 +2182,9 @@ static void timerlat_tracer_stop(struct trace_array *tr)static int timerlat_tracer_init (struct trace_array *tr) {/* Only allow one instance to enable this */ -if (osnoise_busy) +if (osnoise_has_registered_instances ())return -EBUSY ; - osnoise_trace = tr; - tr->max_latency =0 ;timerlat_tracer_start (tr);
@@ -2110,6 +2231,8 @@ __init static int init_osnoise_tracer(void)#endif osnoise_init_hotplug_support (); +INIT_LIST_HEAD_RCU (&osnoise_instances); +init_tracefs ();return 0 ;
Signature · 1 line
2.31.1
PATCH v8 6/9 tracing/osnoise: Remove TIMERLAT ifdefs from inside functions
In reply to Daniel Bristot de OliveiraRemove CONFIG_TIMERLAT_TRACER from inside functions, avoiding compilation problems in the future.
--- kernel/trace/trace_osnoise.c | 150 +++++++++++++++++++++++++---------- 1 file changed, 106 insertions(+), 44 deletions(-)
kernel/trace/trace_osnoise.c +106 -44
@@ -321,6 +321,57 @@ static struct osnoise_data {#endif }; +#ifdef CONFIG_TIMERLAT_TRACER +static inline bool timerlat_enabled (void ) +{ +return osnoise_data.timerlat_tracer; +} + +static inline int timerlat_softirq_exit (struct osnoise_variables *osn_var) +{ +struct timerlat_variables *tlat_var =this_cpu_tmr_var (); +/* +* If the timerlat is enabled, but the irq handler did +* not run yet enabling timerlat_tracer, do not trace. +*/ +if (!tlat_var->tracing_thread) { + osn_var->softirq.arrival_time =0 ; + osn_var->softirq.delta_start =0 ; +return 0 ; + } +return 1 ; +} + +static inline int timerlat_thread_exit (struct osnoise_variables *osn_var) +{ +struct timerlat_variables *tlat_var =this_cpu_tmr_var (); +/* +* If the timerlat is enabled, but the irq handler did +* not run yet enabling timerlat_tracer, do not trace. +*/ +if (!tlat_var->tracing_thread) { + osn_var->thread.delta_start =0 ; + osn_var->thread.arrival_time =0 ; +return 0 ; + } +return 1 ; +} +#else /* CONFIG_TIMERLAT_TRACER */ +static inline bool timerlat_enabled (void ) +{ +return false; +} + +static inline int timerlat_softirq_exit (struct osnoise_variables *osn_var) +{ +return 1 ; +} +static inline int timerlat_thread_exit (struct osnoise_variables *osn_var) +{ +return 1 ; +} +#endif +#ifdef CONFIG_PREEMPT_RT /* * Print the osnoise header info.
@@ -977,21 +1028,9 @@ static void trace_softirq_exit_callback(void *data, unsigned int vec_nr)if (!osn_var->sampling)return ; -#ifdef CONFIG_TIMERLAT_TRACER -/* -* If the timerlat is enabled, but the irq handler did -* not run yet enabling timerlat_tracer, do not trace. -*/ -if (unlikely (osnoise_data.timerlat_tracer)) { -struct timerlat_variables *tlat_var; - tlat_var =this_cpu_tmr_var (); -if (!tlat_var->tracing_thread) { - osn_var->softirq.arrival_time =0 ; - osn_var->softirq.delta_start =0 ; +if (unlikely (timerlat_enabled ())) +if (!timerlat_softirq_exit (osn_var))return ; - } - } -#endif duration =get_int_safe_duration (osn_var, &osn_var->softirq.delta_start);trace_softirq_noise (vec_nr, osn_var->softirq.arrival_time, duration);
@@ -1085,17 +1124,9 @@ thread_exit(struct osnoise_variables *osn_var, struct task_struct *t)if (!osn_var->sampling)return ; -#ifdef CONFIG_TIMERLAT_TRACER -if (osnoise_data.timerlat_tracer) { -struct timerlat_variables *tlat_var; - tlat_var =this_cpu_tmr_var (); -if (!tlat_var->tracing_thread) { - osn_var->thread.delta_start =0 ; - osn_var->thread.arrival_time =0 ; +if (unlikely (timerlat_enabled ())) +if (!timerlat_thread_exit (osn_var))return ; - } - } -#endif duration =get_int_safe_duration (osn_var, &osn_var->thread.delta_start);
@@ -1599,6 +1630,11 @@ static int timerlat_main(void *data)hrtimer_cancel (&tlat->timer); return 0; } +#else /* CONFIG_TIMERLAT_TRACER */ +static int timerlat_main (void *data) +{ +return 0 ; +}#endif /* CONFIG_TIMERLAT_TRACER */ /*
@@ -1641,16 +1677,13 @@ static int start_kthread(unsigned int cpu)void *main = osnoise_main;char comm[24 ]; -#ifdef CONFIG_TIMERLAT_TRACER -if (osnoise_data.timerlat_tracer) { +if (timerlat_enabled ()) {snprintf (comm,24 ,"timerlat/%d" , cpu); main = timerlat_main; }else {snprintf (comm,24 ,"osnoise/%d" , cpu); } -#else -snprintf (comm,24 ,"osnoise/%d" , cpu); -#endif + kthread =kthread_create_on_cpu (main,NULL , cpu, comm);if (IS_ERR (kthread)) {
@@ -1944,6 +1977,35 @@ static const struct file_operations cpus_fops = { .llseek = generic_file_llseek, }; +#ifdef CONFIG_TIMERLAT_TRACER +/* +* init_timerlat_tracefs - A function to initialize the timerlat interface files +*/ +static int init_timerlat_tracefs (struct dentry *top_dir) +{ +struct dentry *tmp; + +#ifdef CONFIG_STACKTRACE + tmp =tracefs_create_file ("print_stack" ,0640 , top_dir, + &osnoise_print_stack, &trace_min_max_fops); +if (!tmp) +return -ENOMEM ; +#endif + + tmp =tracefs_create_file ("timerlat_period_us" ,0640 , top_dir, + &timerlat_period, &trace_min_max_fops); +if (!tmp) +return -ENOMEM ; + +return 0 ; +} +#else /* CONFIG_TIMERLAT_TRACER */ +static int init_timerlat_tracefs (struct dentry *top_dir) +{ +return 0 ; +} +#endif /* CONFIG_TIMERLAT_TRACER */ +/* * init_tracefs - A function to initialize the tracefs interface files *
@@ -1988,19 +2050,10 @@ static int init_tracefs(void) tmp =trace_create_file ("cpus" ,0644 , top_dir,NULL , &cpus_fops);if (!tmp) goto err; -#ifdef CONFIG_TIMERLAT_TRACER -#ifdef CONFIG_STACKTRACE - tmp =tracefs_create_file ("print_stack" ,0640 , top_dir, - &osnoise_print_stack, &trace_min_max_fops); -if (!tmp) - goto err; -#endif - tmp =tracefs_create_file ("timerlat_period_us" ,0640 , top_dir, - &timerlat_period, &trace_min_max_fops); -if (!tmp) + ret =init_timerlat_tracefs (top_dir); +if (ret) goto err; -#endif return 0 ;
@@ -2206,6 +2259,16 @@ static struct tracer timerlat_tracer __read_mostly = { .print_header = print_timerlat_headers, .allow_instances = true, }; + +__init static int init_timerlat_tracer (void ) +{ +return register_tracer (&timerlat_tracer); +} +#else /* CONFIG_TIMERLAT_TRACER */ +__init static int init_timerlat_tracer (void ) +{ +return 0 ; +}#endif /* CONFIG_TIMERLAT_TRACER */ __init static int init_osnoise_tracer (void )
@@ -2222,13 +2285,12 @@ __init static int init_osnoise_tracer(void)return ret; } -#ifdef CONFIG_TIMERLAT_TRACER - ret =register_tracer (&timerlat_tracer); + ret =init_timerlat_tracer ();if (ret) { -pr_err (BANNER "Error registering timerlat\n" ); +pr_err (BANNER "Error registering timerlat!\n" );return ret; } -#endif +osnoise_init_hotplug_support ();INIT_LIST_HEAD_RCU (&osnoise_instances);
Signature · 1 line
2.31.1
PATCH v8 7/9 tracing/osnoise: Allow multiple instances of the same tracer
In reply to Daniel Bristot de OliveiraCurrently, the user can start only one instance of timerlat/osnoise tracers and the tracers cannot run in parallel.
As starting point to add more flexibility, let's allow the same tracer to run on different trace instances. The workload will start when the first trace_array (instance) is registered and stop when the last instance is unregistered.
So, while this patch allows the same tracer to run in multiple instances (e.g., two instances running osnoise), it still does not allow instances of timerlat and osnoise in parallel (e.g., one timerlat and osnoise). That is because the osnoise: events have different behavior depending on which tracer is enabled (osnoise or timerlat). Enabling the parallel usage of these two tracers is in my TODO list.
--- kernel/trace/trace_osnoise.c | 101 +++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 23 deletions(-)
kernel/trace/trace_osnoise.c +78 -23
@@ -64,6 +64,24 @@ static bool osnoise_has_registered_instances(void) list); } +/* +* osnoise_instance_registered - check if a tr is already registered +*/ +static int osnoise_instance_registered (struct trace_array *tr) +{ +struct osnoise_instance *inst; +int found =0 ; + +rcu_read_lock (); +list_for_each_entry_rcu (inst, &osnoise_instances, list) { +if (inst->tr == tr) + found =1 ; + } +rcu_read_unlock (); + +return found; +} +/* * osnoise_register_instance - register a new trace instance *
@@ -2101,6 +2119,16 @@ static int osnoise_workload_start(void) {int retval; +/* +* Instances need to be registered after calling workload +* start. Hence, if there is already an instance, the +* workload was already registered. Otherwise, this +* code is on the way to register the first instance, +* and the workload will start. +*/ +if (osnoise_has_registered_instances ()) +return 0 ; +osn_var_reset_all (); retval =osnoise_hook_events ();
@@ -2128,6 +2156,13 @@ static int osnoise_workload_start(void)*/ static void osnoise_workload_stop (void ) { +/* +* Instances need to be unregistered before calling +* stop. Hence, if there is a registered instance, more +* than one instance is running, and the workload will not +* yet stop. Otherwise, this code is on the way to disable +* the last instance, and the workload can stop. +*/ if (osnoise_has_registered_instances ())return ;
@@ -2149,7 +2184,11 @@ static void osnoise_tracer_start(struct trace_array *tr) {int retval; -if (osnoise_has_registered_instances ()) +/* +* If the instance is already registered, there is no need to +* register it again. +*/ +if (osnoise_instance_registered (tr))return ; retval =osnoise_workload_start ();
@@ -2161,18 +2200,17 @@ static void osnoise_tracer_start(struct trace_array *tr)static void osnoise_tracer_stop (struct trace_array *tr) { -if (!osnoise_has_registered_instances ()) -return ; -osnoise_unregister_instance (tr);osnoise_workload_stop (); }static int osnoise_tracer_init (struct trace_array *tr) { - -/* Only allow one instance to enable this */ -if (osnoise_has_registered_instances ()) +/* +* Only allow osnoise tracer if timerlat tracer is not running +* already. +*/ +if (timerlat_enabled ())return -EBUSY ; tr->max_latency =0 ;
@@ -2201,45 +2239,55 @@ static void timerlat_tracer_start(struct trace_array *tr) {int retval; -if (osnoise_has_registered_instances ()) +/* +* If the instance is already registered, there is no need to +* register it again. +*/ +if (osnoise_instance_registered (tr))return ; - osnoise_data.timerlat_tracer =1 ; - retval =osnoise_workload_start ();if (retval) - goto out_err; +pr_err (BANNER "Error starting timerlat tracer\n" );osnoise_register_instance (tr);return ; -out_err: -pr_err (BANNER "Error starting timerlat tracer\n" ); }static void timerlat_tracer_stop (struct trace_array *tr) {int cpu; -if (!osnoise_has_registered_instances ()) -return ; - -for_each_online_cpu (cpu) -per_cpu (per_cpu_osnoise_var, cpu).sampling =0 ; +osnoise_unregister_instance (tr); -osnoise_tracer_stop (tr); +/* +* Instruct the threads to stop only if this is the last instance. +*/ +if (!osnoise_has_registered_instances ()) { +for_each_online_cpu (cpu) +per_cpu (per_cpu_osnoise_var, cpu).sampling =0 ; + } - osnoise_data.timerlat_tracer =0 ; +osnoise_workload_stop (); }static int timerlat_tracer_init (struct trace_array *tr) { -/* Only allow one instance to enable this */ -if (osnoise_has_registered_instances ()) +/* +* Only allow timerlat tracer if osnoise tracer is not running already. +*/ +if (osnoise_has_registered_instances () && !osnoise_data.timerlat_tracer)return -EBUSY ; - tr->max_latency =0 ; +/* +* If this is the first instance, set timerlat_tracer to block +* osnoise tracer start. +*/ +if (!osnoise_has_registered_instances ()) + osnoise_data.timerlat_tracer =1 ; + tr->max_latency =0 ;timerlat_tracer_start (tr);return 0 ;
@@ -2248,6 +2296,13 @@ static int timerlat_tracer_init(struct trace_array *tr)static void timerlat_tracer_reset (struct trace_array *tr) {timerlat_tracer_stop (tr); + +/* +* If this is the last instance, reset timerlat_tracer allowing +* osnoise to be started. +*/ +if (!osnoise_has_registered_instances ()) + osnoise_data.timerlat_tracer =0 ; }static struct tracer timerlat_tracer__read_mostly = {
Signature · 1 line
2.31.1
PATCH v8 8/9 tracing/osnoise: Remove STACKTRACE ifdefs from inside functions
In reply to Daniel Bristot de OliveiraRemove CONFIG_STACKTRACE from inside functions, avoiding compilation problems in the future.
--- kernel/trace/trace_osnoise.c | 44 ++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 15 deletions(-)
kernel/trace/trace_osnoise.c +29 -15
@@ -645,13 +645,19 @@ __timerlat_dump_stack(struct trace_buffer *buffer, struct trace_stack *fstack, u/* * timerlat_dump_stack - dump a stack trace previously saved */ -static void timerlat_dump_stack (void ) +static void timerlat_dump_stack (u64 latency) {struct osnoise_instance *inst;struct trace_buffer *buffer;struct trace_stack *fstack;unsigned int size; +/* +* trace only if latency > print_stack config, if enabled. +*/ +if (!osnoise_data.print_stack || osnoise_data.print_stack > latency) +return ; +preempt_disable_notrace (); fstack =this_cpu_ptr (&trace_stack); size = fstack->stack_size;
@@ -665,8 +671,8 @@ static void timerlat_dump_stack(void)rcu_read_unlock ();preempt_enable_notrace (); } -#else -#define timerlat_dump_stack () do {} while (0) +#else /* CONFIG_STACKTRACE */ +#define timerlat_dump_stack(u64 latency) do {} while (0)#define timerlat_save_stack (a) do {} while (0)#endif /* CONFIG_STACKTRACE */ #endif /* CONFIG_TIMERLAT_TRACER */
@@ -1631,11 +1637,7 @@ static int timerlat_main(void *data)trace_timerlat_sample (&s); -#ifdef CONFIG_STACKTRACE -if (osnoise_data.print_stack) -if (osnoise_data.print_stack <=time_to_us (diff)) -timerlat_dump_stack (); -#endif /* CONFIG_STACKTRACE */ +timerlat_dump_stack (time_to_us (diff)); tlat->tracing_thread = false;if (osnoise_data.stop_tracing_total)
@@ -1996,26 +1998,38 @@ static const struct file_operations cpus_fops = { };#ifdef CONFIG_TIMERLAT_TRACER -/* -* init_timerlat_tracefs - A function to initialize the timerlat interface files -*/ -static int init_timerlat_tracefs (struct dentry *top_dir) +#ifdef CONFIG_STACKTRACE +static int init_timerlat_stack_tracefs (struct dentry *top_dir) {struct dentry *tmp; -#ifdef CONFIG_STACKTRACE tmp =tracefs_create_file ("print_stack" ,0640 , top_dir, &osnoise_print_stack, &trace_min_max_fops);if (!tmp)return -ENOMEM ; -#endif + +return 0 ; +} +#else /* CONFIG_STACKTRACE */ +static int init_timerlat_stack_tracefs (struct dentry *top_dir) +{ +return 0 ; +} +#endif /* CONFIG_STACKTRACE */ + +/* +* init_timerlat_tracefs - A function to initialize the timerlat interface files +*/ +static int init_timerlat_tracefs (struct dentry *top_dir) +{ +struct dentry *tmp; tmp =tracefs_create_file ("timerlat_period_us" ,0640 , top_dir, &timerlat_period, &trace_min_max_fops);if (!tmp)return -ENOMEM ; -return 0 ; +return init_timerlat_stack_tracefs (top_dir); }#else /* CONFIG_TIMERLAT_TRACER */ static int init_timerlat_tracefs (struct dentry *top_dir)
Signature · 1 line
2.31.1
PATCH v8 9/9 tracing/osnoise: Remove PREEMPT_RT ifdefs from inside functions
In reply to Daniel Bristot de OliveiraRemove CONFIG_PREEMPT_RT from inside functions, avoiding compilation problems in the future.
--- kernel/trace/trace_osnoise.c | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-)
kernel/trace/trace_osnoise.c +4 -9
@@ -1517,9 +1517,11 @@ static enum hrtimer_restart timerlat_irq(struct hrtimer *timer)* running, the thread needs to receive the softirq delta_start. The * reason being is that the softirq will be the last to be unfolded, * resseting the thread delay to zero. +* +* The PREEMPT_RT is a special case, though. As softirqs run as threads +* on RT, moving the thread is enough. */ -#ifndef CONFIG_PREEMPT_RT -if (osn_var->softirq.delta_start) { +if (!IS_ENABLED (CONFIG_PREEMPT_RT ) && osn_var->softirq.delta_start) {copy_int_safe_time (osn_var, &osn_var->thread.delta_start, &osn_var->softirq.delta_start);
@@ -1529,13 +1531,6 @@ static enum hrtimer_restart timerlat_irq(struct hrtimer *timer)copy_int_safe_time (osn_var, &osn_var->thread.delta_start, &osn_var->irq.delta_start); } -#else /* CONFIG_PREEMPT_RT */ -/* -* The sofirqs run as threads on RT, so there is not need -* to keep track of it. -*/ -copy_int_safe_time (osn_var, &osn_var->thread.delta_start, &osn_var->irq.delta_start); -#endif /* CONFIG_PREEMPT_RT */ /* * Compute the current time with the expected time.
Signature · 1 line
2.31.1
kernel test robot
build botRe: [PATCH V8 5/9] tracing/osnoise: Support a list of trace_array *tr
In reply to Daniel Bristot de OliveiraI love your patch! Yet something to improve:
Hi Daniel,
I love your patch! Yet something to improve:
[auto build test ERROR on linux/master]
[also build test ERROR on linus/master v5.15-rc7 next-20211029] [cannot apply to tip/perf/core rostedt-trace/for-next]
[If your patch is applied to the wrong git tree, kindly drop us a note. And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch]
url: https://github.com/0day-ci/linux/commits/Daniel-Bristot-de-Oliveira/tracing-osnoise-Support-multiple-instances-for-RTLA/20211030-033201 base: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git 2f111a6fd5b5297b4e92f53798ca086f7c7d33a4 config: arc-allyesconfig (attached as .config) compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
chmod +x ~/bin/make.cross
# https://github.com/0day-ci/linux/commit/1f3d628d440af696e2e1ef2315903d001e8bed62
git remote add linux-review https://github.com/0day-ci/linux
git fetch --no-tags linux-review Daniel-Bristot-de-Oliveira/tracing-osnoise-Support-multiple-instances-for-RTLA/20211030-033201 git checkout 1f3d628d440af696e2e1ef2315903d001e8bed62
# save the attached .config to linux build tree
COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross ARCH=arc
If you fix the issue, kindly add following tag as appropriate
All errors (new ones prefixed by >>):
>> kernel/trace/trace_osnoise.c:1208:6: error: no previous prototype for 'notify_new_max_latency' [-Werror=missing-prototypes]
1208 | void notify_new_max_latency(u64 latency)
| ^~~~~~~~~~~~~~~~~~~~~~
In file included from include/linux/perf_event.h:25,
from include/linux/hw_breakpoint.h:5,
from kernel/trace/trace.h:15,
from kernel/trace/trace_osnoise.c:27:
arch/arc/include/asm/perf_event.h:126:27: error: 'arc_pmu_cache_map' defined but not used [-Werror=unused-const-variable=]
126 | static const unsigned int arc_pmu_cache_map[C(MAX)][C(OP_MAX)][C(RESULT_MAX)] = {
| ^~~~~~~~~~~~~~~~~
arch/arc/include/asm/perf_event.h:91:27: error: 'arc_pmu_ev_hw_map' defined but not used [-Werror=unused-const-variable=]
91 | static const char * const arc_pmu_ev_hw_map[] = {
| ^~~~~~~~~~~~~~~~~cc1: all warnings being treated as errors
vim +/notify_new_max_latency +1208 kernel/trace/trace_osnoise.c
1204
1205 /*
1206 * notify_new_max_latency - Notify a new max latency via fsnotify interface.
1207 */
> 1208 void notify_new_max_latency(u64 latency)
1209 {
1210 struct osnoise_instance *inst;
1211 struct trace_array *tr;
1212
1213 rcu_read_lock();
1214 list_for_each_entry_rcu(inst, &osnoise_instances, list) {
1215 tr = inst->tr;
1216 if (tr->max_latency < latency) {
1217 tr->max_latency = latency;
1218 latency_fsnotify(tr);
1219 }
1220 }
1221 rcu_read_unlock();
1222 }
1223
---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org