TCP混合慢启动

混合慢启动(Hybrid Slow Start)使用二个信息来决定SlowStart阶段到拥塞避免阶段(Congestion Avoidance)的转换,一是ACK Train的长度;二是报文延迟的增长。ACK Train的长度为在一个RTT周期内,紧密相邻的ACK报文到达的时间间隔之和,内核默认间隔不大于2ms的一系列ACK报文为ACK Train。每个RTT周期,计算一次ACK Train长度,与估算的最小路径发送延迟进行对比,

SlowStart阶段报文延迟的增长也可能意味着路径中的瓶颈路由器已经发生了拥塞。由于报文的突发性传输,瓶颈路由器可能暂时发送拥塞,但是整个RTT时间内的平均发送速率小于瓶颈路由器的带宽,这就导致其在一个RTT周期内,缓存的增长和清空。这种情况下,报文的延时将不能作为拥塞的信号,为解决此问题,我们使用每个报文Train开头的几个报文的延时数据,它们不太受到突发传输的影响。所以,在RTT周期内前几个报文的延时的增长意味着在瓶颈链路中出现的其它的流量。

混合慢启动基于以上的两个信息设置ssthresh,强制SlowStart阶段结束,进入拥塞避免阶段(Congestion Avoidance)。

HYSTART默认参数

宏HYSTART_ACK_TRAIN和HYSTART_DELAY分别表示混合慢启动使用的两种判断方法。

/* Two methods of hybrid slow start */
#define HYSTART_ACK_TRAIN   0x1
#define HYSTART_DELAY       0x2

对于第二种方法,以下为报文延时相关的参数。最小8个采样,延时的合理范围为:[32, 128]。

/* Number of delay samples for detecting the increase of delay */
#define HYSTART_MIN_SAMPLES 8
#define HYSTART_DELAY_MIN   (4U<<3)
#define HYSTART_DELAY_MAX   (16U<<3)
#define HYSTART_DELAY_THRESH(x) clamp(x, HYSTART_DELAY_MIN, HYSTART_DELAY_MAX)

混合慢启动位于内核的Cubic拥塞算法模块中,默认情况下此功能是开启状态(hystart=1),同时采用以上接收的ACK Train和报文延时进行检查(hystart_detect)。混合慢启动的最低拥塞窗口限定为16(hystart_low_window)。默认组成ACK-Train的报文间隔为2毫秒(hystart_ack_delta)。

如果动态加载Cubic模块,可在加载时修改hystart的相关参数。

static int hystart __read_mostly = 1;
static int hystart_detect __read_mostly = HYSTART_ACK_TRAIN | HYSTART_DELAY;
static int hystart_low_window __read_mostly = 16;
static int hystart_ack_delta __read_mostly = 2;

HYSTART初始化与周期

参见函数bictcp_hystart_reset,用于重新设置hystart的相关参数。在hystart检测的每个开始周期,也是使用此函数初始化。

static inline u32 bictcp_clock(void)
{
    ...
    return jiffies_to_msecs(jiffies);
}
static inline void bictcp_hystart_reset(struct sock *sk)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);

    ca->round_start = ca->last_ack = bictcp_clock();
    ca->end_seq = tp->snd_nxt;
    ca->curr_rtt = 0;
    ca->sample_cnt = 0;    

在Cubic的初始化函数bictcp_init中,如果启用了Hystart(hystart为真),初始化Hystart相关参数。

static void bictcp_init(struct sock *sk)
{
    struct bictcp *ca = inet_csk_ca(sk);

    bictcp_reset(ca);

    if (hystart)
        bictcp_hystart_reset(sk);

    if (!hystart && initial_ssthresh)
        tcp_sk(sk)->snd_ssthresh = initial_ssthresh;

其次,当套接口的进入TCP_CA_Loss拥塞状态时,表明之后要开始SlowStart恢复阶段,调用bictcp_hystart_reset函数重设hystart参数,开启新一轮Hystart检测。注意这里也会调用bictcp_reset,其中,将found设置为0,表明还未发现SlowStart的退出点。

static void bictcp_state(struct sock *sk, u8 new_state)
{
    if (new_state == TCP_CA_Loss) {
        bictcp_reset(inet_csk_ca(sk));
        bictcp_hystart_reset(sk);
    }
}
static inline void bictcp_reset(struct bictcp *ca)
{
    ca->cnt = 0;
    ...
    ca->delay_min = 0;
    ca->epoch_start = 0;
    ca->ack_cnt = 0;
    ca->tcp_cwnd = 0;
    ca->found = 0;
}

最后,在SlowStart阶段,ACK确认序号在hystart的结束序号(end_seq)之后,表明当前的检测已经完成,开始新一轮的Hystart检测,重设hystart参数。如下bictcp_cong_avoid函数。

static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 acked)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);

    if (!tcp_is_cwnd_limited(sk))
        return;
    
    if (tcp_in_slow_start(tp)) {
        if (hystart && after(ack, ca->end_seq))
            bictcp_hystart_reset(sk);
        acked = tcp_slow_start(tp, acked);
        if (!acked)
            return;
    } 

