• 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);
        }
    }

     

  • 相关阅读:
    js 删除字符串中所有空格
    jquery easyui datagrid 设置设置在选中的所有行中只选择第一行
    编译Linux内核时出现错误gcc: error: elf_i386: No such file or directory
    AD9打印丝印层
    s3c2410 board.c分析
    2010.03 u-boot--Makefile完全分析
    mini6410移植--uboot移植(2)
    mini6410移植--uboot移植(1)
    uboot之uboot.lds文件分析
    Linux启动过程
  • 原文地址:https://www.cnblogs.com/codestack/p/11919690.html
Copyright © 2020-2023  润新知