前言
本文将结合源码分析 Linux Kernel 实现异步地从网卡收取数据包的过程。
流程总览
sk_buff
.-> gro
gro ---> stack
关键数据结构
环形缓冲区描述符
TODO
环形缓冲区
TODO
net_dev
网络设备实例
softnet
Per-CPU 收包队列
sk_buff
用于存储一个完整的数据包
napi
用于实现轮询收包的实例
Intel I350 Ethernet Controller 网卡收包驱动程序分析
我们分析的网卡是搭载了 Intel 82599ES 以太网控制器的 10-Gigabit SFI/SFP+ 网卡,通过运行 lspci -v
命令,结果如下所示,我们可以发现它所使用的驱动是 ixgbe
:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 04:00.0 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)
Subsystem: Intel Corporation Ethernet Server Adapter X520-2
Physical Slot: 2
Flags: bus master, fast devsel, latency 0, IRQ 46, NUMA node 0
Memory at d0a80000 (64-bit, non-prefetchable) [size=512K]
I/O ports at 3020 [size=32]
Memory at d0b10000 (64-bit, non-prefetchable) [size=16K]
Expansion ROM at d0a00000 [disabled] [size=512K]
Capabilities: <access denied>
Kernel driver in use: ixgbe
Kernel modules: ixgbe
04:00.1 Ethernet controller: Intel Corporation 82599ES 10-Gigabit SFI/SFP+ Network Connection (rev 01)
Subsystem: Intel Corporation Ethernet Server Adapter X520-2
Physical Slot: 2
Flags: bus master, fast devsel, latency 0, IRQ 162, NUMA node 0
Memory at d0980000 (64-bit, non-prefetchable) [size=512K]
I/O ports at 3000 [size=32]
Memory at d0b00000 (64-bit, non-prefetchable) [size=16K]
Expansion ROM at d0900000 [disabled] [size=512K]
Capabilities: <access denied>
Kernel driver in use: ixgbe
Kernel modules: ixgbe
从 Intel 官方的 Intel® Ethernet Controller Products 27.6 Release Notes intel_ethernet_product 也能查到其使用的驱动的相关信息,因此下面我们就以 ixgbe
驱动为例展开分析。
网卡收包
TODO: 描述一下大致流程。
网卡上的硬件处理
TODO: 网卡的部分卸载功能。
TODO: On-NIC 队列是怎么回事?
写入主机内存
接收描述符环形缓冲区
完成网卡硬件部分的数据帧处理后,网卡会将数据帧使用 PCIe DMA 的方式异步传输到主机内存中,同时网卡会向位于主机内存中的
可以看出来,Ring Buffer 就是网卡和主机 CPU 之间实现异步的数据交换的用于存放管理信息的一段内存。Ring Buffer 是在主机探测到网卡设备,调用网卡驱动初始化的时候,由网卡驱动进行分配的。Intel 82599 网卡所维护的 Ring Buffer 的形式如
RDBA
: 这个寄存器存放了 Ring Buffer 在主机内存中的起始物理地址;RDLEN
: 这个寄存器存放了 Ring Buffer 在主机内存中的长度,以字节为单位;RDH
: 这个寄存器存放了网卡第一个可以写入的 Descriptor 在 Ring Buffer 中距离RDBA
的偏移量,以字节为单位;RDT
: 这个寄存器存放了主机第一个可以读取的 Descriptor (i.e. 网卡最后一个可用的 Descriptor 之后的一个 Descriptor) 在 Ring Buffer 中距离RDBA
的偏移量,以字节为单位;
通过维护这四个简单的寄存器,网卡和主机之间就能够实现多个 Descriptors 的异步传输。RDBA
和 RDLEN
寄存器是在网卡驱动分配完 Ring Buffer 的空间之后被初始化的。而当网卡完成 Descriptor 的写入后,则会自动地递推 RDH
寄存器的值至下一个可写入的 Descriptor 的起始位置; 同样地,当主机完成 Descriptor 的写入后,则会自动地递推 RDT
寄存器的值至下一个可读取的 Descriptor 的起始位置。
Intel 82599 Ethernet Controller 官方文档用
现在来看 Ring Buffer 存储的 Descriptor 的格式。Descriptor 中记录了网卡通过 PCIe DMA 向主机内存中写入的数据帧的基本信息。对于 Intel 82599 网卡来说,其支持两种格式的 Descriptor,即 SRRCTL
寄存器的 DRSCTYPE
域进而选择使用某种格式的 Descriptor。
Legacy Descriptor 格式
82599 网卡 Legacy Descriptor 的格式如
Legacy Descriptor 中最重要的字段就是低 8-Bytes 的 Buffer Address[63:0]
,该字段存储的是网卡通过 PCIe DMA 将当前数据帧存储到主机内存中的
Advanced Descriptor 格式
相比于 Legacy Descriptor,Advanced Descriptor 可以用来支持更多的功能特性,如分离数据帧有效负载和数据帧头等。Advanced Descriptor 由于需要支持更多的功能特性,所以分为了
先来看下 Advanced Descriptor 中 Read Format 的定义,如
Advanced Descriptor (Read Format) 低 8-Bytes 的存储的是网卡通过 PCIe DMA 将数据帧中封装的 Packet Payload 存储到主机内存中的起始地址;高 8-Bytes 存放的是
GRO
参考 kk_blog_gro