Linux網絡子系統中舊的報文接收接口netif_rx

linux舊的收包方式提供給驅動的接口netif_rx()
int netif_rx(struct sk_buff *skb)
{
    struct softnet_data *queue;
    unsigned long flags;
 
    /*如果接收skb的時間戳沒設定,設定接收時間戳*/
    if (!skb->tstamp.tv64)
    {
        net_timestamp(skb);
    }
  
    /*禁止本地cpu的中斷*/
    local_irq_save(flags);
  
    /*取得本地cpu的softnet_data*/
    queue = &__get_cpu_var(softnet_data);
                   
    /*每個CPU都有一個統計數據,增加統計數據*/
    __get_cpu_var(netdev_rx_stat).total++;
  
    /*如果本地CPU的輸入隊列中的skb 個數小於允許的最多的個數*/
    if (queue->input_pkt_queue.qlen <= netdev_max_backlog)
    {
        /*如果本地cpu的輸入隊列長度不爲0,表示輸入隊列已經有skb了,
        並且特殊的napi backlog 已經掛入了softnet_data  的
        pool_list上了*/
        if (queue->input_pkt_queue.qlen)
        {
enqueue:
            /*把skb 放入CPU的輸入隊列 input_pkt_queue*/
            __skb_queue_tail(&queue->input_pkt_queue, skb);
                      
            /*使能中斷 並 返回*/
            local_irq_restore(flags);
            return NET_RX_SUCCESS;
        }
        /*如果輸入隊列爲空,則把 特殊的napi backlog 掛到softnet_data
        的 pool_list 上 並返回把skb放入輸入隊列並返回*/
        napi_schedule(&queue->backlog);
        goto enqueue;
    }
    /*如果本地cpu的輸入隊列已經滿了,則丟棄報文,
      並增加丟包計數並返回*/
    __get_cpu_var(netdev_rx_stat).dropped++;
    local_irq_restore(flags);
  
    kfree_skb(skb);
    return NET_RX_DROP;
}


上篇博文裏我們已經分析過,Linux網絡子系統中管理的收包隊列由一個虛擬的NAPI  backlog來進行處理。
驅動調用netif_rx把報文放入隊列,把虛擬的NAPI backlog加入到pool鏈表上,調度收包軟中斷。收包軟中斷會調用backlog的輪詢
函數對隊列中的報文進行處理。

該虛擬的NAPI backlog的輪詢函數在函數net_dev_init中被初始化爲process_backlog
我們看一下NAPI backlog的輪詢函數process_backlog的實現

參數:
napi : 本地cpu上softnet_data 的backlog .
quota :  一次輪詢可以處理的最多報文數。
函數詳解:
static int process_backlog(struct napi_struct *napi, int quota)
{
    int work = 0;
                                         
    /*取得本地CPU上的softnet_data  數據*/
    struct softnet_data *queue = &__get_cpu_var(softnet_data);
  
    /*開始計時,一旦允許時間到,就退出輪詢*/
    unsigned long start_time = jiffies;
    napi->weight = weight_p;
  
    /*循環從softnet_data 的輸入隊列取報文並處理,直到隊列中沒有報文了,
     或處理的報文數大於了允許的上限值了,
     或輪詢函數執行時間大於一個jiffies 了
  */
    do
    {
        struct sk_buff *skb;
        /*禁用本地中斷,要存隊列中取skb,防止搶佔*/
        local_irq_disable();
  
        /*從softnet_data 的輸入隊列中取得一個skb*/
        skb = __skb_dequeue(&queue->input_pkt_queue);
  
        /*如果隊列中沒有skb,則使能中斷並退出輪詢*/
        if (!skb)
        {
            /*把napi 從 softnet_data 的 pool_list 鏈表上摘除*/
            __napi_complete(napi);
            /*使能本地CPU的中斷*/
            local_irq_enable();
            break;
        }
        /*skb 已經摘下來了,使能中斷*/
        local_irq_enable();
  
        /*把skb送到協議棧相關協議模塊進行處理,詳細處理見後續章節*/
        netif_receive_skb(skb);
    } while (++work < quota && jiffies == start_time);
    /*返回處理報文個數*/
    return work;
}

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