仓库里的四本账、开业流程与对账:用「快递站」比喻讲透交换芯片的表项、初始化与对账

一句话简介:前两篇讲了快递站怎么收发货(数据面)、总部怎么指挥分站(控制面)。这一篇钻进仓库内部,讲清楚仓库里的四本账(FDB/VLAN/L3/ACL)、仓库开业时先干什么后干什么(初始化时序与错误恢复)、以及账本对不上时怎么修(软硬件对账)。

项目内容
适合读者接触交换机驱动、想理解芯片表项与初始化流程的工程师
前置知识前两篇的快递站比喻、基本以太网/VLAN/路由概念
阅读时长约 30 分钟
关键词FDB、VLAN、L3、ACL、TCAM、初始化时序、错误恢复、软硬件对账

目录


引子:前两篇讲了“怎么干活”,这一篇讲“账怎么记”

  • 第一篇:包裹怎么从货车进仓库、怎么装纸箱、怎么交给客户(数据面)。
  • 第二篇:总部怎么下命令、分站怎么碰仓库(控制面)。
  • 这一篇:仓库内部有四本账决定“什么货该送到哪”,仓库开业时按什么顺序点火,以及账本和实际货架对不上时怎么修。

延续比喻:

  • 驱动 = 分站
  • 交换芯片 = 仓库
  • 表项 = 仓库里的账本
  • 初始化 = 仓库开业前的点火流程
  • 对账 = 定期盘点,发现账实不符就修

第一部分:仓库里的四本账——FDB / VLAN / L3 / ACL

仓库要决定“一个包裹往哪送”,不是拍脑袋,而是翻四本账。四本账有先后顺序,像流水线一样一本本查。

总览:四本账的查找流水线

   包裹从 3 号门进来
        │
        ▼
   ┌─────────────────────────────────────────────┐
   │ 第一本账:ACL(门卫规则)                     │
   │   “这个包裹允许进吗?要不要直接丢掉/转走?”     │
   │   命中规则 → 按规则动作(丢弃/重定向/放行)     │
   └──────────────────┬──────────────────────────┘
                      │ 放行
                      ▼
   ┌─────────────────────────────────────────────┐
   │ 第二本账:VLAN(分组名册)                     │
   │   “3 号门属于哪个组?这个包裹该进哪个组?”      │
   │   决定:加不加 VLAN 标签、能泛洪到哪些门         │
   └──────────────────┬──────────────────────────┘
                      │ 确定组
                      ▼
   ┌─────────────────────────────────────────────┐
   │ 第三本账:FDB(工位通讯录)                    │
   │   “目的 MAC 在哪个门?”                       │
   │   查到 → 直接送那个门;查不到 → 泛洪            │
   └──────────────────┬──────────────────────────┘
                      │ 需要跨组/跨网段?
                      ▼
   ┌─────────────────────────────────────────────┐
   │ 第四本账:L3(跨组路由表 + 邻居表)            │
   │   “目的 IP 走哪个下一跳?下一跳的 MAC 是谁?”   │
   │   决定:改 MAC 头、减 TTL、从哪个门送出          │
   └─────────────────────────────────────────────┘

一句话记:ACL 先拦,VLAN 分组,FDB 找人,L3 跨组。 四本账按这个顺序查,任何一本命中就可能提前结束。


1. FDB:工位通讯录——“目的 MAC 在哪个门”

FDB(Forwarding Database)就是仓库的工位通讯录:记着“某个 MAC 地址在哪个门后面”。

   FDB 账本长这样:

   ┌──────────────┬────────┬────────┬──────────┐
   │  MAC 地址     │ VLAN   │ 哪个门  │ 状态      │
   ├──────────────┼────────┼────────┼──────────┤
   │ AA:BB:CC:... │ 10     │ 3 号门  │ 已生效    │
   │ DD:EE:FF:... │ 10     │ 5 号门  │ 已生效    │
   │ 11:22:33:... │ 20     │ 7 号门  │ 老化中    │
   └──────────────┴────────┴────────┴──────────┘

三种来源:

来源怎么来的特点
动态学习包裹从 3 号门进来,源 MAC 自动记到 3 号门会老化,一段时间没流量就删
静态配置管理员手动指定“这个 MAC 永远在 5 号门”不老化,重启也在
控制面下发路由/邻居协议算出来后写进来随协议状态变化

