用于弹性网络适配器 (ENA) 系列的 Linux 内核驱动程序¶
概述¶
ENA 是一种网络接口,旨在充分利用现代 CPU 功能和系统架构。
ENA 设备通过管理队列公开了一个轻量级管理接口,该接口具有最少的内存映射寄存器,并且可以通过 Admin Queue 扩展命令集。
该驱动程序支持一系列 ENA 设备,与链路速度无关(即,相同的驱动程序用于 10GbE、25GbE、40GbE 等),并且具有协商和可扩展的功能集。
某些 ENA 设备支持 SR-IOV。该驱动程序用于 SR-IOV 物理功能 (PF) 和虚拟功能 (VF) 设备。
ENA 设备通过提供多个 Tx/Rx 队列对(最大数量由设备通过 Admin Queue 公布)、每个 Tx/Rx 队列对的专用 MSI-X 中断向量、自适应中断调节和 CPU 缓存行优化的数据放置,从而实现高速和低开销的网络流量处理。
ENA 驱动程序支持行业标准的 TCP/IP 卸载功能,例如校验和卸载。接收端缩放 (RSS) 支持多核缩放。
ENA 驱动程序及其对应的设备实现了健康监控机制,例如看门狗,使设备和驱动程序能够以对应用程序透明的方式恢复,以及调试日志。
某些 ENA 设备支持一种称为低延迟队列 (LLQ) 的工作模式,该模式可以节省更多微秒。
ENA 源代码目录结构¶
ena_com.[ch] |
管理通信层。该层负责处理设备和驱动程序之间的所有管理(admin)通信。 |
ena_eth_com.[ch] |
Tx/Rx 数据路径。 |
ena_admin_defs.h |
ENA 管理接口的定义。 |
ena_eth_io_defs.h |
ENA 数据路径接口的定义。 |
ena_common_defs.h |
ena_com 层的通用定义。 |
ena_regs_defs.h |
ENA PCI 内存映射 (MMIO) 寄存器的定义。 |
ena_netdev.[ch] |
主 Linux 内核驱动程序。 |
ena_ethtool.c |
ethtool 回调。 |
ena_xdp.[ch] |
XDP 文件 |
ena_pci_id_tbl.h |
支持的设备 ID。 |
管理接口:¶
ENA 管理接口通过以下方式公开
PCIe 配置空间
设备寄存器
Admin Queue (AQ) 和 Admin Completion Queue (ACQ)
异步事件通知队列 (AENQ)
ENA 设备 MMIO 寄存器仅在驱动程序初始化期间访问,并且在进一步的正常设备操作期间不使用。
AQ 用于提交管理命令,结果/响应通过 ACQ 异步报告。
ENA 引入了一小部分管理命令,并为特定于供应商的扩展留有空间。大多数管理操作都以通用的 Get/Set 功能命令进行构建。
支持以下 admin 队列命令
创建 I/O 提交队列
创建 I/O 完成队列
销毁 I/O 提交队列
销毁 I/O 完成队列
获取功能
设置功能
配置 AENQ
获取统计信息
有关支持的 Get/Set 功能属性的列表,请参阅 ena_admin_defs.h。
异步事件通知队列 (AENQ) 是 ENA 设备用于向驱动程序发送无法使用 ACQ 报告的事件的单向队列。 AENQ 事件分为多个组。 每个组可能有多个综合征,如下所示
事件是
组 |
综合征 |
---|---|
链接状态更改 |
X |
致命错误 |
X |
通知 |
暂停流量 |
通知 |
恢复流量 |
保持活动 |
X |
ACQ 和 AENQ 共享同一个 MSI-X 向量。
保持活动是一种特殊机制,允许监视设备的健康状况。 设备每秒传递一个保持活动事件。 驱动程序维护一个看门狗 (WD) 处理程序,该处理程序记录当前状态和统计信息。 如果未按预期传递保持活动事件,则 WD 会重置设备和驱动程序。
数据路径接口¶
I/O 操作基于 Tx 和 Rx 提交队列(分别对应 Tx SQ 和 Rx SQ)。 每个 SQ 都有一个与之关联的完成队列 (CQ)。
SQ 和 CQ 在连续物理内存中实现为描述符环。
ENA 驱动程序支持 Tx SQ 的两种队列操作模式
常规模式: 在此模式下,Tx SQ 驻留在主机的内存中。 ENA 设备从主机内存中获取 ENA Tx 描述符和数据包数据。
低延迟队列 (LLQ) 模式或“推送模式”: 在此模式下,驱动程序将传输描述符和数据包的前 96 个字节直接推送到 ENA 设备内存空间。 数据包有效负载的其余部分由设备获取。 对于此操作模式,驱动程序使用专用的 PCI 设备内存 BAR,该 BAR 映射有写组合功能。
请注意,并非所有 ENA 设备都支持 LLQ,并且此功能在初始化时与设备协商。 如果 ENA 设备不支持 LLQ 模式,则驱动程序会回退到常规模式。
Rx SQ 仅支持常规模式。
该驱动程序支持 Tx 和 Rx 的多队列。 这有多种好处
减少给定以太网接口上的 CPU/线程/进程争用。
完成时的缓存未命中率降低,特别是对于保存 sk_buff 结构的数据缓存行。
处理收到的数据包时,进程级并行性增加。
通过将数据包的内核处理引导到使用该数据包的应用程序线程正在运行的 CPU,从而提高数据缓存命中率。
硬件中断重定向。
中断模式¶
驱动程序为每个队列对分配一个 MSI-X 向量(对于 Tx 和 Rx 方向)。 驱动程序为管理分配额外的专用 MSI-X 向量(对于 ACQ 和 AENQ)。
管理中断注册在 Linux 内核探测适配器时执行,并在删除适配器时取消注册。 I/O 队列中断注册在打开适配器的 Linux 接口时执行,并在关闭接口时取消注册。
管理中断命名为
ena-mgmnt@pci:<PCI domain:bus:slot.function>
对于每个队列对,中断命名为
<interface name>-Tx-Rx-<queue index>
ENA 设备以自动屏蔽和自动清除中断模式运行。 也就是说,一旦 MSI-X 传递到主机,其 Cause 位将自动清除,并且中断将被屏蔽。 中断在 NAPI 处理完成后由驱动程序取消屏蔽。
中断调节¶
ENA 驱动程序和设备可以在传统或自适应中断调节模式下运行。
在传统模式下,驱动程序指示设备根据静态中断延迟值推迟中断发布。 中断延迟值可以通过 ethtool(8) 配置。 驱动程序支持以下 ethtool 参数:tx-usecs
、rx-usecs
在自适应中断调节模式下,中断延迟值由驱动程序动态更新,并根据流量性质在每个 NAPI 周期进行调整。
可以通过 ethtool(8) 的 adaptive_rx on|off
参数打开/关闭自适应合并。
有关自适应中断调节 (DIM) 的更多信息,请参见 Net DIM - 通用网络动态中断调节
RX copybreak¶
rx_copybreak 默认初始化为 ENA_DEFAULT_RX_COPYBREAK,可以通过 SIOCETHTOOL ioctl 的 ETHTOOL_STUNABLE 命令进行配置。
此选项控制接收数据包的 RX 描述符将被回收的最大数据包长度。 当收到小于 RX copybreak 字节的数据包时,它将被复制到新的内存缓冲区中,并且 RX 描述符将返回到 HW。
统计信息¶
用户可以使用 ethtool 获取 ENA 设备和驱动程序统计信息。 驱动程序可以从设备收集常规或扩展统计信息(包括每个队列的统计信息)。
此外,驱动程序会在设备重置时将统计信息记录到 syslog。
在支持的实例类型上,统计信息还将包括 ENA Express 数据(带有 ena_srd 前缀的字段)。 有关 ENA Express 数据的完整文档,请参阅 https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ena-express.html#ena-express-monitor
MTU¶
该驱动程序支持任意大的 MTU,最大值与设备协商。 驱动程序使用 SetFeature 命令(ENA_ADMIN_MTU 属性)配置 MTU。 用户可以通过 ip(8) 和类似的旧工具更改 MTU。
无状态卸载¶
ENA 驱动程序支持
IPv4 标头校验和卸载
TCP/UDP over IPv4/IPv6 校验和卸载
RSS¶
ENA 设备支持 RSS,允许灵活的 Rx 流量引导。
支持 Toeplitz 和 CRC32 哈希函数。
L2/L3/L4 字段的不同组合可以配置为哈希函数的输入。
驱动程序使用 AQ SetFeature 命令(ENA_ADMIN_RSS_HASH_FUNCTION、ENA_ADMIN_RSS_HASH_INPUT 和 ENA_ADMIN_RSS_INDIRECTION_TABLE_CONFIG 属性)配置 RSS 设置。
如果设置了 NETIF_F_RXHASH 标志,则在收到的 SKB 中设置 Rx CQ 描述符中传递的哈希函数的 32 位结果。
用户可以通过 ethtool(8) 提供哈希密钥、哈希函数并配置间接表。
数据路径¶
Tx¶
堆栈调用 ena_start_xmit()
。 此函数执行以下操作
映射数据缓冲区(
skb->data
和 frags)。为推送缓冲区填充
ena_buf
(如果驱动程序和设备处于推送模式)。为其余 frags 准备 ENA bufs。
从空
req_id
环中分配新的请求 ID。 请求 ID 是 Tx 信息中数据包的索引。 这用于乱序 Tx 完成。将数据包添加到 Tx 环中的正确位置。
调用
ena_com_prepare_tx()
,这是一个 ENA 通信层,它将ena_bufs
转换为 ENA 描述符(并根据需要添加元 ENA 描述符)。此函数还会将 ENA 描述符和推送缓冲区复制到设备内存空间(如果在推送模式下)。
向 ENA 设备写入门铃。
当 ENA 设备完成发送数据包时,会引发完成中断。
中断处理程序计划 NAPI。
调用
ena_clean_tx_irq()
函数。 此函数处理 ENA 生成的完成描述符,每个已完成的数据包都有一个完成描述符。从完成描述符中检索
req_id
。 数据包的tx_info
通过req_id
检索。 数据缓冲区被取消映射,并且req_id
返回到空req_id
环。当完成描述符完成或达到预算时,该函数停止。
Rx¶
当从 ENA 设备收到数据包时。
中断处理程序计划 NAPI。
调用
ena_clean_rx_irq()
函数。 此函数调用ena_com_rx_pkt()
,这是一个 ENA 通信层函数,它返回用于新数据包的描述符数量,如果未找到新数据包,则返回零。ena_rx_skb()
检查数据包长度如果数据包较小 (len < rx_copybreak),则驱动程序会为新数据包分配一个 SKB,并将数据包有效负载复制到 SKB 数据缓冲区中。
这样,原始数据缓冲区不会传递到堆栈,而是被重用于将来的 Rx 数据包。
否则,该函数将取消映射 Rx 缓冲区,将第一个描述符设置为 skb 的线性部分,并将其他描述符设置为 skb 的 frags。
使用必要的信息(协议、校验和硬件验证结果等)更新新的 SKB,然后使用 NAPI 接口函数
napi_gro_receive()
将其传递到网络堆栈。
动态 RX 缓冲区 (DRB)¶
RX 环中的每个 RX 描述符都是单个内存页面(长度为 4KB 或 16KB,具体取决于系统的配置)。 为了减少处理小数据包的高速率时所需的内存分配,如果此页面的剩余空间超过 2KB 未使用,则驱动程序会尝试重用剩余的 RX 描述符空间。
此机制的一个简单示例是以下事件序列
1. Driver allocates page-sized RX buffer and passes it to hardware
+----------------------+
|4KB RX Buffer |
+----------------------+
2. A 300Bytes packet is received on this buffer
3. The driver increases the ref count on this page and returns it back to
HW as an RX buffer of size 4KB - 300Bytes = 3796 Bytes
+----+--------------------+
|****|3796 Bytes RX Buffer|
+----+--------------------+
当加载 XDP 程序时,或者当 RX 数据包小于 rx_copybreak 字节时,不使用此机制(在这种情况下,数据包将从 RX 缓冲区复制到为其分配的新 skb 的线性部分,并且 RX 缓冲区保持相同的大小,请参见 RX copybreak)。