SAI 调试实战:1 号数据库(ASIC_DB)方法论
1 号库(ASIC_DB)是 orchagent 和 syncd 之间每一次 SAI 调用的"黑匣子"。
会读它,绝大多数"配置下了不生效"的问题,能在不翻任何厂商代码的情况下定位到层。
本文只讲思路、结构和判读方法,不贴命令——所有操作都是标准的 Redis 查询和容器内工具,知道名字自然查得到用法。
1. 先建立直觉:1 号库里存的是什么
把 1 号库想象成四本账:
┌─────────────────────────────────────────────────────────────────┐
│ 账本一:当前视图(ASIC_STATE 前缀) │
│ 每个 SAI 对象一条记录:key 里带对象类型和标识, │
│ 内容是该对象全部属性的序列化文本 │
│ → 代表"syncd 认为芯片现在应该是什么状态" │
├─────────────────────────────────────────────────────────────────┤
│ 账本二:期望视图(TEMP 前缀) │
│ 编排阶段 orchagent 写入的期望状态,apply 比对时用 │
├─────────────────────────────────────────────────────────────────┤
│ 账本三:标识映射(VIDTORID / RIDTOVID 前缀) │
│ 虚拟标识 ↔ 真实硬件标识的双向翻译表 │
│ → 一个 VID 能查到 RID,说明厂商 SAI 真的创建成功了 │
├─────────────────────────────────────────────────────────────────┤
│ 账本四:发布订阅频道(不是数据,是通道) │
│ 命令下行的通知频道、结果回传频道、硬件事件上行频道 │
└─────────────────────────────────────────────────────────────────┘两条基本功:
- 按对象类型过滤:key 里都带对象类型名(路由、邻居、端口、VLAN……),按类型名列举就能拿到某类对象的全集;
- 读单个对象:每条记录是一张属性表,左边是属性名,右边是序列化后的值——MAC、IP、对象标识都是可读文本,裸读无门槛。
2. 三分钟定位法:问 1 号库三个问题
拿到任何"XX 不生效"的问题,按固定顺序问三个问题,把故障域一次砍到一层:
现象: "XX 不生效"
│
▼
Q1: 对象进 1 号库了吗?
│
├─ 没有 ──────────────→ 故障域: orchagent 之上
│ (配置库/应用库/编排逻辑,与 SAI 无关)
│
└─ 有
│
▼
Q2: 库里的属性值和预期一致吗?
│
├─ 不一致 ────────────→ 故障域: orchagent 组装错误
│
└─ 一致
│
▼
Q3: 标识映射表里查得到它的 RID 吗?
│
├─ 查不到 ───────────→ 故障域: syncd / 厂商 SAI / 芯片
│ 去看 syncd 日志和调用录像(第 3、4 节)
│
└─ 查得到 ───────────→ SAI 层已宣称成功
故障域: 芯片实际效果
上板用芯片交互命令验证(第 5 节)一个例外要记住:表项类对象(路由、邻居、FDB)没有 RID 概念——它们不走标识分配。这类对象的 Q3 换成:看 syncd 日志里那次创建调用的返回码、以及调用录像里的对应记录。
3. 调用录像:SAI 层的全程行车记录仪
3.1 它是什么
syncd 会把每一次进出 SAI 的调用追加写进一个录像文件(syncd 容器内):对象类型、对象 key、全部属性、调用动作(增/删/改/查)、返回值、以及上行通知,一行一条。
3.2 它为什么比系统日志强
系统日志告诉你: "这次创建失败了,状态码是参数非法"
录像文件告诉你: "失败的是哪一次调用、带的确切属性是什么、
以及它之前之后各发生了什么调用"第二篇讲的 DEF.CHECK 问题(非默认 VRF 第一条路由必须是默认路由),唯一可靠证据就是录像里该 VRF 路由条目的下发顺序——系统日志里永远只有一句干巴巴的"参数非法",看不出顺序。
3.3 回放:把故障现场搬进实验室
录像可以被专用回放工具原样重打给 SAI。三个典型用途:
- 复现:现场抓的录像,实验室里原样重演故障;
- 验证修复:同一份录像,修复后回放应不再报错;
- 裁剪压测:把录像裁成"十万条邻居"之类的片段,精确构造压力场景。
4. 日志分层:每层的声音通道不一样
┌─────────────┬──────────────────────┬────────────────────────────┐
│ 层 │ 怎么开 │ 声音去哪了 │
├─────────────┼──────────────────────┼────────────────────────────┤
│ orchagent │ 宿主日志级别工具 │ 系统日志(swss 前缀) │
├─────────────┼──────────────────────┼────────────────────────────┤
│ syncd 核心 │ 同上 │ 系统日志(syncd 前缀) │
├─────────────┼──────────────────────┼────────────────────────────┤
│ meta 层 │ 随 syncd │ 系统日志 │
├─────────────┼──────────────────────┼────────────────────────────┤
│ 厂商 SAI │ 分级日志宏 │ 系统日志 │
│ (brcm) │ (调试/信息/告警/错误) │ (带 API 模块名+函数名) │
├─────────────┼──────────────────────┼────────────────────────────┤
│ BCM SDK │ 芯片交互 shell 里 │ ★ 默认不进系统日志! │
│ (bsl 通道) │ 配置 bsl 输出 │ 只在芯片日志通道里 │
└─────────────┴──────────────────────┴────────────────────────────┘最高频的误判:在系统日志里 grep 不到 SDK 报错,就断定"SDK 没报错"。实际上 ALPM、DEF.CHECK 这类 SDK 层的详细错误只走 bsl 通道。要么进芯片交互 shell 打开它,要么从返回码反推——grep 系统日志一无所获说明不了任何问题。
4.1 厂商 SAI 日志行的读法
厂商 SAI 的一行错误日志自带四个定位要素:API 模块名、源码行号、函数名、SDK 返回码。返回码以十六进制补码形式出现,换算成负数后查 SDK 错误码表即可。
4.2 错误码速查
┌──────────────────┬────────────────┬──────────────────────────────┐
│ 码 │ 含义 │ 高频场景 │
├──────────────────┼────────────────┼──────────────────────────────┤
│ -4 参数非法 │ SDK 拒绝入参 │ DEF.CHECK 违反; │
│ │ │ v6/128 塞进 64 位 ALPM │
├──────────────────┼────────────────┼──────────────────────────────┤
│ -6 表满 │ 硬件表真满了 │ TH3 主机表 16K 打满 │
├──────────────────┼────────────────┼──────────────────────────────┤
│ 表满(SAI 状态) │ 软件数据库上限到 │ ★ 不一定是芯片满, │
│ │ │ 先对比硬件真实用量 │
├──────────────────┼────────────────┼──────────────────────────────┤
│ 属性非法(meta) │ 守门员层拦截 │ 厂商 SAI 根本没被调用, │
│ │ │ 别去翻厂商代码 │
├──────────────────┼────────────────┼──────────────────────────────┤
│ 属性未实现 │ 属性号超出支持范围 │ 多为头文件版本差异的噪音 │
└──────────────────┴────────────────┴──────────────────────────────┘两个认知要点:
- "表满"要分软硬件:SAI 软件数据库的上限和芯片真实容量是两本账,先对比再下结论;
- -4 和 -6 的分水岭:参数非法往往指向模式约束(DEF.CHECK、位宽限制),表满才指向容量。报错码本身就在给方向。
5. 芯片侧终裁:交互 shell 与 CINT
当 1 号库、录像、日志都显示"成功",但转发行为就是不对时,最后一步是上板看真实表项。芯片交互 shell(syncd 容器内)能直接看到:
可查的东西 回答的问题
────────────────────────── ─────────────────────────────
L3 资源用量统计 容量链哪一环先满(主机/路由/下一跳/ECMP)
路由表内容 路由到底下没下去、指向哪
L2 地址表 MAC 学没学到、静态项在不在
字段处理器 group/表项 ACL 是否真正 install 进 TCAM
当前生效的芯片参数 配置文件的改写是否真的落了地
bsl 日志开关 SDK 层详细报错(DEF.CHECK 等)更进一步,SDK 自带 C 解释器(CINT),可以跑脚本做寄存器级查询而不用重新编译镜像;厂商 SAI 自己也注册了一批调试命令,能把 SAI 层的软件数据库状态 dump 出来——用于对比"SAI 认为的状态"和"SDK/芯片的状态"。
6. 三个真实案例:完整定位链示范
案例 A:v6 ND 邻居到第 8193 个就创建失败
系统日志: 厂商 SAI 报"主机表项添加失败,参数非法(-4)"
│
├─ 1 号库 Q1/Q2: 邻居表项已写入、属性正确 → 上层无辜
│
├─ 返回码是 -4 不是 -6! → 不是简单的"打满",有模式约束
│
├─ 芯片交互 shell 看 L3 用量: 主机表 16384/16384
│ (v6 占双倍宽度 → 实际 8192 条 v6)
│
└─ 根因链:
TH3 主机表 16K 硬件锁死
+ 设备处于 ipv6-64 模式(128 位路由开关关)
+ 该开关恰好是 v6 邻居溢出通道(host-as-route)的门槛
→ 溢出无路,8192 即天花板
→ 修法不在 SAI 代码,在芯片参数层面的权衡案例 B:某 VRF 的路由全部报参数非法
系统日志只有一句"参数非法",无细节
│
├─ 打开调用录像,找该 VRF 的路由创建记录
│ → 第一条不是默认路由 → 命中 DEF.CHECK 实锤
│
├─ 佐证: 芯片交互 shell 打开 bsl,SDK 侧能看到 DEF.CHECK 报错
│ (再次说明: 只看系统日志会错过它)
│
└─ 修法: 编排层保证 VRF 内先下默认路由,或调整 ALPM 模式案例 C:开机刷一堆"属性号未知"告警
厂商 SAI 日志: "switch 属性 0[303] unknown",一连十几条
│
├─ 解码: 属性号超出了当前 SAI 头文件定义的枚举范围
│
├─ 本质: syncd 侧头文件版本比厂商 SAI 新,
│ 询问了一批厂商尚未实现的属性
│
└─ 判读: 纯噪音还是真问题?标准只有一个——
该属性是否影响你要的规格。不影响,就是版本差异噪音,放行。7. 工具地图(按层归纳)
┌──────────────┬────────────────────────────────────────────┐
│ 宿主机 │ Redis 客户端直读 1 号库(Q1/Q2/Q3) │
│ │ 日志级别工具(开 orchagent/syncd 日志) │
├──────────────┼────────────────────────────────────────────┤
│ syncd 容器 │ 视图导出工具:当前视图/期望视图/依赖图 │
│ │ 反向发现工具:从芯片重建对象清单 │
│ │ 调用录像文件 + 回放工具 │
│ │ 芯片交互 shell + CINT 脚本 │
│ │ 厂商 SAI 调试命令(dump 软件数据库) │
└──────────────┴────────────────────────────────────────────┘8. 方法论总结:一张决策树
现象: 转发/配置不生效
│
┌───────────────┴────────────────┐
▼ │
1 号库 Q1: 对象在吗? │
│ │
├─ 不在 → 查编排层以上 (配置库/应用库) │
│ │
▼ │
1 号库 Q2: 属性对吗? │
│ │
├─ 不对 → 查 orchagent 组装 │
│ │
▼ │
录像+日志: SAI 调用成功吗? │
│ │
├─ 失败 → 按返回码分方向: │
│ 参数非法 → 模式约束 (DEF.CHECK/位宽)
│ 表满 → 容量链逐环对比 │
│ meta 拦截 → 厂商代码未被调用 │
│ │
▼ │
芯片交互 shell: 表项对吗? │
│ │
├─ 不对 → SDK 层 (开 bsl 取证) │
│ │
▼ ▼
都对 → 查 trap/驱动/内核侧 (逐层排除, 绝不跳层猜)核心纪律:每一层都有独立的观测手段,自顶向下逐层排除。 这就是 1 号数据库调试法的全部要义。
系列回顾
第一篇 架构逻辑 → 七层链路、进程分工、syncd 机制、厂商 SAI 分层
第二篇 转发逻辑 → L2/L3/ACL 各层对象映射、容量链、隐藏约束
第三篇 调试方法 → 1 号库定位法、录像回放、日志分层、错误码判读
评论 (0)