【C语言】Infiniband驱动mlx4_load_one函数

一、中文注释

以下是针对`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); // 操作队列计数设置为 0INIT_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 {// 如果设备是从设备,从设备数量设置为0dev->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)  // 如果返回的错误码小于 0return err;  // 直接返回错误else {// 如果返回的是非错误码,但仍然失败,说明存在多个 PF,输出警告信息,并返回 EINVAL 错误mlx4_warn(dev, "Multiple PFs not yet supported - Skipping PF\n");return -EINVAL;}}/* 初始化操作请求计数器和内部工作队列 */atomic_set(&priv->opreq_count, 0);  // 将操作请求计数器设置为 0INIT_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 端口的 VFsgoto 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 标志,将中断向量数量置为1dev->flags &= ~MLX4_FLAG_MSI_X;dev->caps.num_comp_vectors = 1;// 禁用 PCI 设备的 MSI-Xpci_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(如果已启用),以及释放内存资源等。
这个函数是一个很好的例子,展示了驱动程序如何与硬件进行底层交互并且处理多个路径和复杂状态的过程。它还关心到机器状态及其所运行的固件和硬件配置的细节。在编写这类代码时需要特别注意错误处理和资源清理,因为任何一个未处理的错误都可能导致系统不稳定或者资源泄露。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/741695.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

获得1688中国站获得工厂档案信息 API

公共参数 名称类型必须描述keyString是免费申请调用key&#xff08;必须以GET方式拼接在URL中&#xff09;secretString是调用密钥api_nameString是API接口名称&#xff08;包括在请求地址中&#xff09;[item_search,item_get,item_search_shop等]cacheString否[yes,no]默认y…

Android中的NFC操作

目录 1.申请权限 2.设置意图过滤器 3.判断NFC功能是否可用(NfcAdapter) 4.NFC感应(NfcAdapter)启用与禁用 5.NFC数据解码 6.案例代码一览 NFC的全称是“Near Field Communication”&#xff0c;意思是近场通信、与邻近的区域通信。 1.申请权限 <!-- NFC权限&#xf…

效果图代渲多少钱一张?带你详细了解它的计费规则!

不知道有没有朋友遇到过渲着渲着就崩溃的情况发生&#xff0c;不然也不会去找代渲染的平台/某宝等渠道 也就是为了图能够顺利的跑出来&#xff0c;做了后期处理后&#xff0c;及时交付给客户。 我们以渲染100云渲染来举例&#xff0c;它成立2015年&#xff0c;是一家效果图代…

接口自动化测试框架:Pytest+Allure+Excel

1. Allure 简介 简介 Allure 框架是一个灵活的、轻量级的、支持多语言的测试报告工具&#xff0c;它不仅以 Web 的方式展示了简介的测试结果&#xff0c;而且允许参与开发过程的每个人可以从日常执行的测试中&#xff0c;最大限度地提取有用信息。 Allure 是由 Java 语言开发…

@Import注解作用

Import注解作用 理解springboot自动装配时&#xff0c;发现SpringBootApplication注解下的EnableAutoConfiguration注解头上有一个Import注解。 关于这个注解的作用&#xff0c;上网查找后发现理解的不是很明白&#xff0c;于是写了下面的Demo去理解。 两个pojo类&#xff1…

c++ stl 之 vector

#include <iostream> #include <vector>int main() {// 创建一个空的 vectorstd::vector<int> myVector;// 向 vector 中添加元素myVector.push_back(10);myVector.push_back(20);myVector.push_back(30);// 遍历 vector 并打印元素std::cout << "…

【InternLM 笔记】使用InternLM2-chat-1.8b制作时事问答知识库

环境版本 模型版本&#xff1a; InternLM2-chat-1.8b 准备环境 还是使用InternStudio进行操作 拉取环境 /root/share/install_conda_env_internlm_base.sh internlm开始实践 创建工作目录 cd ~ mkdir temp cd temp下载模型 import torch from modelscope import snapsh…

Unity DropDown 组件 详解

Unity版本 2022.3.13f1 Dropdown下拉菜单可以快速创建大量选项 一、 Dropwon属性详解 属性&#xff1a;功能&#xff1a;Interactable此组件是否接受输入&#xff1f;请参阅 Interactable。Transition确定控件以何种方式对用户操作进行可视化响应的属性。请参阅过渡选项。Nav…

MySQL 核心模块揭秘 | 09 期 | 二阶段提交 (3) flush、sync、commit 子阶段

1. 写在前面 经过上一篇文章的介绍&#xff0c;我们已经对 commit 阶段有了整体的认识。 这篇文章&#xff0c;我们一起进入各子阶段&#xff0c;看看它们都会干点什么&#xff0c;以及会怎么干。 为了方便理解&#xff0c;我们假设有 30 个事务&#xff0c;它们对应的用户线…

Titanic数据分析项目——Kaggle数据分析项目实战1

目前预测准确度达到77.511%, 会持续优化并且更新。 一、特征工程&#xff1a; 1、先对缺失值进行填充&#xff0c;先找到缺失值的位置&#xff0c;数值型数据填充众数&#xff0c;字符数据或者是离散型数据则填充出现最多的数据。 2、标准化数值型数据&#xff0c; 根据标准化…

Vue使用L2Dwidget

1、在根文件index.html中引入live2dw/lib/L2Dwidget.min.js 下载模型的文件&#xff0c;放在本地或者cdn 切换不同的模型 模型地址&#xff1a;https://github.com/xiazeyu/live2d-widget-models showLive2d(name: String) {var live2dWidget document.querySelector("…

大数据开发(Hive面试真题-卷三)

大数据开发&#xff08;Hive面试真题&#xff09; 1、Hive的文件存储格式都有哪些&#xff1f;2、Hive的count的用法&#xff1f;3、Hive得union和unionall的区别&#xff1f;4、Hive的join操作原理&#xff0c;left join、right join、inner join、outer join的异同&#xff1…

专升本 C语言笔记-01 printf 占位符 转义符

目录 一.printf()函数简介 1.1作用 将格式化后的字符串输出(打印东西) 1.2函数原型 1.3返回值 二.常见占位符 2.1.占位符的使用 2.2.格式修饰符 2.3.输出格式说明 三.转义字符 一.printf()函数简介 1.1作用 将格式化后的字符串输出(打印东西) printf…

Python数值方法在工程和科学问题解决中的应用

&#x1f482; 个人网站:【 海拥】【神级代码资源网站】【办公神器】&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交流的小伙伴&#xff0c;请点击【全栈技术交流群】 随着计算机技术的不断发展&#xff0c;Python作…

sqlserver语句中差集、并集、交集的用法

1、并集UNIONUNION 将两个查询的结果合并为一个结果集&#xff0c;并去除重复的行。 SELECT column1, column2 FROM Table1 UNION SELECT column1, column2 FROM Table2;2、交集INTERSECTINTERSECT 返回两个查询的结果中相同的行。 SELECT column1, column2 FROM Table1 IN…

C++之std::move

std::move 是C标准库中的一个函数模板&#xff0c;用于将其参数转换为右值引用。这通常用于移动语义&#xff0c;用于在避免不必要的复制的情况下&#xff0c;将资源&#xff08;例如内存、文件句柄等&#xff09;从一个对象转移到另一个对象。std::move 是C11引入的特性。 用…

【Python】新手入门学习:详细介绍开放封闭原则(OCP)及其作用、代码示例

【Python】新手入门学习&#xff1a;详细介绍开放封闭原则&#xff08;OCP&#xff09;及其作用、代码示例 &#x1f308; 个人主页&#xff1a;高斯小哥 &#x1f525; 高质量专栏&#xff1a;Matplotlib之旅&#xff1a;零基础精通数据可视化、Python基础【高质量合集】、PyT…

在Java中,Cloneable接口的作用,抛出CloneNotSupportedException的原因

文章目录 一、Cloneable接口的作用二、Cloneable接口的用法三、Cloneable的标记作用实现原理 一、Cloneable接口的作用 继承Cloneable接口并调用super.clone()是为了确保对象的克隆行为符合Java的规范。尽管Cloneable是一个空接口&#xff0c;没有定义任何方法&#xff0c;但如…

MQTT Topic通配符

&#x1f339;作者主页&#xff1a;青花锁 &#x1f339;简介&#xff1a;Java领域优质创作者&#x1f3c6;、Java微服务架构公号作者&#x1f604; &#x1f339;简历模板、学习资料、面试题库、技术互助 &#x1f339;文末获取联系方式 &#x1f4dd; 往期热门专栏回顾 专栏…

如何不依赖Unity直接解压unitypackage的内容

使用场景 我们都知道unity的资源导出是导出成.unitypackage文件,如果要里面的内容,得打开Unity,将unitypackage导入进去才能看到里面的内容。 但是很多时候我们下了几十个unitypackage资源包,又不清楚好不好用,而且导入之后编译特别慢,unity又不提供批量解压的功能,所…