查找结果只有三种:

   目的 MAC 查 FDB
        │
   ┌────┴────┬────────────┐
   查到      查不到       查到但状态是“老化中”
   │          │            │
   ▼          ▼            ▼
  直接送     泛洪          按实现决定:
  那个门     (除入口外    送 + 触发重新学习
            所有同组门)

卡点:动态学习是硬件自动做的,驱动只收到“学习到了”的通知(notifier)。如果驱动在通知里做慢活(比如查数据库),会拖慢整个学习路径。学习通知要快进快出,慢活丢工作队列。


2. VLAN:分组名册——“哪些门是一个组”

VLAN 就是仓库的分组名册:把几十个门分成若干组,组内可以互相送,组间默认隔离。

   VLAN 账本长这样:

   ┌────────┬─────────────────────┬──────────────┐
   │ VLAN   │ 成员门               │ 标签处理       │
   ├────────┼─────────────────────┼──────────────┤
   │ 10     │ 1, 2, 3 号门         │ 3 号门出口打标签│
   │ 20     │ 5, 6, 7 号门         │ 不打标签(untag)│
   │ 30     │ 1, 5 号门(跨组)     │ 按配置         │
   └────────┴─────────────────────┴──────────────┘

每个门有三个关键属性:

属性大白话作用
PVID这个门的“默认组号”进来的无标签包裹,归到这个组
成员关系这个门属于哪些组决定包裹能往哪些门送
Tag/Untag出口要不要贴组标签贴标签给交换机看,不贴给终端看

收包时的 VLAN 处理:

   包裹从 3 号门进来
        │
        ▼
   包裹上有没有 VLAN 标签?
        │
   ┌────┴────┐
   有          没有
   │           │
   ▼           ▼
  按标签的组   按 3 号门的 PVID
   │           │
   └─────┬─────┘
         ▼
   查 VLAN 账本:这个组允许 3 号门进吗?
         │
    ┌────┴────┐
    允许       不允许
    │          │
    ▼          ▼
   继续查 FDB  丢弃(记一笔“VLAN 违规”)

卡点:VLAN 成员关系是全局表,不是每个门私有的。驱动收到“把 3 号门加入 VLAN 10”时,要改的是芯片的全局 VLAN 表,不是 3 号门的私有状态。改错了,要么 3 号门进不了组,要么整个组的成员关系乱掉。


3. L3:跨组路由表 + 邻居表——“跨组怎么走”

FDB 只管同一个组内“找人”。如果包裹要跨组、跨网段,就得查 L3 的两本账:

   L3 账本分两本:

   ┌─────────────────────────────────────────────┐
   │ 路由表(去哪个方向)                          │
   │   目的网段 10.0.2.0/24 → 下一跳 10.0.1.1     │
   │   目的网段 10.0.3.0/24 → 直接连(直连路由)   │
   └──────────────────┬──────────────────────────┘
                      │ 知道下一跳 IP
                      ▼
   ┌─────────────────────────────────────────────┐
   │ 邻居表(下一跳的 MAC 是谁)                   │
   │   IP 10.0.1.1 → MAC AA:BB:CC:...            │
   │   (由 ARP/ND 协议填进来)                    │
   └──────────────────┬──────────────────────────┘
                      │ 知道下一跳 MAC
                      ▼
   改包裹的 MAC 头(目的 MAC 换成下一跳的)
   减 TTL
   从 FDB 查“下一跳 MAC 在哪个门”
   送出去

完整跨组转发流程:

   包裹目的 IP = 10.0.2.5,从 3 号门进来
        │
        ▼
   查路由表:10.0.2.0/24 → 下一跳 10.0.1.1
        │
        ▼
   查邻居表:10.0.1.1 → MAC AA:BB:CC
        │
        ▼
   改包裹:
     · 目的 MAC 改成 AA:BB:CC
     · 源 MAC 改成仓库自己的 MAC
     · TTL 减 1
        │
        ▼
   拿 AA:BB:CC 查 FDB → 在 5 号门
        │
        ▼
   从 5 号门送出

