鲲鹏 ARM64 服务器启动卡死排查:Intel E810 网卡 ice 驱动 RX 路径内核 Oops

0 阅读6分钟

鲲鹏 ARM64 服务器启动卡死排查:Intel E810 网卡 ice 驱动 RX 路径内核 Oops

TL;DR:两台同型号 ARM64 服务器,开机/运行后必崩,根因是 Intel E810-C 100G 网卡的 ice 驱动在 ARM64 SMMU/IOMMU 环境下收包路径解引用了未映射的野地址,触发内核 Oops → Fatal exception in interrupt → panic。临时止血加 modprobe.blacklist=ice,根治走驱动/固件升级或 iommu.passthrough=1 验证。


一、背景

手上有两台同型号的 ARM64 服务器(华为 KunLun 2280,鲲鹏 920),各插了一块 Intel E810-C for QSFP 100GbE 网卡,系统跑在 5.10 LTS 内核(aarch64)上。

  • 机器 A:开机进入 systemd 后直接卡死,画面冻结,起不来。
  • 机器 B:能正常进系统,但运行几分钟后崩溃自动重启。

两台都通过 BMC 一键收集了日志,关键是里面的 SOL 串口日志(system console)

二、现象

机器 A 的 VGA 截图显示:系统已经启动到网络阶段,enp3s0f0 链路 UP(100 Gbps 全双工),紧接着画面定格。约 1 秒后串口里出现同一个内核 panic。

机器 B 更有价值:它开了 kdump,崩溃后自动抓取了 vmcore 再重启。

三、抓日志 & 定位卡死点

BMC 一键收集包里,串口日志一般在 OSDump/systemcom*LogDump/linux_kernel_log。boot hang 的真相几乎都在串口日志的最后几行,所以解包后直接看尾部。

机器 A 尾部完整记录了一次内核 Oops(已脱敏):

[   28.105834] Unable to handle kernel paging request at virtual address ffff75db76a6c000
[   28.177323] Internal error: Oops: 0000000096000004 [#1] SMP
[   28.281718] CPU: 27 PID: 0 Comm: swapper/27  ... 5.10.0-xxx.aarch64
[   28.293557] Hardware name: HAJX KunLun 2280, BIOS 6.70
[   28.310336] pc : ice_process_skb_fields+0x144/0x1ec [ice]
[   28.330xxx] lr : ice_clean_rx_irq+0x1c8/0x6d0 [ice]
...
[   28.499596]  el1_irq+0xb8/0x140          <- 中断上下文
[   28.504792]  arch_cpu_idle ...           <- CPUidle 时被网卡中断切入
[   28.540397] Code: f9400ec0 ... (f8626803)
[   28.548563] ---[ end trace ... ]---
[   28.583006] Kernel panic - not syncing: Oops: Fatal exception in interrupt
[   28.647860] ---[ end Kernel panic ]---

逐字段看:

  • pc : ice_process_skb_fields+0x144/0x1ec [ice] —— 崩在 Intel E810 网卡驱动的收包函数里;
  • 调用栈是 ice_clean_rx_irq → ice_process_skb_fields,且发生在 el1_irq(中断上下文):CPU 在 idle 时被网卡收包中断切入;
  • Unable to handle kernel paging request at virtual address ffff75db76a6c000pgd=0000000000000000ESR 0x96000004 —— 解引用了一个根本没有页表映射的野地址(level-0 页表转换失败);
  • Oops 之后立刻 Kernel panic - not syncing: Oops: Fatal exception in interrupt,系统彻底停住。

四、第二台机器:同一颗雷

机器 B 的崩溃点完全一致,这是判断「共性问题」的最硬证据:

维度机器 A机器 B
机型KunLun 2280 / 鲲鹏 920KunLun 2280 / 鲲鹏 920
内核5.10 LTS aarch645.10 LTS aarch64
故障函数ice_process_skb_fields+0x144ice_process_skb_fields+0x144
故障指令偏移f8626803f8626803(一字不差)
调用栈ice_clean_rx_irq → ice_process_skb_fields相同
ESR / pgd0x96000004 / 0相同
崩溃时机链路 UP 后 ~28s链路 UP 后 ~360s(约 6 分钟)
崩溃后panic 冻结(boot hang)kdump 抓 vmcore 后重启

几点判断:

  1. 同一份二进制里同一处代码崩了(故障指令偏移 f8626803 完全相同),直接排除单机偶发的比特翻转,是这批「鲲鹏 + E810-C」组合的共性 bug
  2. 机器 B 跑了约 6 分钟才崩,说明更像是流量/负载累积触发——RX 处理了一定量的报文、或某个 ring/descriptor 走到特定分支后才拿到野指针,而不是「链路一 UP 就崩」。这对复现和定位很有价值。
  3. 机器 B 还暴露了 enp3s0f1: Module is not present,印证这是 E810-C2 双口卡,只插了 port0 的光模块。
  4. 日志里的 NMIrcu 字样只是启动期的看门狗注册和 RCU 初始化,不是 NMI 风暴也不是 RCU stall,可排除另一类 C-State 相关问题。

五、根因假设

最可能的根因:ice 驱动在 ARM64 SMMU/IOMMU 环境下,RX descriptor / DMA 地址映射存在缺陷,导致收包路径(ice_process_skb_fields)拿到一个未映射的野指针并解引用。

为什么偏 SMMU/IOMMU:故障地址 pgd=0 说明该虚拟地址根本没建立页表映射;而网卡 DMA 收包依赖 IOMMU 把 DMA 地址翻译成内核虚拟地址,一旦映射未建立、或映射被提前回收/复用,驱动就会拿到野地址。x86 平台默认很多走 iommu.passthrough 或 bounce buffer 路径不一样,这类问题在 ARM64 + SMMU 上更容易暴露。

六、临时止血(已验证)

在 GRUB 内核命令行加:

modprobe.blacklist=ice

机器 A 实测可正常启动。业务侧若暂时不依赖那张 100G 卡,先 blacklist 把系统拉起来,再决定根治方案。

七、根治方向

  1. 升级 ice 驱动 + E810 固件到对 ARM64/Kunpeng 兼容的版本,重点看 RX descriptor / DMA 映射相关的修复(上游 intel/ice 近几年在 ARM64 + IOMMU 上有多笔相关补丁)。
  2. 验证 iommu.passthrough=1 是否能稳定绕过——仅作验证用,长期需权衡 DMA 隔离带来的安全收益。
  3. 分析机器 B 的 vmcore(它已经抓到了):用 crash 工具打开,能精确定位 ice_process_skb_fields+0x144 处访问的是哪个 skb/ring 字段、那个野地址 x2 是怎么来的,比串口日志更直指代码行。

八、小结 / 排障经验

  • 启动卡死先看串口日志尾部最后几行,boot hang 的真相几乎都在那。
  • 同类机型批量出问题,第一时间对比"故障指令偏移 + 调用栈 + ESR",能秒判是不是同一个共性 bug。
  • kdump 抓 vmcore 是深挖驱动 Oops 的黄金材料,建议类似场景默认开启,串口日志只能定位到"崩在哪一层",vmcore 才能定位到"具体哪一行、哪个结构体字段"。
  • 网卡类崩溃,BMC 的 PCIe 设备清单(card_manage)里通常直接有 Vendor/Device ID,是确认硬件型号的硬证据,比从驱动日志反推更准。