zoukankan      html  css  js  c++  java
  • tcp输入数据 慢速路径处理 tcp_data_queue_ofo

    tcp_data_queue_ofo

    在新内核的实现中ofo队列实际上是一颗红黑树。
    在tcp_data_queue_ofo中根据序号,查找到合适位置,合并或者添加到rbtree中。
    同时设置dsack和sack,准备ack给发送方。

    //http://abcdxyzk.github.io/blog/2015/04/01/kernel-net-data-queue/
    static void tcp_data_queue_ofo(struct sock *sk, struct sk_buff *skb)
    {
        struct tcp_sock *tp = tcp_sk(sk);
        struct rb_node **p, *q, *parent;
        struct sk_buff *skb1;
        u32 seq, end_seq;
        bool fragstolen;
    /*如果收到乱序包 ,可能在传输过程中出现了 拥塞 
    所以检查ecn 标志如果是路由器 拥塞会设置这个标志 说明路径上存在拥塞,需要
    给发送方 接收方进行拥塞处理  如果没有拥塞 需要尽快通知 发送方*/
        tcp_ecn_check_ce(tp, skb);
    
        if (unlikely(tcp_try_rmem_schedule(sk, skb, skb->truesize))) {//接收缓存不够  
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFODROP);
            tcp_drop(sk, skb);//接收缓存不够 丢弃
            return;
        }
    
        /* Disable header prediction. */
        tp->pred_flags = 0;//收到乱序包,关闭快速路径
        inet_csk_schedule_ack(sk);//乱序包会快速ack
    
        NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOQUEUE);
        seq = TCP_SKB_CB(skb)->seq;
        end_seq = TCP_SKB_CB(skb)->end_seq;
        SOCK_DEBUG(sk, "out of order segment: rcv_next %X seq %X - %X
    ",
               tp->rcv_nxt, seq, end_seq);
    
        p = &tp->out_of_order_queue.rb_node;
        if (RB_EMPTY_ROOT(&tp->out_of_order_queue)) {//ofo队列中为空,简单插入新的sack
            /* Initial out of order segment, build 1 SACK. */
            if (tcp_is_sack(tp)) {
                tp->rx_opt.num_sacks = 1;
                tp->selective_acks[0].start_seq = seq;
                tp->selective_acks[0].end_seq = end_seq;
            }
            rb_link_node(&skb->rbnode, NULL, p);
            rb_insert_color(&skb->rbnode, &tp->out_of_order_queue);
            tp->ooo_last_skb = skb;
            goto end;
        }
    
        /* In the typical case, we are adding an skb to the end of the list.
         * Use of ooo_last_skb avoids the O(Log(N)) rbtree lookup.
         */
        if (tcp_try_coalesce(sk, tp->ooo_last_skb, skb, &fragstolen)) {//对于普遍场景,先尝试合并skb到上一个乱序包
    coalesce_done://合并完成
            tcp_grow_window(sk, skb); //尝试增加窗口通告
            kfree_skb_partial(skb, fragstolen);//skb已经被合并,可以释放
            skb = NULL;
            goto add_sack;
        }
        /* Can avoid an rbtree lookup if we are adding skb after ooo_last_skb */
        if (!before(seq, TCP_SKB_CB(tp->ooo_last_skb)->end_seq)) {//如果序号比ooo_last_skb大,则可以直接添加,避免查找
            parent = &tp->ooo_last_skb->rbnode;
            p = &parent->rb_right;//添加到ooo_last_skb的右子树
            goto insert;
        }
    
        /* Find place to insert this segment. Handle overlaps on the way. */
        parent = NULL;//需要查找这个ofo包的添加位置
        while (*p) {
            parent = *p;
            skb1 = rb_entry(parent, struct sk_buff, rbnode);
            if (before(seq, TCP_SKB_CB(skb1)->seq)) {
                p = &parent->rb_left;//比当前节点小,添加到左子树
                continue;
            }
            if (before(seq, TCP_SKB_CB(skb1)->end_seq)) {
                if (!after(end_seq, TCP_SKB_CB(skb1)->end_seq)) {//序号所有部分都已经在当前节点
                    /* All the bits are present. Drop. */
                    NET_INC_STATS(sock_net(sk),
                              LINUX_MIB_TCPOFOMERGE);
                    __kfree_skb(skb);
                    skb = NULL;
                    tcp_dsack_set(sk, seq, end_seq);
                    goto add_sack;
                }
                if (after(seq, TCP_SKB_CB(skb1)->seq)) {//有部分重叠
                    /* Partial overlap. */
                    tcp_dsack_set(sk, seq, TCP_SKB_CB(skb1)->end_seq);//设置重叠部分dsack
                } else {//skb1->seq = seq <= skb1->end_seq < end_seq
                    /* skb's seq == skb1's seq and skb covers skb1.
                     * Replace skb1 with skb.
                     *///skb中包含了全部的skb1
                     //使用skb替换skb1
                    rb_replace_node(&skb1->rbnode, &skb->rbnode,
                            &tp->out_of_order_queue);
                    //设置或合并现有dsack设置 //因为skb包含了全部skb1部分,则整个skb1都被重传了
                    tcp_dsack_extend(sk,
                             TCP_SKB_CB(skb1)->seq,
                             TCP_SKB_CB(skb1)->end_seq);
                    NET_INC_STATS(sock_net(sk),
                              LINUX_MIB_TCPOFOMERGE);
                    //释放skb1
                    __kfree_skb(skb1);
                    goto merge_right;//还要继续查看skb1的右子数有没有需要合并的部分
                }
            } else if (tcp_try_coalesce(sk, skb1, skb, &fragstolen)) { // skb1->seq < skb1->end_seq <= seq
                goto coalesce_done;//尝试合并
            }
            p = &parent->rb_right;//比当前加点大,查找右子树
        }
    insert:
        /* Insert segment into RB tree. *///找到合适位置后插入ofo队列
        rb_link_node(&skb->rbnode, parent, p);
        rb_insert_color(&skb->rbnode, &tp->out_of_order_queue);
    
    merge_right:
        /* Remove other segments covered by skb. */
        while ((q = rb_next(&skb->rbnode)) != NULL) {//查看右子树中有没需要合并的节点
            skb1 = rb_entry(q, struct sk_buff, rbnode);
    
            if (!after(end_seq, TCP_SKB_CB(skb1)->seq))//没有交集,不需要合并
                break;
            if (before(end_seq, TCP_SKB_CB(skb1)->end_seq)) {//有交集
                tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
                         end_seq);//更新dsack
                break;
            }
            //完全包含当前节点,删除该节点,并更新dsack
            rb_erase(&skb1->rbnode, &tp->out_of_order_queue);
            tcp_dsack_extend(sk, TCP_SKB_CB(skb1)->seq,
                     TCP_SKB_CB(skb1)->end_seq);
            NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPOFOMERGE);
            tcp_drop(sk, skb1);//可以删除skb1
        }
        /* If there is no skb after us, we are the last_skb ! */
        if (!q)//没有下一个skb了,更新ooo_last_skb
            tp->ooo_last_skb = skb;
    
    add_sack:
        if (tcp_is_sack(tp))
            tcp_sack_new_ofo_skb(sk, seq, end_seq);
    end:
        if (skb) {//没有被合并//跟in-order包一样,调整窗口
            tcp_grow_window(sk, skb);
            skb_condense(skb);
            skb_set_owner_r(skb, sk);
        }
    }

     

  • 相关阅读:
    基本技能训练之线程
    关于UEditor的使用配置(图片上传配置)
    PAT 乙级练习题1002. 写出这个数 (20)
    codeforces 682C Alyona and the Tree DFS
    codeforces 681D Gifts by the List dfs+构造
    codeforces 678E Another Sith Tournament 概率dp
    codeforces 680E Bear and Square Grid 巧妙暴力
    codeforces 678D Iterated Linear Function 矩阵快速幂
    codeforces 679A Bear and Prime 100 交互
    XTUOJ 1248 TC or CF 搜索
  • 原文地址:https://www.cnblogs.com/codestack/p/11919690.html
Copyright © 2011-2022 走看看