卡点:L3 转发依赖邻居表有货。如果邻居表里没有下一跳的 MAC,包裹会被“挂起”,等 ARP/ND 解析完再发。驱动要保证挂起的包裹有超时和上限,否则邻居表一直不解析,包裹会无限堆积,把内存吃光。


4. ACL:门卫规则——“什么货不能进、不能出”

ACL 就是仓库的门卫规则:按“源/目的 MAC、IP、端口号、VLAN”等条件匹配,命中就执行动作。

   ACL 账本长这样(TCAM 表):

   ┌────┬──────────────────────────────┬──────────────┐
   │优先级│ 匹配条件                      │ 动作          │
   ├────┼──────────────────────────────┼──────────────┤
   │ 1  │ 目的 MAC = XX:XX, VLAN = 10   │ 丢弃          │
   │ 2  │ 源 IP = 10.0.0.5, 端口 = 80   │ 重定向到 7 号门│
   │ 3  │ 协议 = TCP, 目的端口 = 22     │ 限速 1Mbps    │
   │ 4  │ 其他所有                      │ 放行          │
   └────┴──────────────────────────────┴──────────────┘

ACL 的特殊性:TCAM 是“三态”的

普通账本只能记“0 或 1”。ACL 的账本(TCAM)能记“0、1、不关心”三种状态,所以能做掩码匹配:

   普通内存:  1010  → 只能匹配 1010
   TCAM:      10XX  → 能匹配 1000、1001、1010、1011
                ↑↑
              这两位“不关心”

ACL 下发的难点:

   用户配置了一条 ACL
        │
        ▼
   驱动要把它编译成 TCAM 表项:
     · 选哪些字段做 key(MAC/IP/端口/VLAN)
     · 每个字段多宽、掩码怎么设
     · 放在 TCAM 的哪个位置(优先级)
        │
        ▼
   写入 TCAM
        │
   ┌────┴────┐
   成功       失败(TCAM 满了 / 字段不支持 / 优先级冲突)
   │          │
   ▼          ▼
   生效      返回错误,告诉用户“这条规则下不了”

卡点:

  • TCAM 容量有限,不是想加多少加多少。驱动要在下发前检查容量,满了要返回明确错误,不能默默丢弃。
  • 优先级顺序有讲究。两条规则冲突时,谁先匹配谁生效。驱动要保证“用户配的优先级”和“TCAM 里的物理顺序”一致。
  • 字段支持有限。不是所有芯片都支持“按 TCP 端口号匹配”,驱动要提前检查能力,不支持就报错。

四本账的关系总结

   ┌─────────────────────────────────────────────┐
   │ ACL:先拦一道,决定“进不进、要不要转走”        │
   └──────────────────┬──────────────────────────┘
                      ▼
   ┌─────────────────────────────────────────────┐
   │ VLAN:分组,决定“能去哪些门”                  │
   └──────────────────┬──────────────────────────┘
                      ▼
   ┌─────────────────────────────────────────────┐
   │ FDB:同组内找人,决定“具体送哪个门”            │
   └──────────────────┬──────────────────────────┘
                      ▼
   ┌─────────────────────────────────────────────┐
   │ L3:跨组时改头、换 MAC、减 TTL,再回 FDB 查门   │
   └─────────────────────────────────────────────┘

记忆口诀:ACL 拦,VLAN 分,FDB 找,L3 跨。


第二部分:仓库开业——初始化时序与错误恢复

仓库不是一通电就能用的。它得像飞机起飞一样,按检查单一步步来,任何一步失败都要能安全回滚。

初始化时序:八步点火

   ① 全局复位
      └─ 把芯片所有寄存器、表项、队列清到已知状态
           │
           ▼
   ② 时钟与 PLL 锁定
      └─ 等时钟稳定,PLL 锁定(等不到就报错退出)
           │
           ▼
   ③ SerDes 训练
      └─ 高速接口的收发均衡、时钟恢复,让链路误码率达标
           │
           ▼
   ④ SRAM / 表项内存初始化
      └─ 清空 FDB/VLAN/L3/ACL 表,设置默认值
           │
           ▼
   ⑤ DMA 环建立
      └─ 分配描述符环,告诉硬件“CPU 端口的收发缓冲区在哪”
           │
           ▼
   ⑥ 中断注册
      └─ 申请 MSI-X 向量,绑定 NAPI
           │
           ▼
   ⑦ 默认策略加载
      └─ ACL 默认放行、VLAN 默认配置、QoS 默认队列
           │
           ▼
   ⑧ 使能端口、启动交换引擎
      └─ 最后一个门打开,硬件开始转发

