Elastic Network Adapter (ENA) 系列的 Linux 内核驱动程序¶
概述¶
ENA 是一种网络接口,旨在充分利用现代 CPU 功能和系统架构。
ENA 设备通过最少的内存映射寄存器和通过管理队列的可扩展命令集,公开了一个轻量级管理接口。
该驱动程序支持一系列 ENA 设备,与链路速度无关(即,相同的驱动程序用于 10GbE、25GbE、40GbE 等),并且具有协商的和可扩展的功能集。
某些 ENA 设备支持 SR-IOV。此驱动程序用于 SR-IOV 物理功能 (PF) 和虚拟功能 (VF) 设备。
ENA 设备通过提供多个 Tx/Rx 队列对(最大数量由设备通过管理队列通告)、每个 Tx/Rx 队列对的专用 MSI-X 中断向量、自适应中断调节和 CPU 缓存行优化的数据放置,从而实现高速和低开销的网络流量处理。
ENA 驱动程序支持行业标准 TCP/IP 卸载功能,例如校验和卸载。接收端缩放 (RSS) 支持多核缩放。
ENA 驱动程序及其相应的设备实现了运行状况监视机制,例如看门狗,使设备和驱动程序能够以对应用程序透明的方式恢复,并提供调试日志。
某些 ENA 设备支持一种称为低延迟队列 (LLQ) 的工作模式,该模式可以节省更多的微秒。
ENA 源代码目录结构¶
ena_com.[ch] |
管理通信层。此层负责处理设备和驱动程序之间的所有管理(管理)通信。 |
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 配置空间
设备寄存器
管理队列 (AQ) 和管理完成队列 (ACQ)
异步事件通知队列 (AENQ)
ENA 设备 MMIO 寄存器仅在驱动程序初始化期间访问,在后续的正常设备操作期间不使用。
AQ 用于提交管理命令,结果/响应通过 ACQ 异步报告。
ENA 引入了一小组管理命令,并为特定于供应商的扩展预留了空间。大多数管理操作都在通用的 Get/Set 功能命令中进行。
支持以下管理队列命令:
创建 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 方向)。驱动程序为管理(对于 ACQ 和 AENQ)分配一个额外的专用 MSI-X 向量。
当 Linux 内核探测适配器时执行管理中断注册,并在删除适配器时取消注册。当打开适配器的 Linux 接口时执行 I/O 队列中断注册,并在关闭接口时取消注册。
管理中断被命名为
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 头部校验和卸载
基于 IPv4/IPv6 的 TCP/UDP 校验和卸载
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)。