CPU软中断高排查记录

1. 基本信息

项目内容
告警[P2-告警]越南胡志明01|SEA05|[uvmp|vmzone-10.65.197.5-CPU软中断]当前值6.00%(>=6%)
主机sea05-uvmp-197-5
IP10.65.197.5
架构KVM 宿主机,48 vCPU,Mellanox ConnectX 双口网卡
角色虚拟化宿主机,运行约 20+ VM,OVS 软件转发

2. 根因结论

Mellanox 网卡 RSS 48 队列已开启(硬中断分散),但 RPS 全部关闭rps_cpus=0),导致软中断只能在硬中断触发的那颗 CPU 上处理,无法跨核分流。前 22 颗核承担了绝大部分 NET_RX 软中断,后 26 颗核几乎空转,热点核软中断远超 6% 触发告警。

3. 排查步骤

3.1 第 1 步:perf 定位 CPU 热点方向

目的:确认 CPU 时间花在哪里,是网络、磁盘还是其他。

bashperf record -g -a -- sleep 30
perf report

数据证据

textSamples: 4M of event 'cycles:ppp', Event count (approx.): 1769990165042
  Children      Self  Command          Symbol
+   55.30%     0.35%  swapper          [k] do_idle
+   49.40%    49.40%  swapper          [k] intel_idle
+    9.61%     0.00%  vhost-2393       [k] 0000000000000000
+    7.67%     0.12%  vhost-2393       [k] vhost_worker
+    5.79%     0.00%  vhost-10069      [k] 0000000000000000
+    5.69%     0.02%  swapper          [k] irq_exit
+    5.62%     0.06%  swapper          [k] __softirqentry_text_start
+    4.74%     0.09%  vhost-10069      [k] vhost_worker
+    4.46%     0.03%  swapper          [k] net_rx_action
+    3.73%     0.06%  vhost-2393       [k] handle_tx
+    3.40%     0.06%  swapper          [k] mlx5e_napi_poll
+    3.26%     0.02%  vhost-2393       [k] tun_sendmsg
+    3.25%     0.15%  vhost-2393       [k] tun_get_user
+    2.93%     0.07%  vhost-2393       [k] handle_rx
+    2.24%     0.05%  swapper          [k] __netif_receive_skb_core
+    2.20%     0.04%  vhost-10069      [k] handle_tx
+    2.04%     0.01%  vhost-2393       [k] netdev_frame_hook
+    2.02%     0.02%  vhost-2393       [k] ovs_vport_receive
+    1.97%     0.12%  swapper          [k] mlx5e_poll_rx_cq
+    1.94%     0.01%  vhost-2393       [k] ovs_dp_process_packet
+    1.92%     0.02%  swapper          [k] ovs_vport_receive
+    1.85%     0.04%  swapper          [k] mlx5e_handle_rx_cqe
+    1.84%     0.01%  swapper          [k] ovs_dp_process_packet
+    1.78%     1.78%  vhost-2393       [k] copy_user_enhanced_fast_string
+    1.52%     0.05%  swapper          [k] ovs_execute_actions
+    1.49%     0.05%  vhost-2393       [k] ovs_execute_actions

结论

  • CPU 空闲占 55.3%(整体不繁忙)
  • 软中断入口 __softirqentry_text_start 占 5.62%(全核平均)
  • 热点集中在网络收包路径:net_rx_actionmlx5e_napi_pollovs_vport_receivevhost_worker
  • 两台 VM 的 vhost 线程占大头:vhost-2393(7.67%)+ vhost-10069(5.79%)
  • copy_user_enhanced_fast_string Self 占 1.78%,vhost 数据拷贝开销显著

数据流路径还原

Mellanox NIC → 硬中断 → 软中断(NET_RX) → NAPI poll → OVS datapath → vhost → VM(tun) ↓ ↓ ↓ ↓ ↓ ↓ ↓ mlx5e_handle_rx_cq do_IRQ net_rx_action mlx5e_napi_poll ovs_dp_process_packet vhost_worker tun_get_user ↓ ↓ ↓ ↓ irq_exit __softirqentry ovs_execute_actions handle_tx/handle_rx

3.2 第 2 步:/proc/softirqs 确认软中断类型和分布

