简介:本资源是Mellanox网卡适配器第六版程序员参考手册(PRM),面向RDMA高性能网络开发工程师、内核驱动开发者及DPDK/SPDK底层协议栈研发人员,用于深入理解Mellanox智能网卡的Flow Table硬件编程模型与寄存器级控制逻辑。手册详细定义了流表属性字段(如log_max_flow)、支持的匹配字段位图(outer/inner各层L2-L4及隧道协议字段,含VXLAN/Geneve/GRE等)、字段掩码配置规则及元数据寄存器(metadata_reg_a/b)使用规范,是实现自定义流量分类、硬件卸载策略与高级转发功能的核心依据。资源为单个PDF文件,大小9.28MB,内容完整覆盖命令参考章节中Flow Table相关寄存器布局、字段描述及格式说明(如Table 1249–1251),结构严谨、术语精准,适合需要对接Mellanox硬件进行深度性能调优或定制化开发的技术人员。目前已有39人学习下载。
1. 这份 Mellanox Adapters PRM 第 6 版不是“说明书”,而是你调通 RDMA 流控、e-switch 转发和 Flow Table 精确匹配的底层操作地图
很多工程师拿到 Mellanox 网卡(如 ConnectX-5/6/7)后,第一反应是查驱动文档或 DPDK 示例——但当你要绕过内核协议栈做硬件级流分类、在 e-switch 模式下配置多级 Flow Table、或者调试 SR-IOV VF 的 TCAM 条目命中失败时,Linux driver source 或 libibverbs 的 API 文档就突然失语了。PRM(Programmer’s Reference Manual)第 6 版正是填补这个断层的关键:它不讲怎么装驱动,而是逐寄存器定义 PCIe 配置空间、逐 bit 解释 Flow Table Entry 的match_id字段如何与outer_vlan和inner_vlan双标签联动、明确 e-switch 的ingress_table和egress_table在硬件 pipeline 中的实际触发顺序。它面向的是需要把 RDMA QP 绑定到特定硬件队列、用mlx5dv直接下发 flow rule、或在用户态实现自定义 offload 路径的开发者。如果你正卡在EOP(End of Packet)标志未触发、steering_sw_owner寄存器读值异常、或flow_table_modify返回EINVAL却找不到原因,这份 PRM 就是你必须打开的硬件真相手册。
2. 从 PRM 第 6 版定位 Flow Table 架构:为什么你的 flow rule 总是 miss,而不是 hit?
PRM v6 最核心的演进之一,是将 Flow Table 从单一 flat table 拆解为分层、可嵌套、带 steering domain 的硬件结构。这直接决定了你在用户态调用mlx5dv_create_flow_table()时传入的type、level和log_size参数是否合法,也解释了为何mlx5_flow_table_create失败却只报ENOMEM—— 实际可能是level超出硬件支持范围,而非内存不足。
2.1 Flow Table 的三级物理视图:Root → Namespace → Leaf
PRM v6 明确将 Flow Table 划分为三个逻辑层级,对应硬件中不同的内存映射区域和访问权限:
- Root Table:固定位于 e-switch 的 ingress/egress pipeline 起始点,仅支持
MATCH_HEADER类型的粗粒度匹配(如ethertype、ip_version),不可动态创建,由 firmware 初始化。 - Namespace Table:用户可创建的中间层,用于隔离不同租户或功能域(如 Kubernetes CNI 插件各自独占一个 namespace)。PRM v6 规定其
level必须为1,且log_size不能超过12(即最多 4096 条目)。 - Leaf Table:实际承载 match-action 规则的叶子节点,
level≥2,支持完整匹配字段(包括tcp_flags、vxlan_vni、geneve_oam等),且可启用modify_headeraction。
提示:
level不是“优先级”,而是硬件 pipeline 中的嵌套深度。level=0是 root,level=1是 namespace,level=2才是真正能写规则的 leaf table。若误设level=0创建用户表,ibv_create_flow()会静默失败。
2.1.1 查证硬件支持的 level 与 size 限制
PRM v6 第 12.3.2 节("Flow Table Attributes")给出关键约束。以 ConnectX-6 Dx 为例,其最大level为3,但level=3表仅支持log_size ≤ 8(256 条目),且必须挂载在level=2表之下。验证方式不是查 Linux sysfs,而是直接读取设备 capability 寄存器:
# 读取 Flow Table capability 寄存器(地址偏移 0x100400) sudo setpci -s 0000:04:00.0 100400.L # 输出示例:00000003 → bit[0:3] = 0b0011 = max_level = 3 # 同时需读 0x100404 获取 log_size 支持位图 sudo setpci -s 0000:04:00.0 100404.L # 输出示例:00000f00 → bit[8:11] = 0b1111,表示 log_size 8~11 均支持该命令读取的是MLX5_CAP_FLOW_TABLE寄存器组,其定义在 PRM v6 第 10.2.1 节。setpci结果需按 PRM 表格 10-2 解码,而非直接当作十进制理解。
2.2 Flow Table Entry 的 match_id 字段:双 VLAN 标签匹配的硬件真相
PRM v6 第 13.4.1 节彻底重构了match_id的编码逻辑。旧版(v5)中outer_vlan和inner_vlan是独立字段;v6 引入match_id作为 16-bit 索引,指向一个预定义的 match template,该 template 决定了哪些 header 字段参与比较及比较方式(exact / prefix / range)。
例如,要匹配外层 VLAN ID=100 且内层 VLAN ID=200 的双标签帧,不能简单设置outer_vlan=100, inner_vlan=200,而必须:
- 在 firmware 中预加载一个 match template,声明
outer_vlan和inner_vlan均为exact匹配; - 通过
mlx5_cmd_create_match_def()获取该 template 的match_id(如0x0a); - 在 flow table entry 中填入
match_id = 0x0a,再填入具体值outer_vlan = 100,inner_vlan = 200。
// 伪代码:创建双 VLAN match template struct mlx5_ifc_match_def_in_bits in = {0}; in.match_id = cpu_to_be16(0x0a); // 指定 template ID in.outer_vlan = 1; // 启用 outer_vlan 字段 in.inner_vlan = 1; // 启用 inner_vlan 字段 in.match_criteria.outer_vlan = cpu_to_be16(0); // exact match in.match_criteria.inner_vlan = cpu_to_be16(0); mlx5_cmd_create_match_def(dev, &in);注意:
match_id是 firmware 分配的逻辑 ID,不是用户自定义编号。未预加载 template 就直接使用match_id,会导致 flow rule 创建时errno = EINVAL,且 dmesg 无明确提示。
2.2.1 验证 match_id 是否生效:读取 hardware steering cache
PRM v6 第 14.5.3 节说明,flow rule 下发后,硬件会将其编译为 TCAM 条目并缓存。可通过 debugfs 查看实际加载的 match template:
# 查看当前所有 match template cat /sys/kernel/debug/mlx5/0000:04:00.0/steering/match_def # 输出示例: # match_id: 0x0a, ref_count: 1, outer_vlan: 1, inner_vlan: 1 # match_id: 0x0b, ref_count: 0, ip_version: 1, tcp_dport: 1若ref_count为 0,说明该 template 未被任何 flow rule 引用,可能因 template 创建失败或 rule 未正确关联。
3. e-switch 模式下的 Flow Table Pipeline:Ingress vs Egress,谁先执行?
PRM v6 第 15 章首次以 pipeline diagram 形式公开 e-switch 的完整数据路径。这直接关系到你能否在 VF 上实现 hairpin 转发、或让 host 网络命名空间流量经 e-switch 二次 steering。关键结论是:ingress table 并非只处理“进入网卡”的包,egress table 也并非只处理“离开网卡”的包——它们的触发取决于 packet 的 logical source 和 destination port。
3.1 e-switch 的四类 steering domain:Port-based 与 VF-based 的本质区别
PRM v6 表 15-1 明确定义了 e-switch 的 steering domain 类型:
| Domain Type | Trigger Condition | 典型用途 |
|---|---|---|
INGRESS_PORT | packet arrives at physical port (PF/VF) | 外部流量入口过滤 |
EGRESS_PORT | packet destined to physical port (PF/VF) | 外部流量出口整形 |
INGRESS_VF | packet arrives at specific VF (via vport) | VF 间隔离 |
EGRESS_VF | packet sent from specific VF (via vport) | VF 出口策略 |
重点在于:INGRESS_VFdomain 的 flow table,对从同一 PF 下其他 VF 发来的包同样生效。这意味着,若你为 VF1 创建了INGRESS_VFrule 匹配src_mac,那么 VF2 发给 VF1 的包也会被该 rule 匹配——这是实现 VF 间防火墙的基础,但也是常见误配置根源。
3.1.1 查询当前 e-switch domain 状态
PRM v6 第 15.2.2 节指出,domain 状态由QUERY_ESW_FUNCTIONS命令返回。Linux 用户态可通过ibstat或直接读取 sysfs 获取:
# 查看 e-switch 当前模式(是否启用、mode) cat /sys/class/net/ens1f0/device/mlx5/roce_port # 输出:1 → 表示 RoCE port 已启用,e-switch active # 更详细信息需用 mlxconfig sudo mlxconfig -d /dev/mst/mt4115_pciconf0 q | grep -i "eswitch" # 关键字段:ENABLED_E_SWITCH = True, ESWITCH_MANAGER = Truemlxconfig输出中的ESWITCH_MANAGER表明 firmware 正在管理 e-switch 功能,此时INGRESS_PORT和EGRESS_PORTdomain 才可用。
3.2 Ingress Table 的真实执行时机:早于 checksum offload
PRM v6 第 15.4.1 节明确:INGRESS_PORTtable 的匹配发生在 L2 header 解析后、L3/L4 checksum 验证前。这意味着,如果你的 flow rule 基于ip_checksum_ok字段做动作,该字段在此时尚未计算,永远为 0。正确做法是匹配ip_protocol+ip_version,然后用modify_headeraction 添加自定义 metadata,再由后续软件栈读取。
验证此行为的最简方法是抓包对比:
# 在 host 上启动 tcpdump,捕获 PF 接口 sudo tcpdump -i ens1f0 -w ingress_before_offload.pcap -c 100 # 同时在 VF 中发送已知 checksum 错误的 IP 包(如用 scapy 构造) # 观察 pcap:ingress table rule 若基于 checksum 匹配,将全部 miss # 但若基于 ethertype=0x0800,则 100% hit该实验直接印证 PRM v6 的 pipeline 描述:硬件不会为你等待 checksum 计算完成才做 steering。
4. Mellanox 网卡 DPDK 测试中 PRM v6 的关键参数映射:从 rte_flow 到硬件寄存器
当你运行testpmd并执行flow create命令时,DPDK 的rte_flow层最终会调用mlx5_flow_os_create_flow(),将抽象 rule 编译为 PRM v6 定义的硬件指令。理解这一映射,是调试rte_flow_create()返回ENOTSUP的唯一途径。
4.1 rte_flow_item_eth 的has_vlan字段如何触发 PRM v6 的 double vlan path
DPDKrte_flow_item_eth结构体中的has_vlan字段,并不直接对应 PRM v6 的outer_vlan字段。PRM v6 要求:双 VLAN 必须显式声明match_id,且rte_flow_item_vlan必须出现两次(一次 for outer,一次 for inner):
// 正确:双 VLAN 匹配(PRM v6 要求) struct rte_flow_item_vlan outer_vlan = { .tci = RTE_BE16(0x100), // outer vlan id = 100 }; struct rte_flow_item_vlan inner_vlan = { .tci = RTE_BE16(0x200), // inner vlan id = 200 }; struct rte_flow_item pattern[] = { { .type = RTE_FLOW_ITEM_TYPE_ETH }, { .type = RTE_FLOW_ITEM_TYPE_VLAN, .spec = &outer_vlan }, { .type = RTE_FLOW_ITEM_TYPE_VLAN, .spec = &inner_vlan }, { .type = RTE_FLOW_ITEM_TYPE_END }, };若只写一个RTE_FLOW_ITEM_TYPE_VLAN,DPDK driver 会尝试用match_id=0x01(single vlan template),但 firmware 检测到 packet 有双标签,导致 rule 编译失败,rte_flow_create()返回ENOTSUP。
4.1.1 查看 DPDK 编译后的硬件 rule
PRM v6 第 16.2.4 节提供QUERY_FLOW_TABLE_ENTRY命令。DPDK 本身不暴露此接口,但可通过 debugfs 查看:
# 查看 testpmd 创建的 flow table 条目(需 testpmd 以 --debug 开头) cat /sys/kernel/debug/mlx5/0000:04:00.0/steering/flow_table/0x12345678/entry_0 # 输出包含: # match_id: 0x0a # outer_vlan: 0x0064 (100) # inner_vlan: 0x00c8 (200) # action: fwd_to_vport=0x0001此处0x12345678是 flow table 的 hardware ID,可在 testpmd 日志中找到Flow table 0x12345678 created。
4.2 rte_flow_action_queue 的 queue_index 如何映射到 PRM v6 的dest_qp字段
rte_flow_action_queue中的index并非直接写入硬件dest_qp寄存器。PRM v6 第 17.3.1 节规定:dest_qp是一个 24-bit 字段,其低 16-bit 为 QP number,高 8-bit 为 port number。DPDK driver 会自动将queue_index转换为dest_qp,但前提是该 queue 必须属于同一 port 且已激活。
验证方法:创建 rule 后,检查 QP 状态:
# 查看 QP 0x1234 的状态(QP number 0x1234) ibstat -p | grep -A 5 "0x1234" # 关键字段:State: RTS, Port: 1, GID Index: 0 # 若 Port != 1,而 rule 中 queue_index 对应 port 2,则 dest_qp 高 8-bit 错误,rule 将 drop packet若rte_flow_create()成功但 packet 无响应,首要检查目标 QP 的Port字段是否与 rule 中指定的 port 一致。
5. PRM v6 中最易被忽略的三个寄存器:定位 flow miss 的终极手段
当rte_flow_create()成功、ib_send_wr()无报错、但 packet 却未命中任何 flow rule 时,PRM v6 的以下三个寄存器是唯一真相来源。它们不记录在 dmesg,也不出现在ethtool -S,必须用setpci或 firmware tool 直接读取。
5.1STEERING_SW_OWNER寄存器(偏移 0x100410):确认 firmware 是否接管 steering
该寄存器 bit 0 为steering_sw_owner。PRM v6 第 10.2.3 节说明:若为0,表示 steering 由 firmware 控制(e-switch 模式);若为1,表示由 host software(如 rdma-core)控制。若你启用了 e-switch 但该 bit 为 1,则所有 ingress flow table rule 均无效。
# 读取 steering owner 状态 sudo setpci -s 0000:04:00.0 100410.B # 输出 01 → bit 0 = 1 → firmware 未接管,需检查 mlxconfig 设置 # 正确值应为 00修复方法:sudo mlxconfig -d /dev/mst/mt4115_pciconf0 set ENABLED_E_SWITCH=True
5.2FLOW_TABLE_MISS_COUNTER寄存器(偏移 0x100420):量化 miss rate
PRM v6 第 14.6.2 节定义此寄存器为 64-bit 计数器,累计所有 flow table lookup miss。它不区分 root/namespace/leaf table,是全局 miss 指标。
# 读取 miss counter(需读取两个 32-bit 寄存器) sudo setpci -s 0000:04:00.0 100420.L # 低 32-bit sudo setpci -s 0000:04:00.0 100424.L # 高 32-bit # 若低 32-bit 在 1 秒内增长 > 1000,说明 rule 编译或匹配逻辑存在根本问题提示:该计数器无法清零,只能靠差值判断。建议在创建 rule 前读一次基线,10 秒后再读,差值即为期间 miss 数。
5.3TCAM_HIT_STATUS寄存器(偏移 0x100430):定位具体哪一级 table miss
PRM v6 第 14.7.1 节说明,此寄存器 bit[3:0] 表示最近一次 lookup 在哪一级 table hit:0b0001=root,0b0010=namespace,0b0100=leaf。若为0b0000,表示全程 miss。
# 读取最近一次 lookup 的 hit status sudo setpci -s 0000:04:00.0 100430.B # 输出 04 → bit[2]=1 → hit 在 leaf table # 输出 00 → 全程 miss,需检查 match_id template 是否加载、packet header 是否符合 rule该寄存器是瞬时值,每次 lookup 后覆盖。因此需在复现 miss 场景时实时读取,而非事后查看。
| 寄存器名 | 偏移地址 | 关键 bit | 诊断价值 |
|---|---|---|---|
STEERING_SW_OWNER | 0x100410 | bit 0 | 确认 e-switch steering 是否启用 |
FLOW_TABLE_MISS_COUNTER | 0x100420/0x100424 | all | 量化 miss 严重程度 |
TCAM_HIT_STATUS | 0x100430 | bit[3:0] | 定位 miss 发生在 pipeline 哪一层 |
这些寄存器的值,比任何用户态日志都更接近硬件真相。当你反复修改rte_flowpattern 却始终 miss,停下敲键盘,先读这三个地址。
本文还有配套的精品资源,点击获取