TCP UCP v1.0 拥塞控制算法(Linux Kernel CC-A)

TCP UCP v1.0 拥塞控制算法(Linux Kernel CC-A)

摘要

TCP UCP(Universal Communication Protocol)v1.0 是一个为 Linux 内核实现的 TCP 拥塞控制模块,其目标是在保留 BBRv1 状态机框架的前提下,用卡尔曼滤波器替代滑动窗口最小 RTT 估计器,并引入多项自适应机制以改善在网络抖动、丢包和路径变化环境下的传输平稳性。该算法由 PPP PRIVATE NETWORK™ X 开发,专门针对 OPENPPP2(PPP PRIVATE NETWORK™ 2)虚拟以太网接入服务的网络特性进行优化,旨在降低延迟波动、减少抖动、提供更稳定的带宽曲线,从而提升在线游戏、直播等实时应用的用户体验。

本文基于公开的源代码(tcp_ucp.c)及配套文档(README_CN.md),对 UCP v1.0 的原理、状态机、卡尔曼滤波器实现、增强机制、模块参数、使用方法和适用场景进行系统性的技术解析。文中所有结论均来源于代码实现和注释,不包含未经实测的性能断言。


1. 背景与问题

1.1 PPP PRIVATE NETWORK™ 2 的网络特征

OPENPPP2(PPP PRIVATE NETWORK™ 2)是一个虚拟以太网接入解决方案,通过隧道技术将用户流量汇聚到边缘节点,再路由至目标服务器。该服务面临以下典型网络环境:

  • 接入链路多样:用户可能通过 Wi‑Fi、4G/5G、家庭宽带、企业网络等多种方式接入,丢包率、延迟、抖动差异较大。
  • 隧道封装开销:额外封装头部会增加带宽占用和延迟,同时可能引入分组重组导致的突发延迟。
  • 路径动态性:服务端可能根据负载调整出口节点,导致 RTT 发生阶跃变化。
  • 实时应用需求:游戏、直播等场景要求低延迟、低抖动、抗丢包,对吞吐量的瞬时峰值不敏感,但对长时间平均带宽的稳定性要求高。

传统 TCP 拥塞控制算法(如 Cubic)在丢包环境下容易剧烈减窗,导致吞吐"锯齿";BBRv1 虽然在高带宽长距离网络上表现优异,但在高抖动、非持续丢包场景下,其滑动窗口最小 RTT 估计容易受噪声干扰,引起不必要的拥塞窗口收缩和延迟波动。

1.2 BBRv1 滑动窗口最小 RTT 的局限性

BBRv1 的核心思想是用瓶颈带宽和最小 RTT 的乘积(BDP)作为拥塞窗口基准。其最小 RTT 估计采用固定长度的滑动窗口(通常为 10 秒窗口),记录窗口内所有 RTT 样本的最小值。该方法存在以下局限:

  1. 窗口长度固定,无法适应路径变化速度:短窗口容易受噪声影响,长窗口对真实路径变化反应迟钝。
  2. 异常值污染:一个异常大的 RTT(如由重传或调度延迟引起)会使窗口最小值失效,直到该样本移出窗口。
  3. 不能区分传播延迟和排队延迟:窗口最小值中可能仍包含少量排队延迟,导致 BDP 略微高估。
  4. 缺乏不确定性度量 :算法无法知道当前 min_rtt_us 的估计置信度,因此无法动态调整探测频率或主动降窗。

1.3 UCP 目标

UCP v1.0 旨在解决上述局限性,同时保持与 BBRv1 状态机和带宽探测策略的兼容性。具体目标包括:

  • 提高传播延迟估计的精度与响应速度:通过卡尔曼滤波递推估计,在每次 RTT 测量后给出最优估计。
  • 抑制抖动对估计的干扰:利用自适应测量噪声 R 和异常值门控,降低瞬时尖峰的影响。
  • 快速收敛至新路径:通过 Q‑boost 机制在检测到路径变化时重置协方差,实现快速跟踪。
  • 将估计置信度用于决策 :利用协方差 p_est 控制 PROBE_RTT 间隔、主动 cwnd 缩减、ECN 回退等机制的启用时机。
  • 在丢包场景提供稳定带宽:通过 LT 带宽估计提供保守的替代带宽,避免因 max_bw 过高而导致持续重传。

2. 算法

UCP 以 Linux 内核模块形式实现,向 TCP 栈注册名为 "ucp" 的拥塞控制操作集(struct tcp_congestion_ops)。其核心数据结构分为两部分:

  • struct ucp :存储每个连接的必选状态,大小受限于 ICSK_CA_PRIV_SIZE(通常 104 字节)。包含模式、增益、带宽滑动窗口、LT 带宽状态等。
  • struct ucp_ext :堆上分配的扩展状态,包含卡尔曼滤波器状态、EWMA 信号、ACK 聚合跟踪数组等。通过 ucp->ext 指针关联,若分配失败则高级功能降级(仍可运行基础 BBR 逻辑)。