目的:确认是哪类软中断(NET_RX/BLOCK/TIMER 等),以及在各 CPU 间的分布是否均匀。

bashcat /proc/softirqs

数据证据(NET_RX 部分,48 核):

text          CPU0       CPU1       CPU2       CPU3       CPU4       CPU5       CPU6       CPU7
NET_RX: 877262657 1062407111 3786709548 3836282633 1221278738  245803196 3065264940 1180983488

          CPU8       CPU9       CPU10      CPU11      CPU12      CPU13      CPU14      CPU15
NET_RX:  703971819   67317179  130426552 2421013159  378246083 1442157778 2132081857 1785373778

          CPU16      CPU17      CPU18      CPU19      CPU20      CPU21      CPU22      CPU23
NET_RX: 1392598333 2068876200 2897421430 2449247367 1713507801 1792711186  135158214   99770795

          CPU24      CPU25      CPU26      CPU27      CPU28      CPU29      CPU30      CPU31
NET_RX:  198869772  149926755  129498895  119752931  111555881  104240353   97467479   97609886

          CPU32      CPU33      CPU34      CPU35      CPU36      CPU37      CPU38      CPU39
NET_RX:   85864227   79135108   77026435   75211561   81173695   66945964   57576905   50128112

          CPU40      CPU41      CPU42      CPU43      CPU44      CPU45      CPU46      CPU47
NET_RX:   45400087   40244369   36944925   34185820   33162320   30395995   26706477   24722345

结论

  • 软中断类型确认为 NET_RX(网络收包)
  • 分布极度不均:CPU3 有 38.36 亿次,CPU47 仅 2472 万次,差距 155 倍
  • CPU0~21 承担了绝大部分 NET_RX,CPU22~47 几乎空闲
  • 这直接解释了为什么全核平均 softirq 只有 5.62%,但热点核远超 6%

💡 量化验证:全核平均 softirq = 5.62%(来自 perf),softirq 总消耗 = 48 × 5.62% ≈ 270% 单核时间。CPU3 的 NET_RX 占全系统 ~11% → softirq ≈ 30% 单核时间。CPU47 的 NET_RX 占全系统 ~0.07% → softirq ≈ 0.2%。

3.3 第 3 步:/proc/interrupts 查看网卡中断分布

目的:确认 Mellanox 网卡数据通道中断是否存在及其绑核分布。

bashcat /proc/interrupts | grep -E 'mlx|enp|enx' | head -20
cat /proc/interrupts | grep -E 'mlx5_comp|mlx5_rx' | head -20

数据证据

text# 只有管理面中断,无数据通道中断
 358:  ... CPU12 ...  IR-PCI-MSI 91750400-edge  mlx5_pages_eq@pci:0000:af:00.0
 359:  ... CPU12 ...  IR-PCI-MSI 91750401-edge  mlx5_cmd_eq@pci:0000:af:00.0
 360:  ... CPU12 ...  IR-PCI-MSI 91750402-edge  mlx5_async_eq@pci:0000:af:00.0
 361:  ... CPU12 ...  IR-PCI-MSI 91750403-edge  mlx5_page_fault_eq@pci:0000:af:00.0

# mlx5_comp 搜索结果为空

结论:只看到管理面中断(pages_eq/cmd_eq/async_eq),全部绑在 CPU12。没有看到 mlx5_comp* 数据通道中断,Mellanox 驱动在 NAPI 模式下以 poll 方式工作而非传统中断方式。

3.4 第 4 步:确认网络拓扑

目的:br0 是 Linux bridge 还是 OVS bridge?物理网卡叫什么?

bashls -la /sys/class/net/
ovs-vsctl show
ls /sys/class/net/ | while read n; do d=/sys/class/net/$n/device; [ -d "$d" ] && echo "$n -> $(readlink $d)"; done

网络拓扑还原

net0/1 (1a:00, 管理网卡) ── manbr ── net2 (af:00.0, Mellanox P0) │ 21个VM net3 (af:00.1, Mellanox P1) ── wanbr ── 17个VM GRE 隧道 (wildcard_gre) ── br0 ── 21个VM(无物理网卡,流量经 net2/net3 出去) 三个 OVS 桥共享 Mellanox 网卡带宽,纯软件转发,无硬件卸载。

3.5 第 5 步:确认 Mellanox RSS/RPS/GRO 配置

