rtla/osnoise: Track IPIs —— 把跨 CPU 中断干扰从 IRQ 计数中拆出来

tracing / rtla 工具链 · osnoise 实时内核延迟分析 · IPI 追踪增强(v4,6 补丁系列)

💡 一句话总结

在 CPU 隔离 / 实时延迟分析场景下,IPI(跨处理器中断)打到"本应隔离"的 CPU 上是延迟毛刺的常见元凶,但单个 IPI 往往不触发延迟阈值,只能在 24 小时长跑 timerlat 的尾端才暴露;本系列为 rtla/osnoise 工具新增 --ipi 选项,通过使能 ipi_send_cpu / ipi_send_cpumask 内核 trace 事件、在内核侧按被监控 CPU 过滤、并在用户态按 CPU 累加计数,把 IPI 干扰从 osnoise 的 IRQ 计数中单独拆出一列,帮助运维快速定位隔离配置失误——这是纯用户态工具增强(非内核补丁),无性能基准数据,价值在"诊断能力增强"而非直接提升吞吐或延迟。

📋 补丁基本信息

项目内容
补丁类型新特性(feature)——rtla/osnoise 工具新增 IPI 追踪诊断能力(非内核性能优化补丁)
状态In Review(v4,Red Hat 提出)
当前版本v4 · lore 链接
版本演进 v1(含内核 osnoise_sample 改动,无独立链接)→ v2(改为纯用户态,无独立链接)→ v3(07-15)→ v4(08-04,rebased 到 v7.2-rc4)
(v1/v2 未在 lore 归档中获取到独立消息链接,按 cover letter 修订说明归纳;v3/v4 链接已核验)
作者机构Valentin Schneider(Red Hat)
提交日期2026-08-04(v4,UTC 17:42)
改动范围tools/tracing/rtla/(src/osnoise_top.c、osnoise.c、common.c、common.h、osnoise.h、cli.c、cli_p.h、trace.c)+ Documentation/tools/rtla/,共 9 文件(6 补丁,见"系列整体分析"逐补丁统计)
核心函数osnoise_init_top() / osnoise_ipi_cpu_handler() / osnoise_ipi_cpumask_handler() / account_ipi() / osnoise_init_ipi_filters() / osnoise_init_trace_tool()
评审标签Suggested-by: Tomas Glozar(Red Hat,patch 4/6 过滤器优雅降级)
原始链接cover letter Message-ID: 20260804174242.2288433-1

📊 速览卡片

核心机制
IPI 事件计数
优化目标
定位 IPI 延迟
适用场景
实时/隔离诊断
实测提升
无基准数据

🎯 解决什么问题:系列整体分析

系列整体定位(osnoise/rtla 工具背景 + 本系列在其中的位置)
工具背景:osnoise 是内核的一个 trace tracer(见 osnoise tracer 文档),在某个 CPU 上跑一个紧采样循环,凡是打断这个循环的事件(IRQ、Softirq、线程调度、NMI)都记为一次"噪声",从而量化该 CPU 被系统其它活动干扰的程度。rtla(Real-Time Linux Analysis)是围绕 osnoise/timerlat 的用户态工具套件(见 rtla osnoise top 文档),用 libtracefs 读 trace 实例并渲染出 per-CPU 统计:rtla osnoise top 展示每个 CPU 的 IRQ/Softirq/Thread 干扰计数与最大采样延迟。IPI(Inter-Processor Interrupt,跨处理器中断)到达某 CPU 时以中断形式打断采样循环,因此一直隐含在 IRQ 计数里,无法单独看到。

本系列定位:给 osnoise top 新增 --ipi 选项,使能 ipi_send_cpu / ipi_send_cpumask 两个内核 trace 事件,在用户态按被监控 CPU 累加 IPI 次数,把"IPI 干扰"从 IRQ 黑盒里拆成独立一列;同时让保存的 trace 文件也带上 IPI 事件,便于离线分析。整系列 6 补丁是用户态工具增强(v2 起全部在 userspace,无内核改动),不改变内核行为。

