Mellanox ConnectX 系列网卡的 Linux 驱动 (mlx5) 是一个高性能 RDMA 和 Ethernet 驱动,支持 InfiniBand 和 Ethernet 功能。以下是对 mlx5 驱动代码的关键分析:
1. 代码结构
mlx5 驱动代码主要位于 Linux 内核的 drivers/net/ethernet/mellanox/mlx5/core/
目录下:
mlx5_core/
├── cmd.c # 固件命令接口
├── main.c # 模块初始化和主要逻辑
├── eq.c # 事件队列处理
├── port.c # 端口管理
├── devlink.c # devlink 接口
├── en/ # Ethernet 功能
├── fpga/ # FPGA 相关功能
├── ib/ # InfiniBand 相关功能
├── lib/ # 共享库函数
├── lag/ # LAG 功能
└── vport.c # 虚拟端口管理
2. 关键组件分析
2.1 初始化流程 (main.c
)
static int __init init(void)
{
mlx5_register_debugfs(); // 注册调试文件系统
mlx5e_init(); // 初始化以太网功能
mlx5_ib_init(); // 初始化 IB 功能
mlx5_fpga_init(); // 初始化 FPGA 功能
}
module_init(init);
2.2 设备探测和初始化
static int mlx5_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
{
struct mlx5_core_dev *dev;
// 分配设备结构
dev = mlx5_dev_alloc(pdev);
// 初始化硬件
mlx5_mdev_init(dev, prof_sel);
// 加载固件
mlx5_load_one(dev);
// 创建网络设备
mlx5_eth_add(dev);
}
2.3 数据路径 (en_rx.c
, en_tx.c
)
数据路径实现分为接收和发送两部分:
接收路径
bool mlx5e_post_rx_wqes(struct mlx5e_rq *rq)
{
// 发布接收缓冲区到硬件队列
// 使用 WQE (Work Queue Element) 结构
}
void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe)
{
// 处理接收完成事件
// 将数据包传递给网络栈
}
发送路径
netdev_tx_t mlx5e_xmit(struct sk_buff *skb, struct net_device *dev)
{
// 将 skb 转换为硬件描述符
// 发布到发送队列
}
2.4 硬件抽象层 (fw.c
, cmd.c
)
int mlx5_cmd_exec(struct mlx5_core_dev *dev, void *in, int in_size, void *out, int out_size)
{
// 执行固件命令
// 使用邮箱机制与固件通信
}
3. 性能优化技术
-
多队列支持:支持 RSS (Receive Side Scaling) 和 RPS (Receive Packet Steering)
-
零拷贝:使用 DMA 直接传输数据,减少内存拷贝
-
中断合并:使用自适应中断节流
-
内存预分配:预先分配关键数据结构减少运行时开销
-
内联函数:关键路径使用内联函数减少函数调用开销
4. 关键数据结构
4.1 核心设备结构
(1) 设备 (Device) - struct mlx5_core_dev
表示一个 Mellanox 硬件设备(如 ConnectX 网卡),是所有资源的根容器。
关键成员:
struct mlx5_core_dev {
struct pci_dev *pdev; // 关联的PCI设备
struct mlx5_priv *priv; // 设备私有数据
struct mlx5_eq_table eq_table; // 事件队列表
struct mlx5_cq_table cq_table; // 完成队列表
struct mlx5_qp_table qp_table; // QP表(管理所有QP)
// ...
};
作用:管理硬件资源(如 PCI 配置、DMA 内存)、全局队列表和设备状态。
(2) 完成队列 (CQ) - struct mlx5_core_cq
用于接收硬件通知(如操作完成、错误事件)。
关键成员:
struct mlx5_core_cq {
struct mlx5_core_dev *dev; // 所属设备
u32 cqn; // CQ的唯一标识号
struct mlx5_wq_ctrl wq_ctrl; // 队列控制结构(存储CQE数组)
void (*comp)(struct mlx5_core_cq *); // 完成事件回调函数
// ...
};
作用:
-
存储完成队列条目(CQE),每个 CQE 描述一个已完成的操作(如发送/接收完成)。
-
通过中断或轮询通知软件事件完成。
(3) 队列对 (QP) - struct mlx5_core_qp
包含一对队列(发送队列 SQ 和接收队列 RQ),是数据传输的核心。
关键成员:
struct mlx5_core_qp {
struct mlx5_core_dev *dev; // 所属设备
u32 qpn; // QP的唯一标识号
struct mlx5_wq_sq sq; // 发送队列
struct mlx5_wq_rq rq; // 接收队列
struct mlx5_core_cq *send_cq; // 关联的发送CQ
struct mlx5_core_cq *recv_cq; // 关联的接收CQ
// ...
};
作用:
-
SQ:存放发送请求(WQE),硬件从 SQ 读取描述符并执行发送操作。
-
RQ:存放接收缓冲区(WQE),硬件将接收到的数据写入 RQ 描述的缓冲区。
(4) 三者的关系
Device (mlx5_core_dev)
├── CQ Table (多个CQ)
│ ├── CQ1 → 处理QP1的完成事件
│ └── CQ2 → 处理QP2的完成事件
└── QP Table (多个QP)
├── QP1
│ ├── SQ → 关联CQ1(发送完成通知)
│ └── RQ → 关联CQ2(接收完成通知)
└── QP2
├── SQ → 关联CQ1
└── RQ → 关联CQ3
交互流程
-
数据传输:
-
应用向 QP 的 SQ 提交发送请求(WQE)。
-
硬件执行发送后,在关联的 CQ 中生成 CQE。
-
驱动通过轮询/中断处理 CQE,通知应用完成状态。
-
-
事件通知:
-
当数据到达时,硬件将数据写入 QP 的 RQ 缓冲区,并在关联的 CQ 中生成 CQE。
-
驱动处理 CQE 并将数据传递给上层协议栈(如 TCP/IP 或 RDMA 应用)。
-
5. 调试和分析
-
sysfs 接口 :
/sys/class/net/<dev>/device/
-
ethtool 统计 :
ethtool -S <interface>
-
devlink :
devlink dev info pci/0000:01:00.0
-
tracepoints :
trace-cmd record -e mlx5:*
6. 典型工作流程示例
6.1 数据包接收流程
-
硬件 DMA 数据到预分配缓冲区
-
生成完成队列事件 (CQE)
-
中断处理程序调度 NAPI
-
NAPI 轮询处理 CQE
-
构建 sk_buff 并传递给网络栈
6.2 数据包发送流程
-
网络栈调用驱动 xmit 函数
-
驱动将 skb 转换为 WQE
-
发布 WQE 到发送队列
-
硬件处理完成后生成发送完成事件
-
驱动释放 skb 资源
7. 扩展功能
-
RDMA 支持:通过 mlx5_ib 模块提供
-
SR-IOV:支持虚拟功能
-
TC offload:流量控制卸载
-
TLS offload:加密卸载
mlx5 驱动是一个复杂的高性能网络驱动,充分利用了现代网卡的硬件加速功能,同时提供了丰富的管理和调试接口。