IEEE 802.1Qcr ATS异步流量整形:不靠时间同步的TSN调度机制

发布时间:2026/9/29 15:23:57
IEEE 802.1Qcr ATS异步流量整形:不靠时间同步的TSN调度机制 简介时间敏感网络TSN是工业以太网和车载网络的基础传统流量整形往往依赖全网时间同步而IEEE 802.1Qcr引入的异步流量整形ATS提供了一套更灵活的方案。ATS通过交织整形器UBS按数据流维护信用在不依赖gPTP时间同步的前提下压平突发流量解决了Qbv时间门控过于刚性、CBS队列级整形粒度不足的问题。文章从紧急度、恢复信用和hold time等核心概念切入讲解UBS状态机与CIR/CBS参数换算方法并探讨ATS与Qbv混合部署时的优先级映射。结合实际工程中的踩坑案例帮助网络工程师理解ATS工作原理并快速落地到交换机配置为复杂网络的低时延流量调度提供参考。1. IEEE 802.1Qcr 到底做了什么不靠时间同步的异步流量整形TSN 协议家族里IEEE 802.1Qcr-2020 是被很多人低估的一位。它不依赖 gPTP 全网时间同步也不搬出复杂的时间门控列表而是用一套异步整形机制在每一跳上把突发流量压平这套机制叫 ATSAsynchronous Traffic Shaping调度器叫 UBSUrgency-Based Scheduler紧急度调度器。这份 PDF 就是 IEEE 对 802.1Q 的异步流量整形修订版把交织整形器Interleaved Shaper正式写进了标准。它适合三类人正在对比 TSN 几种调度器、想知道 Qbv 之外还有什么可选方案的人需要自己实现整形状态机的开发以及面对 CIR/CBS 参数不知道如何落到交换机配置的规划工程师。读完你会得到一张技术全景ATS 怎么工作、参数怎么定、标准从哪里读起、实现踩在哪。2. ATS 核心机制UBS 交织整形器怎样把突发压平2.1 为什么需要 ATSQbv 和 CBS 的先天局限802.1Qbv 时间感知整形器TAS的思路是给每类流量开一个门控列表按周期打开关闭队列让高优先级流在特定时间窗内独占线路。代价是全网节点必须共享同一份时间基准任何一跳的时钟漂移都会把时间窗打乱。802.1Qav 的基于信用的整形器CBS不需要全网同步但它作用在队列级别一个队列里混着多条流彼此抢信用整形粒度太粗。Qcr 要补的正是这个空档它按数据流做整形而不是按队列。每一路流都有自己的信用计数器出口调度时逐流判断能不能发。关键设计是交织——多个整形器挂在同一个出口其中某一路没攒够信用时别的路照样能插进来发帧。这样不会出现传统整形器那种一条流堵住整条队的情况也不需要全网时间同步。很多工程师第一次读这份标准时把它和 Qbv 对立起来看其实补丁原文里明确说了 ATS 可以和 TAS 共存Qbv 管大时间窗ATS 管窗口内的突发细节。2.2 紧急度、恢复信用与 hold time三个绕不开的概念UBS 的整套逻辑围绕一个词urgency紧急度。每个整形器维护一个转发信用forwarding credit帧到达时如果信用足够立即转发并扣减如果不够帧被 hold 住等待信用恢复。为了不让低优先级流饿死标准引入了恢复信用recovery credit的概念帧被 hold 期间不停止计费而是按 CIR 的斜率持续累积恢复信用等信用回到允许值再放行。这里有个和直觉相反的地方hold time 越长攒下来的恢复信用越多一旦放行就是一串帧连续发出。标准没有放任这个行为而是用 hold-and-forward 上限做了约束。实际工程里这些参数会映射成驱动里的几个 32 位寄存器。调参时最容易犯的错是把 CIR 当成传统令牌桶的速率而忽略了 ATS 的信用是带符号的转发时扣成负值也允许只要不超过阈值。理解这一点后面读状态机图才会顺。2.3 交织整形状态机从标准的事件表读出发帧逻辑标准的核心不是一段代码而是一张状态机图和一张事件表。我把事件表压缩成四行方便对照实现事件当前状态动作帧到达idle检查转发信用足够则立刻转发并扣减不够则进入 hold 状态信用更新定时hold按 CIR 斜率增加恢复信用若信用超过允许值则切回 idle 并放行帧发送完成hold/idle观察队列里是否还有 backlog逐帧重复上述判断hold 超时hold丢弃或降级处理受 hold-and-forward 上限约束这张表翻译成代码就是两层循环外层看信用够不够内层看队列里有没有帧。多个整形器之间则是一个选择器相当于把多个独立状态机的结果聚合到一个出口。理解状态机比背条款有用得多。标准文档里的图示逻辑和我平时在驱动里写的清空队列、更新信用、触发发送这三个函数的顺序完全一致。3. 这份标准 PDF 怎么读三个区块与参数表速查3.1 拿到 PDF 先看这三个区块IEEE 802.1Qcr-2020 是一份补丁性质的标准不是从零讲起的教科书。从头到尾顺序读很容易迷失在交叉引用里。我的习惯是先看三个区块。第一个区块是最前面的范围、规范性引用和定义这里确认了 ATS 是桥转发过程的一部分不是独立协议同时弄清两个缩写CBS 在 Qav 里是 credit-based shaper在 Qcr 里是 committed burst size同名不同义。第二个区块是第 8 章的转发模型尤其是帧转发和排队那一节ATS 的实现细节全部落在这里。第三个区块是各类数据模型相关的字段定义如果要做网管或控制器对接这里决定 YANG 模型和 MIB 怎么映射。不建议跳过的反而是 Annex 里那些示例。标准正文讲机制Annex 给数值例子例如某个端口速率下 CIR 和 CBS 组合出来是什么效果。对照这种例子去推自己的配置比凭空理解快很多。3.2 参数表的字段是怎么变成实现需求的标准里的参数不是给人直接填的而是给实现者定义接口的。最常见的几个字段长期被混淆我列一张自己整理的速查表参数标准定义我的理解落地时的注意点committedInfoRate提交信息速率该流允许的长线平均速率单位是 bit/s很多设备驱动用 kbit/s换算别漏committedBurstSize提交突发大小信用初始值和上限相关量单位是 bit至少要有 1 个 maxSDU 对应的值maxSDU最大服务数据单元单帧长度上限决定帧能否入队超过的直接进入非 ATS 路径holdAndForwardLimit保持转发上限帧被 hold 的时间上限不设时恢复信用会无限累积产生大突发这张表对我最大的价值是避免把参数直接抄进配置文件。比如 committedBurstSize 的英文缩写刚好是 Qav 的 CBS两个含义差很远maxSDU 又和 VLAN 里的 MTU 概念强相关。标准里每个字段后面都跟着取值范围和默认行为实现时不要只看字段名就定接口。3.3 图纸与流程图把标准图翻译成代码条件补丁里真正描述算法的是图。标准用状态图和流程图表达整形器逻辑图里每个分支都需要变成代码里的条件判断。我的做法是先找出图里的所有分支条件帧长度是否超过 maxSDU、当前信用是否大于 0、是否有恢复信用可用、队列是否非空。把这四个条件列全状态机代码基本就成型了。剩下的定时器中断、发送完成中断都是围绕这四个条件触发的事件源。标准图看得越细写驱动时返工越少。4. 从标准到实现参数换算、状态机代码骨架与 TAS 混跑4.1 CIR/CBS 的单位换算与寄存器映射实现 ATS 第一步不是写代码是把标准参数换算成寄存器能存的值。CIR 在标准里是 bit/s但很多交换芯片的整形器寄存器干脆按 byte/s 算也有按 kbit/s 算的。我一般先全部转成 bit 做中间量再按驱动要求切分。换算规则如下1 Mbps 1,000,000 bit/s一个 1500 字节的帧占 12,000 bit。如果端口是 100M 全双工一个 1500B 帧在线上占用 120 微秒。这些基础值决定了 CBS 的取值有没有意义。CBS 至少要能装下一条流里最大帧的突发也就是一个 maxSDU 对应的比特数否则帧到达时永远没有足够信用流直接卡死。标准里的数值没有预设单位陷阱但实现时的寄存器一定是定宽整数。CIR 可能只有 16 位存不下 10G 端口的线速值常见做法是提高时间粒度把一个定时周期从 1 微秒放大到 10 微秒甚至 100 微秒用粗粒度换能存下的最大值。这个取舍在标准里不会写却决定了你的整形精度。4.2 整形器状态机的 C 语言参考骨架以下是一段参考骨架不是完整实现但能看明白状态机的搬运逻辑。我用单流加简化定时触发来演示多流就是把这个结构体复制 N 份再挂到选择器上/* 单流交织整形参考骨架伪代码非官方实现 */ typedef struct { uint64_t cir_bps; /* 提交信息速率bit/s */ uint64_t cbs_bits; /* 提交突发大小bit至少 1 个 maxSDU */ uint32_t max_sdu; /* 最大帧长字节 */ int64_t credit; /* 转发信用有符号bit */ int64_t recover; /* 恢复信用非负bit */ int held; /* 当前是否存在被 hold 的帧 */ } ats_shaper; /* 定时器中断按 CIR 累积信用斜率由周期决定 */ void ats_update_credit(ats_shaper *s, uint64_t period_ns) { int64_t inc (int64_t)(s-cir_bps * period_ns / 1000000000ULL); if (s-held) { s-recover inc; /* hold 期间攒恢复信用 */ if (s-recover s-cbs_bits) { /* 攒够了放行 */ s-held 0; s-recover 0; s-credit s-cbs_bits; } } else { s-credit inc; /* idle 时累积转发信用 */ if (s-credit s-cbs_bits) s-credit s-cbs_bits; /* 不能无限累积 */ } } /* 帧到达够信用就立刻发不够就 hold */ int ats_frame_arrive(ats_shaper *s, uint32_t frame_len) { if (frame_len * 8 s-cbs_bits) return 0; /* 超过 CBS走非 ATS 路径 */ if (!s-held s-credit frame_len * 8) { s-credit - frame_len * 8; /* 转发并扣减信用 */ return 1; /* 允许发给调度选择器 */ } s-held 1; /* 进入 hold 状态 */ return 0; }逻辑说明定时器中断负责给信用补充积分hold 状态和 idle 状态分开累积避免低优流在 hold 期间丢光信用帧到达函数只做两个判断——帧长度有没有超过突发上限以及当前信用够不够发。参数说明cir_bps 用 bit/s 让换算统一cbs_bits 直接用标准单位max_sdu 只做准入校验。代码里没有处理多流选择真实驱动里要在此基础上加一个调度选择器决定多个整形器同时就绪时谁先上路那部分由优先级表控制。4.3 与 802.1Qbv 混合部署时的优先级映射ATS 并不排斥 TAS实际部署经常两个一起开。TAS 用门控隔离不同类型的流ATS 在门控打开的窗口内继续做逐流整形。这里要处理好优先级映射802.1Qcr 的整形器对象挂在某个优先级队列上而 Qbv 的 GCL 也是按队列做门控。我的实践是先画清四层映射帧的 VLAN priority 映射到 TC流量类别TC 映射到队列队列对应 GCL 门控队列上的 ATS 整形器再按 CIR/CBS 生效。四层映射里任何一层对不上ATS 看起来就没生效。规划时先定 GCL 的时间窗再算 ATS 的 hold time 上限让帧在窗口内刚好能被放完。一次失败的混跑调试十有八九是相位没对齐跟算法本身无关。5. 避坑清单五个翻车现场与根因5.1 CBS 设成 0 导致流根本发不出去现象配置了 ATS 后该流的计数一直是零端口上完全看不到帧。原因我把 committedBurstSize 填成了 0且实现了缓存区初始化没有给预警帧到达时 credit 是 0永远小于帧长度所有帧都进入 hold 状态最终被 hold 超时丢弃。解决CBS 至少给一个 maxSDU 对应的 bit 数例如 1500 字节帧就给 12,000 bit。这个教训是我对着标准看了半天状态机才反应过来初始 credit 是 0 还是 CBS标准里写的是实现选择不是约定俗成。5.2 maxSDU 没配造成整形后突发不减反增现象加了整形器之后抓包发现突发比不加还大帧与帧连成片。原因我先配了 CIR但没配 maxSDU系统把超过上限的大帧直接放行到非 ATS 路径等于旁路了整形同时恢复信用在 hold 期间持续累积攒到上限后一次性放出一串帧。解决把每路流的 maxSDU 按实际应用报文长度填准并且限制 hold-and-forward 上限给恢复累积设一个天花板。之后再看抓包突发形态就正常了。5.3 与 TAS 混跑时低优流被饿死现象Qbv 和 ATS 同时部署后低优先级流平均时延翻了三四倍甚至出现长时间断流。原因GCL 门控把低优队列的窗口排在高优窗口后面而 ATS 的 hold time 按自己的节奏计算低优帧攒够信用后正好错过门控窗口等于每个周期都被挡一次。解决先定 GCL 相位再倒推 ATS 的 hold time让信用恢复到临界值的时间落在门控打开之前。我把这个做法记成了自己的配置模板先 TAS 后 ATS顺序不能反。5.4 把 ATS 的 credit 当 Qav 的 credit 理解现象对照 802.1Qav 的 CBS 经验读 Qcr发现状态机推演总是差一步尤其恢复信用出现负值的场景完全对不上。原因Qav 的 credit 是单调速率桶Qcr 的转发信用和恢复信用是拆开维护的hold 状态下恢复信用照样增长而 Qav 里 hold 就意味着不动作。解决强制自己按状态机事件表逐格推演丢开直觉。我后来把事件表打印出来贴在工位上再没出过这种错。5.5 iperf3 测不出整形成效就断言失败现象用 iperf3 打流验证整形效果改 CIR 改 CBS 看曲线都差不多于是断定实现有 bug。原因iperf3 的 TCP 模式自带拥塞控制速率本身就平滑UDP 模式默认也是均匀发包天然就是整过的形状测的是链路能力不是整形能力。解决改用 pktgen 或自写 UDP 突发流固定时间间隔塞一串大帧再在出口抓时间戳。从那以后我验证整形器一律用一个字段可调的 UDP burst 脚本不用 iperf3 当证据。6. 验证 ATS 是否生效用到达间隔方差说话判断整形器有没有起作用最高效的办法是看出口帧的到达间隔分布。原始突发流的帧间隔波动很大经过 ATS 之后应该收敛到接近 CIR 斜率的均匀间隔。具体流程先在发送端用 pktgen 打一个突发流每 50 微秒连发 10 个 1500 字节帧持续 1 秒在交换机出口抓包导出帧时间戳再用下面这个 Python 脚本统计到达间隔的均值与方差。# 读取 tshark 导出的时间戳文件行格式相对时间(秒) import sys import statistics ts [] with open(sys.argv[1]) as f: for line in f: ts.append(float(line.strip())) deltas [ts[i1] - ts[i] for i in range(len(ts)-1)] mean statistics.mean(deltas) var statistics.pvariance(deltas) # 理论间隔1500B 帧在 10Mbps CIR 下的间隔 12000 / 10e6 1.2ms print(fmean{mean*1e6:.1f}us var{var*1e9:.1f}ns^2)判断标准均值落在理论间隔的 ±10% 内且方差量级不到均值的平方说明整形器在稳定工作。如果均值正常但方差很大说明恢复信用没有限幅hold 后一次性放行了整串帧如果均值偏小说明有非 ATS 路径把帧旁路了。我在一次现场调试里就是因为方差异常大才发现 maxSDU 漏配五分钟定位到问题。从那以后每次交付 ATS 配置我都先在本机跑一遍这个统计确认方差形状对了再上真机希望帮到你。本文还有配套的精品资源点击获取