網卡驅動:stmmac DMA接收流程

1. dma buffer及zero-copy

在打開網卡時,stmmac_init_rx_buffers()函數負責分配dma buffer。

static int stmmac_init_rx_buffers(struct stmmac_priv *priv, struct dma_desc *p,
                  int i, gfp_t flags, u32 queue)
{
    struct stmmac_rx_queue *rx_q = &priv->rx_queue[queue];
    struct sk_buff *skb;
    skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags);
    if (!skb) {
        netdev_err(priv->dev,
               "%s: Rx init fails; skb is NULL\n", __func__);
        return -ENOMEM;
    }
    rx_q->rx_skbuff[i] = skb;
    rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data,
                        priv->dma_buf_sz,
                        DMA_FROM_DEVICE);
    if (dma_mapping_error(priv->device, rx_q->rx_skbuff_dma[i])) {
        netdev_err(priv->dev, "%s: DMA mapping error\n", __func__);
        dev_kfree_skb_any(skb);
        return -EINVAL;
    }
    if (priv->synopsys_id >= DWMAC_CORE_4_00)
        p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
    else
        p->des2 = cpu_to_le32(rx_q->rx_skbuff_dma[i]);
    if ((priv->hw->mode->init_desc3) &&
        (priv->dma_buf_sz == BUF_SIZE_16KiB))
        priv->hw->mode->init_desc3(p);
    return 0;
}

1)skb = __netdev_alloc_skb_ip_align(priv->dev, priv->dma_buf_sz, flags); 分配skb

2)rx_q->rx_skbuff[i] = skb; 用於zero-copy,以後再接收軟中斷中直接使用rx_q->rx_skbuff[i],並交給上層協議處理

3)rx_q->rx_skbuff_dma[i] = dma_map_single(priv->device, skb->data, priv->dma_buf_sz, DMA_FROM_DEVICE); 進行dma映射,得到rx_q->rx_skbuff_dma[i] 源地址(即物理地址)

4)p->des0 = cpu_to_le32(rx_q->rx_skbuff_dma[i]); 在des0設置源地址(即填充buffer地址)

 

 

2. 接收數據流程:

2.1. DMA產生中斷,調用 stmmac_interrupt服務程序

 

2.2. stmmac_interrupt 通過調用 stmmac_dma_interrupt 處理DMA相關中斷(包括髮送和接收)。在stmmac_dma_interrupt中,通過NAPI機制觸發軟中斷,調用stmmac_poll接收數據包。

 

2.3. 在stmmac_poll中調用如下代碼收數據包

   work_done = stmmac_rx(priv, budget, rx_q->queue_index);
    if (work_done < budget) {
        napi_complete_done(napi, work_done);
        stmmac_enable_dma_irq(priv, chan);
    }
    return work_done;

1)調用stmmac_rx接收,budget爲循環讀取dma descriptor(通過處理descriptor來獲取數據包,每個descriptor對應一個數據包)的最大次數,work_done爲實際循環的次數。

2)if (work_done < budget) 代表實際循環讀取dma descriptor的次數小於最大次數budget,代表已經處理完所有需要被處理的descriptor,取完所有的數據包。這時,調用stmmac_enable_dma_irq()開啓中斷,再次接收數據。如果work_done = budget,代表可能還有數據包需要處理,那麼這些數據包留到net_rx_action再次調用stmmac_poll時處理。

3)return work_done; 該返回值會被net_rx_action使用。

 

2.4. 在stmmac_rx中,循環收包,循環次數while (count < limit)。每次循環以DMA descriptor爲處理單位,即每次循環時從一個descriptor指定的buffer讀取一個數據包,再通過 napi_gro_receive 送給協議層,處理完後count++。這裏limit = budget。

 

2.5. 調用 stmmac_rx_refill重新填充descriptor。

在觸發DMA中斷前,DMA已經將網卡收到的數據包搬到descriptor指定的buffer,而這個buffer又採用了zero-copy機制,所以直接將該buffer的地址copy給一個skb,再將此skb送到協議層處理,最後再調用 stmmac_rx_refill重新填充descriptor並設置buffer。之前descriptor指定的buffer地址已經被賦值給skb,由協議層負責該skb(buffer)的管理(釋放內存等)。

 

2.6. 總結:

這裏有三種“一次獲取多個數據包”的情況

1)一次軟中斷可能會多次調用net_rx_action

2)net_rx_action可能會多次調用stmmac_poll

3)stmmac_poll處理多個descriptor

 

 

 

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