在打开网卡时,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地址)
DMA产生中断,调用 stmmac_interrupt服务程序
stmmac_interrupt 通过调用 stmmac_dma_interrupt 处理DMA相关中断(包括发送和接收)。在stmmac_dma_interrupt中,通过NAPI机制触发软中断,调用stmmac_poll接收数据包。
在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使用。
在stmmac_rx中,循环收包,循环次数while (count < limit)。每次循环以DMA descriptor为处理单位,即每次循环时从一个descriptor指定的buffer读取一个数据包,再通过 napi_gro_receive 送给协议层,处理完后count++。这里limit = budget。
调用 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)的管理(释放内存等)。
这里有三种“一次获取多个数据包”的情况
1)一次软中断可能会多次调用net_rx_action
2)net_rx_action可能会多次调用stmmac_poll
3)stmmac_poll处理多个descriptor