linux 协议栈学习 第八节 链路层GRO的处理

来源:互联网 发布:java 各版本新特性 编辑:程序博客网 时间:2024/05/16 18:57

linux 协议栈学习 第八节 链路层GRO的处理

链路层接收匹配函数__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);}
0 0
原创粉丝点击