☰
23、DMA与网络子系统:MTK Ethernet GMAC与WiFi的DMA加速实战
2026/10/7 13:39:14 网站建设 项目流程

网络数据包的收发,说白了就是一场与时间的赛跑。CPU如果亲自去搬每个数据包,那基本就不用干别的了。DMA在这里扮演的角色,就是那个不知疲倦的搬运工。

我在MTK平台上调试GMAC时,遇到过一个问题:千兆以太网跑不满带宽。查来查去,发现是DMA描述符环配置不合理。嗯,今天我们就来聊聊这个。

23.1 MTK Ethernet GMAC的DMA架构

MTK的GMAC(Gigabit MAC)内部集成了专用的DMA引擎。它负责把网络数据包从FIFO搬到系统内存,或者反过来。

核心结构就两个:

  • 发送描述符环(Tx Descriptor Ring):CPU准备好数据,DMA负责发送
  • 接收描述符环(Rx Descriptor Ring):DMA接收数据,CPU负责处理

每个描述符里包含了:

  • 数据缓冲区的物理地址
  • 数据长度
  • 状态标志位(是否完成、是否有错误)

我个人习惯把描述符环设计成256个条目。太少容易丢包,太多浪费内存。你想想看,256个刚好能扛住突发流量。

关键点:描述符环必须放在非Cacheable的内存区域。否则CPU和DMA看到的数据不一致,你会疯掉的。

23.2 WiFi模块的DMA传输机制

MTK的WiFi芯片(比如MT7668)内部也有DMA。但和GMAC不同,WiFi的DMA要处理更多的协议头。

WiFi数据包的DMA流程大致是这样:

  1. WiFi固件把收到的数据写到内部SRAM
  2. DMA把数据从SRAM搬到主机内存
  3. 驱动解析802.11头,转换成802.3格式
  4. 交给网络协议栈

我曾经踩过一个坑:WiFi的DMA描述符里有个「数据偏移」字段。默认值是0,但WiFi数据包前面有24字节的802.11头。如果不设置偏移,DMA会把头和数据一起搬,导致协议栈解析失败。

注意:WiFi DMA的缓冲区大小要预留足够的头部空间。我一般设置2048字节,够用。

23.3 NAPI与DMA的配合机制

NAPI(New API)是Linux网络子系统的中断缓解机制。它的核心思想是:

  • 有数据来时,先触发中断
  • 中断处理函数关闭中断,切换到轮询模式
  • 轮询完所有数据后,再开启中断

为什么要配合DMA?因为DMA已经把数据搬好了。NAPI只需要去描述符环里取就行了。

在MTK平台上,典型的NAPI轮询函数长这样:

static int mtk_napi_poll(struct napi_struct *napi, int budget) { struct mtk_eth *eth = container_of(napi, struct mtk_eth, napi); int rx_done = 0; // 遍历接收描述符环 while (rx_done < budget) { struct mtk_rx_desc *desc = &eth->rx_ring[rx_done]; // 检查DMA是否完成 if (!(desc->status & MTK_RX_DMA_DONE)) break; // 处理数据包 skb = build_skb(desc->data, desc->len); napi_gro_receive(napi, skb); // 重新填充描述符 desc->status = 0; rx_done++; } // 如果没处理完,继续轮询 if (rx_done == budget) return budget; // 处理完了,重新开启中断 napi_complete_done(napi, rx_done); mtk_irq_enable(eth, MTK_RX_DONE_INT); return rx_done; }

这段代码里有个细节:每次处理完一个包,要立即重新填充描述符。否则DMA没地方放新数据,会丢包。

我的经验:budget值不要设太大。我一般设64,既能保证吞吐量,又不会让CPU饿死其他任务。

23.4 性能调优实战

在实际项目中,DMA和NAPI的配合有几个关键参数:

参数推荐值说明
描述符环大小256平衡内存和吞吐量
NAPI budget64单次轮询最大包数
DMA burst size8一次DMA传输的字节数
中断合并阈值4攒够4个包再触发中断

我曾经在MT7621平台上调试WiFi吞吐量。默认配置下,2.4G只能跑到80Mbps。后来我把DMA burst size从4改成8,NAPI budget从32改成64,直接飙到150Mbps。

为什么会这样?因为burst size大了,DMA每次搬运的数据更多,减少了总线占用。NAPI budget大了,一次轮询能处理更多包,减少了中断次数。

核心原则:让DMA多干活,让CPU少中断。NAPI就是那个「攒一波再处理」的机制。

23.5 避坑指南

最后分享几个我踩过的坑:

  • Cache一致性:DMA缓冲区一定要用dma_alloc_coherent()分配,或者手动做cache flush。否则你会看到数据错乱。
  • 描述符环溢出:如果CPU处理速度跟不上DMA,描述符环会满。这时候要丢包还是反压?我建议丢包,反压会导致DMA卡死。
  • 中断亲和性:多核CPU上,把网络中断绑定到同一个核。否则cache来回迁移,性能下降30%。

嗯,DMA和网络子系统的配合,说白了就是「让硬件干硬件的活,让软件干软件的活」。NAPI就是那个协调者,告诉DMA「你先搬,搬完了叫我」。搞懂了这一点,MTK平台的网络驱动就没什么秘密了。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询