侧边栏壁纸
  • 累计撰写 53 篇文章
  • 累计收到 3 条评论

SAI 调试实战:1 号数据库(ASIC_DB)方法论

PacketForge
2026-08-18 / 0 评论 / 2 阅读 /

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

评论 (0)

取消