IPI 追踪的价值:隔离/实时场景下,IPI 打到隔离 CPU 是最常见的延迟毛刺来源之一,但单个 IPI 通常不会让延迟越过阈值——它是与其它干扰叠加才导致毛刺。有了独立的 IPI 计数,运维能直接看到"这个隔离 CPU 每秒被多少 IPI 打扰",在毛刺出现前就发现隔离配置失误。
背景 / 原始动机
来自 cover letter 原文:作者多次看到由 IPI 引起的延迟毛刺报告,根因通常是隔离配置失误,但只在例如 24 小时 timerlat 长跑的尾端才暴露。"并不是因为 IPI 稀少,而是它们单独不足以让被监控 CPU 达到延迟阈值——通常是叠加干扰才到阈值。"作者最初在 timerlat 里加了个临时选项(一旦 IPI 发往被监控 CPU 就停止追踪),"勉强能用,但 Tomáš Glozar 说服我 timerlat 不是做这件事的地方"——于是改为给 osnoise 加 IPI 追踪。Tomas 还建议把实现全部放到用户态,"这样部署到旧内核会容易得多"(引 v3 cover letter)。动机完全来自真实运维痛点,非推断。
系统层面:IPI 干扰在 IRQ 计数里不可见
osnoise tracer 把打断采样循环的中断统一记为 IRQ 计数,不区分"本地定时器/外设中断"与"别的 CPU 主动发来的 IPI"。内核里 IPI 的发送路径会触发 ipi:ipi_send_cpu(单 CPU 目标)和 ipi:ipi_send_cpumask(广播/组播目标)两个 trace 事件,但 rtla/osnoise 默认不开启它们,因此用户看不到 IPI 维度。本系列在用户态开启这两个事件、读取其 cpu/cpumask 字段并按被监控 CPU 计数——不触碰内核,只新增可观测性。
场景层面:隔离/实时负载 + 多核拓扑放大 IPI 干扰
负载特征:RT 线程、DPDK、云原生在线业务等把对延迟敏感的任务钉在(isolated)CPU 上,期望该 CPU 只跑自己的任务、不被其它活动打扰。拓扑特征:多核系统上,其它 CPU 的调度唤醒、RCU、TLB shootdown、irqbalance 都会发出 IPI;若 housekeeping 配置不当,这些 IPI 就会落在"本该隔离"的 CPU 上。为什么难查:单次 IPI 可能只带来几微秒延迟、远低于 osnoise 默认阈值,用户看到的只是偶发毛刺;没有 IPI 计数就无从把"毛刺"与"IPI 来源"对上。本系列的价值:把 IPI 变成可见计数,配合 trace 文件里的 IPI 事件序列,就能在毛刺出现时(或之前)指出是哪类 IPI、来自哪个 CPU。
受影响负载:CPU 隔离 / 实时低延迟负载 · 为什么解决此场景:把"IPI 干扰"从 IRQ 黑盒中拆出,让隔离配置失误可被直接观测

🧩 核心机制

整条链路:--ipi 开关 → 使能两个 IPI trace 事件 →(可选)内核事件过滤器只留被监控 CPU → 用户态事件回调读字段并按 CPU 计数 → osnoise top 新增 IPI 列 / trace 文件同步记录。核心是"让 IPI 以事件形式流到用户态,再按目标 CPU 记账"。

从系统层面看
系列改动集中在 tools/tracing/rtla/src/ 的 osnoise 工具链,是一个串行递进的 6 补丁:
  • 1/6 加开关:OSNOISE_OPT_IPI 定义 --ipi 长选项(布尔),写入 common_params.ipi,并补文档。v3 起故意只用长选项,把短 -i 留给别的用途。
  • 2/6 计数主体:osnoise_init_top() 在 params->ipi 时开启 ipi:ipi_send_cpu 与 ipi:ipi_send_cpumask 事件,注册两个回调;account_ipi() 把每 CPU 的 ipi_count 累加;top 表头与每行新增 IPI 列。
  • 3/6 内核侧过滤:当用 -c 只追踪部分 CPU 时,借助内核 trace 过滤器(cpu & CPUS{...} / cpumask & CPUS{...})让 IPI 事件只在目标 CPU 命中时产生,减少无关事件。ipi_send_cpumask 仍需用户态重算交集(事件只保证"部分目标被监控")。
  • 4/6 优雅降级:v6.6 之前的内核没有 39f7c41c908b(cpumask 字段过滤),过滤器会失败;此时回退到 osnoise_ipi_cpu_unfiltered_handler,在用户态用 CPU_ISSET() 自己过滤(Tomas 建议)。
  • 5/6 清理残留过滤器:用户自定义 -e 事件时,无条件清掉预置过滤器,保证从已知状态开始。
  • 6/6 trace 文件同步:把过滤逻辑重构为可复用的 osnoise_init_ipi_filters();osnoise_init_trace_tool() 改为接收 params,在保存 trace 文件时同样使能 IPI 事件 + 过滤器,使离线文件与 top 分析口径一致。
