Osnoise already implictly accounts IPIs via its IRQ tracking, however it
can be interesting to distiguish between the two: undesired IPIs usually
imply a software configuration issue (e.g. wrong/incomplete CPU isolation)
whereas undesired (non-IPI) IRQs usually imply a hardware configuration
issue.

Signed-off-by: Valentin Schneider <[email protected]>
---
Note that this is modifying the osnoise:osnoise_entry Ftrace entry; I know
trace events are sort of supposed to be stable, but I'm not sure about
ftrace entries.

Alternatively I can have this be purely supported in userspace osnoise by
hooking into the IPI events and counting IPIs separately from the osnoise
events.
---
 include/trace/events/osnoise.h |  1 +
 kernel/trace/trace_entries.h   |  6 ++-
 kernel/trace/trace_osnoise.c   | 80 ++++++++++++++++++++++++++++++++--
 3 files changed, 81 insertions(+), 6 deletions(-)

diff --git a/include/trace/events/osnoise.h b/include/trace/events/osnoise.h
index 3f42736238014..58442e58fe652 100644
--- a/include/trace/events/osnoise.h
+++ b/include/trace/events/osnoise.h
@@ -19,6 +19,7 @@ struct osnoise_sample {
        int                     irq_count;      /* # IRQs during this sample */
        int                     softirq_count;  /* # softirqs during this 
sample */
        int                     thread_count;   /* # threads during this sample 
*/
+       int                     ipi_count;       /* # IPIs during this sample */
 };
 
 #ifdef CONFIG_TIMERLAT_TRACER
diff --git a/kernel/trace/trace_entries.h b/kernel/trace/trace_entries.h
index 54417468fdeb1..aed778d859d37 100644
--- a/kernel/trace/trace_entries.h
+++ b/kernel/trace/trace_entries.h
@@ -430,16 +430,18 @@ FTRACE_ENTRY(osnoise, osnoise_entry,
                __field(        unsigned int,           irq_count       )
                __field(        unsigned int,           softirq_count   )
                __field(        unsigned int,           thread_count    )
+               __field(        unsigned int,           ipi_count       )
        ),
 
