收包軟中斷和netif_rx (linux網絡子系統學習 第四節 )

初始化報文接收軟中斷

static int __init net_dev_init(void)
{
    ......
    open_softirq(NET_RX_SOFTIRQ, net_rx_action);
    ......
}


報文接收軟中斷的處理函數net_rx_action詳解:

static void net_rx_action(struct softirq_action *h)
{
    /*取得本地cpu 的softnet_data 的poll_list  鏈表*/
    struct list_head *list = &__get_cpu_var(softnet_data).poll_list;
    /*設置軟中斷處理程序一次允許的最大執行時間爲2個jiffies*/
    unsigned long time_limit = jiffies + 2;
  
    /*設置軟中斷接收函數一次最多處理的報文個數爲 300 */
    int budget = netdev_budget;
    /*關閉本地cpu的中斷,下面判斷list是否爲空時防止硬中斷搶佔*/
    local_irq_disable();
    /*循環處理pool_list 鏈表上的等待處理的napi*/
    while (!list_empty(list))
    {
        struct napi_struct *n;
        int work, weight;
                                                                                                                                      
        /*如果處理報文超出一次處理最大的個數
          或允許時間超過最大時間就停止執行,
          跳到softnet_break 處*/
        if (unlikely(budget <= 0 || time_after(jiffies, time_limit)))
        {
            goto softnet_break;
        }
        /*使能本地中斷,上面判斷list爲空已完成,下面調用NAPI
          的輪詢函數是在硬中斷開啓的情況下執行*/
        local_irq_enable();
                                                                                                                                      
        /* 取得softnet_data pool_list 鏈表上的一個napi,
           即使現在硬中斷搶佔軟中斷,會把一個napi掛到pool_list的尾端
           軟中斷只會從pool_list 頭部移除一個pool_list,這樣不存在臨界區*/
        n = list_entry(list->next, struct napi_struct, poll_list);
        /*用weighe 記錄napi 一次輪詢允許處理的最大報文數*/
        weight = n->weight;
        /* work 記錄一個napi總共處理的報文數*/
        work = 0;
  
        /*如果取得的napi狀態是被調度的,就執行napi的輪詢處理函數*/
        if (test_bit(NAPI_STATE_SCHED, &n->state))
        {
            work = n->poll(n, weight);
        }
        WARN_ON_ONCE(work > weight);
        /*預算減去已經處理的報文數*/
        budget -= work;
        /*禁止本地CPU 的中斷,下面會有把沒執行完的NAPI掛到softnet_data
          尾部的操作,和硬中斷存在臨界區。同時while循環時判斷list是否
          爲空時也要禁止硬中斷搶佔*/
        local_irq_disable();
  
        /*如果napi 一次輪詢處理的報文數正好等於允許處理的最大數,
          說明一次輪詢沒處理完全部需要處理的報文*/
        if (unlikely(work == weight))
        {
            /*如果napi已經被禁用,就把napi 從 softnet_data 的pool_list 上移除*/
            if (unlikely(napi_disable_pending(n)))
            {
                local_irq_enable();
                napi_complete(n);
                local_irq_disable();
            }
            else
            {
                /*否則,把napi 移到 pool_list 的尾端*/
                list_move_tail(&n->poll_list, list);
            }
        }
    }
out:
    local_irq_enable();
    return;
  
    /*如果處理時間超時,或處理的報文數到了最多允許處理的個數,
      說明還有napi 上有報文需要處理,調度軟中斷。
      否則,說明這次軟中斷處理完全部的napi上的需要處理的報文,不再需要
      調度軟中斷了*/
softnet_break:
    __get_cpu_var(netdev_rx_stat).time_squeeze++;
    __raise_softirq_irqoff(NET_RX_SOFTIRQ);
    goto out;
}


虛擬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;
}


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;
}


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