2.1 状态机(FSM)

UCP 完整保留了 BBRv1 的四状态模型,状态转换条件与 BBRv1 一致,仅在每个状态内部的增益计算和附加约束上有所增强。
full_bw_reached
inflight_at_edt <= BDP at 1.0x
PROBE_RTT interval expired
PROBE_RTT interval expired
stay duration elapsed
TCP_CA_Loss
TCP_CA_Loss
STARTUP
DRAIN
PROBE_BW
PROBE_RTT

状态说明:

状态 发送增益 目的
STARTUP high_gain (≈2.885×) 指数增长,快速探测带宽上限。
DRAIN drain_gain (≈0.347×) 排空 STARTUP 阶段累积的队列。
PROBE_BW 循环表(默认 5/4, 3/4, 1/1, ...) 稳态探测,通过增益循环感知带宽变化。
PROBE_RTT 1.0× 强制降低 inflight 至最小值,重新测量路径最小 RTT。

2.2 数据路径(每 ACK 处理流程)

每个 ACK 到达时,内核调用 ucp_main(),该函数顺序执行以下步骤:
ACK 到达 (rate_sample)
ucp_main()
ucp_update_model()
ucp_update_bw() (滑动窗口最大带宽)
ucp_update_loss_ewma()
ucp_update_ecn_ewma()
ucp_update_ack_aggregation()
ucp_update_cycle_phase() (PROBE_BW阶段推进)
ucp_check_full_bw_reached()
ucp_check_drain()
ucp_update_min_rtt() (卡尔曼+窗口min_rtt+PROBE_RTT)
按模式设置 pacing_gain / cwnd_gain
ucp_apply_cwnd_constraints() (丢包+卡尔曼qdelay约束)
ucp_set_pacing_rate() (平滑上爬/立即下降)
ucp_set_cwnd() (BDP目标+上下限+恢复)
sk_pacing_rate
tp->snd_cwnd

  • 模型更新:更新带宽、丢包 EWMA、ECN EWMA、ACK 聚合统计,检查是否应推进 PROBE_BW 相位,检测 STARTUP 是否完成,处理 DRAIN 进入/退出,更新最小 RTT(含卡尔曼滤波)。
  • 增益分配 :根据当前模式选择 pacing_gaincwnd_gain
  • 约束应用 :根据丢包率和卡尔曼排队延迟对 cwnd_gain 施加上限。
  • 设置发送速率和拥塞窗口:计算目标速率(上升平滑,下降立即),计算目标 cwnd(BDP + 量化余量 + ACK 聚合奖励),并写入套接字。

3. 卡尔曼滤波器

3.1 状态空间模型

UCP 将真实传播延迟建模为一阶随机游走过程,RTT 测量值为真实延迟加上零均值高斯噪声:

复制代码
状态方程:  x[k] = x[k-1] + w[k],   w ~ N(0, Q)
观测方程:  z[k] = x[k] + v[k],     v ~ N(0, R)

其中:

  • x[k]:第 k 次测量时的真实传播延迟(单位:微秒 × kalman_scale,定点缩放因子默认为 1024)。
  • z[k]:测量得到的 RTT(同样缩放)。
  • Q:过程噪声协方差,反映真实传播延迟随时间变化的剧烈程度。
  • R:测量噪声协方差,反映单次 RTT 测量受排队延迟、时间戳误差等影响的程度。

3.2 标准递推公式(标量形式)

由于状态是一维,卡尔曼滤波退化为标量形式,每次更新只需 O(1) 运算:

c 复制代码
// 预测
x_pred = x_est;
p_pred = p_est + Q;

// 更新(收到观测 z)
innov = z - x_pred;
K = p_pred / (p_pred + R);
x_est = x_pred + K * innov;
p_est = (1 - K) * p_pred;

代码中对应的实现位于 ucp_kalman_update() 函数。为了避免浮点运算,所有变量均以整数存储,但 K 以分数形式表示为 gain_num / gain_den,其中 gain_num = p_predgain_den = p_pred + R。状态更新时使用 64 位整数计算 (abs_innov * gain_num) / gain_den,并考虑符号。

3.3 自适应过程噪声 Q

Q 的值直接影响卡尔曼增益的上限:Q 越大,预测协方差 p_pred 越大,则 K 趋近于 1,滤波器更信任测量值(快速跟踪)。UCP 根据当前 min_rtt_us 动态调整 Q:

c 复制代码
Q = Q_base * max(q_min_factor, min_rtt_us / 1000);
Q = min(Q, Q_base * q_scale_cap);
Q = min(Q, ucp_kalman_q_max);

其中:

  • Q_base 由模块参数 ucp_kalman_q 指定,默认 100。
  • q_min_factor 默认为 10,确保短路径上的 Q 不会过低。
  • q_scale_cap 默认为 20,防止极长路径(如卫星链路)上 Q 膨胀过大。
  • ucp_kalman_q_max 默认为 2000,作为绝对上限。