为什么顺序不能乱?

   如果先开端口(⑧)再初始化表项(④):
       端口一开,包裹进来
       表项还是空的/脏的
       → 包裹被泛洪、被错误丢弃、甚至踩坏内存
   
   如果先建 DMA 环(⑤)再等 PLL 锁定(②):
       DMA 环建好了,但时钟还没稳
       → 硬件读写描述符时序错乱,读到垃圾

每一步都要有超时和回滚:

   每一步的模板:

   开始这一步
        │
        ▼
   等硬件确认(带超时)
        │
   ┌────┴────┐
   成功       超时/失败
   │          │
   ▼          ▼
   下一步     回滚到上一步的干净状态
              上报错误
              退出 probe

错误恢复:开业失败怎么收场

初始化失败不可怕,可怕的是失败后留下一地鸡毛。驱动的错误恢复要遵守两条原则:

原则一:哪一步失败,就从哪一步往回拆

   第 ⑤ 步(DMA 环)失败:
        │
        ▼
   拆 ⑤ 已分配的部分
   拆 ④ 已初始化的表项(可选,复位会清)
   拆 ③ SerDes(关掉)
   拆 ② PLL(关掉)
   拆 ① 复位(保持复位状态)
        │
        ▼
   返回错误,probe 失败

原则二:运行中出错,能局部恢复就不整体重启

   运行中某次表项下发超时
        │
        ▼
   只回滚这一条表项(状态机回滚)
   不影响其他表项
   记一笔错误日志
        │
        ▼
   如果连续多次超时 / 寄存器读回全 F
        │
        ▼
   触发通道复位(只复位出问题的模块)
        │
        ▼
   如果复位也救不回来
        │
        ▼
   上报“硬件故障”,停止该端口/该模块

常见错误与处理:

错误症状处理思路
PCIe link down寄存器读回全 F停止访问,上报,等待热插拔/重启
寄存器超时轮询 ack 一直不置位带超时返回错误,不无限等
SerDes 训练失败端口链路起不来重试 N 次,失败则标记端口不可用
TCAM 满ACL 下发返回“资源不足”返回明确错误,让用户删规则
DMA 环错误描述符 own 位异常、数据错乱停止队列、复位 DMA 通道、重建环
表项下发失败影子表是“已生效”但硬件没有回滚影子表状态,重试或上报

卡点:错误恢复本身不能成为新的错误源。 比如复位通道时如果还拿着总线锁,可能死锁;回滚表项时如果状态机没设计好,可能把“已生效”的表项误删。


第三部分:软硬件对账——账本对不上怎么办

即使初始化正确、下发流程严谨,运行久了账本还是会“对不上”:

  • 硬件自己老化了一条 FDB,驱动没收到通知;
  • 控制面下发失败但影子表已标记“已生效”;
  • 硬件复位后表项全丢,驱动不知道;
  • 并发操作导致某条表项只写了一半。

对账就是定期(或触发时)对比影子账本和硬件账本,发现差异就修。

对账的基本流程

   触发对账(定时 / 收到错误 / 人工命令)
        │
        ▼
   逐条读硬件表项(或读统计/校验和)
        │
        ▼
   和影子账本对比
        │
   ┌────┴────┬────────────┬────────────┐
   硬件多了   硬件少了      值不对
   │          │            │
   ▼          ▼            ▼
   删掉      补上         改成影子账本的值
   (可能是  (可能是      (可能是并发
   老化没通知)下发失败)   写坏了)
        │
        ▼
   记录差异日志(谁、哪条、怎么修的)

三种对账策略

策略怎么做优点缺点
全量对账定期把整张硬件表读一遍,逐条对比最彻底慢,大表可能几秒钟,期间影响转发
增量对账只对比最近变更过的表项快漏掉“悄悄变化”的表项
事件驱动对账收到硬件错误/复位事件时触发对账及时依赖事件通知可靠

