KCC 的全局稳态带宽估计的同步:为什么我们不需要 CAS KCC 的全局稳态带宽估计的同步为什么我们不需要 CAS1. 背景KCC 维护了一个全局 Kalman 滤波器用来估计共享瓶颈链路的稳态可用带宽kcc_kf_x带宽估计值BW_UNITkcc_kf_P估计协方差每一条 TCP 流在 PROBE_BW 的 cruise 阶段pacing_gain BBR_UNIT会将当前测得的带宽样本喂给这个滤波器。多个 CPU 上的不同流会并发调用kcc_kf_update()执行 read-modify-write 操作。2. 可能的质疑内核工程师审计时可能会指出“kcc_kf_update中先用atomic64_read读取 x 和 P计算新值后再atomic64_set写回。这不是一个原子 RMW 操作。两个 CPU 同时更新时后写回的那个会覆盖前一个的更新导致更新丢失。这是一个并发 bug。”3. 为什么这不是 bug因为全局带宽估计不需要每一次更新都被精确保留。原因有三3.1 估计对象是稳态值滤波器估计的是瓶颈链路的稳态可用带宽。这个值在秒级甚至分钟级尺度上才可能发生显著变化。而 ACK 间隔是毫秒级的。在两次有效变化之间滤波器会被更新几百上千次。丢失其中任意一次更新后续的样本会在几毫秒内把估计值拉回正确方向。最终收敛结果不变。3.2 “丢失”本身就是一种噪声采样多个 CPU 同时写入时后写覆盖先写等价于丢弃了一个样本。但如果这个样本是离群值瞬时噪声丢弃反而是有益的。如果这个样本是有效信号下一个 ACK同样来自 cruise 阶段几乎立刻会再送一个相似的样本进来。在稳态估计的场景下覆盖率 ≈ 采样率只要采样率远高于信号变化率丢失个别样本没有统计影响。3.3 加锁是过度工程如果换成 CAS 自旋锁或atomic64_cmpxchg高并发下1000 流会产生锁竞争增加软中断延迟代码复杂度上升引入新的优先级反转或死锁风险换来的是“微观精确但宏观无意义”的一致性我们选择不加锁不是因为我们不会写而是因为在这里不需要。4. 那为什么还要用 atomic64既然普通u64读写也够用64位平台对齐访问是原子的为什么代码里用的是atomic64_t三个原因按重要性排序4.1 内核规范与静态检查内核有 Data Race DetectorKCSAN和 Sparse 等工具。共享变量如果不标记为原子类型会被报告为潜在数据竞争。即使我们明确知道这个竞争无害也必须通过工具检查才能合入主线或通过 CI。用atomic64_t是最低成本的合规手段。4.2 保证编译器不优化普通u64可能被编译器缓存在寄存器中在多核环境下长期看不到其他 CPU 的更新。atomic64_read/set隐含了编译器屏障volatile语义强制每次读写都从内存或通过缓存一致性获取最新值。4.3 加速缓存同步atomic64_set会触发写屏障视架构而定促使当前 CPU 将更新尽快 flush 到缓存一致性域中让其他核心更快看到新值。对于稳态带宽估计快几百纳秒或几微秒没有实质收益。但几乎没有成本且符合“能快则快”的工程习惯。5. 结论这不是一个 bug。全局带宽估计容忍更新丢失这是设计意图。我们不需要 CAS 或自旋锁。它们解决了一个不存在的问题同时引入不必要的复杂性和开销。用atomic64_t是为了合规、可读和轻微的性能收益而不是为了原子 RMW。拥塞控制的核心是概率与统计不是数据库 ACID。接受不确定性接受“足够好”然后在物理法则的边界内做到最不坏——这就是 KCC 的态度。如果仍然有人坚持要在这里加锁我们只能说欢迎提交 patch但我们会 NACK。