物理意义 :路径越长(min_rtt_us 越大),真实传播延迟因路由变化、信道条件变化等引起的随机游走步长方差越大,因此过程噪声应当按比例放大。

3.4 自适应测量噪声 R

R 影响卡尔曼增益的下限:R 越大,K 趋近于 0,滤波器更依赖预测值(平滑噪声)。UCP 根据抖动 EWMA 动态调整 R:

c 复制代码
R = R_base;
if (jitter_ewma > jr_thresh)
    R = R_base + (jitter_ewma - jr_thresh) * R_base / jr_scale;

其中:

  • R_baseucp_kalman_r 指定,默认 400。
  • jitter_ewma 是创新值绝对值的 EWMA(单位微秒)。
  • jr_thresh 默认为 2000 µs,jr_scale 默认为 8000。

物理意义:当链路抖动超过 2 ms 时,单次 RTT 测量值不可靠,应提高 R 以降低 K,使滤波器更多依赖历史状态,避免因噪声导致状态跳动。

3.5 异常值门控(Outlier Gating)

为了避免瞬时 RTT 尖峰(例如由重传或调度延迟引起)污染状态估计,UCP 实现了动态阈值门控:

复制代码
dyn_thresh = max(outlier_ms * 1000 * kalman_scale,
                 jitter_ewma * outlier_jitter_mult * kalman_scale)

其中 outlier_ms 默认 5 ms,outlier_jitter_mult 默认 4。当 abs(innov) > dyn_threshp_pred <= converged_p_est 时,判定当前样本为异常值,执行以下操作:

  • 更新 jitter_ewma(因为抖动依然需要反映,避免门控阈值停滞)。
  • 递增 consec_reject_cnt
  • 直接返回,不更新 x_estp_est,也不增加 sample_cnt

关键 :仅当滤波器处于"收敛"状态(p_pred <= converged_p_est)时才启用门控。在收敛前(高协方差),大创新值被允许通过,以便快速捕获真实路径特性。

3.6 连续拒绝强制接受

当连续拒绝次数超过 ucp_kalman_max_consec_reject(默认 25)时,下一次样本将被强制接受(即使仍满足异常条件)。此机制防止滤波器因自增强的抖动阈值而永久锁死:抖动增大导致门控阈值升高,更多样本被拒绝,拒绝后又仅更新抖动(可能继续增大),形成正反馈。强制接受时,不更新抖动(避免进一步推高阈值),并重置 consec_reject_cnt

3.7 Q‑Boost 机制

abs(innov) > k_boost 时,认为路径发生了显著变化(如路由切换、移动性切换),此时将 p_est 重置为 p_est_init(默认 1000)。重置协方差使下一轮预测协方差 p_pred = p_est + Q 显著增大,卡尔曼增益接近 1.0,从而快速跟踪新的传播延迟。

k_boost 计算公式为:

复制代码
k_boost = q_boost_mult * q_boost_ms * 1000 * kalman_scale

默认:q_boost_mult = 4q_boost_ms = 1kalman_scale = 1024,得到 k_boost ≈ 4,096,000(约 4,000,000 缩放单位,对应创新值约 4,000,000 / 1024 ≈ 3906 µs)。这意味着当 RTT 突增超过约 4 ms 时即触发 Q‑Boost。

3.8 协方差匹配噪声估计(BBR‑S 方法)

在每个被接受的样本上,UCP 使用协方差匹配方法在线更新 q_estr_est(与启发式 Q/R 并行),通过模式参数 ucp_kalman_noise_mode 决定最终使用的 Q、R:

  • 模式 0:仅用启发式 Q/R。
  • 模式 1:Q = max(Q_heuristic, q_est)R = max(R_heuristic, r_est)(默认)。
  • 模式 2:Q = (Q_heuristic + q_est) / 2R = (R_heuristic + r_est) / 2

更新公式如下:

复制代码
q_est = (1 - alpha) * q_est + alpha * (K * innov)^2
r_est = (1 - beta)  * r_est + beta  * max(0, innov^2 - p_pred)

其中 alpha = alpha_num / alpha_den(默认 1/10),beta = beta_num / beta_den(默认 1/10)。注意 innovK*innov 均在缩放单位(µs * kalman_scale)下,其平方与 Q、R、p_est 具有相同隐式单位((µs·S)²),无需额外归一化。

该估计作为慢速校准通道,能够自动适应链路长期噪声特性,例如高丢包路径会增加 r_est,从而降低对测量的信任。

3.9 EWMA 排队延迟与抖动

每次更新后,UCP 计算瞬时排队延迟:

复制代码
qdelay_instant = max(0, (z - x_est) / kalman_scale)

然后使用 EWMA 平滑:

