鏈路層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);
}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章