为什么这样能定位延迟毛刺:IPI 事件携带目标 CPU(单播)或目标 cpumask(广播),用户态据此把每一次 IPI 归到具体 CPU 头上;配合 osnoise 采样延迟,就能回答"哪个 CPU 被什么 IPI 打扰、频率多少、是否与毛刺相关"。
rtla/osnoise IPI 追踪机制流程图
图 1:rtla/osnoise IPI 追踪链路——内核 IPI trace 事件 →(可选)事件过滤器 → 用户态回调按 CPU 计数 → top IPI 列 / trace 文件同步记录
来源:基于 lore 真实补丁 diff(v4)绘制
步骤操作目的
使能事件tracefs_event_enable(inst, "ipi", "ipi_send_cpu"/"ipi_send_cpumask")让内核在 IPI 发送时产生 trace 事件(默认未开启)
事件过滤tracefs_event_file_write(..., "filter", "cpu & CPUS{...}")只对 -c 指定的被监控 CPU 产生事件,减噪声(pre-v6.6 优雅降级)
回调计数读 cpu/cpumask 字段 → CPU_ISSET/CPU_AND → account_ipi()把每次 IPI 归到目标 CPU,累加 ipi_count
top 展示osnoise_top_print() 输出新增 IPI 列运维直接看到每 CPU 收到的 IPI 数(IRQ/Softirq/Thread 之外新维度)
trace 记录osnoise_init_trace_tool() 同步使能 IPI 事件 + 过滤器保存的 trace 文件含 IPI 事件序列,可离线关联毛刺
最核心的代码证据(逐字,来自 patch 2/6 与 6/6)
+static void account_ipi(struct osnoise_tool *tool, unsigned long long dst_cpu)
+{
+	struct osnoise_top_cpu *cpu_data;
+	struct osnoise_top_data *data;
+	unsigned long long inc = 1;
+
+	data = tool->data;
+	cpu_data = &data->cpu_data[dst_cpu];
+
+	update_sum(&cpu_data->ipi_count, &inc);
+}
+
+/*
+ * osnoise_ipi_cpu_handler - this is the handler for single CPU IPI events.
+ */
+static int
+osnoise_ipi_cpu_handler(struct trace_seq *s, struct tep_record *record,
+		     struct tep_event *event, void *context)
+{
+	struct osnoise_tool *tool;
+	struct osnoise_params *params;
+	unsigned long long dst_cpu;
+	struct trace_instance *trace = context;
+
+	tool = container_of(trace, struct osnoise_tool, trace);
+	params = to_osnoise_params(tool->params);
+
+	tep_get_field_val(s, event, "cpu", record, &dst_cpu, 1);
+
+	if (CPU_ISSET(dst_cpu, &params->common.monitored_cpus))
+		account_ipi(tool, dst_cpu);
+
+	return 0;
+}

▲ 这是 2/6 的计数核心:account_ipi() 把目标 CPU 的 ipi_count 加一(复用已有 update_sum() 聚合逻辑);osnoise_ipi_cpu_handler() 从事件里取出 cpu 字段,用 CPU_ISSET 确认它是被监控 CPU 才记账。关键在"dst_cpu 判定"——IPI 是发给别人的,收到事件的是任意 CPU,必须按事件携带的目标 CPU 归属,而不是按执行回调的 CPU 归属。

