[RFC PATCH 1/2] tracing/osnoise: Sample IPI counts
From: Valentin Schneider
Date: Wed Jun 10 2026 - 09:11:26 EST
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 <vschneid@xxxxxxxxxx>
---
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