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