说明

在 ROCK 4D 上接入 Radxa Dual 2.5G Router HAT、ASM1182E 扩展板、两块 RTL8125、MT7922 和 NVMe 后,系统冷启动可以正常工作。但只要启用 MT7922 的 5 GHz AP,运行一段时间后 LAN 会出现严重丢包,SSH 逐渐卡顿,最后近似失联。重启后网络暂时恢复,重新开启无线发射后又会复现。

最初怀疑过 mt76、hostapd 和 MT7922 固件,但实际故障点不在无线驱动本身。完整 PCIe 拓扑耗尽了 RK3576 主机仅有的 32 个 MSI vector,导致两块 RTL8125 和 MT7922 都退回 legacy INTx。AP 开始发射后,中断负载增加,暴露了级联 Switch 下 INTx 路径的问题,最终使 r8169 的发送完成处理中断无法及时到达。

本文使用的修复保存在我的 GitHub 仓库 ovo-ukiyo/openwrtrock-4d-aic8800-router-hat 分支中。MSI 修改、mt76 回移植和阶段性验证记录均拆成独立 commit,本文重点说明 MSI 资源耗尽这一条主线。

故障现象

设备刚启动时,网页、SSH 和有线转发都正常。启用 MT7922 AP 后,网络延迟开始升高,随后出现大量丢包。故障后可以看到类似日志:

1
r8169 0000:0b:00.0 eth1: NETDEV WATCHDOG: transmit queue 0 timed out

这条日志说明 r8169 的发送队列长时间没有完成,并不直接说明 RTL8125 芯片损坏。检查 MT7922 日志时,也没有在故障点出现对应的 MCU timeout。

两个现象对定位很重要:

  • MT7922 不启用 AP 时,系统可以长时间保持可用;
  • AP 开始工作后,最终报错的是有线网卡发送队列。

这更像是多个 PCIe 设备之间共享的底层资源出了问题,而不是单个无线配置错误。

先排除 hostapd 和常见 mt76 问题

排查期间先将精简的 wpad 组件换成完整 hostapd:

1
apk add hostapd-any

系统实际安装的是 2025.08.26 版本的 hostapd 包。随后还单独回移植了几项 mt76 PCIe AER、复位和锁处理修复。这些修改能够减少驱动已知问题,但都没有改变本次故障模式。AP 启动后,LAN 仍会在相近条件下失效。

这部分尝试也保留在独立的 mt76 稳定性回移植 commit:c64075d 中。它与后面的 PCIe MSI 补丁分开,原因是两者解决的层次不同:mt76 补丁处理无线驱动自身的错误恢复和并发问题,MSI 补丁处理上游 PCIe Host 的中断资源分配。

这一步说明继续调整 hostapd 参数或反复重载 MT7922 只能绕开触发时机,不能处理根因。

检查网卡使用的中断

两块 RTL8125 的 PCIe 路径分别为:

1
2
3
4
5
/sys/devices/platform/soc/22000000.pcie/pci0000:00/0000:00:00.0/
0000:01:00.0/0000:02:06.0/0000:0b:00.0

/sys/devices/platform/soc/22000000.pcie/pci0000:00/0000:00:00.0/
0000:01:00.0/0000:02:0e.0/0000:0c:00.0

两块网卡都由 r8169 驱动:

1
2
3
4
5
0b:00.0 Ethernet controller [10ec:8125]
Kernel driver in use: r8169

0c:00.0 Ethernet controller [10ec:8125]
Kernel driver in use: r8169

/proc/interrupts 中只有一条共享中断:

1
34:  ...  INTx  2 Edge  eth1, eth2

进一步查看 PCI capability,可以看到 RTL8125 支持 MSI-X,MT7922 也支持 MSI,但修复前都处于 Enable-。MT7922 使用另一条 legacy INTx。也就是说,这些设备具备消息中断能力,却没有得到可用的 MSI vector。

32 个 MSI vector 如何被耗尽

RK3576 使用 DesignWare PCIe Host,这个控制器在当前平台上提供 32 个 MSI vector。完整链路中有 ASM2806 和 ASM1182E 两级 ASMedia Switch,以及 NVMe、无线和两块有线网卡。

修复前的分配大致为:

1
2
3
4
5
6
7
RK3576 PCIe MSI domain:共 32 个 vector

Root Port 服务: 16 个
8 个 ASMedia downstream port: 8 个
NVMe 队列: 8 个
--------------------------------------
剩余: 0 个

Root Port 的服务消息布局要求一个按 16 对齐的 MSI block。随后,ASM2806 和 ASM1182E 的八个下游端口各自为 PCIe bandwidth notification 服务申请一个 MSI。当前系统并没有实际使用该带宽通知服务,但这些 port service device 仍占用了 vector。最后 NVMe 获得余下的八个。

RTL8125 和 MT7922 的驱动加载得更晚,此时 MSI domain 已经没有空闲项,只能退回 legacy INTx。