目的:确认网卡多队列、RPS、offload 的当前状态。

bashethtool -l net2 && ethtool -l net3
ethtool -k net2 | grep -E 'generic-receive-offload|generic-segmentation-offload|tcp-segmentation-offload|hw-tc-offload|rx-hashing'
for q in /sys/class/net/net2/queues/rx-*/rps_cpus; do echo "$q: $(cat $q)"; done
for q in /sys/class/net/net3/queues/rx-*/rps_cpus; do echo "$q: $(cat $q)"; done

关键发现

配置项状态说明
RSS 多队列✅ 已配 48 队列硬中断分散到多核
GRO/GSO/TSO✅ 已开启减少包数,降低 CPU 开销
hw-tc-offload✅ 已开启硬件条件具备
RPS❌ 全部关闭rps_cpus 全部 = 0,核心问题

RSS 48 队列已配(硬中断分散到多核),RPS 全部关闭是核心问题——硬中断虽然在多核触发,但软中断只能原地处理,不能迁移到其他核。

3.6 第 6 步:确认高流量 VM

bashps -eo pid,comm,%cpu,psr | grep vhost

vhost-2393:12 线程,每线程 6.9%~8.4% CPU,总计 ~95% CPU。vhost-10069:12 线程,每线程 4.2%~7.6% CPU,总计 ~70% CPU。与 perf 数据吻合。

3.7 第 7 步:确认网络流量规模

bashsar -n DEV 1 3 | grep -E 'net2|net3'
网卡RX ppsTX ppsRX 带宽TX 带宽
net2486K pps529K pps581 MB/s681 MB/s
net3103K pps37K pps127 MB/s2.7 MB/s

net2 承载主要流量(~487K pps),在这个量级下 RPS 全关闭影响巨大。

4. 根因分析

4.1 因果链条

RPS 关闭(rps_cpus=0)软中断无法跨核迁移
硬中断绑定 = 软中断绑定硬中断在哪颗 CPU 触发,软中断就只能在那颗 CPU 处理
RSS 只解决硬中断分布48 个 RSS 队列分散硬中断,但软中断无法跨核迁移
前 22 核包揽、后 26 核空闲热点核软中断远超 6%,冷核接近 0%
全核平均被冷核拉低perf 显示 5.62%,但热点核实际 20-30%

5. 优化方案

5.1 P0:开启 RPS(最关键,即时生效,风险极低)

bash# net2(主流量网卡)
for q in /sys/class/net/net2/queues/rx-*/rps_cpus; do
  echo "ffff,ffffffffffff" > $q
done
for q in /sys/class/net/net2/queues/rx-*/rps_flow_cnt; do
  echo 4096 > $q
done

# net3
for q in /sys/class/net/net3/queues/rx-*/rps_cpus; do
  echo "ffff,ffffffffffff" > $q
done
for q in /sys/class/net/net3/queues/rx-*/rps_flow_cnt; do
  echo 4096 > $q
done

# OVS 桥
echo "ffff,ffffffffffff" > /sys/class/net/br0/queues/rx-0/rps_cpus
echo 4096 > /sys/class/net/br0/queues/rx-0/rps_flow_cnt
echo "ffff,ffffffffffff" > /sys/class/net/wanbr/queues/rx-0/rps_cpus
echo 4096 > /sys/class/net/wanbr/queues/rx-0/rps_flow_cnt

# 全局 RPS 流表
echo 196608 > /proc/sys/net/core/rps_sock_flow_entries

预期效果

指标优化前优化后(预期)
参与 NET_RX 的 CPU~22 核(前半)48 核全部参与
热点核 CPU 软中断占比20-30%< 2%
CPU 软中断告警6% ≥ 6% 触发< 3%,告警消除

5.2 P1:持久化 RPS 配置

bash# sysctl 持久化
cat >> /etc/sysctl.conf << 'EOF'
net.core.rps_sock_flow_entries = 196608
EOF
sysctl -p

# RPS 持久化脚本
cat > /etc/rc.d/init.d/set-rps.sh << 'SCRIPT'
#!/bin/bash
for nic in net2 net3; do
  for q in /sys/class/net/$nic/queues/rx-*/rps_cpus; do
    echo "ffff,ffffffffffff" > $q 2>/dev/null
  done
  for q in /sys/class/net/$nic/queues/rx-*/rps_flow_cnt; do
    echo 4096 > $q 2>/dev/null
  done
