链路层GRO的处理 (linux网络子系统学习 第八节)

链路层接收匹配函数__napi_gro_receive(napi, skb):

该函数对报文进行匹配,并不合并报文。

匹配规则(必须同时满足以下两个条件):

1、两个报文的接收dev必须相同。

2、两个报文的以太头必须相同。


static int __napi_gro_receive(struct napi_struct *napi,
                              struct sk_buff *skb)
{
    struct sk_buff *p;
    /*遍历napi 实例上的gro_list上挂的skb,
      根据上面说的匹配规则设置链表上报文的same字段*/
    for (p = napi->gro_list; p; p = p->next)
    {
        NAPI_GRO_CB(p)->same_flow = (p->dev == skb->dev)
                 && !compare_ether_header(skb_mac_header(p),
                     skb_gro_mac_header(skb));
        NAPI_GRO_CB(p)->flush = 0;
    }
    return dev_gro_receive(napi, skb);
}
int dev_gro_receive(struct napi_struct *napi,
                    struct sk_buff *skb)
{
    struct sk_buff **pp = NULL;
    struct packet_type *ptype;
    __be16 type = skb->protocol;
    struct list_head *head =
            &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
    int same_flow;
    int mac_len;
    int ret;
    /*如果接收网络设备设置成不支持GRO功能,就不进行GRO合并处理*/
    if (!(skb->dev->features & NETIF_F_GRO))
    {
        goto normal;
    }
                                                 
    /*如果是ip 分片报文,不进行GRO处理,
     * 因为在ip层会对ip分片报文进行合并
     */
    if (skb_is_gso(skb) || skb_has_frags(skb))
    {
        goto normal;
    }
    /*加RCU读锁对 ptype_base hahs 链表进行保护*/
    rcu_read_lock();
                                                  
    /*遍历链表,找到处理该类型报文的ptype,
     *并且该类型的ptype  实现了处理gro 的函数
     */
    list_for_each_entry_rcu(ptype, head, list)
    {
        if (ptype->type != type ||
            ptype->dev || !ptype->gro_receive)
            continue;
        /*如果找到了,初始化报文头指针,
         *并重置 skb中GRO使用的私有字段,
         *这些字段会在相应协议实现的GRO处理函数中进行设置
         */
        skb_set_network_header(skb, skb_gro_offset(skb));
        mac_len = skb->network_header - skb->mac_header;
        skb->mac_len = mac_len;
        NAPI_GRO_CB(skb)->same_flow = 0;
        NAPI_GRO_CB(skb)->flush = 0;
        NAPI_GRO_CB(skb)->free = 0;
        /*调用该协议类型注册的GRO处理函数对报文进行处理*/
        pp = ptype->gro_receive(&napi->gro_list, skb);
        break;
    }
    rcu_read_unlock();
    /*如果没找到对该协议类型报文进行处理的GRO,不进行GRO操作*/
    if (&ptype->list == head)
    {
        goto normal;
    }
    same_flow = NAPI_GRO_CB(skb)->same_flow;
    ret = NAPI_GRO_CB(skb)->free ? GRO_MERGED_FREE :
                                   GRO_MERGED;
    /*如果协议的GRO处理函数返回了合并后的报文,
     *就调用napi_gro_complete把报文送进协议栈进行处理
     */
    if (pp)
    {
        struct sk_buff *nskb = *pp;
        *pp = nskb->next;
        nskb->next = NULL;
        napi_gro_complete(nskb);
        napi->gro_count--;
    }
    /*如果same 被设置了,说明在链表上找到了相匹配的报文了,
     *已经合并过了,不再需要缓存了
     */
    if (same_flow)
    {
        goto ok;
    }
                                                  
    /*如果没找到相匹配的报文,需要缓存。
     *缓存前需要判断队列是否已满或该报文是否应该缓存
     */
    if (NAPI_GRO_CB(skb)->flush ||
        napi->gro_count >= MAX_GRO_SKBS)
    {
        goto normal;
    }
    /*缓存没有匹配的报文到gro_list,返回值为GRO_HELD*/
    napi->gro_count++;
    NAPI_GRO_CB(skb)->count = 1;
    skb_shinfo(skb)->gso_size = skb_gro_len(skb);
    skb->next = napi->gro_list;
    napi->gro_list = skb;
    ret = GRO_HELD;
pull:
    /*经过这个协议栈的GRO receive的处理,
     *这时NAPI_GRO_CB(skb)->data_offset字段已经设置好了。
     *如果GRO需要处理的数据不在skb的线性区,
     *把需要的数据copy到线性区,方便以后操作
     */
    if (skb_headlen(skb) < skb_gro_offset(skb))
    {
        int grow = skb_gro_offset(skb) - skb_headlen(skb);
        BUG_ON(skb->end - skb->tail < grow);
        memcpy(skb_tail_pointer(skb),
               NAPI_GRO_CB(skb)->frag0, grow);
        skb->tail += grow;
        skb->data_len -= grow;
        skb_shinfo(skb)->frags[0].page_offset += grow;
        skb_shinfo(skb)->frags[0].size -= grow;
                                                      
        /*如果把数据移入线性区后第一页就空了,
         *释放空页并把后续页依次前移
         */
        if (unlikely(!skb_shinfo(skb)->frags[0].size))
        {
            put_page(skb_shinfo(skb)->frags[0].page);
            memmove(skb_shinfo(skb)->frags,
                    skb_shinfo(skb)->frags + 1,
                    (--skb_shinfo(skb)->nr_frags *
                     sizeof(skb_frag_t)));
         }
    }
ok:
    return ret;
normal:
    ret = GRO_NORMAL;
    goto pull;
}

链路层的GRO完成函数:


合并完成后的报文调用该函数来把报文送入协议栈。



static int napi_gro_complete(struct sk_buff *skb)
{
    struct packet_type *ptype;
    __be16 type = skb->protocol;
    struct list_head *head =
              &ptype_base[ntohs(type) & PTYPE_HASH_MASK];
    int err = -ENOENT;
    /*如果没有和别的报文合并过,
     *就可以直接送协议栈进行处理了
     */
    if (NAPI_GRO_CB(skb)->count == 1)
    {
        skb_shinfo(skb)->gso_size = 0;
        goto out;
    }
    /*找到相关协议把报文送给协议的grp_complete函数处理*/
    rcu_read_lock();
    list_for_each_entry_rcu(ptype, head, list)
    {
        if (ptype->type != type
            || ptype->dev
            || !ptype->gro_complete
            )
            continue;
        err = ptype->gro_complete(skb);
        break;
    }
    rcu_read_unlock();
    if (err)
    {
        WARN_ON(&ptype->list == head);
        kfree_skb(skb);
        return NET_RX_SUCCESS;
    }
    /*各层协议处理完成后,送给协议栈进行处理*/
out:
    return netif_receive_skb(skb);
}


你可能感兴趣的:(linux,linux协议栈,GRO)