PE31625G24DIRA 折腾日记-00

learn network linux

最近买了一块 Silicom 的 PE31625G24DIRA,一块基于 Intel EZFM10840 交换芯片的 100G 六端口交换卡,或者说是一张 switch on NIC。可能有大船二手价格很便宜,但折腾起来挺麻烦的。这篇文章就是把这段时间的发现都记下来,想到哪写到哪。


先说说这块卡是什么

PE31625G24DIRA 不是普通的网卡,它更像是一块完整的交换机。核心是 Intel EZFM10840 FocalPoint 交换芯片,支持 100G/40G/25G/10G/1G 多速率,有 6 个外部端口(两个 MPO24),算上 PCIE 金手指,一共八个可用端口。

最有意思的地方是:板子上集成了一块 Atom E3826作为控制平面。这个 SoM 运行完整的 Linux 系统,FM10K 通过 PCIe 连接到它。所有的 SDK 操作、配置下发、状态查询都在这个 SoM 上完成,外部主机只是通过网口或管理口与它通信。

所以这块卡的架构大概是:

外部主机
    │  (网络流量)
    ▼
6 x 100G 外部端口 + 2 x PCIE3.0x8
    │
    ▼
fm10k 交换芯片  ←──PCIe──→  Atom E3826 SoM (控制平面, Linux)

SSH 进 SoM 之后,lspci 能看到 FM10K 的设备 ID 0x15A4(Intel 厂商 ID 0x8086),内核已经加载了 fm10k 驱动。


IES SDK:控制这块芯片的软件栈

要真正控制 FM10K 的交换功能,光有内核驱动不够,需要 Intel 的 IES SDK(Intel Ethernet Switch SDK)。在 SoM 的文件系统里找到了 IES SDK 4.3.2,及其源码,预编译二进制里面有两个核心库:

3338 个函数这个数字挺庞大的。我后来做了个交叉引用,把这些导出符号和头文件声明对应起来,大概分布是:

分类 数量
内部 API(internal-api 1752
公开 API(public-api 663
平台相关(platform 339
调试接口(debug 155
ALOS 抽象层(alos 113
未匹配声明 203

公开 API 只有 663 个,但整个库导出了 3338 个符号——大量内部函数也被导出了,这在闭源 SDK 里挺常见的,方便调试工具直接调用内部函数。


构建系统:比想象中复杂

IES SDK 的构建系统是基于 GNU Make 的,入口在 ies/src/Makefile,需要在 ies/src/ 目录下执行 make

构建系统支持多个平台:

平台 说明
libertyTrail 主参考平台,x86_64/i386,FM10K
atwoodChannel 基于 libertyTrail,单交换机
boulderRapids 基于 libertyTrail,单交换机
cathedralGlen 基于 libertyTrail,支持多交换机 SWAG
cliftonGorge 基于 libertyTrail,2~3 交换机
rubyRapids 基于 libertyTrail,单交换机

PE31625G24DIRA 对应的是 libertyTrail 平台。

有一个坑值得记录:make install 的依赖链里不包含 basic 示例程序和 rdifd 守护进程。rdifd 被定义为 EXAMPLES_SIL(Silicom 专用示例,而且 rdif 的其他工具也没有包含),默认 make 会构建它,但 make installmake install-examples 都不会安装它,需要手动复制:

cp -f ../examples/rdifd/rdifd ${INSTALL_DIR}/examples/

另外,构建前必须确认内核支持 UIO:

grep CONFIG_UIO /boot/config-$(uname -r)
# CONFIG_UIO=y 或 CONFIG_UIO=m 都行
# 如果是 m,需要 modprobe uio && modprobe fm10k

配置文件:SDK 的"地图"

SDK 启动时会读取一个平台属性配置文件(fm_platform_attributes.cfg),这个文件定义了端口映射、SerDes 参数、I2C 外设等所有硬件相关的配置。

配置文件里有几个关键参数:

numSwitches = 1          # 系统中只有 1 个 FM10K
platformName = libertyTrail
uioDevName = /dev/uio0   # UIO 设备节点,用于 PCIe BAR4 mmap
msiEnabled = true        # 启用 MSI 中断
netDevName = enp1s0      # Raw socket 收发包用的网卡名
cpuPort = 9              # CPU 逻辑端口号

端口拓扑映射也在这里定义。FM10K 内部有 EPL(以太网端口逻辑)、PCIE(PCIe Endpoint)、TE(隧道引擎)三类接口,配置文件把逻辑端口号映射到物理接口:

逻辑端口  物理接口          用途
LOG=0     PCIE=8           CPU 端口 (PEP#8)
LOG=1     EPL=0 LANE=0-3   QSFP0 (100G)
LOG=2     EPL=1 LANE=0-3   QSFP1
...
LOG=7     PCIE=0           PCIe 端口 0

注意 EPL 编号不连续(0,1,2,5,6,7),跳过了 EPL 3 和 4,说明板卡物理布线跳过了这两个 SerDes 组。PCIE 4 应该是 SOM 所连接的那个 PCIE。


硬件访问:UIO + mmap,不是 ioctl

这是我觉得最有意思的发现。

通常想到"用户态程序控制硬件",第一反应是通过内核驱动的 ioctl 接口。但 IES SDK 的主要路径完全不是这样——它通过 UIO(Userspace I/O)驱动,把 FM10K 的 PCIe BAR4 寄存器空间直接 mmap 到用户态进程的地址空间,然后像读写普通内存一样操作硬件寄存器,整个 SDK 完全运行在用户态。

整条链路:

SDK API 层(fmSetPortAttribute 等)
        │
        ▼
寄存器抽象层(ReadUINT32 / WriteUINT32)
        │
        ▼
UIO 内存映射(/dev/uio0 → mmap BAR4 64MB)
        │
        ▼
FM10K 硬件寄存器(PCIe BAR4)

mmap 完成后,switchMem 指针就指向了 FM10K 的寄存器空间,之后的读写是这样的:

// 读寄存器——直接内存读,没有系统调用
*value = GET_PLAT_MEMMAP_CSR(sw)[addr];

// 写寄存器——直接内存写,没有系统调用
GET_PLAT_MEMMAP_CSR(sw)[addr] = value;

GET_PLAT_MEMMAP_CSR(sw) 展开后就是 (volatile fm_uint32 *)switchMem,对 mmap 地址的直接 32 位读写。

映射地址被固定在 0x08000000,这是为了保证多进程场景下指针的一致性。

中断处理也走 UIO:对 UIO fd 调用 select() 等待中断,read() 读取中断计数(同时重新使能中断),write(1/0) 使能或禁止中断。SDK 里有专门的中断监听线程处理这个。


RDIF 工具链:Silicom 的控制平面实现(并不是很有用,代码还存在 fall through bug)

在 SDK 的示例程序里,有一个叫 rdifd 的守护进程,版本号 6.0.14i,4339 行 C 代码。这不是一个简单的示例,而是 Silicom 实际用于生产的控制平面守护进程。

rdifd 的架构是这样的:

外部控制程序 (rdifctl / 网管软件)
        │
        │  Unix socket: /var/run/rdifctl.sock
        │  协议: if_rdi_t 结构体序列化
        ▼
    rdifd (用户空间守护进程)
        │
        │  IES SDK API
        │  fmAddACLRule / fmSetPortAttribute / fmGetPortCounters ...
        ▼
    libFocalpointSDK.so
        │
        │  UIO mmap → /dev/uio0 → PCIe BAR4
        ▼
    FM10K 硬件

任何需要控制 FM10K 的程序,只需要包含 rdi_lib.h,连接到 /var/run/rdifctl.sock,发送 if_rdi_t 结构体就能下发命令。

支持的命令覆盖面很广:

  • ACL 规则:下发 DROP/PERMIT/REDIRECT/MIRROR 规则,支持五元组、VLAN、MPLS、UDF 匹配
  • 端口管理:设置端口转发掩码、查询链路状态、设置环回
  • 负载均衡组(LBG):创建/删除 LBG,管理成员端口
  • 镜像组:创建/删除镜像,管理镜像端口
  • 哈希配置:配置 L2/L3 哈希键,影响 LAG/ECMP 负载均衡
  • 统计查询:读取端口 80+ 计数器、16 个优先级队列包计数
  • SerDes 诊断:动态调整预游标/主游标/后游标,设置 PRBS 测试
  • 硬件诊断:PHY 寄存器读写、CPLD 读写、GPIO 操作

rdifd 支持几种工作模式,通过 RDI_SET_CFG 命令切换:

模式 说明
RDI_CFG_BYPASS_ALL 旁路模式:端口两两直连,流量直通
RDI_CFG_INLINE 内联模式:端口 1-4 流量通过 ACL 重定向到端口 5
RDI_CFG_MON2 监控模式:所有端口隔离
RDI_CFG_INIT 初始化模式:启动时默认,同 MON2

ACL 规则下发有一个细节值得记录:所有规则都会自动附加 FM_ACL_ACTIONEXT_COUNT,支持后续查询命中次数。规则更新使用 FM_ACL_COMPILE_FLAG_NON_DISRUPTIVE,不中断现有流量。


光模块访问:I2C + PCA MUX

FM10K 的两个 FCI 光模块通过 I2C 访问,但因为端口数量多,需要 PCA9548 I2C 开关(MUX)来扩展 I2C 总线。板子上并不存在 PCA MUX,所有功能是由板载的 Lattice 的 FPGA 实现的。

访问流程大概是:

SDK → fmPlatformI2cWriteRead()
    → 选择 PCA9545 通道(对应目标光模块槽位)
    → 访问模块的 I2C 地址(0x50 页 0 / 0x51 页 1)
    → 读取光功率、温度、电压等 DOM 信息

光功率读取需要从模块的 DOM(Digital Optical Monitoring)寄存器里读原始 ADC 值,再按厂商规格换算成 dBm。这部分逻辑在 SDK 的平台代码里有实现,但需要配置文件里正确配置 PCA MUX 的地址和通道映射。


SAI 适配:让 SONiC 能控制这块卡

研究到后期,开始考虑一个更大的问题:能不能让这块卡跑 SONiC?

SONiC 通过 OCP SAI(Switch Abstraction Interface)标准接口控制交换芯片,而 IES SDK 有自己的一套 API。要让两者对接,需要写一个 SAI 适配层,把 SAI 的调用翻译成 IES SDK 的调用。

整体架构是:

SONiC (syncd)
    │ SAI API 调用
    ▼
IES-SAI 适配层(libsai.so)
    │ IES SDK API 调用
    ▼
libFocalpointSDK.so
    │
    ▼
FM10K 硬件

IES SDK 的 API 覆盖面很广,大部分 SAI 模块都能找到对应的实现:

SAI 模块 支持程度
Switch / Port / VLAN / FDB / LAG / STP ✅ 完全支持
Virtual Router / Route / NextHop / Neighbor ✅ 完全支持
ACL / Mirror / QoS / Policer ✅ 完全支持
Tunnel (VxLAN/NVGRE) / Multicast / sFlow ✅ 完全支持
MACsec / MPLS ❌ 硬件不支持

适配层有几个设计要点:

OID 管理:SAI 用 64-bit sai_object_id_t 标识对象,IES SDK 用整数 ID。适配层需要维护双向映射哈希表,OID 编码格式为 [对象类型 8bit][交换机索引 8bit][内部索引 48bit]

SerDes Lane 分配:SAI 的 create_port 通过 SAI_PORT_ATTR_HW_LANE_LIST 指定物理 lane 列表,适配层需要把 lane 数量和速率组合转换成 IES SDK 的 FM_PORT_ETHERNET_INTERFACE_MODE 枚举值(比如 4 lanes + 100G → FM_ETH_MODE_100GBASE_SR4)。

事件通知:IES SDK 的事件(MAC 学习、端口 link 变化、CPU 收包)通过事件线程异步通知,适配层需要把这些转换成 SAI notification callback。

这个适配层目前还在规划阶段,但 API 映射关系已经梳理清楚了,实现路径是明确的。


一些零散发现

关于 TestPoint:SDK 里有一个基于 Perl 的测试框架叫 TestPoint,通过 SWIG 生成的 Perl 绑定(SDK.soFM10000.so 等)直接调用 SDK 函数。这个框架主要用于芯片验证,但也可以用来做快速原型验证。可以快速确定你的交换机是不是完好的。

关于 SWAG:SDK 支持多交换机聚合(Switch Aggregation),通过 SUPPORT_SWAG=1 编译选项启用。支持 Fat-Tree 和 Mesh 拓扑,最多可以聚合多个 FM10K 芯片。PE31625G24DIRA 是单芯片,用不上这个特性,但了解一下也好。

关于端口速率协商:FM10K 的端口速率协商(Auto-Negotiation)实现在 SDK 内部,有一套完整的状态机。rdifd 里有 RDI_SET_PRECURSOR/RDI_SET_CURSOR/RDI_SET_POSTCURSOR 命令可以动态调整 SerDes 均衡参数,这在调试信号完整性问题时很有用。


总结

我也是第一次折腾类似的东西,折腾这块卡的收获比预期多很多。从硬件架构到 SDK 内部实现,从 UIO 的 mmap 机制到 RDIF 的控制协议,每一层都有值得深挖的细节。

后续打算继续研究 SAI 适配层的实现,以及看看能不能在这块卡上跑起来一个简化版的 SONiC。


硬件:Silicom PE31625G24DIRA(Intel FM10840 + Atom E3826 SoM)
软件:IES SDK 4.3.2,Linux,fm10k 内核驱动