Lazy loaded image
Words 0Read Time 1 min
Invalid Date
🌐
案例定位
这页不是教你背某个具体网卡驱动的源码,而是给你一套 阅读网卡 ring DMA 驱动 的抓手:怎么从 descriptor ring、NAPI、TX/RX 回收、doorbell 和中断合并里,看出真实的 DMA 设计逻辑。

一、为什么网卡驱动是 DMA 经典案例

如果音频驱动最适合讲 cyclic / period 节拍,那网卡驱动最适合讲的是:
  • descriptor ring
  • TX / RX 双向队列
  • producer / consumer 指针
  • 中断合并与吞吐权衡
  • buffer 回收与 reuse
 
一句话:
网卡 ring DMA 是“高吞吐 + 高并发 + 高频 completion”场景下的标准教材。

二、先把三条主线分开

1. TX 路径

  • 上层把 skb 或 packet 交给驱动
  • 驱动把数据映射成 DMA 地址
  • 填 TX descriptor
  • doorbell / tail 更新,通知设备发送
  • 完成后 unmap 并释放 skb

2. RX 路径

  • 驱动预先准备好 RX buffer ring
  • 设备收到包后 DMA 写入对应 buffer
  • 完成中断或 NAPI poll 回收完成 descriptor
  • CPU 读取、封装 skb、补充新 buffer 回 ring

3. 回收路径

  • TX completion 后释放已发送资源
  • RX 消费后补回空 buffer
  • reset / link flap / stop 时统一清理 in-flight 状态

三、为什么网卡一定要看 ring,而不是只看函数

网卡 DMA 的本质不是“发一个包”“收一个包”,而是:
很多个 descriptor 同时在不同生命周期里并发推进。
 
你如果不先抓 ring,就会被一堆函数名淹死;
你先抓 ring,就能看清:
  • 谁推进 tail
  • 谁回收 head
  • 哪些 descriptor 现在归设备
  • 哪些 descriptor 已经完成但尚未回收

四、读网卡 DMA 驱动时优先看什么

1. descriptor 结构体

重点看:
  • buffer DMA 地址字段
  • 长度字段
  • done / owner / status bit
  • next 或 ring index 关系
 
这东西本质上就是“设备任务说明书”。

2. ring 初始化

重点看:
  • descriptor ring 放在哪种内存里
  • RX buffer 如何预填
  • TX / RX ring 大小是多少
  • head / tail / clean 指针怎么初始化

3. doorbell / tail 更新

网卡世界里常见动作是:
  • 软件填好 descriptor
  • 更新 producer / tail
  • 写寄存器告诉设备有新包可发
 
这一步背后对应的不是“写个寄存器”,而是 CPU_OWNED → DEVICE_OWNED 的 ownership 切换。

4. completion / NAPI poll

很多网卡不会每来一个包就傻乎乎中断一次,而是:
  • 做中断合并
  • 用 NAPI poll 批量回收完成队列
 
所以你要重点看:
  • done bit 何时被检查
  • reclaim 是逐包还是批量
  • 何时重新启用中断

五、网卡 DMA 最值钱的几个观察点

观察点 1:TX 和 RX 的 ownership 完全不同

  • TX:CPU 先生产,设备后消费
  • RX:驱动先提供空 buffer,设备先生产,CPU 后消费
 
也就是说,一个网卡驱动往往同时包含两套方向相反的生命周期。

观察点 2:回收边界比提交更容易写烂

很多驱动看着提交逻辑挺顺,真正翻车点在:
  • descriptor 已 done,但 skb 没释放
  • RX 包被 CPU 消费了,但新 buffer 没补回去
  • reset 后旧 completion 还在污染 ring

观察点 3:吞吐优化会直接改写调试难度

中断合并、批量回收、NAPI poll 都是性能优化,但它们也会让:
  • 单次事件边界变模糊
  • “哪个包在哪一轮完成”更难跟
  • 重现偶发 bug 更痛苦

六、把网卡案例放回本专题主线

网卡 ring DMA 最适合对应这些主线章节:
 
它和音频案例形成的对照很有价值:
  • 音频案例强调 周期流节拍
  • 网卡案例强调 ring 并发推进与批量回收

七、读这类驱动时该追的问题

  • descriptor ring 是 coherent 还是别的形态?
  • packet payload 映射是单 buffer 还是 SG?
  • TX completion 后谁负责 unmap 和 free skb?
  • RX buffer 用完后谁补回 ring?
  • NAPI poll 一次最多回收多少?
  • reset 后 ring 如何重建,旧 completion 如何作废?

八、最容易被忽略的坑

1. 只盯发送,不盯回收

这会让你误以为驱动挺简单,直到跑久了 ring 卡死。

2. 只盯单包,不盯批量回收

网卡最关键的往往不是单包路径,而是批量路径。

3. 只盯寄存器,不盯生命周期

寄存器是表象,descriptor ownership 才是骨架。

九、这页真正要你带走什么

如果你之后去看 e1000、igb、stmmac、ena 这类驱动,应该先抓住这句:
🧠
网卡 DMA 驱动的本质,不是收发包 API,而是 descriptor ring 上多生命周期并发推进的治理系统
只要这句话立住,源码就不再是“很多包在飞”,而是“很多 ownership 在流转”。
上一篇
Data Structure and Algorithm
下一篇
用面试拷问嵌入式技术栈

Comments
Loading...