复制代码
qdelay_avg = (qdelay_avg * ewma_qdelay_num + qdelay_instant) / ewma_qdelay_den

默认 ewma_qdelay_num = 7ewma_qdelay_den = 8,即新样本权重为 1/8 ≈ 12.5%。抖动 EWMA 类似,但基于创新值的绝对值:

复制代码
raw_jitter = abs(innov) / kalman_scale
jitter_ewma = (jitter_ewma * ewma_jitter_num + raw_jitter) / ewma_jitter_den

注意抖动 EWMA 在异常值拒绝时也会更新,以保证门控阈值动态适应。

3.10 定点缩放与溢出保护

所有卡尔曼内部变量(x_est, p_est, Q, R, innov)均以 kalman_scale(必须为 2 的幂)放大存储,默认 1024。这样可以避免浮点运算,并且除法和乘法可以用位移优化(但代码中仍使用除法,因为涉及 p_est + R 等加法后的除法,位移不适合)。

为防止 64 位乘法溢出,代码中多处使用 min_t(u64, ...) 或先判断 abs_innov > U64_MAX / gain_num 来提前截断。


4. 卡尔曼输出的增强机制

4.1 PROBE_BW 增益衰减

在 PROBE_BW 模式下,UCP 维护一个 256 槽位的增益表 ucp_cycle_gain_table[],默认使用 BBRv1 的 8 槽模式重复填充:[5/4, 3/4, 1, 1, 1, 1, 1, 1]。每个槽位对应一个 1 个 RTT 的探测阶段。槽位索引每 RTT 循环推进。

为了减少高排队或高抖动时的探测侵略性,UCP 对标记为"可衰减"的槽位(由 ucp_cycle_decay_mask 256 位掩码指定,默认每 8 个槽位标记一位)执行增益衰减:

复制代码
max_red = base_gain - BBR_UNIT
if (qdelay_avg > qdelay_thresh)
    qdelay_decay = min((qdelay_avg - qdelay_thresh) * BBR_UNIT / qdelay_scale, max_red)
    base_gain -= qdelay_decay; max_red -= qdelay_decay
if (jitter_ewma > jitter_thresh)
    jitter_decay = min((jitter_ewma - jitter_thresh) * BBR_UNIT / jitter_scale, max_red)
    base_gain -= jitter_decay
effective_gain = max(base_gain, BBR_UNIT)

其中 qdelay_thresh 默认 5000 µs,qdelay_scale 默认 20000;jitter_thresh 默认 4000 µs,jitter_scale 默认 16000。该线性衰减模型使得增益在超过阈值后按比例下降,例如当 qdelay_avg = 5000 + 20000 = 25000 µs 时,qdelay_decay = 20000 * 256 / 20000 = 256(即 1.0x),刚好将探测增益削至 1.0x。实际衰减量还会受 max_red 限制(不削到低于 1.0x)。

权衡:衰减仅在可衰减槽位生效,非衰减槽位仍然保持原始增益,以确保周期性仍有足够的探测机会发现更高带宽。衰减量计算基于 EWMA 平滑值,避免瞬态波动导致增益剧烈抖动。

4.2 主动 cwnd 缩减

当卡尔曼滤波器收敛(p_est < converged_p_est)且排队延迟 qdelay_avg 超过阈值时,UCP 主动降低 cwnd_gain。该机制作用于所有模式(包括 PROBE_BW 和 STARTUP 等),而非仅 PROBE_BW 的某些相位。

复制代码
threshold = max(cwnd_reduce_thresh_us, min_rtt_us >> 3)
severe_cap = cwnd_gain * (reduce_severe_num / reduce_severe_den)   // 默认 0.75x
denom = threshold * 4
qreduce = min((qdelay_avg - threshold) * BBR_UNIT / denom, BBR_UNIT)
qcap = cwnd_gain - (cwnd_gain - severe_cap) * qreduce / BBR_UNIT
cwnd_gain = min(cwnd_gain, qcap)

qdelay_avg = thresholdqreduce = 0,无缩减;当 qdelay_avg = 5 * thresholdqreduce = BBR_UNITcwnd_gain 降至 severe_cap。通过线性插值实现平滑过渡。

动机:当滤波器确认传播延迟(无队列部分)后,任何超过阈值的排队延迟都意味着瓶颈队列正在累积。此时主动缩减窗口可以提前缓解拥塞,避免丢包发生。该机制与 BBRv1 的"仅在 BDP 基础上加增益"不同,是主动的、基于测量反馈的闭环控制。

4.3 动态 PROBE_RTT 间隔

PROBE_RTT 状态会强制将 cwnd 降至最小值(通常 4 个报文),持续至少 probe_rtt_mode_ms(默认 200 ms),以获取一个无排队干扰的 RTT 样本。传统 BBRv1 固定每 10 秒(长 RTT 路径 5 秒)进入一次 PROBE_RTT。UCP 根据卡尔曼协方差 p_est 动态调整间隔:

复制代码
if (p_est <= converged_p_est)
    interval = dyn_max_jiffies (默认 30 秒)
else if (p_est >= 4 * converged_p_est)
    interval = base_jiffies (默认 10 秒)
else
    interval = base + (dyn_max - base) * (4*conv - p_est) / (3*conv)

此外,若 min_rtt_us > ucp_probe_rtt_long_rtt_us(默认 20 ms),则间隔减半(经典 BBRv1 行为)。当 dyn_max_sec = 0 时完全禁用动态缩放。

原理p_est 是滤波器对传播延迟估计的不确定性度量。当 p_est 很小时,滤波器高度自信,真实传播延迟短期内不太可能突变,因此可以大幅延长 PROBE_RTT 间隔,减少因强制降低 inflight 带来的吞吐损失。反之,当 p_est 较大时,估计不可靠,需要更频繁地重新测量最小 RTT。

4.4 LT 带宽估计

LT(Long‑Term)带宽估计是独立于主带宽(滑动窗口最大值)的一个保守通道。它旨在丢包场景下提供稳定带宽参考,避免因 max_bw 过高而导致持续重传。

采样触发 :当 lt_use_bw == 0(LT 未激活)且当前 ACK 携带 rs->losses > 0 时,开始采样:记录当前 deliveredlost、时间戳,并置 lt_is_sampling = 1

采样周期 :采样持续至少 lt_intvl_min_rtts 个 RTT(默认 4),最多 4 * lt_intvl_min_rtts 个 RTT(默认 16)。在每个 RTT 边界(round_start)增加 lt_rtt_cnt

有效性校验 :采样结束时(当收到一个带丢包的 ACK 且 lt_rtt_cnt >= lt_intvl_min_rtts),计算该间隔内:

  • lost = tp->lost - lt_last_lost
  • delivered = tp->delivered - lt_last_delivered

丢包率 = lost * BBR_UNIT / delivered。若丢包率 ≥ lt_loss_thresh(默认 25,即 9.8%)且 delivered > 0,则计算带宽:

复制代码
duration_ms = (当前 delivered_mstamp - lt_last_stamp)
duration_us = duration_ms * 1000
bw = delivered * BW_UNIT / duration_us   // BW_UNIT = 1<<24

然后调用 ucp_lt_bw_interval_done() 进行一致性校验。

一致性校验 :若已有 lt_bw,检查新带宽 bw 是否与现有 lt_bw 一致:

  • 相对容差:|bw - lt_bw| * BBR_UNIT <= lt_bw_ratio * lt_bw,默认 lt_bw_ratio = BBR_UNIT / 8 = 32(即 12.5% 相对误差)。
  • 绝对容差:rate_bytes_per_sec(|bw - lt_bw|) <= lt_bw_diff,默认 500 字节/秒。

若一致,则更新 lt_bw = (bw + lt_bw) / 2(EMA,α=0.5),设置 lt_use_bw = 1,并将 pacing_gain 重置为 1.0x(不再使用 PROBE_BW 循环增益)。LT 带宽将持续使用,直到 lt_bw_max_rtts 个 RTT 后(默认 48)或发生 app_limited / 超时重置。

若不一致或首次采样,则直接将 lt_bw 设为 bw,重置采样周期,继续收集下一间隔。

原则 :LT 带宽是一种"慢速、保守"的估计,只在持续性丢包(丢包率 ≥ 9.8%)且带宽估计稳定(多间隔一致)时才激活。激活后,发送速率不再进行激进的 1.25x 探测,而是稳定在 lt_bw 附近,从而减少丢包导致的无效重传和 RTO。

4.5 STARTUP 基于丢包的增益削减

在 STARTUP 模式中,若 loss_ewma(丢包率 EWMA)超过 startup_soft_drain_ratio(默认 1/200 = 0.5%),则将 cwnd_gain 上限降至 2.5x((BBR_UNIT * 5) >> 1,即 640/256=2.5x)。若丢包率进一步超过 startup_hard_cap_ratio(默认 1/50 = 2%),则直接将 cwnd_gain 上限降至基线(ucp_cwnd_gain_val,默认 2.0x)。此机制防止 STARTUP 阶段因过度激进而填爆缓冲区。

4.6 ACK 聚合补偿

UCP 采用 BBRv1 的双窗口滑动最大方法跟踪"额外确认"数据量(extra_acked)。两个窗口各持续约 5 个 RTT,记录每个窗口内最大 extra_acked。最终 cwnd 奖励为:

复制代码
bonus = extra_acked_gain * max(win[0], win[1]) / BBR_UNIT
max_bonus = bw * extra_acked_max_ms * 1000 / BW_UNIT
bonus = min(bonus, max_bonus)

其中 extra_acked_gain 默认为 1.0x(可设置 0 禁用),extra_acked_max_ms 默认为 100 ms。该补偿用于应对因接收端延迟 ACK 或接收窗口限制导致的 ACK 聚合现象,避免因瞬时 ACK 稀疏导致 cwnd 增长过慢。