实际做法通常是组合:日常用增量对账 + 事件驱动,低峰期做全量对账。

对账的难点:硬件表不一定“读得回来”

有些芯片的 TCAM、哈希表不支持逐条读回,只能:

  • 读“表项数量”统计,和影子账本数量对比;
  • 读“校验和”或“哈希摘要”,和影子账本算出来的对比;
  • 用“探测包”验证某条表项是否真的生效(发一个匹配的包,看它走不走)。
   不支持读回的表的对账方法:

   ① 数量对账:
      硬件统计“当前 FDB 条数” vs 影子账本条数
      不一致 → 触发全量重建(清空硬件表,按影子账本重写)

   ② 行为对账:
      构造一个匹配某条 ACL 的探测包
      看它是否被丢弃/重定向
      和影子账本预期对比

卡点:对账本身要和正常下发互斥。 对账读硬件表时,如果有新表项正在下发,可能读到“半成品”。所以对账通常要暂停表项下发(或加读写锁),对完再放开。


全景串联:一个包裹、一次开业、一轮对账

   【包裹转发】
   3 号门进来
     → ACL 拦(放行)
     → VLAN 分组(VLAN 10)
     → FDB 找人(目的 MAC 在 5 号门)
     → 从 5 号门送出
   (跨组时多查 L3:路由表 + 邻居表,改 MAC、减 TTL,再回 FDB)

   【仓库开业】
   复位 → PLL → SerDes → 表项初始化 → DMA 环 → 中断 → 默认策略 → 开端口
   每一步:带超时,失败就回滚到上一步

   【定期对账】
   读硬件表 / 统计 / 行为验证
     → 和影子账本对比
     → 多了删、少了补、错了改
     → 记日志
   (对账期间暂停下发,避免读到半成品)

常见坑速查

坑生活比喻后果正确做法
VLAN 成员关系当私有状态改改的是工位自己的小本本,不是车间名册整个组的成员关系乱掉VLAN 是全局表,改全局表
L3 挂起包裹没上限邻居表不解析,包裹无限堆积内存吃光挂起队列有超时和上限
TCAM 满了默默丢规则门卫规则写不进去也不说用户以为规则生效了下发前检查容量,满了报错
初始化顺序颠倒先开门再摆货架包裹进来踩坏内存严格按八步顺序,每步带超时
错误恢复时还拿着锁复位通道时死锁系统卡死恢复前释放锁,恢复用独立路径
对账和下发并发读到半成品表项误判差异、误删表项对账期间暂停下发或加读写锁
不支持读回的表硬读读回全 F 当成“表空了”误删所有表项用数量/行为对账,不硬读

自检清单

能用自己的话答上来,这部分就算过关。

四本账

  • FDB、VLAN、L3、ACL 四本账的查找顺序是什么?哪本可能提前结束?
  • FDB 的三种来源分别是什么?动态学习由谁做?
  • VLAN 的 PVID、成员关系、Tag/Untag 分别管什么?
  • L3 转发为什么要查“路由表 + 邻居表”两本?邻居表没货时包裹怎么办?
  • TCAM 的“三态”是什么意思?ACL 下发失败常见原因有哪些?

初始化

  • 初始化八步的顺序是什么?为什么不能先开端口再初始化表项?
  • 每一步失败后怎么回滚?运行中出错和 probe 失败的处理有什么不同?
  • 错误恢复时为什么要先释放锁?

对账

  • 对账的三种触发方式是什么?各有什么优缺点?
  • 硬件表不支持读回时,怎么对账?
  • 对账为什么要和正常下发互斥?

一句话总结

仓库四本账:ACL 拦、VLAN 分、FDB 找、L3 跨;开业八步走,每步带超时、失败往回拆;账本对不上就定期盘,多了删、少了补、错了改,盘点时别让人同时改账。

这三件事做好了,交换机才“转得对、起得来、错能修”;做不好,就是“表项时有时无、端口起不来、复位后全乱”这类最让人头疼的问题。


版权说明:本文为原创技术笔记,欢迎转载,转载请注明出处。文中比喻仅用于降低理解门槛,具体实现请以对应芯片手册和内核版本源码为准。

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