解决什么问题
上一节你能读懂别人的 Qbv 配置了。这一节让你亲手下发一张,并验证它真的按时间开关门。不需要专门的 TSN 交换机,也不需要带硬件 offload 的网卡——`tc-taprio` 有纯软件模式,一块普通网卡(甚至一对 veth)就能建立第一手直觉。
准备:一个能用的 CLOCK_TAI
软件模式 taprio 的内部定时器默认用 `CLOCK_TAI`。在真实部署里,`CLOCK_TAI` 应由 gPTP 喂(见第三章):
# 真实部署:用 802.1AS 把网卡硬件时钟(PHC)同步到 grandmaster
sudo ptp4l -i eth0 -f /etc/linuxptp/gPTP.cfg --step_threshold=1 &
# 再把 PHC 喂给系统 CLOCK_TAI
sudo phc2sys -s eth0 -O 0 -m &学习阶段只想看 taprio 的行为,可以先不接 PTP——但要记住:没有同步的 `CLOCK_TAI`,`base-time` 这个绝对时刻只在本机有意义,不能跨设备对齐。这正是“为什么 Qbv 依赖 802.1AS”的现场版。
第一步:规划一张最小 GCL
目标:cycle = 1ms,前 300us 只放关键流量(TC6),后 700us 放其余流量。
| 窗口 | 时长 | 开放的 TC | gate mask |
|---|---|---|---|
| 关键窗 | 300us | 只有 TC6 | `40`(`0100 0000`) |
| 普通窗 | 700us | TC0–TC5 + TC7 | `bf`(`1011 1111`) |
cycle = 300000 + 700000 = 1,000,000 ns = 1ms。两段相加自洽(回顾上一节的“cycle 自洽”检查)。
第二步:下发(软件模式)
sudo tc qdisc replace dev eth0 parent root handle 100 taprio \
num_tc 8 \
map 0 1 2 3 4 5 6 7 \
queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \
base-time 0 \
sched-entry S 40 300000 \
sched-entry S bf 700000 \
clockid CLOCK_TAI`base-time 0` 是一个过去时刻,taprio 会自动加上整数倍 cycle,对齐到下一个 1ms 边界再启动——所以不必费心算一个“未来”时刻。`clockid CLOCK_TAI` 在软件模式必须写(硬件 offload 才省略)。
第三步:验证它真的生效
下发成功不等于按预期工作。至少做三层验证:
# 1) 表结构对不对:能看到 taprio、cycle-time 与两条 sched-entry
tc qdisc show dev eth02) 计数对不对:持续打流时,看每个 TC 的发送计数变化 tc -s qdisc show dev eth0
3) 时序对不对:在对端抓包,确认关键帧只在关键窗口附近出现 sudo tcpdump -ni eth0 --time-stamp-precision=nano -ttt ```
要让帧落到 TC6,发送端要给 socket 设 `SO_PRIORITY`(优先级经 `map` 映射到 TC6)。一个简单做法:
# 让 ping 走优先级 6(再由 map 落到 TC6)
sudo ping -Q 0xC0 <对端IP> # 0xC0 的 PCP 位对应优先级 6
# 或用支持 --tos/SO_PRIORITY 的压测工具打 TC6 与 TC0 两路流对比判断标准:关键流(TC6)的抓包时间戳应集中在每个 1ms 周期的前 300us;普通流(TC0)应只在后 700us 出现。只有计数上升不够——它只证明“发出去了”,不证明“准时”;时序证据要靠抓包时间戳。
软件模式 → txtime-assist → 硬件 offload
软件模式适合建立直觉,但精度受内核调度影响。要拿微秒级窗口的真实数据,得换模式:
| 模式 | flags | 精度 | 适用场景 |
|---|---|---|---|
| 软件 | (无) | 受内核调度影响,常有几十 us 抖动 | 学习、功能验证 |
| txtime-assist | `0x1` | 配合 `etf` qdisc 在发送侧排序,较好 | 部分支持 LaunchTime 的网卡 |
| 硬件 offload | `0x2` | 网卡硬件循环执行 GCL,最准 | TSN 网卡 / 交换机(如 SW0) |
换到硬件 offload,命令主体不变,去掉 `clockid`、加 `flags 0x2`,并要求网卡支持:
sudo tc qdisc replace dev eth0 parent root handle 100 taprio \
num_tc 8 map 0 1 2 3 4 5 6 7 queues 1@0 1@1 1@2 1@3 1@4 1@5 1@6 1@7 \
base-time 0 sched-entry S 40 300000 sched-entry S bf 700000 \
flags 0x2到了真实 SW0 这类交换机,你不再敲 `tc`,而是在它的 Qbv 配置页填同样的语义(门控循环时间、循环开始基准时间、GCL 门控列表)——但前提一致:时钟先由 802.1AS 同步好,offload 由硬件执行。
常见报错与排查
- `Specified qdisc kind is unknown`:内核或 iproute2 太老,缺 taprio 支持,先升级。
- gate mask 被当十进制 / 报越界:用十六进制,回看上一节的 gate mask 表。
- `base-time` 给了很久远的过去 + 很大 cycle:对齐计算慢、不直观,给 `0` 或近未来时刻更好。
- `flags 0x2` 还写了 `clockid`:冲突,硬件 offload 必须省略 `clockid`。
- 清除配置重来:`sudo tc qdisc del dev eth0 parent root`。
带来了什么新问题
软件模式能让你跑通流程、建立直觉,但它的精度不代表硬件现场。内核调度会给发包时刻带来几十微秒级抖动,所以软件模式适合验证“表对不对、门开没开”,不适合用来证明微秒级窗口的最坏情况。
把同一张表搬到真实部署,还有三件事要补:接上 802.1AS 让 `CLOCK_TAI` 跨设备对齐、选择硬件 offload 拿到稳定精度、为多跳路径重新计算窗口相位和误差预算。换句话说,本地单机验证过的 GCL,到了多设备路径上不能直接照搬——这正好接回前面“base time 与 offset”和“误差预算”两节。
检查点
- 把关键窗口从 300us 改成 200us、cycle 仍保持 1ms,命令要怎么改?
- 只看 `tc -s qdisc show` 的计数在上升,能不能证明关键帧“准时”?还缺什么证据?
- 要在真实 SW0 交换机上落地同一张表,相比软件模式要先满足哪些前提(时钟、offload)?