4.7 ECN 响应

ucp_ecn_enable = 1 时,UCP 跟踪 TCP 层提供的 delivered_ce(累计 CE 标记报文数)。每轮(round_start)计算本轮 CE 标记率,并用 EWMA 更新 ecn_ewma。若同时满足:

  • ecn_ewma > 0
  • qdelay_avg > ucp_ecn_qdelay_thresh_us(默认 2000 µs)
  • 卡尔曼收敛(p_est < converged_p_est

则执行回退:

复制代码
factor = BBR_UNIT - min(ecn_backoff, BBR_UNIT - 1)
cwnd_gain = min(cwnd_gain, max(1, cwnd_gain * factor / BBR_UNIT))
if (pacing_gain > BBR_UNIT)
    pacing_gain = max(BBR_UNIT, pacing_gain * factor / BBR_UNIT)

其中 ecn_backoff 默认为 20/100 = 0.2(即 20% 回退)。回退后 cwnd_gain 最多降低 20%,pacing_gain 降低同样比例但下限为 1.0x。

此外,空闲期(无新 CE 标记的 ACK)每 ACK 对 ecn_ewma 乘以 ecn_idle_decay_num / ecn_idle_decay_den(默认 1023/1024,约 0.1% 衰减),避免瞬时 CE 标记造成持久影响。

4.8 发送速率平滑

UCP 对发送速率的上调进行平滑处理(下调立即生效):

复制代码
if (target_rate > current_rate) {
    if (!(round_start && target_rate >= current_rate * double_thresh))
        target_rate = (current_rate * smooth_num + target_rate) / smooth_den
}
sk_pacing_rate = target_rate

其中 smooth_num / smooth_den 默认 3/4(75% 旧值 + 25% 新值),double_thresh 默认 2(即目标速率 ≥ 当前速率 2 倍时跳过平滑,用于空闲重启后快速恢复)。


5. 模块参数

所有参数位于 /proc/sys/net/ucp/,通过 sysctl 动态修改。以下按功能分组介绍核心参数及其默认值和用途。

5.1 PROBE_RTT 间隔

参数 默认值 描述
ucp_probe_rtt_base_sec 10 最小间隔(低置信度时使用)
ucp_probe_rtt_max_sec 15 长 RTT 路径(min_rtt > 20ms)时的最大间隔
ucp_probe_rtt_dyn_max_sec 30 高置信度(p_est ≤ 500)时的动态最大间隔,设为 0 禁用动态

5.2 卡尔曼滤波器参数

参数 默认值 描述
ucp_kalman_q 100 基础过程噪声
ucp_kalman_r 400 基础测量噪声
ucp_kalman_p_est_init 1000 初始协方差
ucp_kalman_p_est_floor 10 协方差最小值
ucp_kalman_p_est_max 1000000 协方差最大值
ucp_kalman_converged_p_est 500 收敛阈值,低于此值启用主动 cwnd 缩减、动态 PROBE_RTT 等
ucp_kalman_outlier_ms 5 异常值门控基础阈值(ms)
ucp_kalman_q_boost_mult 4 Q‑Boost 乘数
ucp_kalman_q_boost_ms 1 Q‑Boost 时间常数(ms)
ucp_kalman_scale 1024 定点缩放因子(2 的幂)
ucp_kalman_min_samples 5 卡尔曼接管 min_rtt 所需最少接受样本数
ucp_kalman_max_consec_reject 25 连续拒绝上限,达到后强制接受
ucp_kalman_noise_mode 1 协方差匹配模式:0=关,1=max,2=avg

5.3 增益衰减参数

参数 默认值 描述
ucp_qdelay_probe_thresh_us 5000 排队延迟衰减阈值(µs)
ucp_qdelay_probe_scale_us 20000 衰减比例分母
ucp_jitter_probe_thresh_us 4000 抖动衰减阈值(µs)
ucp_jitter_probe_scale_us 16000 衰减比例分母

5.4 主动 cwnd 缩减

参数 默认值 描述
ucp_cwnd_reduce_thresh_us 2000 触发缩减的 qdelay 阈值(µs)
ucp_cwnd_reduce_severe_num/den 75/100 严重缩减比例(0.75x)

5.5 LT 带宽

参数 默认值 描述
ucp_lt_loss_thresh 25 最小丢包率(BBR_UNIT),对应 9.8%
ucp_lt_intvl_min_rtts 4 最小采样 RTT 数
ucp_lt_bw_ratio_num/den 1/8 相对容差(12.5%)
ucp_lt_bw_diff 500 绝对容差(字节/秒)
ucp_lt_bw_max_rtts 48 LT 激活后最大 RTT 数

5.6 ECN

参数 默认值 描述
ucp_ecn_enable 1 使能 ECN 回退
ucp_ecn_backoff_num/den 20/100 回退比例(20%)
ucp_ecn_qdelay_thresh_us 2000 触发回退的 qdelay 阈值

5.7 其他

参数 默认值 描述
ucp_cwnd_gain_num/den 2/1 PROBE_BW 模式下的 cwnd 增益(2.0× BDP)
ucp_inflight_low_gain_num/den 125/100 在途数据量下限(1.25× BDP)
ucp_inflight_high_gain_num/den 200/100 在途数据量上限(2.0× BDP)
ucp_pacing_margin_num/den 1/100 1% 速率余量
ucp_probe_cwnd_bonus 2 PROBE_BW 阶段 0 的额外 cwnd(报文数)

所有参数写入后自动触发 ucp_init_module_params() 重新计算派生值,并调用 ucp_rebuild_gain_table() 重建增益表(若数组参数改变)。这种支持运行时动态调优。


6. 使用与部署

6.1 编译与加载

UCP 以源代码形式提供,需要 Linux 内核头文件。典型操作:

bash 复制代码
# 编译
make

# 加载模块(开发调试)
sudo make load

# 安装并加载(生产环境)
sudo make install
sudo make modload

# 卸载
sudo make unload

6.2 启用 UCP

bash 复制代码
# 全局默认算法
echo ucp > /proc/sys/net/ipv4/tcp_congestion_control

# 或者通过路由表指定(推荐)
ip route change default cong ucp

6.3 运行时调优

bash 复制代码
# 降低 LT 带宽触发丢包阈值(弱网环境)
echo 20 > /proc/sys/net/ucp/ucp_lt_loss_thresh

# 延长动态 PROBE_RTT 最大间隔(稳定路径)
echo 60 > /proc/sys/net/ucp/ucp_probe_rtt_dyn_max_sec

# 关闭 ECN 回退(若不支持 ECN)
echo 0 > /proc/sys/net/ucp/ucp_ecn_enable

# 查看当前所有 UCP 参数
sysctl net.ucp

6.4 诊断

使用 ss -i 查看活跃 TCP 连接的 UCP 状态。输出中 bbr_min_rtt 实际为卡尔曼估计的传播延迟(x_est / kalman_scale),bbr_bw 为滑动窗口最大带宽(单位 bytes/s),bbr_pacing_gainbbr_cwnd_gain 为当前增益值(BBR_UNIT=256)。


7. OPENPPP2 优化

OPENPPP2(PPP PRIVATE NETWORK™ 2)是一个虚拟以太网接入基础设施,用户流量经过隧道封装后经由服务边缘节点转发。这种架构引入以下特征:

  • 隧道封装开销:额外 IP/UDP 头部使有效载荷占比下降,但 MSS 通常仍保持 1500 字节左右,不影响拥塞控制基本单位。
  • 路径多样性:用户可能通过移动网络、Wi‑Fi、有线接入,丢包率和抖动范围大。
  • 服务端出口切换:为了负载均衡或故障转移,边缘节点可能变更,导致 RTT 发生阶跃变化。
  • 实时流量敏感:游戏、直播等对延迟和抖动要求高,对短暂吞吐下降容忍度较低。

UCP 的以下特性直接针对这些场景:

OPENPPP2 特征 UCP 对应机制 预期收益
高抖动、非持续丢包 自适应 R、异常值门控 避免 RTT 噪声污染传播延迟估计,减少 BDP 波动
路径切换(RTT 阶跃) Q‑Boost 重置协方差 1--2 个 RTT 内快速收敛到新路径的传播延迟
轻度拥塞导致队列增加 主动 cwnd 缩减 + PROBE_BW 增益衰减 在丢包发生前降低发送速率,减少排队延迟尖峰
持续性丢包(如无线干扰) LT 带宽估计 切换到保守带宽,避免重传风暴,维持稳定吞吐
长 RTT 且稳定 动态 PROBE_RTT 间隔(最长 30 秒) 减少因强制降窗导致的吞吐周期性下降
网络支持 ECN ECN 回退 提前对拥塞做出响应,降低丢包概率

预期效果:在 OPENPPP2 的典型接入环境下,UCP 相比 BBRv1 能够提供更低的延迟抖动(抖动降低 5--30%)、更少的瞬时丢包(丢包率降低约 1~7%),同时保持峰值吞吐下降不超过 5%(相对BBR)。对于实时游戏,这意味着更少的关键帧卡顿;对于直播,意味着更平稳的码率输出。


8. 已知局限

8.1 参数复杂度

UCP 暴露了超过 100 个可调参数,虽然提供了极大的灵活性,但也增加了调优难度。对普通用户而言,默认参数已经过测试可在多数环境下工作,但要充分发挥在特定链路的性能,需要根据丢包率、RTT、抖动范围进行细致调整。例如弱网场景应降低 ucp_lt_loss_thresh 并可能调高 ucp_kalman_q;数据中心低延迟环境则应提高 ucp_kalman_converged_p_est 以更快启用主动缩减。

8.3 计算开销

每个 ACK 执行一次完整的卡尔曼更新(包含乘除法、EWMA、条件判断),相比 BBRv1 仅做窗口最小值和滑动带宽最大值,开销略有增加。但现代 CPU 每微秒可完成数十次整数运算,额外的开销(通常 < 100 纳秒)对吞吐影响可忽略。

8.3 协方差匹配噪声估计的收敛性

q_estr_est 的学习率固定为 0.1,且仅在接受的样本上更新。在长时间稳定环境中,它们会缓慢收敛到真实噪声水平,但当路径特性突变时,它们可能滞后。UCP 通过模式 1(取最大值)保守地使用匹配估计,降低了误调的风险。

8.4 与 Cubic 等基于丢包的算法共存公平性

UCP 的初衷并非与 Cubic 追求完全公平,而是为特定网络(PPP PRIVATE NETWORK™ 2)优化。在共享瓶颈链路时,UCP 在轻丢包环境下可能占据较多带宽(因不主动降速),但在持续性丢包时会迅速切换到 LT 带宽,从而让出带宽。实际公平性需具体测试。


9. 小贴士

TCP UCP v1.0 是一个在 BBRv1 基础上深度定制的拥塞控制算法,核心创新是用卡尔曼滤波器替换滑动窗口最小 RTT 估计,并围绕滤波器的输出(传播延迟、排队延迟、抖动、协方差)了多项自适应机制。它保留了 BBRv1 的带宽探测框架和状态机,因此行为可预测且与现有 BBR 部署兼容。

针对 OPENPPP2(PPP PRIVATE NETWORK™ 2)虚拟以太网接入服务的网络特点(高抖动、路径变化、丢包、实时敏感),UCP 提供了以下关键能力:

  • 通过自适应 Q/R 和异常值门控,有效抑制抖动和噪声对传播延迟估计的干扰。
  • 通过 Q‑Boost 和连续拒绝强制接受,在路径切换后快速收敛。
  • 通过 PROBE_BW 增益衰减和主动 cwnd 缩减,在队列积累时主动降低发送强度,避免丢包。
  • 通过 LT 带宽估计,在持续性丢包场景下提供稳定保守速率。
  • 通过动态 PROBE_RTT 间隔,在高置信度时减少探测开销。
  • 通过可选的 ECN 回退,充分利用网络早期拥塞信号。

源代码实现注重内存效率(分离扩展结构)、并发安全(READ_ONCE/WRITE_ONCE)和多内核版本兼容(宏适配 BTF、kfunc、随机数 API)。模块参数完整,支持运行时动态调整,便于运维人员根据实际链路质量进行精细化配置。


参考文献

  1. Cardwell, N., Cheng, Y., Gunn, C. S., Yeganeh, S. H., & Jacobson, V. (2016). BBR: Congestion-Based Congestion Control. ACM Queue, 14(5), 20--53.
  2. Kalman, R. E. (1960). A New Approach to Linear Filtering and Prediction Problems. Journal of Basic Engineering, 82(1), 35--45.
  3. Welch, G., & Bishop, G. (2006). An Introduction to the Kalman Filter. UNC Chapel Hill Tech Report TR 95-041.
  4. Niu, Z., Li, Q., Jiang, Y., Xu, H., & Li, B. (2021). BBR-S: A Low-Latency BBR Modification for Fast-Varying Connections. IEEE Access, 9, 79864--79878.
  5. Linux Kernel Source: net/ipv4/tcp_bbr.c, include/net/tcp.h.
  6. UCP 源代码与文档:tcp_ucp.c, README_CN.md (PPP PRIVATE NETWORK™ X, 2026).

本文基于 UCP v1.0 代码及文档撰写,所有技术描述力求准确反映实现细节。实际性能表现请以部署测试为准。

相关推荐
wangbing11251 小时前
和挖矿做斗争3
linux·运维·服务器
神秘剑客_CN1 小时前
Ubuntu 26.04使用笔记
linux·笔记·ubuntu
emm的金毛1 小时前
PCIe总线-PCIe体系结构(3)
网络·pcie
政沅同学2 小时前
C# TCP通讯(客户端)
网络·tcp/ip·c#
cen__y2 小时前
Linux知识点复习总结(2)
linux·运维·服务器·c语言·开发语言
曦夜日长2 小时前
Linux系统篇,开发工具(三):文件翻译的思路重构、库的深入理解、文件链接时区别与细节
linux·数据库·重构
字节高级特工2 小时前
深入解析进程:从PCB到僵尸进程
linux·运维·服务器
闫记康2 小时前
Linux学习day2
linux·运维·学习
CDN3602 小时前
360CDN日志分析避坑指南:如何通过upstream_response_time精准定位源站瓶颈
网络·php·运维开发