案例定位
这页不是教你背某个具体网卡驱动的源码,而是给你一套 阅读网卡 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 在流转”。





