1. 基本信息
| 项目 | 内容 |
|---|---|
| 告警 | [P2-告警]越南胡志明01|SEA05|[uvmp|vmzone-10.65.197.5-CPU软中断]当前值6.00%(>=6%) |
| 主机 | sea05-uvmp-197-5 |
| IP | 10.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_action→mlx5e_napi_poll→ovs_vport_receive→vhost_worker - 两台 VM 的 vhost 线程占大头:vhost-2393(7.67%)+ vhost-10069(5.79%)
copy_user_enhanced_fast_stringSelf 占 1.78%,vhost 数据拷贝开销显著
数据流路径还原:
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
网络拓扑还原:
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 pps | TX pps | RX 带宽 | TX 带宽 |
|---|---|---|---|---|
| net2 | 486K pps | 529K pps | 581 MB/s | 681 MB/s |
| net3 | 103K pps | 37K pps | 127 MB/s | 2.7 MB/s |
net2 承载主要流量(~487K pps),在这个量级下 RPS 全关闭影响巨大。
4. 根因分析
4.1 因果链条
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
6.2 有 RPS
6.3 RSS vs RPS
| 机制 | 层级 | 作用 | 本机状态 |
|---|---|---|---|
| RSS | 硬件 | 多队列,硬中断分散到多核 | ✅ 已开(48 队列) |
| RPS | 软件 | 软中断跨核分流 | ❌ 全关(根因) |
RSS 解决硬中断分布,RPS 解决软中断分布。两者互补,缺一不可。
7. 附录:perf 热点符号解读
| 符号 | 占比 | 含义 |
|---|---|---|
intel_idle | 49.40% | CPU 执行 mwait/hlt 指令等待中断唤醒(空闲) |
vhost_worker | 7.67%+4.74% | vhost 工作线程主循环,处理 VM 网络数据 |
__softirqentry_text_start | 5.62% | 软中断入口,告警"CPU软中断"的计数点 |
net_rx_action | 4.46% | NET_RX 软中断主函数,调用 NAPI poll 收包 |
mlx5e_napi_poll | 3.40% | Mellanox 驱动 NAPI 轮询入口 |
handle_tx | 3.73%+2.20% | vhost 发包方向(宿主机→VM) |
tun_get_user | 3.25% | 从 tun 设备读取数据(vhost 拷贝数据) |
handle_rx | 2.93% | vhost 收包方向(VM→宿主机) |
__netif_receive_skb_core | 2.24% | 内核收包核心,将 skb 分发给协议栈或 OVS |
mlx5e_poll_rx_cq | 1.97% | Mellanox RX 完成队列轮询 |
netdev_frame_hook | 2.04% | OVS 注册的钩子,拦截进入 OVS 桥的包 |
ovs_vport_receive | 2.02% | OVS 虚拟端口收包 |
ovs_dp_process_packet | 1.94% | OVS 数据路径:查流表决定转发动作 |
copy_user_enhanced_fast_string | 1.78% | vhost 将数据从内核拷贝到 VM 的 vring(Self 最高) |
ovs_execute_actions | 1.52% | 执行 OVS 流表动作 |