+int osnoise_init_ipi_filters(struct osnoise_tool *tool,
+			    struct common_params *params,
+			    bool *filters_enabled)
+{
+	char filter[MAX_PATH];
+	int retval;
+	/*
+	 * If tracing on a subset of possible CPUs, leverage the kernel filtering
+	 * infrastructure to only generate events on traced CPUs.
+	 * Older kernels (pre v6.6) may have the IPI events but not the ability
+	 * to filter them, so allow that to fail gracefully.
+	 */
+
+	snprintf(filter, ARRAY_SIZE(filter), "cpu & CPUS{%s}\n", params->cpus);
+	retval = tracefs_event_file_write(tool->trace.inst,
+					  "ipi", "ipi_send_cpu", "filter",
+					  filter);
+	if (retval < 0) {
+		debug_msg("Could not set ipi_send_cpu CPU filter\n");
+		*filters_enabled = false;
+		return 0;
+	}
+
+	snprintf(filter, ARRAY_SIZE(filter), "cpumask & CPUS{%s}\n", params->cpus);
+	retval = tracefs_event_file_write(tool->trace.inst,
+					  "ipi", "ipi_send_cpumask", "filter",
+					  filter);
+	if (retval < 0) {
+		/*
+		 * If we managed to set up the previous filter but not
+		 * this one, something's really wrong
+		 */
+		err_msg("Could not set ipi_send_cpumask CPU filter\n");
+		*filters_enabled = false;
+		return -1;
+	}
+
+	*filters_enabled = true;
+	return 0;
+}

▲ 这是 6/6 抽取的过滤器初始化:把"按被监控 CPU 过滤 IPI 事件"的逻辑从 osnoise top 里抽成公共函数,供 trace 文件记录复用。核心在 cpu & CPUS{cpus} 语法——内核 trace 过滤器只让目标 CPU 在被监控集合内的事件通过;写失败时(pre-v6.6)不报错退出,而是返回 0 并置 filters_enabled=false,让调用方回退到用户态过滤(patch 4/6)。

📈 性能影响

提升角度(方法论分类)
按 on-CPU / off-CPU 视角分类,本系列既不是 on-CPU 计算效率优化,也不是 off-CPU 等待优化——它不改变内核任何执行路径,是纯可观测性 / 诊断能力增强(observability tooling)。它不减少 IPI、不缩临界区、不减延迟;它做的是把"IPI 干扰"这个本已存在、但被 IRQ 计数吞掉的信号暴露出来。因此性能影响角度属于"为性能问题定位提供工具",而非直接提升任何基准指标。
受益场景
受益最大的场景是CPU 隔离 / 实时低延迟系统的运维与调优:当隔离 CPU 出现偶发延迟毛刺时,启用 --ipi 能直接看到每 CPU 的 IPI 计数与 trace 文件里的 IPI 事件序列,把根因从"猜"变成"看"。间接收益:更快定位隔离配置失误 → 减少无效排查时间 → 缩短毛刺窗口。这是对"性能问题定位效率"的改善,不是对运行时性能的改善。
场景/用例运行环境改进前改进后
定位隔离 CPU 的 IPI 干扰rtla osnoise top + --ipi(任意带 ipi trace 事件的内核)IPI 混在 IRQ 计数中,不可见IPI 独立成列 + trace 文件含 IPI 事件(可观测性增强)

说明:补丁未提供基准数据(纯用户态工具增强,无 before/after 性能指标)。上表"改进前/后"为能力差异,非性能数字,属"解读(AI 分析)"。作者在 cover letter 中未声称任何吞吐/延迟收益。

on-CPU vs off-CPU 视角
延迟 = on-CPU 计算时间 + off-CPU 等待时间。IPI 干扰本质是打断被监控 CPU 执行(近似"被抢占/被中断"的等待性质),属于把 off-CPU 侧的干扰源显性化。本系列不消除该等待,而是让等待的成因可见——这决定了它的价值判定标准是"诊断效率",不是"数字下降"。需如实区分:它帮用户发现并修掉 IPI 来源后,延迟才可能改善,而改善幅度取决于具体配置。

🔄 方案演进

本系列从最初提出到 v4 的演进过程(基于 cover letter 修订说明 + lore 各版本真实信息):

