拥塞算法中消除delay_ack的影响

    在2.6.32内核中,基于丢包的拥塞算法基本都需要考虑,delay_ack所带来的影响。例如每个ack都确认两个数据包,如果被拥塞算法当做一个ack确认一个数据包,那窗口的增加速率必然下降。在4.9内核版本之前,由于拥塞窗口接口函数原型中并不携带本次ack确认多少个数据包信息,当拥塞窗口进行调整时,确需要考虑到delay_ack进行适当的调整。主要的过程是使用类似计算srtt_us的方式,估算一个ack平均确认多少个数据包,在拥塞窗口调整时,对调整幅度进行微调。

主要数据结构

    struct tcp_congestion_ops {

    struct list_head list;

    unsigned long flags;

    /* initialize private data (optional) */

    void (*init)(struct sock *sk);

    /* cleanup private data  (optional) */

    void (*release)(struct sock *sk);

    /* return slow start threshold (required) */

    u32 (*ssthresh)(struct sock *sk);

    /* lower bound for congestion window (optional) */

    u32 (*min_cwnd)(const struct sock *sk);

    /* do new cwnd calculation (required) */

    void (*cong_avoid)(struct sock *sk, u32 ack, u32 in_flight);//拥塞窗口调整接口

    /* call before changing ca_state (optional) */

    ...

    void (*pkts_acked)(struct sock *sk, u32 num_acked, s32 rtt_us);//收到ack确认数据包调用接口

    /* get info for inet_diag (optional) */

    void (*get_info)(struct sock *sk, u32 ext, struct sk_buff *skb);

    char name[TCP_CA_NAME_MAX];

    struct module *owner;

    };

需要注意的是4.9内核中拥塞窗口调整函数原型已经变为

    void (*cong_avoid)(struct sock *sk, u32 ack, u32 acked);

不同之处在于最后一个变量,网络中存在数据包个数in_flight变为了本次ack确认的数据包个数。已经将该ack确认的数据包个数传递进来,因此,不需要再进行delay_ack的比例估算。

以2.6.32内核中的bic算法为例,使用计算平均delay_ack比例在拥塞窗口调整函数bictcp_cong_avoid中调用bictcp_update函数

    static void bictcp_cong_avoid(struct sock *sk, u32 ack, u32 in_flight)

    {

    struct tcp_sock *tp = tcp_sk(sk);

    struct bictcp *ca = inet_csk_ca(sk);

    if (!tcp_is_cwnd_limited(sk, in_flight))

        return;

    if (tp->snd_cwnd <= tp->snd_ssthresh)

        tcp_slow_start(tp);

    else {

        bictcp_update(ca, tp->snd_cwnd);

        tcp_cong_avoid_ai(tp, ca->cnt);

    }

    }

bictcp_update函数中结尾部分,对控制调整拥塞窗口快慢的变量ca->cnt进行按比例调整。

    static inline void bictcp_update(struct bictcp *ca, u32 cwnd)

    {

    ....

    ca->cnt = (ca->cnt << ACK_RATIO_SHIFT) / ca->delayed_ack; //对调整拥塞窗口的幅度进行按比例调整

    if (ca->cnt == 0) /* cannot be zero */

    ca->cnt = 1;

    }

而估算delay_ack比例的部分在(*pkts_acked)接口函数中进行

    #define ACK_RATIO_SHIFT 4

    /* Track delayed acknowledgment ratio using sliding window

    * ratio = (15*ratio + sample) / 16

    */

    static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt)

    {

    const struct inet_connection_sock *icsk = inet_csk(sk);

    if (icsk->icsk_ca_state == TCP_CA_Open) {

        struct bictcp *ca = inet_csk_ca(sk);

        cnt -= ca->delayed_ack >> ACK_RATIO_SHIFT;

        ca->delayed_ack += cnt;

    }

    }

代码比较简单就不分析了,但是存在一个bug,假设初始状态ca->delayed_ack = 32,每次ack都不是delay_ack,都确认一个数据包,ca->delay_ack最小值却停留在31,不会继续减小。原因就是先ca->delay_ack/16,由于整型除法是向下取整,因此

cubic版本中对delay_ack的计算也类似,也存在ca->delay_ack最小值为31的问题,不同的是增加了最大值限制。

    #define ACK_RATIO_SHIFT 4

    #define ACK_RATIO_LIMIT (32u << ACK_RATIO_SHIFT)

    static void bictcp_acked(struct sock *sk, u32 cnt, s32 rtt_us)

    {

        const struct inet_connection_sock *icsk = inet_csk(sk);

        const struct tcp_sock *tp = tcp_sk(sk);

        struct bictcp *ca = inet_csk_ca(sk);

        u32 delay;

        if (icsk->icsk_ca_state == TCP_CA_Open) {

            u32 ratio = ca->delayed_ack;

            ratio -= ca->delayed_ack >> ACK_RATIO_SHIFT;

            ratio += cnt;

            ca->delayed_ack = min(ratio, ACK_RATIO_LIMIT);//最大比例不能超过32

        }

        ......

    }

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容