done
for br in br0 wanbr; do
  echo "ffff,ffffffffffff" > /sys/class/net/$br/queues/rx-0/rps_cpus 2>/dev/null
  echo 4096 > /sys/class/net/$br/queues/rx-0/rps_flow_cnt 2>/dev/null
done
SCRIPT
chmod +x /etc/rc.d/init.d/set-rps.sh

grep -q set-rps /etc/rc.local || echo '/etc/rc.d/init.d/set-rps.sh' >> /etc/rc.local

5.3 P2:验证效果

bash# 观察 NET_RX 软中断分布是否均匀
watch -n2 'cat /proc/softirqs | head -1; cat /proc/softirqs | grep NET_RX'

# 各核 CPU 使用率(关注 %soft 列)
mpstat -P ALL 2 3

# 确认 RPS 已开启
cat /sys/class/net/net2/queues/rx-0/rps_cpus
cat /sys/class/net/net3/queues/rx-0/rps_cpus

5.4 P3(后续优化):OVS 硬件卸载

当前 OVS 版本 2.10.5 较老,hw-tc-offload 支持有限。若后续升级 OVS 或内核,可考虑:

bash# 确认 hw-tc-offload 硬件条件已具备(当前已开启)
ethtool -k net2 | grep hw-tc-offload   # on

# 配置 OVS 硬件卸载(需 OVS 2.12+ 且内核支持)
ovs-vsctl set Open_vSwitch . other_config:hw-offload=true
systemctl restart openvswitch

预期将 OVS 转发卸载到网卡硬件,CPU 几乎不参与转发,软中断降低 60-80%。

6. 附录:RPS 原理

RPS(Receive Packet Steering) 是 Linux 内核的软件级网络收包分流机制。

6.1 没有 RPS

网卡收到包 → 硬中断(IRQ)绑到 CPU2 → CPU2 触发软中断 → CPU2 完成所有处理 → CPU2 跑满 硬中断在哪颗 CPU 触发,后续所有软中断处理就死死绑在这颗核上,不能迁移。

6.2 有 RPS

网卡收到包 → 硬中断(IRQ)绑到 CPU2 → RPS 根据 hash(五元组) 分发 → CPU0~47 都参与软中断处理 RPS 在硬中断处理后,根据数据包的五元组(srcIP/dstIP/srcPort/dstPort/protocol)计算 hash, 将包分发到不同 CPU 的 backlog 队列,让多核并行处理软中断。

6.3 RSS vs RPS

机制层级作用本机状态
RSS硬件多队列,硬中断分散到多核✅ 已开(48 队列)
RPS软件软中断跨核分流❌ 全关(根因)

RSS 解决硬中断分布,RPS 解决软中断分布。两者互补,缺一不可。

7. 附录:perf 热点符号解读

符号占比含义
intel_idle49.40%CPU 执行 mwait/hlt 指令等待中断唤醒(空闲)
vhost_worker7.67%+4.74%vhost 工作线程主循环,处理 VM 网络数据
__softirqentry_text_start5.62%软中断入口,告警"CPU软中断"的计数点
net_rx_action4.46%NET_RX 软中断主函数,调用 NAPI poll 收包
mlx5e_napi_poll3.40%Mellanox 驱动 NAPI 轮询入口
handle_tx3.73%+2.20%vhost 发包方向(宿主机→VM)
tun_get_user3.25%从 tun 设备读取数据(vhost 拷贝数据)
handle_rx2.93%vhost 收包方向(VM→宿主机)
__netif_receive_skb_core2.24%内核收包核心,将 skb 分发给协议栈或 OVS
mlx5e_poll_rx_cq1.97%Mellanox RX 完成队列轮询
netdev_frame_hook2.04%OVS 注册的钩子,拦截进入 OVS 桥的包
ovs_vport_receive2.02%OVS 虚拟端口收包
ovs_dp_process_packet1.94%OVS 数据路径:查流表决定转发动作
copy_user_enhanced_fast_string1.78%vhost 将数据从内核拷贝到 VM 的 vring(Self 最高)
ovs_execute_actions1.52%执行 OVS 流表动作
目录