v1 → v2 → v3 → v4 演进脉络
v1(首版,日期未获取到独立链接,按修订说明归纳,推测约 7 月上旬):在内核 osnoise_sample 里加改动 + 用户态配合。
v2:删除全部内核改动,改为纯用户态——Tomas Glozar 指出这样"部署到旧内核容易得多"(引 v3 cover letter)。
v3(07-15):删除短选项 -i(把该标志位留空给其它用途);重排 top 表头打印;修复 tracefs_event_file_write() 返回值处理;IPI 过滤器在旧内核上优雅失败;为 -e 事件清理预置过滤器确保已知状态。
v4(08-04):rebased 到 v7.2-rc4;修正 changelog 一处笔误。功能相对 v3 基本稳定。
设计权衡 / 讨论推进
放哪里:作者最初在 timerlat 里塞"IPI 一到就停追踪"的临时方案,Tomas 说服他 timerlat 不是合适位置,改为 osnoise(cover letter)。
内核 vs 用户态:v1 在内核采样里动手,v2 起全部搬用户态(Tomas 建议,兼顾旧内核可部署性)。
选项命名:Tomas 在 v3 2/6 上追问选项名("这是现在的 --irq 吗?")并自答"s/irq/ipi/"(message-id CAP4=nvSsYuibT7Oq-hAF9b=dZx==FcR1bK5d3zNmYcWgAZmCGQ);v3 起只用长选项 --ipi。
优雅降级:过滤能力依赖 39f7c41c908b(v6.6 才有的 cpumask 字段过滤),Tomas 建议让过滤器失败可接受并回退用户态过滤(patch 4/6 的 Suggested-by)。

⚠️ 风险与局限

收益成立的前提
① 需要内核有 ipi:ipi_send_cpu / ipi:ipi_send_cpumask trace 事件(CONFIG_TRACING 且事件存在,主流内核均有);② 事件过滤需要 39f7c41c908b(v6.6+)才支持"按 cpumask 字段过滤"——旧内核会优雅降级为用户态过滤,功能仍在但事件量更大;③ 收益只对"隔离/实时且确有 IPI 干扰"的场景成立,普通服务器上 IPI 干扰本就是正常现象,多出一列价值有限。
生产落地影响
观测者效应(最需关注):--ipi 会让每一次 IPI 都写一条 trace 记录。在 IPI 高频(如大规模 TLB shootdown、频繁跨核唤醒)的机器上,trace 缓冲占用与 tracing 本身的开销可能不小,而 osnoise 恰恰在测延迟,额外的 tracing 开销本身就是一种噪声——这是分析工具固有的"观测干扰"风险。好在它是 --ipi 显式开启(opt-in),默认不启用,不改变默认路径。建议在低干扰窗口短时开启、或配合 -c 子集过滤控制事件量。
部署:rtla 是内核源码树内工具(tools/tracing/rtla),需随内核源码编译安装;无新增内核依赖,旧内核也可用(v2 的设计目标)。
可观测性:新增 IPI 列是"诊断信号"而非内核计数器,不改变 tracefs 语义,对既有依赖无影响。
生态/兼容性
纯用户态、无 ABI/内核接口变更,向后兼容好;与 libtracefs/tep 的交互(tep_get_field_val、tracefs_event_file_write)依赖既有库能力。对 ipi_send_cpumask 事件,内核过滤器只保证"部分目标被监控"就产生事件,用户态必须重算交集(patch 3/6 已处理),否则会高估 IPI 计数——这是该方案内在的精确性边界。
review 质疑(若有)
公开讨论主要来自同部门同事 Tomas Glozar(Red Hat),均已在 v3/v4 解决:① 建议把 IPI 追踪从 timerlat 移到 osnoise(cover letter);② 建议纯用户态实现(v2 落实);③ 对选项命名提问(message-id),v3 起只用 --ipi 长选项;④ 建议过滤器优雅降级(patch 4/6 落实,Suggested-by)。截至 v4 发布,未发现未解决的 NACK 或阻塞性质疑(若后续有维护者讨论将补充)。
严重度:MINOR(相对内核补丁,工具改动本身风险很低;主要风险是观测者效应与事件量放大)· 落地场景:隔离/实时系统上启用 --ipi 做诊断时,注意 trace 事件量对被测系统造成的观测干扰

🔗 交叉引用

📌 系列各补丁(v4,共 6 补丁)
📚 关联工作 / 参考
rtla osnoise top 文档 — 本系列增强的工具;--ipi 选项说明在此文档新增
osnoise tracer 文档 — 内核侧采样循环原理(IRQ/Softirq/Thread 噪声来源)
commit 39f7c41c908b(tracing/filters: Enable filtering a cpumask field by another cpumask) — 过滤器优雅降级所依赖的内核能力(v6.6)
v3 cover letter(07-15) — 上一版,含 v1→v2→v3 修订说明与用户态化动机
⚠️ 免责声明

本站内容均由 AI 基于公开知识辅助生成,仅供学习参考,请勿直接引用作为依据。作者不对信息的准确性、完整性及适用性作保证,亦不对因使用本站内容产生的任何损失承担责任。