Hystart检测

在tcp_ack处理ACK报文的过程中,将根据ACK报文确认的数据情况,清理重传队列,之后调用拥塞控制的pkts_acked函数指针,对于Cubic而言,其为bictcp_acked。目前,Cubic和Hystart仅使用到了ack_sample结构中的rtt_us字段。

static int tcp_clean_rtx_queue(struct sock *sk, u32 prior_fack,
                   u32 prior_snd_una, struct tcp_sacktag_state *sack)
{
    ...
    if (icsk->icsk_ca_ops->pkts_acked) {
        struct ack_sample sample = { .pkts_acked = pkts_acked,
                         .rtt_us = sack->rate->rtt_us,
                         .in_flight = last_in_flight };

        icsk->icsk_ca_ops->pkts_acked(sk, &sample);
    }

如下函数bictcp_acked,变量delay_min保存最小的RTT值。在拥塞窗口大于hystart定义的最低窗口值16(hystart_low_window)时,hystart才开始执行。

static void bictcp_acked(struct sock *sk, const struct ack_sample *sample)
{   
    const struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);
    ...
    
    delay = (sample->rtt_us << 3) / USEC_PER_MSEC;
    if (delay == 0)
        delay = 1;
    
    /* first time call or link delay decreases */
    if (ca->delay_min == 0 || ca->delay_min > delay)
        ca->delay_min = delay;
    
    /* hystart triggers when cwnd is larger than some threshold */
    if (hystart && tcp_in_slow_start(tp) &&
        tp->snd_cwnd >= hystart_low_window)
        hystart_update(sk, delay);

如下Hystart检查函数hystart_update,首先,如果found如果设置了HYSTART_ACK_TRAIN或者HYSTART_DELAY标志位,这里依据启用的hystart检测方式,即hystart_detect的值,表明已经找到SlowStart的退出点,不在执行检测。

static void hystart_update(struct sock *sk, u32 delay)
{
    struct tcp_sock *tp = tcp_sk(sk);
    struct bictcp *ca = inet_csk_ca(sk);

    if (ca->found & hystart_detect)
        return;

其次,如果启用了HYSTART_ACK_TRAIN检测方式,并且当前ACK报文时间戳(此函数在tcp_ack中调用,所以now相当于ACK报文的时间戳),与上一个ACK报文时间戳小于设定的ACK-Train间隔(变量hystart_ack_delta,默认2ms,函数bictcp_clock返回的为ms为单位的时间戳),更新last_ack时间戳为当前ACK报文时间戳。

如果当前时间减去此轮ACK-Train测量开始时间戳round_start,大于最小RTT延时的一半(delay_min保存了8倍的最小RTT延时),表明网络情况恶化,退出SlowStart,将当前拥塞窗口设置为ssthresh。

    if (hystart_detect & HYSTART_ACK_TRAIN) {
        u32 now = bictcp_clock();

        /* first detection parameter - ack-train detection */
        if ((s32)(now - ca->last_ack) <= hystart_ack_delta) {
            ca->last_ack = now;
            if ((s32)(now - ca->round_start) > ca->delay_min >> 4) {
                ca->found |= HYSTART_ACK_TRAIN;
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINDETECT);
                NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTTRAINCWND,
                          tp->snd_cwnd);
                tp->snd_ssthresh = tp->snd_cwnd;
            }
        }
    }

最后,如果启用了HYSTART_DELAY检测方式,并且当前采用的数量小于hystart定义的HYSTART_MIN_SAMPLES(8),如果当前测量轮次的curr_rtt为零或者大于当前报文的RTT,更新curr_rtt。

