为 BBRv3 打上 LFN 补丁:在高延迟跨境链路上突破 2% 丢包限制
TL;DR:BBRv3 默认将丢包率上限锁死在 2%,这在跨境、卫星等长肥管道(LFN)上会严重限制吞吐量。本文介绍一个安全的可调参数补丁,允许在特定条件下放宽该限制,同时保持 AQM 友好性。
背景:BBRv3 的 2% 天花板
BBRv3 通过测量带宽和 RTT 来估计瓶颈容量,并在探测带宽时设置了一个硬性丢包上限:
static const u32 bbr_loss_thresh = BBR_UNIT * 2 / 100; /* 2% loss */
一旦丢包率超过 2%,BBR 会认为发生了拥塞,冻结 inflight_hi ,停止探测更高带宽。
这在数据中心和低延迟网络中非常合理,但在以下场景中却成了瓶颈:
- 跨境链路(中美/中欧):存在 2–5% 的稳定物理丢包
- 卫星链路:高 RTT + 随机丢包
- 高 BDP 链路:缓冲区深,但丢包并非总是拥塞
结果是:BBR 永远无法达到真实瓶颈带宽。
BBRv3 源码中 bbr_loss_thresh 定义见 google/bbr@90210de4为什么不切回 BBRv1?
虽然 BBRv1 没有 2% 的硬性丢包限制,但它存在两个致命缺陷:
- AQM 不友好:在 fq_codel / CAKE 等主动队列管理环境下,V1 容易填满缓冲,导致延迟飙升(Bufferbloat)。
- 公平性较差:V1 的 Probe Up 阶段过于激进,在高并发场景下会挤压 Reno/Cubic 及其他 BBR 流的生存空间。
因此,我们需要的是一个保留 BBRv3 的 AQM 友好性和公平性,同时修补其 LFN 缺陷的方案,而非简单地倒退版本。
核心思想:条件放宽,而非无脑放开
我们的补丁引入两个 sysctl参数:
net.ipv4.tcp_bbr_lfn_loss_thresh_pct = 0
net.ipv4.tcp_bbr_lfn_min_rtt_fresh_ms = 5000仅在满足以下 4 个条件时,才允许将 2% 放宽到 pct% :
mode == PROBE_BW && cycle_idx != PROBE_UP避开 RTT 滞后的探测上升期rtt_us < 1.2 × min_rtt_us 确认没有队列堆积min_rtt_stamp新鲜(≤ 5s) 防止使用陈旧的 RTT 基准tx_in_flight ≤ 1.15 × BDP 防止 AQM 丢包被误判为物理丢包这四个条件构成了一个安全沙箱,确保只有在“看起来真的只是背景噪音丢包”时才放宽限制。
内核核心变更 / 实现细节
1. Sysctl 模块变更(net/ipv4/sysctl_net_ipv4.c)
新增两个全局 sysctl 变量,供 BBR 模块读取:
int sysctl_tcp_bbr_lfn_loss_thresh_pct __read_mostly = 0;
int sysctl_tcp_bbr_lfn_min_rtt_fresh_ms __read_mostly = 5000;
EXPORT_SYMBOL(sysctl_tcp_bbr_lfn_loss_thresh_pct);
EXPORT_SYMBOL(sysctl_tcp_bbr_lfn_min_rtt_fresh_ms);2. BBR 模块变更(net/ipv4/tcp_bbr.c)
在 bbr_is_inflight_too_high() 中加入动态阈值逻辑:
pct = READ_ONCE(sysctl_tcp_bbr_lfn_loss_thresh_pct);
if (pct > 0 &&
bbr->mode == BBR_PROBE_BW &&
bbr->cycle_idx != BBR_BW_PROBE_UP &&
bbr_min_rtt_is_fresh(bbr) &&
rs->rtt_us < (bbr->min_rtt_us * 6 / 5)) {
u32 roof = bbr_inflight_roof(sk);
if (rs->tx_in_flight > roof)
goto skip_relax;
eff_loss = max_t(u32, eff_loss,
((u32)pct * BBR_UNIT + 50) / 100);
}3. 工程健壮性
- ✅ GCC/Clang 双兼容:采用内核标准
u32jiffies 减法,jiffies_to_msecs()自动扩展,无编译器特有语法 - ✅ jiffies 回绕安全:
u32减法天然处理回绕,符合内核惯用法 - ✅ 零开销:仅在发生丢包时读取 sysctl
- ✅ 无残留风险:sysctl 注册在内核核心,非模块私有
Qdiscs 适配
很多读者会担心:“放宽丢包阈值会不会把队列撑爆?”
答案是:不会,因为补丁内置了 AQM 防护机制,
强制约束在途数据量,即使 AQM 主动丢包,BBR 也不会无限制填充缓冲区。
Fq
「EDT 调度器 + 本地 bloat 压制 + sparse 出队隔离」 三位一体,
四条件 Guard 几乎恒真,使得 fq 成为 BBRv3-LFN 在波动带宽环境下的唯一最优解,形成完美闭环。
- EDT Pacing:
网卡驱动层精准控速,本地积压仅1–2个TSO段,彻底消除Qdisc层Bufferbloat。 - Sparse Flow 保护:
new_flows/old_flows双链实现出队顺序隔离,大流不阻塞小包。 - 建议值:
pct=5-7lfn_min_rtt_fresh_ms=15000
Fq_codel
这是最容易出问题的组合,但补丁已针对性处理:
- CoDel 误判风险:
默认target=5ms对跨境 RTT(150–300ms)过于苛刻,易误杀补丁守卫条件。 - Inflight Roof 兜底:
补丁强制inflight_latest ≤ 1.15×BDP时才放宽阈值,
超容时立即拒绝豁免,由 fq_codel 接管拥塞控制。 - 建议值:
pct = 5-7lfn_min_rtt_fresh_ms = 30000
CAKE
CAKE 是智能 AQM,但仅限固定带宽--精品专线场景:
- 严格禁用场景:
波动带宽线路(如甲骨文云)严禁使用——无bandwidth参数时AQM完全瘫痪,
且缺失EDT原生支持,软件定时器毛刺会直接熔断补丁的1.2×min_rtt守卫。 - 有限优势:
显式指定bandwidth并启用oceanic/satelliteprofile时,target自动放宽至30–50ms适配跨境链路,但DRR轮询仍需小包等待大流配额,时延表现远差于fq的sparse流插队。 - 建议值:
pct = 4-6lfn_min_rtt_fresh_ms = 10000
进阶阅读
关于 LFN 补丁与 Qdisc 交互机制的深度解析,请参考官方文档:Qdisc_Compatibility.md
如何使用
1. 编译内核
2. 配置参数
写入 /etc/sysctl.d/99-bbr-lfn.conf :
# BBR LFN: allow up to 10% loss in PROBE_BW if RTT<1.2x min_rtt & inflight<=1.15xBDP
net.ipv4.tcp_bbr_lfn_loss_thresh_pct = 7
net.ipv4.tcp_bbr_lfn_min_rtt_fresh_ms = 5000应用: sysctl --system
3. 推荐值速查
场景推荐值默认 / 数据中心0(关闭)跨境 / 高 RTT5–7卫星 / 极端 LFN10实验环境2–20预期效果
指标原版 BBRv3补丁后跨境 3-5% 丢包吞吐❌ 受限✅ 满速队列敏感性✅ 高✅ 不变AQM(CAKE/fq_codel)✅ 友好✅ 友好突发拥塞反应✅ 快⚠️ 略慢注:突发拥塞下"略慢"指 pct>0 时放宽阈值会让 BBR 多扛一小段丢包再降速,代价是瞬时 inflight 可能略超原版;在物理丢包为主的 LFN 链路上收益远大于此代价。
注意事项
- ⚠️ 不要在数据中心或低 RTT 链路上开启
- ⚠️ 不要设置
pct > 20 - ✅ 始终配合
inflight roof使用(补丁已内置) - ✅ 建议先在边缘节点进行灰度测试
总结
这个补丁并没有推翻 BBR 的设计哲学,而是在长肥管道的灰色地带里,给它一把更聪明的尺子。
默认关闭,按需开启;条件苛刻,绝不滥用。
如果你也在跨境或卫星链路上被 BBR 的 2% 天花板困扰,不妨试试这个补丁。
补丁地址:Linux-BBRv3-LFN-Patch
适用内核:Linux 6.13+(BBRv3 主线)
许可证:GPLv2(与 Linux 内核一致)
Happy hacking 🚀