zoukankan      html  css  js  c++  java
  • 一个linux bbr存在的调用顺序bug

    最近跟踪bbr的状态转换的代码,发现一个问题:

    [11241.360364]mode=3,min_rtt_us=553,full_bw=0,cycle_idx=0,pacing_gain=0,cwnd_gain=0,rtt_cnt=0
    [11241.360373] main mode=3,min_rtt_us=553,cur_bw=0,cycle_idx=0,pacing_gain=256,cwnd_gain=256,rtt_cnt=0,snd_cwnd=4
    [11241.360377]mode=0,min_rtt_us=553,full_bw=0,cycle_idx=0,pacing_gain=256,cwnd_gain=256,rtt_cnt=0

    可以看到,bbr的第一个处理,是mode=3,也就是执行链:

    bbr_main-->bbr_update_model-->bbr_update_min_rtt-->
     
    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)) {
            bbr->min_rtt_us = rs->rtt_us;
            bbr->min_rtt_stamp = tcp_jiffies32;
        }
    
        if (bbr_probe_rtt_mode_ms > 0 && filter_expired &&
            !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) {
            bbr->mode = BBR_PROBE_RTT;  /* dip, drain queue */-----------------------第一个状态值

    而按到正常的设计,一般来说,是先调用init再执行拥塞控制。按道理,filter_expired怎么会为1呢?因为链路刚建立,bbr->min_rtt_stamp  的初始化值是当前时间啊,还没有经过10s,

    经过打点,发现bbr->min_rtt_stamp 是0,而不是bbr_init之后的值。然后继续分析代码:

    static struct tcp_congestion_ops tcp_bbr_cong_ops __read_mostly = {
        .flags        = TCP_CONG_NON_RESTRICTED,
        .name        = "bbr",
        .owner        = THIS_MODULE,
        .init        = bbr_init,----------------------------初始化函数
        .cong_control    = bbr_main,------------------------bbr拥塞控制的主函数
        .sndbuf_expand    = bbr_sndbuf_expand,
        .undo_cwnd    = bbr_undo_cwnd,
        .cwnd_event    = bbr_cwnd_event,
        .ssthresh    = bbr_ssthresh,
        .tso_segs_goal    = bbr_tso_segs_goal,
        .get_info    = bbr_get_info,
        .set_state    = bbr_set_state,
    };

    这样就导致了,init函数的调用在bbr_main函数之后,而不是之前。

    根据tcp链接的建立调用链:

    tcp_rcv_synsent_state_process-->tcp_finish_connect-->tcp_init_transfer-->tcp_init_congestion_control-->icsk->icsk_ca_ops->init(sk);
    这个时候发起请求的客户端会调用拥塞控制函数的init,
    tcp_rcv_synsent_state_process-->tcp_ack-->tcp_cong_control-->
    static void tcp_cong_control(struct sock *sk, u32 ack, u32 acked_sacked,
                     int flag, const struct rate_sample *rs)
    {
        const struct inet_connection_sock *icsk = inet_csk(sk);
    
        if (icsk->icsk_ca_ops->cong_control) {------------------------bbr走这个分支
            icsk->icsk_ca_ops->cong_control(sk, rs);
            return;
        }

    而在 tcp_rcv_synsent_state_process 函数中,

    static int tcp_rcv_synsent_state_process(struct sock *sk, struct sk_buff *skb,
                         const struct tcphdr *th)
    {
    。。。。
    tcp_ack();
    。。。。
    smp_mb();
    
    tcp_finish_connect(sk, skb);
    。。。。
    }

    所以可以看出,拥塞函数调用是在拥塞init调用之前。

    从github上最新的内核2019-2-21号的版本来看,也存在这个问题,不知道是google故意为之还是bug,个人认为应该是bug,毕竟影响了一段时间的状态以及初始值。

    有心的童鞋可以去提交一个patch解决。

  • 相关阅读:
    第四章学习小结
    第三章学习小结
    第二章学习小结
    DS第五章学习小结
    DS第四章学习小结
    DS第三章小结
    第五章小结
    第四章小结
    第三章小结
    第二章小结
  • 原文地址:https://www.cnblogs.com/10087622blog/p/10412440.html
Copyright © 2011-2022 走看看