否则,如果已经采用HYSTART_MIN_SAMPLES个报文,并且,采样到的curr_rtt值大于最小的RTT值加上1/8被的最小RTT值,即当curr_rtt的值大于9/8倍的最小RTT(delay_min)时,认为延时增加过大,退出SlowStart,将当前拥塞窗口值设置为ssthresh。

    if (hystart_detect & HYSTART_DELAY) {
        /* obtain the minimum delay of more than sampling packets */
        if (ca->sample_cnt < HYSTART_MIN_SAMPLES) {
            if (ca->curr_rtt == 0 || ca->curr_rtt > delay)
                ca->curr_rtt = delay;

            ca->sample_cnt++;
        } else {
            if (ca->curr_rtt > ca->delay_min +
                HYSTART_DELAY_THRESH(ca->delay_min >> 3)) {
                ca->found |= HYSTART_DELAY;
                NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTDELAYDETECT);
                NET_ADD_STATS(sock_net(sk), LINUX_MIB_TCPHYSTARTDELAYCWND,
                          tp->snd_cwnd);
                tp->snd_ssthresh = tp->snd_cwnd;

注意curr_rtt和delay_min保存的都是8倍的原值,而宏定义HYSTART_DELAY_THRESH,将delay_min>>3的值限定在了[4, 16]之间。

内核版本 5.0

目 录 译者序 前言 第一部分 TCP/IP基础 第1章 开放式通信模型简介 1 1.1 开放式网络的发展 1 1.1.1 通信处理层次化 2 1.1.2 OSI参考模型 3 1.1.3 模型的使用 5 1.2 TCP/IP参考模型 7 1.3 小结 7 第2章 TCP/IP和Internet 8 2.1 一段历史 8 2.1.1 ARPANET 8 2.1.2 TCP/IP 9 2.1.3 国家科学基金会(NSF) 9 2.1.4 当今的Internet 12 2.2 RFC和标准化过程 12 2.2.1 获得RFC 13 2.2.2 RFC索引 13 2.2.3 有关RFC的幽默 13 2.3 Internet服务简介 13 2.3.1 Whois和Finger 14 2.3.2 文件传输协议 14 2.3.3 Telnet 14 2.3.4 Email 14 2.3.5 WWW 14 2.3.6 USENET News 15 2.4 Intranet和Extranet概览 15 2.4.1 Intranet 15 2.4.2 将Intranet对外开放 16 2.5 Internet的明天 16 2.5.1 下一代Internet(NGI) 16 2.5.2 超速骨干网服务 16 2.5.3 Internet2(I2) 17 2.6 Internet管理组织 17 2.6.1 Internet协会 17 2.6.2 Internet体系结构组 17 2.6.3 Internet工程任务组 17 2.6.4 Internet工程指导组 17 2.6.5 Internet编号管理局 18 2.6.6 Internet名字和编号分配组织 (ICANN) 18 2.6.7 Internet网络信息中心和其他注 册组织 18 2.6.8 RFC编辑 18 2.6.9 Internet服务提供商 18 2.7 小结 19 第3章 TCP/IP概述 20 3.1 TCP/IP的优点 20 3.2 TCP/IP的层和协议 21 3.2.1 体系结构 21 3.2.2 传输控制协议 21 3.2.3 IP协议 23 3.2.4 应用层 25 3.2.5 传输层 25 3.2.6 网络层 25 3.2.7 链路层 25 3.3 远程登录(Telnet) 25 3.4 文件传输协议(FTP) 25 3.5 普通文件传输协议(TFTP) 26 3.6 简单邮件传输协议(SMTP) 26 3.7 网络文件系统(NFS) 26 3.8 简单网络管理协议(SNMP) 27 3.9 TCP/IP和系统结合 27 3.10 内部网概述 28 3.11 小结 28 第二部分 命名和寻址 第4章 IP网络中的名字和地址 29 4.1 IP寻址 29 4.1.1 二进制和十进制数 30 4.1.2 IPv4地址格式 30 4.2 子网的出现 34 4.2.1 分子网 35 4.2.2 可变长子网掩码(VLSM) 37 4.3 无类域前路由(CIDR) 38 4.3.1 无类地址 38 4.3.2 强化路由汇聚 39 4.3.3 超网化 39 4.3.4 CIDR怎样工作 39 4.3.5 公共地址空间 40 4.3.6 RFC 1597和1918 40 4.4 小结 40 第5章 ARP和RARP 41 5.1 使用地址 41 5.1.1 子网寻址 41 5.1.2 IP地址 43 5.2 使用地址解析协议 44 5.2.1 ARP cache 45 5.2.2 代理ARP 47 5.2.3 反向地址解析协议 47 5.3 使用ARP命令 47 5.4 小结 47 第6章 DNS:名字服务器 48 6.1 域名系统概述 48 6.2 授权局 50 6.3 DNS分布数据库 50 6.4 域和区 50 6.5 Internet顶级域 51 6.6 选择一个域名服务器 52 6.7 名字服务解析过程 52 6.7.1 递归查询 52 6.7.2 叠代查询 52 6.8 高速缓存 52 6.9 反向解析(Pointer)查询 52 6.10 DNS安全 52 6.11 资源记录 53 6.12 小结 54 第7章
相关推荐
©️2020 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页