因此,MT7922 并不是“占用了太多 MSI”。正好相反,发生故障时它根本没有分配到 MSI。无线 AP 只是触发条件:Beacon、管理帧和 DMA completion 带来持续中断负载,使级联 PCIe Switch 后的 legacy INTx 路径变得繁忙,丢失或延迟的有线网卡完成中断最终让 r8169 TX ring 停住。

这也解释了系统刚开机时看起来正常的原因。无线未发射时,中断压力较低,INTx 路径的问题不容易显现;AP 正常工作一段时间后才会逐渐卡顿。

保留 MSI vector

在这套拓扑中,ASMedia 下游端口申请的 bandwidth notification service 没有驱动使用。处理方式是在 drivers/pci/pcie/portdrv.c 中,不为 ASMedia downstream port 创建该服务,同时保留 Root Port 原有处理:

1
2
3
4
5
-if (pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM ||
- pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {
+if ((pci_pcie_type(dev) == PCI_EXP_TYPE_DOWNSTREAM &&
+ dev->vendor != PCI_VENDOR_ID_ASMEDIA) ||
+ pci_pcie_type(dev) == PCI_EXP_TYPE_ROOT_PORT) {

OpenWrt 中的补丁路径为:

1
target/linux/rockchip/patches-6.12/999-pcie-asmedia-preserve-msi-vectors.patch

完整修改可以直接查看:

Linux 的 PCIe port driver 会根据每个桥端口声明的 capability 创建 PME、AER、DPC、hotplug 和 bandwidth notification 等服务设备。服务设备需要通过中断接收端口事件。在本机的 ASMedia 下游端口上,bandwidth notification capability 虽然存在,却没有对应的实际服务消费者,仍然会提前占用一个稀缺的 MSI vector。补丁只阻止这类无消费者的服务设备被创建,让 vector 留给网卡和无线设备。

这项修改只跳过 ASMedia 下游端口的带宽通知 service device,不关闭整个 PCIe port service,也没有改动 PCIe 枚举、bus number 分配、链路训练和之前配置的 FPC POWER_EN。Root Port 的 PME 和 AER 服务仍然保留。

八个下游端口不再各占一个 vector 后,正好释放出八个 MSI vector,足够关键网络设备使用。

冷启动后的中断分配

重新编译内核并彻底冷启动后,中断分配变为:

1
2
3
RTL8125 eth1  PCI-MSI/MSI-X IRQ 81
RTL8125 eth2 PCI-MSI/MSI-X IRQ 82
MT7922 PCI-MSI IRQ 83

通过下面的命令可以检查设备 capability 和实际中断:

1
2
3
4
lspci -vv -s 0b:00.0
lspci -vv -s 0c:00.0
lspci -vv -s <MT7922的BDF>
cat /proc/interrupts | grep -Ei 'PCI-MSI|mt792|eth1|eth2|r8169'

两块 RTL8125 的 MSI-X 变为 Enable+,MT7922 的 MSI 也变为 Enable+,不再出现两个有线接口共享一条 edge-triggered INTx 的情况。

验证结果

MT7922 配置为信道 36、HE80 的 5 GHz AP。验证分为两次:第一次手动启动 AP,第二次正常重启并让 AP 自动启动。

每次启动后都确认:

  • phy*-ap0 处于 UP,LOWER_UP
  • 无线接口有实际发送流量;
  • MT7922 使用 PCI-MSI;
  • 两块 RTL8125 使用 MSI-X;
  • 两组独立的 150 次 LAN ping 均为 0% 丢包;
  • 最大延迟约为 1 至 2 ms;
  • 超过原先约 140 秒的典型故障时间后,没有出现 NETDEV WATCHDOG、TX queue timeout、mt76 reset 或 MCU timeout。

排查期间为了保持路由器可访问,曾增加 MT7922 延迟启动和重新绑定脚本。确认 MSI 分配修复后,这些脚本已经从固件和设备中删除:

1
2
/etc/init.d/mt7922-rebind
/usr/sbin/mt7922-delayed-start

这些脚本会在启动阶段主动解绑无线设备,只改变故障出现的时间。保留它们反而会增加启动流程的不确定性。源码中的清理记录见 移除 MT7922 延迟重绑方案的 commit:ae09b20

小结

本次网络卡死的根因是 PCIe MSI 资源耗尽,不是 MT7922 单独失控,也不是 hostapd 配置错误。MT7922 AP 开始发射后增加了中断压力,使 RTL8125 和无线设备退回 legacy INTx 所带来的问题稳定复现。

排查这类问题时,除了查看报错设备的驱动日志,还应检查所有 PCIe Endpoint 的 MSI/MSI-X 是否实际启用,并结合完整拓扑统计上游控制器的 vector 数量。设备显示 MSI-X: Enable- 不一定是驱动主动禁用了 MSI,也可能是上游 MSI domain 已经被先探测的桥和设备耗尽。

当前补丁已经解决这套硬件上的确定性故障,但它仍是针对 ASMedia Switch 行为的策略修改。正式提交 Linux 或 OpenWrt 上游前,需要进行更长时间的无线客户端吞吐和多网口压力测试,并由 PCI 子系统维护者确认跳过 bandwidth notification service 的适用范围。