-       
F_printk("noise:%llu\tmax_sample:%llu\thw:%u\tnmi:%u\tirq:%u\tsoftirq:%u\tthread:%u\n",
+       
F_printk("noise:%llu\tmax_sample:%llu\thw:%u\tnmi:%u\tirq:%u\tsoftirq:%u\tthread:%u\tipi:%u\n",
                 __entry->noise,
                 __entry->max_sample,
                 __entry->hw_count,
                 __entry->nmi_count,
                 __entry->irq_count,
                 __entry->softirq_count,
-                __entry->thread_count)
+                __entry->thread_count,
+                __entry->ipi_count)
 );
 
 FTRACE_ENTRY(timerlat, timerlat_entry,
diff --git a/kernel/trace/trace_osnoise.c b/kernel/trace/trace_osnoise.c
index 75678053b21c5..574629a6b22b3 100644
--- a/kernel/trace/trace_osnoise.c
+++ b/kernel/trace/trace_osnoise.c
@@ -35,6 +35,7 @@
 
 #include <trace/events/irq.h>
 #include <trace/events/sched.h>
+#include <trace/events/ipi.h>
 
 #define CREATE_TRACE_POINTS
 #include <trace/events/osnoise.h>
@@ -83,6 +84,10 @@ struct osnoise_instance {
 
 static struct list_head osnoise_instances;
 
+static struct cpumask osnoise_cpumask;
+static struct cpumask save_cpumask;
+static struct cpumask kthread_cpumask;
+
 static bool osnoise_has_registered_instances(void)
 {
        return !!list_first_or_null_rcu(&osnoise_instances,
@@ -203,6 +208,11 @@ struct osn_thread {
        u64     delta_start;
 };
 
+/* IPI runtime info */
+struct osn_ipi {
+       u64 count;
+};
+
 /*
  * Runtime information: this structure saves the runtime information used by
  * one sampling thread.
@@ -215,6 +225,7 @@ struct osnoise_variables {
        struct osn_irq          irq;
        struct osn_softirq      softirq;
        struct osn_thread       thread;
+       struct osn_ipi          ipi;
        local_t                 int_counter;
 };
 
@@ -505,6 +516,7 @@ __record_osnoise_sample(struct osnoise_sample *sample, 
struct trace_buffer *buff
        entry->irq_count        = sample->irq_count;
        entry->softirq_count    = sample->softirq_count;
        entry->thread_count     = sample->thread_count;
+       entry->ipi_count        = sample->ipi_count;
 
        trace_buffer_unlock_commit_nostack(buffer, event);
 }
@@ -1288,6 +1300,7 @@ trace_sched_switch_callback(void *data, bool preempt,
  * Hook the osnoise tracer callbacks to handle the noise from other
  * threads on the necessary kernel events.
  */
+
 static int hook_thread_events(void)
 {
        int ret;
@@ -1319,6 +1332,60 @@ static void unhook_thread_events(void)
        unregister_migration_monitor();
 }
 
+static void ipi_emission(struct osnoise_variables *osn_var, unsigned int 
dst_cpu)
+{
+       if (!osn_var->sampling)
+               return;
+
+       osn_var->ipi.count++;
+}
+
+static void trace_ipi_send_cpu_callback(void *data, unsigned int cpu,
+                                       unsigned long callsite, void *callback)
+{
+       struct osnoise_variables *osn_var;
+
+       osn_var = per_cpu_ptr(&per_cpu_osnoise_var, cpu);
+       ipi_emission(osn_var, cpu);
+}
+
+static void trace_ipi_send_cpumask_callback(void *data, const struct cpumask 
*cpumask,
+                                           unsigned long callsite, void 
*callback)
+{
+       struct osnoise_variables *osn_var;
+       int cpu;
+
+       for_each_cpu_and(cpu, cpumask, &osnoise_cpumask) {
+               osn_var = per_cpu_ptr(&per_cpu_osnoise_var, cpu);
+               ipi_emission(osn_var, cpu);
+       }
+}
+
+static int hook_ipi_events(void)
+{
+       int ret;
+
+       ret = register_trace_ipi_send_cpu(trace_ipi_send_cpu_callback, NULL);
+       if (ret)
+               return -EINVAL;
+
+       ret = register_trace_ipi_send_cpumask(trace_ipi_send_cpumask_callback, 
NULL);
+       if (ret)
+               goto out_unreg;
+
+       return 0;
+
+out_unreg:
+       unregister_trace_ipi_send_cpu(trace_ipi_send_cpu_callback, NULL);
+       return -EINVAL;
+}
+
+static void unhook_ipi_events(void)
+{
+       unregister_trace_ipi_send_cpu(trace_ipi_send_cpu_callback, NULL);
+       unregister_trace_ipi_send_cpumask(trace_ipi_send_cpumask_callback, 
NULL);
+}
+
 /*
  * save_osn_sample_stats - Save the osnoise_sample statistics
  *
@@ -1333,6 +1400,7 @@ save_osn_sample_stats(struct osnoise_variables *osn_var, 
struct osnoise_sample *
        s->irq_count = osn_var->irq.count;
        s->softirq_count = osn_var->softirq.count;
        s->thread_count = osn_var->thread.count;
+       s->ipi_count = osn_var->ipi.count;
 }
 
 /*
@@ -1349,6 +1417,7 @@ diff_osn_sample_stats(struct osnoise_variables *osn_var, 
struct osnoise_sample *
        s->irq_count = osn_var->irq.count - s->irq_count;
        s->softirq_count = osn_var->softirq.count - s->softirq_count;
        s->thread_count = osn_var->thread.count - s->thread_count;
+       s->ipi_count = osn_var->ipi.count - s->ipi_count;
 }
 
 /*
@@ -1613,10 +1682,6 @@ static int run_osnoise(void)
        return ret;
 }
 
-static struct cpumask osnoise_cpumask;
-static struct cpumask save_cpumask;
-static struct cpumask kthread_cpumask;
-
 /*
  * osnoise_sleep - sleep until the next period
  */
@@ -2892,12 +2957,18 @@ static int osnoise_hook_events(void)
                goto out_unhook_irq;
 
        retval = hook_thread_events();
+       if (retval)
+               goto out_unhook_softirq;
+
+       retval = hook_ipi_events();
        /*
         * All fine!
         */
        if (!retval)
                return 0;
 
+       unhook_thread_events();
+out_unhook_softirq:
        unhook_softirq_events();
 out_unhook_irq:
        unhook_irq_events();
@@ -2906,6 +2977,7 @@ static int osnoise_hook_events(void)
 
 static void osnoise_unhook_events(void)
 {
+       unhook_ipi_events();
        unhook_thread_events();
        unhook_softirq_events();
        unhook_irq_events();
-- 
2.54.0


Reply via email to