linux GRO相关

本文主要注解skb_gro_receive函数,并画出示意图,便于理解。该函数是把前面已经判断过属于同一条流的skb合并到之前已经聚合过的skb上,把要合并的数据payload整理到已经合并的skb上去,下面根据代码逻辑画出4组示意图。

int skb_gro_receive(struct sk_buff **head, struct sk_buff *skb)
{
        struct skb_shared_info *pinfo, *skbinfo = skb_shinfo(skb);
        unsigned int offset = skb_gro_offset(skb);
        unsigned int headlen = skb_headlen(skb);//线性区域长度
        unsigned int len = skb_gro_len(skb);//data部分的长度
        struct sk_buff *lp, *p = *head;//已经合并的报文的首地址
        unsigned int delta_truesize;

        if (unlikely(p->len + len >= 65536))
                return -E2BIG;

        lp = NAPI_GRO_CB(p)->last;
        pinfo = skb_shinfo(lp);
//headlen是skb的线性区域长度,offset是payload的偏移,如果headlen<offset说明还有一部分头部在非线性区域,也说明数据部分都在非线性区域
        if (headlen <= offset) {
                skb_frag_t *frag;
                skb_frag_t *frag2;
                int i = skbinfo->nr_frags;//skb的frags的个数
                int nr_frags = pinfo->nr_frags + i;//合并之后的总的frags的个数

                if (nr_frags > MAX_SKB_FRAGS) //如果nr_frags大于最大的限制,那么直接merge
                        goto merge;

                offset -= headlen;//此时的offset表示,在非线性区域,头部的长度
                pinfo->nr_frags = nr_frags;//更新nr_frags
                skbinfo->nr_frags = 0;//skb的nr_frags置0

                frag = pinfo->frags + nr_frags;//指向已经合并的skb的最后一个frags[]
                frag2 = skbinfo->frags + i;//指向待合并的skb的最后一个frags[]
                do {
                        *--frag = *--frag2;//已经合并的skb的frags[]指针指向待合并的skb的frags[]指针(先--,再赋值)
                } while (--i);
                frag->page_offset += offset;//frag[0]需要操作一下offset的问题,把头部的部分剔除
                skb_frag_size_sub(frag, offset);
                /* all fragments truesize : remove (head size + sk_buff) */
                delta_truesize = skb->truesize -
                                 SKB_TRUESIZE(skb_end_offset(skb));

                skb->truesize -= skb->data_len;
                skb->len -= skb->data_len;
                skb->data_len = 0;

                NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE;
                goto done;
} else if (skb->head_frag) {
                int nr_frags = pinfo->nr_frags;
                skb_frag_t *frag = pinfo->frags + nr_frags;//此flow合并后的最后一个分片包的skb_frag_t结构体执针
                struct page *page = virt_to_head_page(skb->head);//获取skb->head所在的page
                unsigned int first_size = headlen - offset; //data部分的长度
                unsigned int first_offset;

                if (nr_frags + 1 + skbinfo->nr_frags > MAX_SKB_FRAGS) //合并后的frag数目大于最大的frag(17)数目直接merge
                        goto merge;

                first_offset = skb->data -
                               (unsigned char *)page_address(page) +
                               offset; //获取到payload到本page的offset

                pinfo->nr_frags = nr_frags + 1 + skbinfo->nr_frags;//合并后分片的数量

                frag->page.p          = page;//页数据的首地址
                frag->page_offset = first_offset;//payload部分在该页的偏移
                skb_frag_size_set(frag, first_size);//frag->size是page中数据的长度

                memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags);// skb如果有 分片数组直接拷贝到后面
                /* We dont need to clear skbinfo->nr_frags here */

                delta_truesize = skb->truesize - SKB_DATA_ALIGN(sizeof(struct sk_buff));//对齐
                NAPI_GRO_CB(skb)->free = NAPI_GRO_FREE_STOLEN_HEAD;//置位,当前报文已经做过gro,待释放。
                goto done;
        }

merge:
        delta_truesize = skb->truesize;
        if (offset > headlen) {
                unsigned int eat = offset - headlen;

                skbinfo->frags[0].page_offset += eat;
                skb_frag_size_sub(&skbinfo->frags[0], eat);
                skb->data_len -= eat;
                skb->len -= eat;
                offset = headlen;
        }

        __skb_pull(skb, offset);

        if (NAPI_GRO_CB(p)->last == p)
                skb_shinfo(p)->frag_list = skb;
        else
                NAPI_GRO_CB(p)->last->next = skb;
        NAPI_GRO_CB(p)->last = skb;
        __skb_header_release(skb);
        lp = p;

done:
        NAPI_GRO_CB(p)->count++;
        p->data_len += len; //非线性区域长度
        p->truesize += delta_truesize;
        p->len += len;
        if (lp != p) {
                lp->data_len += len;
                lp->truesize += delta_truesize;
                lp->len += len;
        }
        NAPI_GRO_CB(skb)->same_flow = 1;
        return 0;
}
EXPORT_SYMBOL_GPL(skb_gro_receive);

(1)headlen>offset

        把待合并的skb的page首地址和offset保存到已经合并的skb的frags[]中保存。如果待合并的skb在非线性区域中还有数据,那么直接调用函数:memcpy(frag + 1, skbinfo->frags, sizeof(*frag) * skbinfo->nr_frags) 把待合并的skb的非线性区域的数据拷贝到已经合并的skb的非线性区域中。

a.待合并的skb的payload部分全部存在非线性区域

 

b.payload一部分在线性区域,一部分在非线性区域,存在frags[]中,示意图如下:

(2)headlen <= offset

a.headlen < offset 说明待拷贝的skb还有一部分头部在非线性区frags[0].

 

 b.headlen = offset,头部全部存在线性区域

若有问题欢迎各位大神指正,互相学习进步

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值