交换芯片数据通路四结构:Cell、Crossbar、VOQ与共享缓冲 📅 发布时间:2026/9/17 8:53:38 👁 浏览次数: 两年多前我接手过一个挺磨人的排障任务一台 64 端口 400GE 的交换芯片平均负载常年不到三成却每隔几个小时冒一次丢包而且只丢在少数几个出端口上。宏观计数翻了一圈没看出毛病最后把芯片手册里 Crossbar、VOQ、Shared Buffer、Cell Fabric 这几章重新啃了一遍才意识到问题根本不在某个计数器上而在数据通路的某一段结构对这种流量形态天生就不友好。这篇就按数据从端口进、到端口出的顺序把交换芯片数据通路里最关键的四个结构拆开讲报文怎么被切成定长 Cell、Crossbar 如何在没有中央大脑的情况下完成每时隙的匹配、VOQ 怎么把队头阻塞从根上拆掉、Shared Buffer 凭什么用有限的片上 SRAM 扛住微突发以及 Cell Fabric 上的乱序、重组和顺序保证到底怎么落地。内容偏硬件微架构但做交换系统、DPU、数据中心网络调优的人都会用到——你不需要会写 RTL但最好知道每一次丢包和时延毛刺背后是哪一段结构在起作用。1. 从一块 64 字节的 Cell 说起数据通路到底在搬运什么1.1 报文一进芯片就被切碎定长 Cell 是被逼出来的以太网帧是变长的从 64 字节一路到 1500 字节开了巨帧还能到 9KB 甚至 12KB。如果芯片内部直接搬变长报文会连着踩三个坑。第一Crossbar 的时隙是固定节拍仲裁器每一拍只能建立一组连接报文长度不固定时隙就没法固定仲裁也就没法流水线化最后只能退化成等整包搬完再调度下一包吞吐直接崩。第二片上缓冲要按变长分配得做类似 first-fit 的碎片管理这套逻辑放在 SRAM 里做面积和时序都撑不住。第三多播复制时变长更麻烦每个副本的长度都不一样复制引擎得带一堆状态。所以交换芯片的通行做法是报文在入口被切成定长的 Cell内部全程以 Cell 为单位搬运和调度出口再拼回完整的帧。Cell 大小常见的是 64 字节也有用 128 字节甚至 256 字节的。选 64 字节有个很直接的道理——它就是以太网最小帧的长度64 字节的小包刚好切一块不用跨 Cell。代价在于那些不整的帧长比如 65 字节的帧要切两块 Cell内部搬运 128 字节效率只剩一半。以太网帧长字节需要的 64B Cell 数内部实际搬运字节内部搬运效率64164100%65212850.8%1282128100%5128512100%150024153697.7%9000141902499.7%选更大的 Cell比如 256 字节能显著降低调度频率代价是小包场景下内部浪费更严重而且最小调度粒度变粗时延抖动变大。所以大 Cell 一般出现在面向大包和高吞吐的机型上面向通用数据中心、小包占比高的芯片更倾向 64 字节。提示Cell 大小是芯片流片时定死的改不了。选型时如果业务以小包为主一定要把内部搬运效率这一项算进去它直接决定了你标称的转发能力在真实流量下能兑现多少。1.2 400GE 上的一笔账为什么内部必须以 Cell 而不是包为单位先把基础换算捋清楚。400GE 端口的线速是 400 Gbps也就是 50 GB/s。这个数字有个很好用的等价形式——1 纳秒约等于 50 字节。一块 64 字节的 Cell 在 400GE 上占用的时间就是 512 bit ÷ 400 Gbps约 1.28 纳秒。再看包。以太网在线上除了帧本身还有 8 字节前导码和 12 字节帧间隙一共 20 字节额外开销。一个 64 字节的小包在线上一共占 84 字节有效载荷率只有 76.2%。换算成包速率400e9 ÷ (84×8) ≈ 5.95 亿包每秒。也就是说一个 400GE 端口跑满小包每秒要处理近 6 亿个包。一台 25.6Tbps 的芯片64 个 400GE 端口全跑小包聚合包速率是 380 亿包每秒平均每个包的预算只有 26 皮秒。26 皮秒是什么概念芯片主频按 1.6GHz 算一个时钟周期是 625 皮秒。也就是说每包 26 皮秒等于说一个时钟周期里要处理二十多个包。单条流水线做包级处理是不可能的只能靠两条路一是把包处理拆成深度流水线让不同包处在不同阶段二是把调度和搬运的粒度从包降到Cell用并行度换时间。25.6Tbps 芯片的聚合 Cell 速率是 25.6e12 ÷ 512 500 亿 Cell 每秒平均 20 皮秒一块 Cell。这个数字同样吓人但它至少是可并行的——按平面拆、按 buffer bank 拆、按 VOQ 拆。端口速率64B Cell 占用时间64B 小包线速 PPS100GE5.12 ns1.49 亿400GE1.28 ns5.95 亿800GE0.64 ns11.9 亿1.3 超速比内部带宽为什么总要做得比端口大很多刚接触这一行的人会问端口加起来 25.6T内部 fabric 也做 25.6T 不就够了吗答案是远远不够内部必须超速通常 1.5 倍到 2 倍。原因至少有四条。第一条是读写带宽。共享缓冲的每个 Cell 都要写一次、读一次占用两倍的内存带宽。25.6Tbps 的芯片光是缓冲的聚合读写带宽就要 51.2Tbps 量级这还没算多播复制带来的额外读操作。第二条是头部开销。每个 Cell 都要带 Cell 头里面有源端口、目的端口、队列号、序号、SOP/EOP 标志、有效字节数、优先级、多播位图实打实的几十比特。20 字节的头挂在 64 字节净荷上开销就是 30% 以上内部带宽自然要相应放大。第三条是匹配失败的补偿。Crossbar 的仲裁器每个时隙解一次匹配不可能保证每次都百分之百匹配成功。只要有输入和输出空着没配上那部分带宽就浪费了。超速比给调度器留了容错空间。第四条是排队时延。这条最容易被忽略。理论上只要输入端有队列、输出端有队列中间用匹配算法做调度就构成了 CIOQCombined Input-Output Queued结构。有经典结论表明当内部超速比达到 2 时CIOQ 可以模拟输出排队OQ交换机的行为也就是说它能达到和理想交换机一样的吞吐和时延特性。1.5 倍是工程上常见的折中——功耗、走线、面积都还能接受性能损失又不明显。1.4 共享缓冲的容量账1MB 能撑多久片上的共享缓冲是有限资源一般几十 MB 量级。64MB 的缓冲池分到 64 个 400GE 端口上平均每端口 1MB。1MB 在 400GE 上能缓冲多久1MB ÷ 50GB/s 20.5 微秒。光在光纤里 1 微秒大约跑 200 米20 微秒相当于 4 公里光纤的传播时延。这个对比很能说明问题片上缓冲看起来不小实际连一条长距离链路的往返时延都覆盖不了几条。所以交换芯片的缓冲从来不是用来存的而是用来吸收瞬时的不均匀——也就是微突发。指望靠加大缓冲解决拥塞方向从一开始就错了端到端的拥塞控制才是正解。2. Crossbar一张只在时隙上成立的无阻塞承诺2.1 物理形态与 request-grant-accept 三次握手Crossbar 的本质是一张 N×N 的交叉点矩阵每个交叉点是一个受控开关。每个时隙里每个输入最多连到一个输出每个输出最多被一个输入连到——这就是它无阻塞的前提任意输入到任意输出只要不被别人占用就一定能连上。但问题来了谁来决定每个时隙怎么连如果做一个中央仲裁器统一决策N64 时的匹配问题规模是 64×64每个时隙1.28 纳秒解一次还要考虑可扩展性显然不现实。所以实际芯片用的是分布式的请求-授权-接受request-grant-accept三次握手。请求阶段每个输入端口检查自己的 VOQ只要有非空的 VOQ就向对应的输出端口发请求。一个输入可能同时向多个输出发请求。授权阶段每个输出端口收到一堆请求按自己的策略选一个输入发授权。接受阶段每个输入可能收到多个授权它只能选一个输出接受其余忽略。为什么非得分三步因为输入和输出各自都有约束输入这一侧每个时隙只能发一个 Cell输出那一侧每个时隙也只能收一个 Cell。这两组约束分别落在两端集中求解就是一个二分图最大匹配问题分散求解就是这套握手协议。代价是匹配质量会打折可能解出的是极大匹配而不是最大匹配这也是超速比存在的原因之一。2.2 匹配算法的取舍从 Round Robin 到 iSLIP最简单的做法是每输出独立轮询Round Robin。每个输出维护一个指针每次从指针位置往后找第一个发请求的输入授权给它。单个输出来看很公平但放到整个系统里会出问题所有输出的指针容易同相导致大家都盯着同一批输入端口另一些输入端口长期没人授权吞吐掉得很难看。iSLIP是这一行的经典解法核心思路是让指针只在成功握手时才推进用非对称的更新规则打破同步。它每个时隙迭代若干次常见做法是迭代三次。每个输入 i 维护授权指针 g_i每个输出 j 维护接受指针 a_j 迭代 1..3 次: // 请求每个非空 VOQ(i,j) 向输出 j 发请求 // 授权输出 j 从所有请求中从 a_j 开始轮询选一个输入发授权 // 接受输入 i 从收到的多个授权中从 g_i 开始轮询选一个输出 // 发接受并把 g_i 更新为被选中输出的下一位 // 被接受的输出 j 把 a_j 更新为对应输入的下一位第一次迭代时所有指针都从初始位置开始容易同相但第一轮结束后指针就被打散了后面几轮迅速收敛。工程上多数实现对均匀流量一次迭代就能接近满载非均匀流量下多迭代几次收益明显硬件为了保证时序通常做两到四次迭代。涉及的另一个取舍是 PIM 类算法用随机指针而非轮询随机化能天然避免同步但硬件里生成高质量随机数也不便宜。像 iSLIP、DRRM 这类确定性算法实现简单、可验证是主流选择。2.3 加速比 1.5x / 2x 换来的到底是什么前面提到过CIOQ 在超速比 2 的条件下可以模拟 OQ。这句话的工程含义值得展开讲。输出排队OQ交换机是理论上的理想模型每个输出端口有自己的队列任何时刻进来的包都能立刻被放到目的地队列里不存在输入侧等待。它的时延最优但需要每个端口 N 倍于线速的内存带宽64 个 400GE 端口就是 64 倍的聚合带宽SRAM 根本扛不住功耗更不用谈。CIOQ 用输入侧 VOQ 加中间 Crossbar 来近似 OQ。超速比越大调度器越有机会在下一个时隙把本该立刻转发的 Cell 提前送出去行为就越接近 OQ。工程上的现实选择是这样超速比吞吐表现时延表现功耗与面积典型场景1.0非均匀流量下明显掉速抖动大最低低端或成本敏感型1.5多数场景接近满载抖动可接受中等主流数据中心交换2.0理论上可模拟 OQ接近理想高高性能/无损网络选型时经常有人问1.5 倍够不够我的经验是如果业务以 TCP 大流为主1.5 倍基本无感如果跑的是无损网络、AI 训练集合通信那种突发极强、对尾时延极敏感的场景2 倍甚至更高的超速比带来的收益是能测出来的。2.4 多平面与 striping把一根粗管子拆成几根细管子单平面 Crossbar 到了 25.6Tbps 这个量级走线宽度和功耗都很难接受。所以现代芯片普遍把 fabric 拆成多个平面plane每个平面是一张规模小一些的 Crossbar每个平面的速率是总速率的一部分。Cell 在进入 fabric 前做 striping也就是轮转着分配到各个平面上。比如四个平面Cell 就按 0-1-2-3 的顺序轮着发。这么做有三个好处单平面速率降低走线和时序压力小了每个平面有自己的仲裁器仲裁并行度成倍提升前面算出来的 20 皮秒一块 Cell 才有可能被消化某个平面出故障时其它平面还能继续工作带宽降级但不全断。代价也很明确同一个包的不同 Cell 走了不同平面到达出口的时间不再有保证乱序是必然的。这就引出了后面第五部分要讲的顺序保证和重组问题。很多第一次设计这类系统的人会忽略这一点以为 Cell 按顺序发出去就按顺序到达实际不是。3. VOQ把队头阻塞从根上拆掉3.1 单 FIFO 的 58.6%一个绕不过去的数字如果输入端口只有一个先入先出队列会发生什么假设队头那个包要去端口 5但端口 5 此刻正忙那这个包只能等。问题是它后面的包可能要去端口 7而端口 7 完全空闲——但因为队头堵着后面的包一个也走不了。这就是队头阻塞HOL Blocking。这个问题的严重程度有经典结论支撑在均匀随机流量下单 FIFO 输入队列的吞吐上限只有约 58.6%准确值是 2 减根号 2。也就是说你买了一块 25.6T 的芯片如果输入侧只有一个队列实际能跑出来的可能连 15T 都不到。解决思路非常直接按目的地拆队列。到端口 5 的包放进队列 5到端口 7 的放进队列 7两者互不干扰。这就是虚拟输出队列 VOQVirtual Output Queue。队头阻塞被消掉了因为队头永远是去往某个特定输出的包只要那个输出空闲它就能走。3.2 N² 队列的代价资源不是免费的VOQ 的代价是队列数量按平方增长。64 个输入端口每个输入要为 64 个输出各维护一组队列如果每个端口对之间再分 8 个优先级就是 64 × 64 × 8 32768 个队列。每个队列都需要一个描述符、一个深度计数器、一对头尾指针还要参与每时隙的请求-授权流程。32768 个队列的调度器规模在片上是很实在的一块面积和功耗。工程上有几种折中。一是端口分组不按单个输出端口建 VOQ而是把输出分成若干组组内共用队列。组数少了队列规模降下来代价是组内还是有一点残余的队头阻塞不过比全局 FIFO 好太多。二是优先级裁剪不是所有端口对都需要 8 个优先级很多实现按端口角色配置队列数。三是队列描述符外置把大表放到片外或者用更紧凑的编码。注意VOQ 数量不是越多越好。队列数增加会让每个队列的平均积压变浅调度器在浅队列之间频繁切换反而可能拉高调度开销和时延抖动。选型时看的是VOQ 数量 × 优先级数和实际业务队列需求的匹配度而不是单纯比数字大小。3.3 单芯片与多芯片信用机制的两种用法单芯片内部的 Crossbar 是无阻塞的出方向又有共享缓冲兜底所以很多实现里输入侧不需要复杂的信用机制直接按请求-授权走就行。但一旦 fabric 跨了芯片——比如多芯片拼成一台大盒子或者整机采用独立的交换网板——情况就变了。这时的关键问题是如果远端出口缓冲满了怎么让近端停止发送如果不管Cell 发过去也会被丢掉白占带宽。所以引入了基于信用的流控credit-based flow control接收侧为每条队列预留一定量的缓冲把可用空间折算成信用credit告诉发送侧发送侧每发一个 Cell 扣一个信用扣到零就停。这里有两个必须一起算的量。一个是信用往返时延从发送侧扣信用到收到新信用中间隔着链路传播、接收侧处理、返回路径这个时间乘以线速就是在途数据量信用池必须至少覆盖它否则链路会出现空转。400GE 端口往返 1.5 微秒在途就是 50GB/s × 1.5µs 75KB。另一个是信用粒度信用按 Cell 记还是按字节记按 Cell 记简单但会有取整浪费按字节记精确但计数器位宽更大。实际实现里常见按 Cell 记配合 Cell 大小做粒度换算。3.4 队列选择与出队顺序SP DWRR 是最常见的组合VOQ 解决的是包放在哪个队列还有一个问题没解决同一个输出的多个队列先发谁。跨端口可能同时有高优先级信令和低优先级备份流量两者的诉求完全不同。主流做法是**严格优先级SP加加权轮询DWRR**的组合最高优先级队列用 SP保证低时延业务不被拖剩下的队列用 DWRR 按权重分配带宽。权重配置的关键是让它和实际业务带宽匹配比如 400GE 端口上三个队列配 1:1:2意思是在拥塞时它们的保底带宽比是 1:1:2。我踩过的坑是权重配成比例数值而没考虑端口速率差异。同一组权重在 100GE 端口上和 400GE 端口上对应的绝对带宽差了四倍如果有跨端口的一致性要求权重必须按速率归一化或者用百分比形式表达。4. Shared Buffer把有限的片上 SRAM 用在最需要的地方4.1 共享与独享为什么共享能扛住微突发出方向缓冲有两种组织方式。**独享dedicated**是每个出端口分一块固定大小的缓冲比如 64MB 分给 64 个端口每端口 1MB谁也别抢谁的。**共享shared**是所有出端口共用一个大池子谁有需要谁多占一点。网络流量的一个基本特征是不均匀。同一时刻总有几个端口在堵另外几个端口很闲。独享方案下堵的端口那 1MB 用完就开始丢闲的端口那 1MB 基本闲置。共享方案下闲端口的空间可以被堵端口临时借用整个池子的利用率高得多。有研究做过对比同样大小的池子共享方式能支撑的突发吸收能力通常是独享的两到四倍。代价也很明确。共享池的聚合读写带宽要做到 2 倍线速以上前面算过是 51.2Tbps 这个量级多端口同时访问同一个 bank 会冲突需要做 bank 交织和访问调度而且共享必然带来某条队列吃光整个池子的风险必须有门限管理。4.2 动态门限 DT 与 α 的取值门限管理最简单的形式是静态门限每条队列最多占 X 字节超了就丢。它的问题是 X 很难定——定小了池子空着也不用定大了几条激进队列就能把池子占满别的队列全被饿死。所以主流实现用动态门限Dynamic Threshold, DT。核心思想是某条队列能占多少取决于当前池子里还有多少空闲。空闲多的时候允许它多占空闲少的时候收紧。简化形式大概是这样threshold(q) alpha * (Fb - Qb) 其中 Fb 共享池总容量 - 为无损预留的 headroom Qb 当前所有队列已占用的总字节数 alpha 调节系数常见 1/8、1/4 若 队列 q 的占用量 本次入队量 threshold(q) 触发丢弃有损场景或反压无损场景α 的取值是这套机制里最需要拿捏的旋钮。取 1/8 比较保守队列之间隔离性好但池子利用率偏低取 1/4 激进一些利用率和突发吸收都更好但要小心某条大流把池子吃得太满取到 1/2 就非常危险了一条队列能吃掉半个池子其它端口的时延会被严重拖累。我的经验是面向通用数据中心的场景用 1/8 到 1/4面向存储和无损网络用更保守的值因为无损场景对丢包的容忍度是零。注意厂商文档里的动态门限公式各不相同有按剩余空间算的有按占用比例算的还有带指数加权移动平均的。改这些参数前一定要找到对应芯片的手册确认公式形式不同实现里同样叫 α 的系数含义可能完全相反。4.3 链表式缓冲管理free list、链表与多播复制缓冲池里存的是 Cell管理它们的数据结构是链表。初始化时所有 Cell 描述符串成一条空闲链表free list入队时从 free list 摘一个挂到目标队列尾部出队时把描述符还回 free list。整个过程中 Cell 本体在 SRAM 里不动只动描述符里的 next 指针。这套设计有几个细节容易出问题。一是描述符耗尽Cell 存储还有空位但描述符用完了此时新来的包只能丢监控里要单独看这个计数。二是链表过长导致的遍历开销出队是从头部取入队是往尾部挂两端都要能 O(1) 访问所以是双向链表或者头尾指针分离的环形结构。三是多播一个多播包要复制到多个出端口如果每个副本都单独存一份 Cell缓冲消耗按多播组规模线性放大64 个出端口的多播组就是 64 倍占用。省空间的做法是单次写入、多次读取也叫复制读。Cell 在池子里只存一份每个出端口在读出阶段各自读一遍读的时候把目的端口位图bitmap作为 Cell 头的一部分出方向按位图决定哪些端口要取。这样缓冲占用只算一份代价是读带宽需求增加——但读带宽本来就是共享池的瓶颈之一所以真实实现里通常还有个折中小规模多播用复制读规模特别大的多播可能用专门的复制引擎或者干脆限制。4.4 无损网络的 headroom缓冲到底该留多少无损网络RDMA over Converged Ethernet 这类场景不允许丢包靠的是优先级流控PFC。但流控信号不是瞬间到达的从出口缓冲接近满、到发出暂停帧、到上游真的停下来中间隔着一个往返时间。这段时间里上游还在全速发出口必须留出一块缓冲来接住这些还在路上的数据这块预留就叫headroom。headroom 怎么算核心就是线速乘以反应时间。400GE 端口链路加芯片内部流水线的反应时间按 1.5 微秒估headroom 50GB/s × 1.5µs 75KB。工程上一般乘个安全系数取 2也就是 150KB 左右。看起来不多但要注意这是每个端口都要预留的而且是只进不出的死区。端口数每端口 headroomheadroom 总量占 64MB 池子比例32150 KB4.8 MB7.5%64150 KB9.6 MB15%128150 KB19.2 MB30%表里能看出来端口数一多光 headroom 就吃掉三成池子。这就是为什么高密度端口芯片上能用的共享缓冲比标称小不少规划 DT 和 α 的时候必须先把 headroom 扣掉再算。5. Cell Fabric 上的乱序、重组与顺序保证5.1 Cell 头里到底带了哪些信息Cell 是内部搬运单位它必须自带足够的元信息让下游知道怎么处理。典型字段包括源端口与目的端口目的端口用于出口归宿源端口用于回填统计和必要的源检查。VOQ / 队列标识标识这个 Cell 属于哪条队列出口重组和顺序保证靠它。包标识与序号同一个包的多个 Cell 共享一个包 ID序号标明在包内的位置。SOP / EOP 标志标记包头和包尾重组引擎靠它判断一个包的边界。有效字节数最后一个 Cell 通常不满需要知道真实长度。优先级 / 流量类别调度和缓冲管理用。多播位图多播场景下标识需要复制到哪些端口。ECC 校验位内部传输也得过校验尤其是长走线的并行总线。这些字段加起来几十比特占了 Cell 总宽度的相当一部分。做带宽预算的时候不能只算净荷头开销必须一起算进去。5.2 同一条流为什么不能乱序以太网本身不保证顺序理论上乱序是允许的。但实际网络中乱序的代价很大。TCP 收到乱序段会触发重复 ACK三次重复 ACK 就触发快速重传把本来没丢的包重传一遍有效带宽直接打对折RDMA 这类协议对乱序更敏感很多实现会把乱序当作错误处理。所以交换设备必须保证同一条流内部的顺序这是硬约束不是可选项。麻烦在于多平面 striping。前面说过同一个包的 Cell 会轮流分配到不同平面走不同路径到达出口的时间不再一致乱序几乎必然发生。解决办法大致有三类。第一类是同一包不跨平面。一个包的所有 Cell 都走同一个平面包与包之间才做 striping。这保证了包内的顺序实现简单代价是包级别的负载均衡粒度变粗短包多的时候平面间负载不均。第二类是按 VOQ 保持顺序。每个平面的调度器对同一个 VOQ 的 Cell 保持先进先出出口侧按 VOQ 做汇聚只要每个平面内部的顺序是对的汇聚时再按平面顺序取就能恢复全局顺序。代价是汇聚逻辑要考虑多个平面的到达状态缓存需求增加。第三类是序号重组。每个 Cell 带全局序号出口按序号重排。灵活性最高但需要重组缓冲和超时机制最坏情况下一个 Cell 迟到就得一直等。实际芯片多数是第二类和第三类的组合包内顺序靠序号保包间顺序靠 VOQ 汇聚保。5.3 重组缓冲与超时丢弃出口拿到一堆乱序 Cell需要把它们拼回完整的帧。重组引擎的逻辑是遇到 SOP 就开一块新缓冲把后续 Cell 按序号填进去遇到 EOP 就把整包提交给出口队列。麻烦的是异常情况。如果某个 Cell 在 fabric 里被丢了比如校验错、信用不足这个包的 EOP 就永远等不来重组缓冲会被一直占着。所以必须有超时机制一段时间内没等到 EOP就把这个半包丢掉释放缓冲同时把该包已经拼好的部分一并丢弃绝不能发一个残缺的帧出去。相关的计数器包括Cell 失序计数、重组超时计数、半包丢弃计数。这几个计数在排障时非常关键它们异常基本可以锁定 fabric 或者重组环节出了问题。提示重组超时值不能设得太短。设短了正常但路径较长的包会被误杀表现出来就是零星丢包和 CRC 错误而且只在多平面负载不均时出现很难复现。留足最坏路径的余量再往下调。5.4 降级路径与背压传导真实系统里 fabirc 也可能出问题某个平面失效、某条链路的信用长期批不下来、某个 SRAM bank 报 ECC 错误。这些情况下系统需要优雅降级而不是直接崩。单平面失效时剩下的平面继续工作聚合带宽按比例下降。如果 striping 是按固定轮转做的失效率是 1/N如果做了动态权重可以先重分配。关键在于出口侧的重组引擎要能识别这个平面不会再来 Cell 了否则重组缓冲会被永久占用。信用枯竭的传导路径值得单独说一下。出口缓冲满暂停返还信用输入侧信用见底停止发送VOQ 开始快速积压积压到门限触发入口反压或丢包。这条链路上任何一环配置不当都会表现为某个端口吞吐突然掉一截过一会又恢复。排查时要顺着链路一段段看计数而不是只盯终端表现。6. 把架构映射到计数器实测与排障怎么做6.1 症状、位置、计数器的对照表架构讲完落到实战就是一件事拿到一个现象能快速定位到是哪一段结构出了问题。下面这张表是我自己排障时常用的对照。现象大概率出问题的位置优先看的计数平均负载不高但偶发丢包出方向共享缓冲、动态门限出队列水位峰值、DT 丢弃计数、headroom 使用量单个出端口吞吐上不去VOQ 或 Crossbar 匹配各 VOQ 深度、授权成功率、fabric 信用余量同一入端口到不同出端口时延差异大VOQ 队列选择、调度权重各队列积压、DWRR 权重与实际带宽比小包达不到线速流水线、Cell 调度Cell 速率计数、流水线停顿计数重启后短暂丢包重组与顺序保证Cell 失序计数、重组超时计数多播业务一开就丢多播复制与缓冲占用多播缓冲占用、复制读带宽看这张表的时候要有个意识现象的位置和原因未必在一块芯片上。比如端到端时延抖动可能是上游某台设备的 VOQ 积压造成的而你在下游设备上看到的只是结果。所以定位的第一步永远是确定责任设备第二步才是看它的内部结构。6.2 微突发怎么观测为什么平均值看不出问题微突发是这类系统里最常见也最难查的问题。它的时间尺度通常在微秒到几十微秒而监控系统默认的采样窗口往往是 1 秒甚至 5 分钟。一个持续 10 微秒、打满 400GE 的突发摊到 1 秒里只让平均速率上升了 0.4%图上完全看不出来但它已经足以把某个出队列的水位推过动态门限。观测微突发有几个可行的办法。一是看端口峰值计数很多芯片提供了带时间窗口的水位峰值寄存器比平均值有用得多。二是看出队列丢弃的突发特征如果丢弃是成簇出现的而不是均匀分布基本可以确认是突发而非持续拥塞。三是缩小采样窗口把统计周期从秒级降到毫秒级甚至更细代价是数据量大得有选择地开。四是看ECN 标记和 PFC 暂停帧的时间分布这两个信号比速率计数更贴近瞬时状态。6.3 几个我踩过的坑第一个坑是把共享缓冲当成独立缓冲来算。规划的时候按 64MB ÷ 64 端口算得出每端口 1MB然后按 1MB 去设计 DT 门限。结果一上量就丢包。真实可用的空间是 64MB 减去 headroom 预留、减去控制结构占用的那部分实际能给数据用的可能只有 80%。第二个坑是 VOQ 优先级配少了。一开始只配了 4 个优先级觉得业务够用。后来上了新的业务类型两类流量被塞进同一个优先级队列一拥塞就互相干扰高时延的那类被低时延大流拖得很难看。VOQ 数量在流片时是定死的规划阶段一定要留余量。第三个坑是忽略多播的缓冲放大效应。一个 64 个出端口的多播组如果按每副本一份存储缓冲占用是单播的 64 倍。测试的时候只跑单播一切正常一开多播业务缓冲瞬间被吃光。后来确认了芯片用的是复制读方案缓冲占用只算一份但复制读带宽成了新瓶颈需要给多播单独留带宽配额。第四个坑是用平均值判断微突发。前面讲过这个坑很经典。我们花了不少时间才把监控改成毫秒粒度加峰值水位的方式改完之后很多偶发的问题一下就解释得通了。第五个坑是调 α 调过头。为了提升缓冲利用率把 α 从 1/8 调到 1/4短期看丢包确实少了但某条大流开始能吃掉相当比例的池子其它端口的时延明显抬升。最后回到 1/8改从流量整形侧解决效果反而更稳。6.4 一个实用的验证思路如果你手上有设备想验证自己是不是真的理解了这套数据通路我建议做一个简单的对照实验构造一组从端口 0 到端口 1 的大流同时构造一组从端口 0 到端口 1 之外的多个端口的小流让它们共用端口 0 的入方向带宽。如果 VOQ 工作正常小流不应该被大流显著影响因为它们在不同的 VOQ 里。如果观测到小流的时延随大流增大而明显上升说明队列隔离没做到位可能是 VOQ 数量不够或者调度权重配置把两类流量耦合在了一起。这个实验不需要很贵的仪表用两台服务器打流加基础计数就能看出趋势是我做方案验证时的第一步。关注后续我会再写一篇讲控制通路的部分——查表、ACL、计量这些模块和今天讲的数据通路是怎么咬合的那块出问题的方式和数据通路完全不同经常表现为配置生效了但行为不对这种更绕的现象。