zoukankan      html  css  js  c++  java
  • TCP的核心系列 — SACK和DSACK的实现(五)

    18版本对于每个SACK块,都是从重传队列头开始遍历。37版本则可以选择性的遍历重传队列的某一部分,忽略

    SACK块间的间隙、或者已经cache过的部分。这主要是通过tcp_sacktag_skip()和tcp_sacktag_walk()完成的。

    tcp_sacktag_skip()可以直接找到包含某个序号的skb,通常用于定位SACK块的开头。

    tcp_sacktag_walk()则遍历两个序号之间的skb,通常用于遍历一个SACK块。

    本文主要内容:SACK的遍历函数tcp_sacktag_skip()和tcp_sacktag_walk()。

    Author:zhangskd

    tcp_sacktag_skip

    从当前skb开始遍历,查找skip_to_seq序号对应的skb,同时统计fackets_out。

    这样可以从当前包,直接遍历到某个块的start_seq,而不用从头开始遍历,也可以跳过块间的间隙。

    /* Avoid all extra work that is being done by sacktag while walking in a normal way */
    static struct sk_buff *tcp_sacktag_skip(struct sk_buff *skb, struct sock *sk,
                                   struct tcp_sacktag_state *state, u32 skip_to_seq)
    {
        tcp_for_write_queue_from(skb, sk) {
            if (skb == tcp_send_head(sk)) /* 到了发送队列头,即下一个将要发送的数据包 */
                break;
     
            if (after(TCP_SKB_CB(skb)->end_seq, skip_to_seq)) /* 找到包含skip_to_seq序号的数据包了 */
                break;
    
            state->fack_count += tcp_skb_pcount(skb); /* 统计fackets_out个数 */
        }
    
        return skb; /* 返回包含skip_to_seq的skb */
    }
    

     

    tcp_sacktag_walk

    遍历一个SACK块,如果SACK块包含了多个连续的skb,那么先尝试合并这些段。

    为什么要合并呢?因为下次遍历的时候,要遍历的包个数就减少了,能提高效率。

    如果skb完全包含在块中,则调用tcp_sacktag_one更新该段的记分牌。

    static struct sk_buff *tcp_sacktag_walk(struct sk_buff *skb, struct sock *sk,
                                            struct tcp_sack_block *next_dup,
                                            struct tcp_sacktag_state *state,
                                            u32 start_seq, u32 end_seq,
                                            int dup_sack_in)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *tmp;
    
        tcp_for_write_queue_from(skb, sk) {
            int in_sack = 0;
            int dup_sack = dup_sack_in;
    
            if (skb == tcp_send_head(sk)) /* 遍历到发送队列头了 */
                break;
    
            if (! before(TCP_SKB_CB(skb)->seq, end_seq)) /* skb序号超过SACK块了 */
                break;
    
            /* 如果下一个块是DSACK,且此skb可能包含在其中 */
            if ((next_dup != NULL) && 
                before(TCP_SKB_CB(skb)->seq, next_dup->end_seq)) {
    
                /* 此skb是否完全包含在DSACK块中 */
                in_sack = tcp_match_skb_to_sack(sk, skb, next_dup->start_seq, next_dup->end_seq);
    
                if (in_sack > 0)
                    dup_sack = 1; /* 表示这个skb被DSACK */
            }
    
            if (in_sack <= 0) {
                /* 一个SACK块可能包括多个skb,尝试把这些连续的skb合并 */
                tmp = tcp_shift_skb_data(sk, skb, state, start_seq, end_seq, dup_sack);
    
                if (tmp != NULL) { /* 合并成功 */
                    if (tmp != skb) { /* tmp和当前段地址不同,则跳到合并后的段处理 */
                        skb = tmp;
                        continue;
                    }
                    in_sack = 0;
    
                } else { /* 合并不成功,单独处理这个段 */
                    in_sack = tcp_match_skb_to_sack(sk, skb, start_seq, end_seq); /* 段是否完全包含在块中 */
                }
            }
            
            if (unlikely(in_sack < 0))
                break;
    
            /* 如果这个段完全包含在块中,进行处理 */
            if (in_sack) {
    
                /* 就是在这里:标志这个段的记分牌!*/
                TCP_SKB_CB(skb)->sacked = tcp_sacktag_one(skb, sk, state, dup_sack, tcp_skb_pcount(skb));
     
                /* 如果当前skb的开始序列号大于被SACK的包的最高初始序列号 */
                if (! before(TCP_SKB_CB(skb)->seq, tcp_highest_sack_seq(tp)))
                    tcp_advance_highest_sack(sk, skb); /*把highest_sack设为skb->next */
            }
    
            state->fack_count += tcp_skb_pcount(skb); /* 更新fackets_out */
        }
    
        return skb; /* 遍历到此skb退出 */
    } 
    

    tcp_match_skb_to_sack()用于检查一个数据段是否完全包含在一个SACK块中,主要考虑到GSO分段。

    /* Check if skb is fully within the SACK block.
     * In presence of GSO skbs, the incoming SACK may not exactly match but we can find smaller MSS
     * aligned portion of it that matches. Therefore we might need to fragment which may fail and creates
     * some hassle (caller must handle error case returns).
     * FIXME: this could be merged to shift decision code
     */
    static int tcp_match_skb_to_sack(struct sock *sk, struct sk_buff *skb, u32 start_seq, u32 end_seq)
    {
        int in_sack, err;
        unsigned int pkt_len;
        unsigned int mss;
    
        /* 如果start_seq <= skb->seq < skb->end_seq <= end_seq,说明skb完全包含在SACK块中 */
        in_sack = ! after(start_seq, TCP_SKB_CB(skb)->seq) &&
                           ! before(end_seq, TCP_SKB_CB(skb)->end_seq);
    
        /* 如果有GSO分段,skb可能部分包含在块中 */
        if (tcp_skb_pcount(skb) > 1 && ! in_sack &&
            after(TCP_SKB_CB(skb)->end_seq, start_seq)) {
    
            mss = tcp_skb_mss(skb);
            in_sack = ! after(start_seq, TCP_SKB_CB(skb)->seq); /* 前半部在块中 */
     
            /* 这里根据skb->seq和start_seq的大小,分情况处理 */
            if (! in_sack) { /* 后半部在块中 */
                pkt_len = start_seq - TCP_SKB_CB(skb)->seq; /* skb在块之前的部分 */
                if (pkt_len < mss)
                    pkt_len = mss;
    
            } else {
                pkt_len = end_seq - TCP_SKB_CB(skb)->seq; /* skb在块内的部分 */
                if (pkt_len < mss)
                    return -EINVAL;
            }
            
            /* Round if necessary so that SACKs cover only full MSSes and/or the remaining
             * small portion (if present)
             */
            if (pkt_len > mss) {
                unsigned int new_len = (pkt_len / mss) * mss;
                if (! in_sack && new_len < pkt_len) {
                    new_len += mss;
                    if (new_len > skb->len)
                        return 0;
                }
                pkt_len = new_len;
            }
    
            err = tcp_fragment(sk, skb, pkt_len, mss); /* 把skb分为两个包,SACK块内的和SACK块外的 */
        }
    
        return in_sack;
    }
    

     

    tcp_shift_skb_data()尝试把SACK块内的多个包合成一个,可以提升遍历效率。

    一个SACK块可能包括多个skb,尝试把这些连续的skb合成一个。

    /* Try to collapsing SACK blocks spanning across multiple skbs to a single skb. */
    static struct sk_buff *tcp_shift_skb_data(struct sock *sk, struct sk_buff *skb,
                                              struct tcp_sacktag_state *state,
                                              u32 start_seq, u32 end_seq, int dup_sack)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct sk_buff *prev;
        int mss;
        int pcount = 0;
        int len;
        int in_sack;
    
        if (! sk_can_gso(sk))
            goto fallback;
    
        ...
    
    fallback:
        NET_INC_STATS_BH(sock_net(sk), LINUX_MIB_SACKSHIFTFALLBACK);
        return NULL;
    }
    
  • 相关阅读:
    DEDECMS里面DEDE函数解析
    dede数据库类使用方法 $dsql
    DEDE数据库语句 DEDESQL命令批量替换 SQL执行语句
    织梦DedeCms网站更换域名后文章图片路径批量修改
    DSP using MATLAB 示例 Example3.12
    DSP using MATLAB 示例 Example3.11
    DSP using MATLAB 示例 Example3.10
    DSP using MATLAB 示例Example3.9
    DSP using MATLAB 示例Example3.8
    DSP using MATLAB 示例Example3.7
  • 原文地址:https://www.cnblogs.com/aiwz/p/6333335.html
Copyright © 2011-2022 走看看