Linux TCP Timestamps 没鸟用

本文针对 Linux TCP 的 Timestamps 精度问题进行分析。通过源码分析发现,在 ms 精度时代其计算 RTT 是优化,但在 us 精度时代成累赘,会拉偏 RTT 和 RTO。在 IDC 环境建议关闭,公网环境影响较小,但会影响 BBR 状态机。最后提出修改、参考 Google 或关闭的建议。

对 Linux TCP 特性表明一个否定态度并非大不敬,而是这玩意儿没鸟用。

还是 TCP Timestamps 的精度问题,本文仅针对 Linux TCP,不针对别的实现,看一下为什么它没鸟用。

源码分析,看我添加的注释:

static bool tcp_ack_update_rtt(struct sock *sk, const int flag,
			       long seq_rtt_us, long sack_rtt_us,
			       long ca_rtt_us, struct rate_sample *rs)
{
	const struct tcp_sock *tp = tcp_sk(sk);

	/* Prefer RTT measured from ACK's timing to TS-ECR. This is because
	 * broken middle-boxes or peers may corrupt TS-ECR fields. But
	 * Karn's algorithm forbids taking RTT if some retransmitted data
	 * is acked (RFC6298).
	 */
        // 不要优先选择时间戳计算 RTT,但这里是另一个理由。
	if (seq_rtt_us < 0)
		seq_rtt_us = sack_rtt_us;

	/* RTTM Rule: A TSecr value received in a segment is used to
	 * update the averaged RTT measurement only if the segment
	 * acknowledges some new data, i.e., only if it advances the
	 * left edge of the send window.
	 * See draft-ietf-tcplw-high-performance-00, section 3.3.
	 */
        // 到此为止,看看 seq_rtt_us,ca_rtt_us 的单位,us,这是微秒,1000 us = 1 ms
	if (seq_rtt_us < 0 && tp->rx_opt.saw_tstamp && tp->rx_opt.rcv_tsecr &&
	    flag & FLAG_ACKED) {
		u32 delta = tcp_time_stamp(tp) - tp->rx_opt.rcv_tsecr;
                // TCP 时间戳单位是 ms,这里 delta 是两个 ms 相见,只有 ms 精度。
		if (likely(delta < INT_MAX / (USEC_PER_SEC / TCP_TS_HZ))) {
			if (!delta)
				delta = 1;
                        // seq_rtt_us 的最小值是 1000,这里就是问题所在。
                        // 曾经,没有 delta = max(delta, 1),那时 delta 最小值就是 0。
			seq_rtt_us = delta * (USEC_PER_SEC / TCP_TS_HZ);
			ca_rtt_us = seq_rtt_us;
		}
	}
	rs->rtt_us = ca_rtt_us; /* RTT of last (S)ACKed packet (or -1) */
	if (seq_rtt_us < 0)
		return false;

	/* ca_rtt_us >= 0 is counting on the invariant that ca_rtt_us is
	 * always taken together with ACK, SACK, or TS-opts. Any negative
	 * values will be skipped with the seq_rtt_us < 0 check above.
	 */
	tcp_update_rtt_min(sk, ca_rtt_us, flag);
	tcp_rtt_estimator(sk, seq_rtt_us);
	tcp_set_rto(sk);

	/* RFC6298: only reset backoff on valid RTT measurement. */
	inet_csk(sk)->icsk_backoff = 0;
	return true;
}

发生重传且没有序列被 SACKed ,若开启 Timestamps,Linux TCP 使用 tsecr 和当前 ms 的差计算 RTT,这在 ms 精度时代是个优化,但在 us 精度时代就是累赘。

上述代码来自 5.10 内核,检测到乱序或丢包时,灌入 tcp_update_rtt_min 和 tcp_rtt_estimator 的 RTT 参数最小值为 1000,这对于 IDC 网络而言实在太大了,在 50 us 级 RTT 的连接中,1000 us 就是个噪点,它会拉偏 RTT 的移动指数平均值,进而拉偏 RTO。

检测到乱序或丢包,对端立即回复 ACK,此时实际 RTT 足够小到排除掉对端任何 Delay,若丢包属于随机丢包而非拥塞,该 RTT 有希望更新 rtt_min,但为了避免精度损失导致 delta == 0 异常,增加 delta = max(delta, 1) 约束,从而将足够大的值注入计算,反而帮了倒忙。

可若不增加 delta = max(delta, 1) 约束,又会遇到 0 这个极小异常点,怎么都不行。

看下面的脚本:

#!/usr/local/bin/stap

global dsrtt

probe kernel.function("tcp_ack_update_rtt")
{
	srtt = (@cast($sk,"struct tcp_sock")->srtt_us);
	dport =(@cast($sk, "struct sock")->__sk_common->skc_dport);
	if (dport == 0x8913) { // 过滤 iperf 端口
		dsrtt <<< srtt
	}
}

probe timer.s(2) {
	printf("-----------\n")
	print(@hist_log(dsrtt))
	delete dsrtt
}

分别设置 net.ipv4.tcp_timestamps 为 1 或 0,观察一下分布,可呈现:
在这里插入图片描述
如果将 sack 也关闭,GBN 将持续使用 Timestamps 计算 RTT,结果就是持续值为 1000 us 的采样值被灌入移指平均计算。关掉 sack ,持续采样,不再 delete,分布如下:
在这里插入图片描述
所以,在 IDC 环境,请关闭 Timestamps。

在公网环境,这个问题不大,广域网 RTT 大于 1 ms,时间戳损失的精度有限,2.9 - 1.1 约等于 2,按 ms 精度计算,结果是 1,大约损失一半,这是损失的最大误差,在普遍 RTT 均大于 10 ms 的环境,偶尔的计算精度损失不会带来问题。

然而,对于 BBR ,即便精度损失不会带来计算问题,也还是会影响 BBR 的状态机。问题出在下面代码:

static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs)
{
	struct tcp_sock *tp = tcp_sk(sk);
	struct bbr *bbr = inet_csk_ca(sk);
	bool filter_expired;

	/* Track min RTT seen in the min_rtt_win_sec filter window: */
	filter_expired = after(tcp_jiffies32,
			       bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ);
	if (rs->rtt_us >= 0 &&
	    (rs->rtt_us <= bbr->min_rtt_us ||
	     (filter_expired && !rs->is_ack_delayed))) {
		bbr->min_rtt_us = rs->rtt_us;
		bbr->min_rtt_stamp = tcp_jiffies32;
	}
...

在 Recovery 状态用时间戳计算 RTT,精度损失将使 rs->rtt_us <= bbr->min_rtt_us 恒成立,进而持续 reset min_rtt_stamp,也就进不去 ProbeRTT 状态了。该问题已解决,去掉等号即可:
[PATCH net] tcp: only postpone PROBE_RTT if RTT is < current min_rtt estimate
无论如何,Timestamps 总是在帮倒忙。

如果 Timestamps 没鸟用,还留着它空消耗选项空间有何用?

  • 要么改了它,像我一样改成 us 精度。
  • 要么学 Google:draft-yang-tcpm-ets-00
  • 要么关了它。

周末测的那版 BBR 发现一个问题,最近我总吐槽的 TCP Timestamps 精度问题还真的出了问题,换成我修改的那个 us 精度 Timestamps 后问题解决,我发现在 100 us 量级 RTT 的链路上问题更严重。梳理了之后,这是两个问题,三言两语试图说清楚。

浙江温州皮鞋湿,下雨进水不会胖。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值