一、中文注释
以下是针对`mlx4_load_one`函数的主要代码路径的中文注释。该函数是用于加载并初始化Mellanox网络设备的驱动函数。通过注释,可以了解函数在初始化过程中执行的关键步骤。
/* mlx4_load_one函数:用于加载并初始化PCI设备(例如网卡)的函数 */
/* 函数参数解释:
pdev:指向PCI设备的指针。
pci_dev_data:附加的PCI设备数据(可以用来判断设备类型等)。
total_vfs:要配置的虚拟函数(VF)的总数量。
nvfs:指向数组,保存各个端口的VF数量信息。
priv:指向设备私有数据结构的指针。
reset_flow:重置流程的标志。
*/
static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data,
int total_vfs, int *nvfs, struct mlx4_priv *priv,
int reset_flow)
{
struct mlx4_dev *dev; // 指向网络设备结构体的指针
/* 各种变量初始化 */
/* 获取设备功能和状态 */
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
/* 如果检测到设备是虚拟功能(VF),则将其设置为从模式 */
} else {
/* 尝试获取设备所有权,以确定是否可以作为物理功能(PF)初始化设备 */
/* 如果不支持多个PF,则返回错误 */
}
/* 重置设备,以确保从定义状态开始初始化 */
err = mlx4_reset(dev);
/* 如果设备支持SR-IOV,则配置相应的标志和状态 */
/* 初始化命令接口 */
err = mlx4_cmd_init(dev);
/* 初始化固件(FW) */
err = mlx4_init_fw(dev);
/* 检查和更新设备功能 */
if (!dev_cap) {
/* 分配和查询设备的功能(capabilities) */
} else {
/* 再次查询设备功能,得到新的设备信息 */
}
/* 初始化主机通道适配器(HCA) */
err = mlx4_init_hca(dev);
/* 配置SR-IOV */
if (mlx4_is_master(dev)) {
/* Master模式具体的SR-IOV配置 */
}
/* 分配中断请求(Interrupt Request,IRQ)等资源 */
/* 初始化网络设备和端口 */
for (port = 1; port <= dev->caps.num_ports; port++) {
err = mlx4_init_port_info(dev, port);
}
/* 注册设备到系统 */
err = mlx4_register_device(dev);
/* 其他初始化,例如启动sensor、请求模块加载等 */
priv->removed = 0; // 设置设备尚未被移除的标记
/* 如果初始化过程的任何阶段出现错误,则跳转到错误处理标签 */
/* 错误处理标签,用于根据不同的错误情况进行清理和回滚 */
err_port:
/* 清理端口信息 */
err_steer:
/* 清理网络流控制 */
err_disable_msix:
/* 禁用MSI-X中断模式 */
err_free_eq:
/* 释放事件队列 */
err_master_mfunc:
/* 清理多功能资源 */
err_close:
/* 关闭HCA */
err_fw:
/* 关闭固件 */
err_mfunc:
/* 清理多功能设置 */
err_cmd:
/* 清理命令接口 */
err_sriov:
/* 如果需要,禁用SR-IOV */
return err; // 返回错误代码
}
注意:
1. 此代码摘自驱动文件中的一个函数`mlx4_load_one`。
2. mlx4_load_one函数负责对一个PCI设备(如网卡)进行初始化和设置,并处理可能出现的错误情况,同时也负责重置设备和设置SR-IOV(单根I/O虚拟化)。
3. 代码中包含了多个错误处理标签(如`err_port`、`err_steer`等),它们用于在函数执行中遇到错误时,跳转到相应的代码位置进行资源清理和释放操作。
4. 该函数对于硬件初始化的深入理解以上节选可能还不足,需要阅读整个驱动代码和相关硬件文档以获得完整理解。
这段代码是Mellanox网络驱动mlx4的一部分,具体实现的是在Linux内核中加载一个PCIe设备(在本例中是一个网卡)。这个函数负责执行初始化操作,包括检测设备类型、重置硬件、初始化硬件资源等。
对这个函数的每个关键部分进行中文注释,为了保持注释的简洁性,将只针对代码块提供注释,而不是每一行代码。
// drivers\net\ethernet\mellanox\mlx4\main.c
// mlx4_load_one函数,负载一个PCIe设备(PF或者VF),并初始化相关资源。
static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data,
int total_vfs, int *nvfs, struct mlx4_priv *priv,
int reset_flow)
{
// 省略部分变量声明
/* existing_vfs 将包含在调用PF驱动的remove_one时仍处于活跃状态的VFs数量。
* 在这种情况下,PF驱动在remove_one期间没有禁用SRIOV。
* 因此当PF重新加载(mlx4_load_one)时,SRIOV仍然启用了,
* 并且不应调用pci_enable_sriov。
*/
// 省略初始化代码...
/* 检测该设备是否是虚拟功能(VF) */
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
// 正在以从模式运行
} else {
// 重置设备并且仅为物理设备启用SRIOV(单根IO虚拟化)。
// 尝试获取对设备的所有权,如果已经被占用,则跳过。
}
// 移除之前的内部错误指示,设备目前处于上线状态。
// 根据设备是处于主模式还是从模式,初始化和启动不同的功能和命令接口。
// 初始化固件
// 如果是主模式,确保之前初始化的dev_cap有效。
// 初始化HCA(主机通信适配器)
// 如果是主模式并支持系统事件队列,则可能需要再次启动初始化序列。
// 检查设备是否在其可能的最大速度下运行。
// 如果是主模式,初始化通信通道,并且分配和注册设备资源。
// 分配和初始化事件队列。
// 如果设备是多功能设备并且没有启用MSI-X,则报错并退出。
// 如果是从设备,则不需要执行流量控制初始化。
// 设置硬件资源使用配额。
// 完成HCA设置。
// 如果是主模式,则准备PF资源并准备接收命令。
// 初始化所有端口。
// 注册该mlx4设备。
// 请求相关模块。
// 初始化并启动传感器。
// 设置设备为非移除状态。
// 减少PF加载计数器。
// 清理和返回。
// 错误处理代码,回滚之前的操作,释放分配的资源,关闭设备等。
}
这个函数的核心部分涉及到各种硬件和资源的初始化过程。在发生错误时,函数会跳转到相应的错误处理代码块进行清理工作,确保不会有资源泄漏。这种错误处理方式在内核编程中是常见的实践,因为它使得代码在异常情况下可以安全地撤销前面的操作。
原代码中的注释是用英文写成的,而直接翻译代码中的注释可能会造成一些上下文相关的意义丢失。但下面是一个大致的翻译,用于帮助理解代码的工作原理,特别是对于不熟悉英文注释或内核编程的人来说:
//drivers\net\ethernet\mellanox\mlx4\main.c
static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data,
int total_vfs, int *nvfs, struct mlx4_priv *priv,
int reset_flow)
{
// 省略变量声明...
/* existing_vfs 将包含在 PF 驱动程序上调用 remove_one 时处于活动状态的 VFs 的数量。
* 在这种情况下,PF 驱动程序在 remove_one 期间没有禁用 SRIOV。
* 因此,当 PF 被重新加载(mlx4_load_one)时,SRIOV 仍然处于启用状态,
* 且不应调用 pci_enable_sriov。
*/
int existing_vfs = 0;
// 省略代码...
/* 检测这个设备是否是虚拟功能 */
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
// 如果是,设置为从模式运行。
} else {
// 如果不是,尝试获取设备所有权;
// 如果已被占用,则跳过——不允许多个 PFs。
// 在触摸 PCI 能力或尝试固件命令之前,现在重置 HCA,
// 因为启动 ROM 可能使 HCA 处于未定义状态。
// ...省略代码...
if (total_vfs) {
// 如果已指定 VF 数量,则将此设备设置为主 PF。
}
}
// 当加载时,删除任何之前的内部错误指示,设备现在是"上行"状态。
/* 在从设备功能中,通信通道必须在发送命令之前初始化。
* 在调用 mlx4_init_hca 之前也要初始化 num_slaves。
*/
// ... 省略代码 ...
/* 在主功能中,通信通道必须在从固件获取其地址后初始化。 */
// ... 省略代码 ...
/* 当 PF 资源准备就绪时,武装它的通信通道以使能接收命令。 */
// ... 省略代码 ...
/* 如果发生错误,执行清理代码,释放分配的资源,
* 并恢复设备到一个已知的运行状态。
*/
// ... 省略错误处理代码 ...
return 0; // 如果所有操作都成功,则返回 0。
}
这段代码是从 Mellanox 网络驱动中提取的一部分,用来加载并初始化一个 Mellanox 设备。它涉及初始化硬件、检测其功能(它是 PF 还是 VF),并在复杂的硬件架构中设置不同的操作模式。代码涵盖了错误处理逻辑以确保资源正确释放,以及最终的设备注册以供以后使用。这段代码是内核编程中的一个实例,显示了如何与硬件及其固件交互,并考虑到可能出现各种运行时问题。
下面是分段的完整注释:
// Mellanox 网络驱动的主要功能实现接口。这个函数负责初始化指定的 PCI 设备。
static int mlx4_load_one(struct pci_dev *pdev, int pci_dev_data,
int total_vfs, int *nvfs, struct mlx4_priv *priv,
int reset_flow)
{
struct mlx4_dev *dev; // 用于存储 mlx4 设备的结构体指针
unsigned sum = 0; // 用于跟踪已经配置的虚拟功能(VFs)的计数
int err; // 错误码变量,用于存储发生错误的返回码
int port; // 临时变量,用于遍历网络端口的索引
int i; // 临时变量,用于循环的迭代索引
struct mlx4_dev_cap *dev_cap = NULL; // 设备能力结构体指针,用于存储设备的各种能力
int num_vfs_argc = // 从 DBDF 到值映射表中获取 num_vfs 参数的数量
mlx4_get_argc(num_vfs.dbdf2val.tbl, pci_physfn(pdev));
int probe_vfs_argc = // 从 DBDF 到值映射表中获取 probe_vf 参数的数量
mlx4_get_argc(probe_vf.dbdf2val.tbl, pci_physfn(pdev));
/* existing_vfs 将包含当 remove_one 在物理功能(PF)驱动上被调用时处于活跃状态的 VFs 的数量。
* 在这种情况下,PF 驱动在 remove_one 期间并未禁用 SRIOV。
* 当 PF 被重新加载 (mlx4_load_one) 时,SRIOV 因此仍然是启用的,不应该调用 pci_enable_sriov。
*/
int existing_vfs = 0; // 已存在的 VFs 数量
// 初始化设备结构
dev = &priv->dev;
// 初始化设备私有结构中的各种链表和锁
INIT_LIST_HEAD(&priv->ctx_list);
spin_lock_init(&priv->ctx_lock);
mutex_init(&priv->port_mutex);
mutex_init(&priv->bond_mutex);
INIT_LIST_HEAD(&priv->pgdir_list);
mutex_init(&priv->pgdir_mutex);
spin_lock_init(&priv->cmd.context_lock);
INIT_LIST_HEAD(&priv->bf_list);
mutex_init(&priv->bf_mutex);
// 初始化设备和 PCI 之间的关联
dev->rev_id = pdev->revision;
dev->numa_node = dev_to_node(&pdev->dev);
if (dev->numa_node == -1)
dev->numa_node = first_online_node;
// 从 PCI 设备结构中获取设备的版本ID,并将其保存到 mlx4 设备结构中
dev->rev_id = pdev->revision;
// 将 PCI 设备所在的 NUMA 节点编号保存到 mlx4 设备结构中。如果获取失败,则默认为第一个在线 NUMA 节点
dev->numa_node = dev_to_node(&pdev->dev);
if (dev->numa_node == -1)
dev->numa_node = first_online_node;
// 判断这个 PCI 设备是否是虚拟功能(VF),即是不是一个虚拟化出来的设备
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
mlx4_warn(dev, "Detected virtual function - running in slave mode\n"); // 如果是 VF,则发出警告并设置设备工作在从模式下
dev->flags |= MLX4_FLAG_SLAVE; // 设置标志位,表明此设备在从模式下工作
} else {
// 以下处理逻辑针对物理设备(PF,即物理功能)
// 尝试获取设备所有权,多个物理功能案例不支持
err = mlx4_get_ownership(dev);
if (err) {
if (err < 0)
return err; // 如果是错误代码,则直接返回错误
else {
// 如果不是错误代码,但获取所有权失败,可能意味着多个 PF 运行在一块硬件上,这目前不被支持
mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");
return -EINVAL; // 返回 "无效参数" 错误
}
}
// 初始化操作队列计数和工作队列
atomic_set(&priv->opreq_count, 0); // 操作队列计数设置为 0
INIT_WORK(&priv->opreq_task, mlx4_opreq_action); // 初始化工作队列项,关联处理函数
/*
* 在我们触及 PCI 功能或尝试固件指令之前先复位 HCA(主机通道适配器),
* 因为引导 ROM 可能使 HCA 处于未定义状态。
*/
err = mlx4_reset(dev);
if (err) {
mlx4_err(dev, "Failed to reset HCA, aborting\n"); // 复位失败,返回错误并标识操作的终止
goto err_sriov; // 跳转到错误处理逻辑
}
这是一个函数(mlx4_load_one)的开始部分,在这里面设置了网络设备的一些初始状态以及对设备的复位操作。
if (total_vfs) {
// 如果用户请求了虚拟功能(VFs),标记当前设备为宿主(主设备-PF)
dev->flags = MLX4_FLAG_MASTER;
// 检查PCI设备上已经激活的VFs数量
existing_vfs = pci_num_vf(pdev);
// 如果有激活的VFs,标记SRIOV已经启用
if (existing_vfs)
dev->flags |= MLX4_FLAG_SRIOV;
// 存储请求的VFs总数
dev->persist->num_vfs = total_vfs;
}
// 标记设备为运行状态
dev->persist->state = MLX4_DEVICE_STATE_UP;
slave_start:
// 初始化命令接口
err = mlx4_cmd_init(dev);
if (err) {
// 初始化命令接口失败,记录错误并退出
mlx4_err(dev, "Failed to init command interface, aborting\n");
goto err_sriov;
}
// 在从设备中,必须先初始化通信通道才能发送命令。
// 还需要在调用mlx4_init_hca之前初始化num_slaves。
if (mlx4_is_mfunc(dev)) {
// 如果设备处于多功能模式,设置相关参数
if (mlx4_is_master(dev)) {
// 如果设备是宿主设备,设置从设备的数量
dev->num_slaves = MLX4_MAX_NUM_SLAVES;
} else {
// 如果设备是从设备,从设备数量设置为0
dev->num_slaves = 0;
// 初始化从设备的多功能接口
err = mlx4_multi_func_init(dev);
if (err) {
// 初始化多功能接口失败,记录错误并退出
mlx4_err(dev, "Failed to init slave mfunc interface, aborting\n");
goto err_cmd;
}
}
}
// 初始化固件
err = mlx4_init_fw(dev);
if (err) {
// 初始化固件失败,记录错误并退出
mlx4_err(dev, "Failed to init fw, aborting.\n");
goto err_mfunc;
}
if (mlx4_is_master(dev)) {
/* 我们重置设备并且只有在物理设备上启用SRIOV服务。
* 尝试声明对设备的所有权;如果已经被占用,则跳过
* -- 不允许多个PFs(物理功能) */
err = mlx4_get_ownership(dev);
if (err) {
if (err < 0) // 如果返回错误码表示获取所有权失败,则直接返回错误码
return err;
else {
/* 如果获取所有权返回的不是错误码,但是声明了所有权已经存在,
* 则表示当前不支持多个PF,在这里打印警告信息并且返回无效参数错误码 */
mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");
return -EINVAL;
}
}
/* 初始化操作请求计数器和相关工作队列 */
atomic_set(&priv->opreq_count, 0);
INIT_WORK(&priv->opreq_task, mlx4_opreq_action);
/*
* 现在在我们触碰PCI能力或尝试固件命令之前重置HCA,
* 因为引导ROM可能已经把HCA留在了一个未定义的状态。
*/
err = mlx4_reset(dev);
if (err) { // 如果重置HCA失败,则打印错误信息并跳转到错误处理代码段
mlx4_err(dev, "Failed to reset HCA, aborting\n");
goto err_sriov;
}
/* 如果传入的total_vfs(预期的VFs总数)不为零 */
if (total_vfs) {
dev->flags = MLX4_FLAG_MASTER; // 设置设备标志为主设备
existing_vfs = pci_num_vf(pdev); // 获取已经存在的VFs的数量
if (existing_vfs) // 如果存在VFs,则设置SRIOV标志
dev->flags |= MLX4_FLAG_SRIOV;
dev->persist->num_vfs = total_vfs; // 设置持久层的VFs数量
}
}
此段代码主要完成当PCI设备是主设备(物理功能)时的初始化准备工作,包括对设备所有权的检查和声明,设备的重置,以及虚拟功能相关的设置。
/* 在加载时删除任何之前的内部错误指示,
* 设备处于活动状态。
*/
dev->persist->state = MLX4_DEVICE_STATE_UP; // 设置设备的状态为"活动"(UP)
/* 检测这个设备是否是一个虚拟功能(VF) */
if (pci_dev_data & MLX4_PCI_DEV_IS_VF) {
mlx4_warn(dev, "Detected virtual function - running in slave mode\n"); // 如果是虚拟功能(VF),则输出警告信息,并指示设备运行在从模式
dev->flags |= MLX4_FLAG_SLAVE; // 设置设备标志为从模式
} else {
/* 只为物理设备(PF)执行设备复位和启用 SRIOV 的操作。
* 尝试声明对设备的所有权;如果已经被占用则跳过 -- 不允许有多个 PF。
*/
err = mlx4_get_ownership(dev); // 尝试获取设备所有权
if (err) { // 如果获取设备所有权失败
if (err < 0) // 如果返回的错误码小于 0
return err; // 直接返回错误
else {
// 如果返回的是非错误码,但仍然失败,说明存在多个 PF,输出警告信息,并返回 EINVAL 错误
mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");
return -EINVAL;
}
}
/* 初始化操作请求计数器和内部工作队列 */
atomic_set(&priv->opreq_count, 0); // 将操作请求计数器设置为 0
INIT_WORK(&priv->opreq_task, mlx4_opreq_action); // 为操作请求初始化一个工作队列
/*
* 在触摸 PCI 能力或尝试固件命令之前,先对 HCA 进行复位,
* 因为引导 ROM 可能已经让 HCA 处于未定义状态。
*/
err = mlx4_reset(dev); // 对设备进行复位操作
if (err) { // 如果复位操作失败
mlx4_err(dev, "Failed to reset HCA, aborting\n"); // 输出错误信息,并进入错误处理流程
goto err_sriov; // 跳转到错误处理标签
}
/* 检查设备是否以其最大可能速度运行。
* 此调用没有返回代码,仅在 PCI express 设备的能力没有得到总线充分支持时,警告用户。
*/
if (!mlx4_is_slave(dev))
pcie_print_link_status(dev->persist->pdev); // 如果设备不是从设备,则打印 PCI-E 链路状态
/* 在主功能中,通信通道必须在获取其来自固件的地址后初始化 */
if (mlx4_is_master(dev)) { // 如果设备处于主控状态
int ib_ports = 0; // 用于计算 InfiniBand 端口数量的变量
mlx4_foreach_port(i, dev, MLX4_PORT_TYPE_IB)
ib_ports++; // 遍历并统计设备的 InfiniBand 端口数量
if (ib_ports && (num_vfs_argc > 1 || probe_vfs_argc > 1)) {
mlx4_err(dev,
"Invalid syntax of num_vfs/probe_vfs with IB port - single port VFs syntax is only supported when all ports are configured as ethernet\n");
err = -EINVAL; // 如果 InfiniBand 端口存在并且 num_vfs 或 probe_vfs 参数数量大于 1,报告错误
goto err_close; // 跳转到错误处理代码块
}
if (dev->caps.num_ports < 2 && num_vfs_argc > 1) {
err = -EINVAL; // 如果设备端口数量少于 2 且 num_vfs 参数数量大于 1,报告错误
mlx4_err(dev,
"Error: Trying to configure VFs on port 2, but HCA has only %d physical ports\n",
dev->caps.num_ports); // 打印错误,尝试在只有 1 个物理端口的设备上配置第 2 端口的 VFs
goto err_close; // 跳转到错误处理代码块
}
memcpy(dev->persist->nvfs, nvfs, sizeof(dev->persist->nvfs)); // 复制虚拟函数(VFs)数量到持久化数据结构
// 更新每个虚拟功能的端口配置
for (i = 0;
i < sizeof(dev->persist->nvfs) / sizeof(dev->persist->nvfs[0]); i++) {
unsigned j;
for (j = 0; j < dev->persist->nvfs[i]; ++sum, ++j) {
dev->dev_vfs[sum].min_port = i < 2 ? i + 1 : 1; // 设置最小端口号
dev->dev_vfs[sum].n_ports = i < 2 ? 1 : dev->caps.num_ports; // 设置端口数量
}
}
// 在主功能中,必须在从固件获取地址后初始化通信通道
err = mlx4_multi_func_init(dev); // 初始化多功能设备
if (err) { // 如果初始化失败
mlx4_err(dev, "Failed to init master mfunc interface, aborting.\n"); // 打印错误信息
goto err_close; // 跳转到错误处理代码块
}
}
这段代码包含了Mellanox驱动中一个部分的初始化过程。它检查设备是否运行在其可能的最大速度,在主功能模式下初始化网络端口、虚拟功能的数量,并尝试激活多功能能力。如果在上述的过程中遇到错误,代码将执行错误处理流程。
// 为设备分配事件队列(EQ)表
err = mlx4_alloc_eq_table(dev);
if (err) // 如果分配失败
goto err_master_mfunc; // 跳转到错误处理代码
// 初始化控制中断数的 bitmap 和锁
bitmap_zero(priv->msix_ctl.pool_bm, MAX_MSIX);
mutex_init(&priv->msix_ctl.pool_lock);
// 为设备启用 MSI-X 中断
mlx4_enable_msi_x(dev);
// 如果设备是多功能设备,并且不支持 MSI-X 中断,那么报错并跳转错误处理代码
if ((mlx4_is_mfunc(dev)) && !(dev->flags & MLX4_FLAG_MSI_X)) {
err = -EOPNOTSUPP;
mlx4_err(dev, "INTx is not supported in multi-function mode, aborting\n");
goto err_free_eq;
}
// 如果设备不是从属设备,则初始化 steering 规则
if (!mlx4_is_slave(dev)) {
err = mlx4_init_steering(dev);
if (err)
goto err_disable_msix;
}
// 初始化设备的端口资源配额
mlx4_init_quotas(dev);
// 设置 HCA(硬件)资源,包括驱动与设备的接口配置等
err = mlx4_setup_hca(dev);
// 如果配置失败,并且设备支持 MSI-X 中断,并且不是多功能设备
if (err == -EBUSY && (dev->flags & MLX4_FLAG_MSI_X) && !mlx4_is_mfunc(dev)) {
// 清除 MSI-X 标志,将中断向量数量置为1
dev->flags &= ~MLX4_FLAG_MSI_X;
dev->caps.num_comp_vectors = 1;
// 禁用 PCI 设备的 MSI-X
pci_disable_msix(pdev);
// 重新尝试设置 HCA(硬件)资源
err = mlx4_setup_hca(dev);
}
// 如果再次失败,则进行错误处理
if (err)
goto err_steer;
// 如果设备是主设备(物理功能),则初始化其通讯信道
if (mlx4_is_master(dev)) {
// 对于主设备,初始化通讯信道必须在获取其地址之后
err = mlx4_ARM_COMM_CHANNEL(dev);
// 如果初始化信道失败
if (err) {
mlx4_err(dev, " Failed to arm comm channel eq: %x\n", err);
goto err_steer;
}
}
// 遍历并初始化设备的所有端口
for (port = 1; port <= dev->caps.num_ports; port++) {
err = mlx4_init_port_info(dev, port);
if (err) // 如果初始化某个端口失败,进行错误处理
goto err_port;
}
// 设置虚拟到物理端口的映射
priv->v2p.port1 = 1;
priv->v2p.port2 = 2;
// 将 mlx4 设备注册到系统中,这样它就可以被应用程序访问了
err = mlx4_register_device(dev);
if (err) // 如果注册失败,进行错误处理
goto err_port;
// 请求模块加载
mlx4_request_modules(dev);
// 初始化设备的传感功能,用于感知端口的连接状况
mlx4_sense_init(dev);
// 开始感知任务
mlx4_start_sense(dev);
// 标记该设备已经没有被移除
priv->removed = 0;
// 如果设备是主设备并且配置了 VF(虚拟功能)但没有重置流
if (mlx4_is_master(dev) && dev->persist->num_vfs && !reset_flow)
atomic_dec(&pf_loading); // 减少 pf_loading 计数器
// 如果所有的初始化步骤都执行成功,释放设备能力结构的内存,并返回 0 表示成功
kfree(dev_cap);
return 0;
err_port:
// 从出错的端口开始,进行清理操作
for (--port; port >= 1; --port)
mlx4_cleanup_port_info(&priv->port[port]); // 清理端口信息结构
// 清理默认的计数器
mlx4_cleanup_default_counters(dev);
// 如果当前设备不是从设备(slave),则清理计数器表
if (!mlx4_is_slave(dev))
mlx4_cleanup_counters_table(dev);
// 清理QP表
mlx4_cleanup_qp_table(dev);
// 清理SRQ表
mlx4_cleanup_srq_table(dev);
// 清理CQ表
mlx4_cleanup_cq_table(dev);
// 使用轮询模式来执行命令,以清理EQ表
mlx4_cmd_use_polling(dev);
// 清理EQ表
mlx4_cleanup_eq_table(dev);
// 清理多播组(mcg)表
mlx4_cleanup_mcg_table(dev);
// 清理存储器区域(MR)表
mlx4_cleanup_mr_table(dev);
// 清理xRCD表(与可靠连接相关)
mlx4_cleanup_xrcd_table(dev);
// 清理保护域(PD)表
mlx4_cleanup_pd_table(dev);
// 清理用户接入区域(UAR)表
mlx4_cleanup_uar_table(dev);
err_steer:
// 如果当前设备不是从设备(slave),则清除转向(steering)策略设置
if (!mlx4_is_slave(dev))
mlx4_clear_steering(dev);
err_disable_msix:
// 如果设置了MSI-X标志,则禁用PCIe MSI-X中断
if (dev->flags & MLX4_FLAG_MSI_X)
pci_disable_msix(pdev);
err_free_eq:
// 释放EQ表资源
mlx4_free_eq_table(dev);
err_master_mfunc:
// 如果设备工作在主设备(master)模式,释放资源追踪器,并清理多功能设备管理接口
if (mlx4_is_master(dev)) {
mlx4_free_resource_tracker(dev, RES_TR_FREE_STRUCTS_ONLY);
mlx4_multi_func_cleanup(dev);
}
// 如果设备工作在从设备(slave)模式,销毁特殊的队列对(special QP)的能力
if (mlx4_is_slave(dev))
mlx4_slave_destroy_special_qp_cap(dev);
err_close:
// 关闭HCA结构
mlx4_close_hca(dev);
err_fw:
// 关闭固件接口
mlx4_close_fw(dev);
err_mfunc:
// 如果设备工作在从设备(slave)模式,清理多功能设备(multi-function)管理接口
if (mlx4_is_slave(dev))
mlx4_multi_func_cleanup(dev);
err_cmd:
// 清理命令结构,并关闭所有类型的命令接口
mlx4_cmd_cleanup(dev, MLX4_CMD_CLEANUP_ALL);
err_sriov:
// 如果设置了SR-IOV标志且不是在重置流程中,禁用SR-IOV,并更新标志位
if (dev->flags & MLX4_FLAG_SRIOV && !existing_vfs) {
pci_disable_sriov(pdev);
dev->flags &= ~MLX4_FLAG_SRIOV;
}
// 如果设备工作在主设备(master)模式且存在虚拟功能(VF)且不是在重置流程中,减少PF加载计数
if (mlx4_is_master(dev) && dev->persist->num_vfs && !reset_flow)
atomic_dec(&pf_loading);
// 释放dev_vfs结构的内存
kfree(priv->dev_vfs);
// 如果设备不是从设备(slave),释放设备所有权
if (!mlx4_is_slave(dev))
mlx4_free_ownership(dev);
// 释放设备能力结构(dev_cap)的内存
kfree(dev_cap);
// 返回错误码
return err;
二、中文讲解
这段代码是Linux内核中用于Mellanox网络卡(特别是mlx4系列)驱动程序的`mlx4_load_one`函数。它主要负责初始化PCIe设备的一系列复杂操作,包括将HCA(Host Channel Adapter)置于工作状态,为物理和虚拟功能(PF和VF)配置资源。
下面尽量简化地解释这段复杂代码的关键部分,以供理解其工作原理:
1. mlx4_load_one函数开始时,首先对传入的结构体进行初始化,比如设备上下文列表、锁等。
2. 如果检测到设备是一个VF(虚拟功能),那么它将被标记为slave模式。
3. 如果设备是PF(物理功能),会尝试获得对设备的所有权,并且如果已经有其他PF,则不允许多重PFs并存。
4. 设备重置(`mlx4_reset`)是加载过程的一部分,以确保设备的正常状态。此操作仅适用于物理设备。
5. 初始化固件(`mlx4_init_fw`)操作会为设备加载必要的固件。
6. 查询设备的功能(`mlx4_QUERY_DEV_CAP`),这有助于驱动了解设备的能力并据此配置。
7. 如果驱动程序识别到系统是多功能的,并且设备是master,则初始化多功能接口(`mlx4_multi_func_init`)。
8. 为设备分配事件队列表(`mlx4_alloc_eq_table`),这是中断处理的重要组成部分。
9. 对PCIe连接状态进行一些检查,确认设备是否以预期的速率工作。
10. 如果系统是多功能的且当前驱动在master模式工作,则初始化通信信道。
11. 对每个端口进行初始化(`mlx4_init_port_info`),这对于网络功能至关重要。
12. 最后,当设备配置完成后,会注册到内核网络设备子系统中(`mlx4_register_device`)并请求相关模块的加载。
如果在这个配置过程中的任何步骤遇到错误,该函数会逐步回滚它所做的工作,关闭相关资源,并进行清理操作。
错误处理部分(`err_port`、`err_steer`、`err_disable_msix`等标签)包括停止设备,关闭固件界面,清理命令接口,禁用SR-IOV(如果已启用),以及释放内存资源等。
这个函数是一个很好的例子,展示了驱动程序如何与硬件进行底层交互并且处理多个路径和复杂状态的过程。它还关心到机器状态及其所运行的固件和硬件配置的细节。在编写这类代码时需要特别注意错误处理和资源清理,因为任何一个未处理的错误都可能导致系统不稳定或者资源泄露。