对应路线图:Phase 3 (Day 92-119) | DPDK 24.11.6 LTS | 内核对照 v6.6
代码出处:code/sai-dpdk-mini/(当前实际内容)与source/dpdk-stable-24.11.6/lib/,引用均标文件:行号
上一篇:P3-04 SAI 头文件全景 | 下一篇:P3-06 host_if 打通
一句话定位
sai-dpdk-mini 回答一个问题:不买 ASIC,纯 DPDK 软件能不能当 SAI 的一个"厂商实现"? 能——把 SAI 的 L2/L3/ACL 语义逐条映射到 rte_hash/rte_lpm/rte_flow 上,SONiC 全家桶就能跑在 x86 软件交换机上。如实交代现状:截至写这篇博客,code/sai-dpdk-mini/ 里只有一份 CMake 骨架和空目录——所以这篇是"接口映射设计 + 骨架精读",每个映射目标都给出 DPDK 侧真实行号,实现代码随学习进度补。
现状:骨架的真实内容
code/sai-dpdk-mini/CMakeLists.txt(全文,当前唯一文件):
cmake_minimum_required(VERSION 3.16)
project(sai-dpdk-mini C)
set(CMAKE_C_STANDARD 11)
add_library(sai-dpdk SHARED src/sai_switch.c src/sai_port.c src/sai_fdb.c src/sai_route.c src/sai_neighbor.c)
target_include_directories(sai-dpdk PRIVATE include ${CMAKE_SOURCE_DIR}/../../references/sai-headers/inc)这份骨架已经锁定了三个关键决策:
- 产物是共享库
libsai-dpdk.so——它扮演厂商 SDK 的角色,syncd 加载它就像加载 libsai-brcm; - 模块划分 = SAI 模块划分:switch/port/fdb/route/neighbor 五个 .c,每个导出一张 SAI API 表(对应 P3-04 的
sai_switch_api_t等); - 直接 include 真实 SAI 头文件(
references/sai-headers/inc)——API 签名零翻译、零自造,sai_api_query换表流程原样可用(sai.h:252)。
src/、include/、test/ 目录已建但为空——按学习节奏逐模块填充,这是刻意的:先定接口契约,再写实现。
映射设计:SAI 语义 → DPDK 原语
① L2 FDB → rte_hash(哈希表)
SAI 的 sai_fdb_entry_t(MAC+VLAN+port,saifdb.h:357 的 key 参数)天然是哈希 key。DPDK 侧的真实接口(lib/hash/rte_hash.h):
// rte_hash.h:146
struct rte_hash *
rte_hash_create(const struct rte_hash_parameters *params);
// rte_hash.h:246
int rte_hash_add_key_data(const struct rte_hash *h, const void *key, void *data);
// rte_hash.h:454
int
rte_hash_lookup_data(const struct rte_hash *h, const void *key, void **data);设计要点:key 用 12 字节结构 {mac[6], vlan_id[2], port_id[4]} 对齐填零;data 存出端口号;rte_hash_add_key_data 把"查表→拿值"合成一步。学习型 FDB 的洪泛逻辑(查不到就广播)在 SAI 层用 create_fdb_entry 的删除回调 + 超时实现,与内核 br_fdb_update 的职责对照(内核版见 net/bridge/br_fdb.c:863)。
② L3 路由 → rte_lpm(DIR-24-8 最长前缀匹配)
SAI 的 sai_route_entry_t(sairoute.h:180 的 destination 前缀)对应 DPDK 的真实 LPM 接口(lib/lpm/rte_lpm.h):
// rte_lpm.h:162-164
struct rte_lpm *
rte_lpm_create(const char *name, int socket_id,
const struct rte_lpm_config *config);
// rte_lpm.h:221
int
rte_lpm_add(struct rte_lpm *lpm, uint32_t ip, uint8_t depth, uint32_t next_hop);
// rte_lpm.h:279
static inline int
rte_lpm_lookup(const struct rte_lpm *lpm, uint32_t ip, uint32_t *next_hop)next_hop 参数直接挂 sai_next_hop_t 的 OID 低 32 位(P3-04:OID 是 64 位句柄,高位编码类型)。DIR-24-8 算法本身就是"软件交换机的最佳路由表实现"——这也是 P3-01 里 OVS dpcls 和内核 fib_trie 之外的第三条路线(内核对照:net/ipv4/fib_trie.c:1425 的 fib_table_lookup 用 level-compressed trie)。
③ ARP 邻接 → rte_hash
sai_neighbor_entry_t(IP+rif,saineighbor.h:206)同样进哈希,key 是 {ip[4], rif_oid[8]},data 是 {mac[6], 出口}。它和 FDB 共用同一份 rte_hash 初始化代码——两个 SAI 模块映射到同一个 DPDK 原语,这就是适配层的乐趣。
④ ACL → rte_flow
SAI 的 create_acl_table(saiacl.h:3713)+ entry 匹配动作,翻译成 DPDK 的 flow 规则(lib/ethdev/rte_flow.h):
// rte_flow.h:89
struct rte_flow_attr {
uint32_t group;
uint32_t priority; /**< Rule priority level within group. */
uint32_t ingress:1;
uint32_t egress:1;
// ...
};
// rte_flow.h:4695
struct rte_flow *
rte_flow_create(uint16_t port_id,
const struct rte_flow_attr *attr,
const struct rte_flow_item pattern[],
const struct rte_flow_action actions[],
struct rte_flow_error *error);映射规则:SAI ACL table → rte_flow_attr.group;SAI 规则优先级 → priority;匹配字段翻译成 pattern(ETH/IPV4/5-tuple);动作翻译成 actions(DROP/COUNT/redirect)。如果 NIC 不支持 offload,DPDK 会退软件 fallback——sai-dpdk-mini 的 ACL 一开始就走软件路径,等价于一张每核免锁的精确匹配表(P3-01 的 EMC 思路复用)。
⑤ port/switch → ethdev
create_port(saiport.h:4296)→ rte_eth_dev_info_get/rte_eth_dev_configure;set_port_attribute(saiport.h:4320)的 admin state → rte_eth_dev_start/stop。switch 属性(saiswitch.h:4263 表)落在全局配置结构里。
🐧 内核对照:这三张表在内核长什么样
| SAI 语义 | DPDK 原语 | 内核对应 |
|---|---|---|
| FDB entry | rte_hash_add_key_data(rte_hash.h:246) | br_fdb_update(net/bridge/br_fdb.c:863) |
| route entry | rte_lpm_add(rte_lpm.h:221) | fib_table_insert(net/ipv4/fib_trie.c:1199) |
| neighbor | rte_hash_add_key_data | neigh_update(ARP 表) |
| ACL | rte_flow_create(rte_flow.h:4695) | tc cls flower + net/sched/ |
| hostif 发包 | sai_send_hostif_packet_fn(saihostif.h:1385) | tun_net_xmit(drivers/net/tun.c:1256) |
注意 DPDK 和内核的查表数据结构选择完全一致:哈希表做精确匹配(FDB/邻居),trie/LPM 做前缀匹配(路由)。区别在于 DPDK 的表是用户态预分配、每核可私有、批量查询(rte_lpm_lookup_bulk);内核表要扛 RCU 和路由事件通知。sai-dpdk-mini 就是把这个对照写成代码。
面试 3 问
- sai-dpdk-mini 的产物为什么是共享库? → 它扮演厂商 SDK:syncd 通过
sai_api_query换表(sai.h:252),CMake 骨架add_library(sai-dpdk SHARED ...)已定;模块按 SAI 模块切 .c 文件(CMakeLists 的 5 个源文件)。 - FDB 和 neighbor 为什么都用 rte_hash 而不是各自的表? → 两者都是精确匹配语义,key 结构不同(
{MAC,VLAN,port}vs{IP,rif})但原语相同;SAI 层两个模块、DPDK 层一个原语。 - SAI ACL 怎么映射 rte_flow? → table →
rte_flow_attr.group,优先级 →priority(rte_flow.h:89-101),匹配字段 → pattern,动作 → actions,rte_flow_create(rte_flow.h:4695)下发;软件 fallback 时等价 per-core 精确匹配表。
🔧 动手实验
cmake -B build验证骨架可配置(src 为空时先补桩函数);- 实现
sai_fdb.c的第一版:create_fdb_entry用rte_hash_add_key_data,写 gdb 断点验证 key 布局(P0-06 的 gdb 功夫); - 实现
sai_route.c用rte_lpm_add,和fib_trie.c:1199的插入路径对照读; - 跑通
sai_api_query换表 →create_switch(INIT_SWITCH=false)的最小 demo。 - 产出
perf-data/P3/05-sai-dpdk-mini.md(三张表插入/查询的单核 OPS 曲线)。
评论 (0)