【C语言】Infiniband驱动mlx4_reset

2024-03-27 12:36

本文主要是介绍【C语言】Infiniband驱动mlx4_reset,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、注释

这个 mlx4_reset 函数负责重置 Mellanox 设备。它保存了设备的 PCI 头信息,然后重置了设备,之后还原保存的 PCI 头信息。请注意,该函数是用英文注释的,下面提供中文注释的版本。以下是该函数的流程:
1. 为保存 PCI 头信息分配内存。
2. 如果存在 PCI Express 能力结构,则保存 PCI 头信息。
3. 映射硬件重置寄存器地址空间。
4. 获取硬件信号量以防止闪存更新期间冲突。
5. 实际执行硬件重置操作。
6. 等待一段时间以确保设备重置完成。
7. 检查设备是否恢复到操作状态。
8. 如果设备已经恢复操作状态,则还原 PCI 头信息。
9. 清理和释放所有分配的资源。
以下是注释翻译成中文后的代码片段:

int mlx4_reset(struct mlx4_dev *dev) {// ...之前的代码略.../* 实际执行硬件重置 */writel(MLX4_RESET_VALUE, reset + MLX4_RESET_OFFSET);iounmap(reset);/* 文档称重置后应等待一秒钟才能访问设备 */msleep(1000);// ...之后的代码略.../* 现在恢复 PCI 头信息 */if (pcie_cap) {// 恢复 PCI Express Device Control 寄存器devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4];if (pcie_capability_write_word(dev->persist->pdev,PCI_EXP_DEVCTL,devctl)) {err = -ENODEV;mlx4_err(dev, "无法恢复 HCA PCI Express 设备控制寄存器,中止操作\n");goto out;}// 恢复 PCI Express Link Control 寄存器linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];if (pcie_capability_write_word(dev->persist->pdev,PCI_EXP_LNKCTL,linkctl)) {err = -ENODEV;mlx4_err(dev, "无法恢复 HCA PCI Express 链接控制寄存器,中止操作\n");goto out;}}// ...之后的清理代码略...return err;
}

请注意,这些注释更确切地描述了代码中的每个重要步骤,并对原开发者的注释进行翻译以更好地帮助中文读者理解代码。在 Linux 内核代码中,通常不鼓励添加太多注释,而是倾向于通过代码清晰表达意图。不过,如果在维护的项目中需要用中文注释,可以按照实际情况将代码中的关键部分翻译成中文。

注释,对关键代码部分进行解释:

#include <linux/errno.h>  // 包含Linux内核的错误号定义
#include <linux/pci.h>    // 包含用于PCI操作的函数和宏定义
#include <linux/delay.h>  // 包含延时函数
#include <linux/slab.h>   // 包含内存分配函数
#include <linux/jiffies.h>// 包含时间管理函数和宏#include "mlx4.h" // 包含mlx4驱动的定义和声明// 定义重置操作需要用到的常量
#define MLX4_RESET_BASE             0xf0000
#define MLX4_RESET_SIZE             0x400
#define MLX4_SEM_OFFSET             0x3fc
#define MLX4_RESET_OFFSET           0x10
#define MLX4_RESET_VALUE            swab32(1)#define MLX4_SEM_TIMEOUT_JIFFIES    (10 * HZ)
#define MLX4_RESET_TIMEOUT_JIFFIES  (2 * HZ)int mlx4_reset(struct mlx4_dev *dev)
{// 变量定义void __iomem *reset;     // 映射重置寄存器的地址u32 *hca_header = NULL;  // 存储设备PCI头信息的缓冲区int pcie_cap;u16 devctl, linkctl, vendor;unsigned long end;u32 sem;int i;int err = 0;// 以下是函数主要逻辑// 动态分配256字节的内存用于存储PCI头信息hca_header = kmalloc(256, GFP_KERNEL); if (!hca_header) {// 分配内存失败的错误处理}// 获取PCIe能力寄存器的偏移量pcie_cap = pci_pcie_cap(dev->persist->pdev);// 备份当前的PCI配置头信息,忽略有特殊含义的配置偏移22和23for (i = 0; i < 64; ++i) {if (i == 22 || i == 23)continue;// 读PCI配置头信息到 hca_header 中}// 映射设备的重置寄存器地址reset = ioremap(pci_resource_start(dev->persist->pdev, 0) + MLX4_RESET_BASE, MLX4_RESET_SIZE);if (!reset) {// 重置寄存器映射失败处理}// 获取硬件信号量以防止在设备复位期间进行闪存更新end = jiffies + MLX4_SEM_TIMEOUT_JIFFIES;do {sem = readl(reset + MLX4_SEM_OFFSET);if (!sem)break;msleep(1);} while (time_before(jiffies, end));// sem仍然有效,则获取信号量失败if (sem) {// 获取信号量失败的错误处理}// 实际执行重置操作writel(MLX4_RESET_VALUE, reset + MLX4_RESET_OFFSET);// 释放重置寄存器的映射iounmap(reset);// 根据文档,重置后需要等待一秒钟再访问设备msleep(1000);// 等待设备复位完成并确认PCI设备可被访问end = jiffies + MLX4_RESET_TIMEOUT_JIFFIES;do {// 检查PCI_VENDOR_ID是否有效,如果有效,则设备复位成功} while (time_before(jiffies, end));// 如果vendor无效,代表设备复位失败if (vendor == 0xffff) {// 设备复位失败的错误处理}// 恢复之前保存的PCI配置头信息if (pcie_cap) {// 恢复PCIe设备控制和链接控制寄存器}// 恢复其他PCI配置头信息for (i = 0; i < 16; ++i) {// 写回PCI配置信息}// 最后,确保PCI_COMMAND寄存器被正确恢复if (pci_write_config_dword(dev->persist->pdev, PCI_COMMAND, hca_header[PCI_COMMAND / 4])) {// PCI_COMMAND寄存器恢复失败的错误处理}out:// 释放之前分配的内存kfree(hca_header);return err;
}

这段代码涉及到硬件层面的操作,这其中包括了对PCI设备的配置空间的保存、设备重置以及恢复的过程。遵循Linux内核中的错误处理机制,使用goto语句跳转到错误处理代码段。通过互斥机制和延时确保操作的安全性。大部分详细的注释已经涵盖在代码里。

完整注释:

这段代码相对较长,将注释分成几部分进行讲解,每部分涵盖一个逻辑模块。

请注意:由于代码中使用了很多Linux内核特定的函数和宏定义,某些注释可能需要一定的内核知识才能完全理解。

//drivers\net\ethernet\mellanox\mlx4\reset.c
/** 版权所有 (c) 2006, 2007 Cisco Systems, Inc. 保留所有权利。* 版权所有 (c) 2007, 2008 Mellanox Technologies. 保留所有权利。** 这款软件可以在两种许可下的任何一种下提供给您。* 您可以选择被GNU通用公共许可证(GPL)版本2的条款所许可,* 可从该源代码树主目录中的COPYING文件中获取,* 或者选择下面的OpenIB.org BSD许可证:**     源代码和二进制形式的再分发和使用是允许的,*     无论是否进行了修改,只要满足以下条件:**      - 源代码的再分发必须保留上述版权声明、此条件列表和下面的免责声明。**      - 以二进制形式进行的再分发必须在分发的文件和/或材料中复制上述版权声明、*        此条件列表以及随附的文件和/或材料中的免责声明。** 本软件是按“原样”提供的,没有任何形式的保证,* 无论是明示的或暗示的,包括但不限于适销性、* 适用于特定用途的保证和非侵权。在任何情况下,* 作者或版权所有者均不对任何索赔、损害或其他责任承担责任,* 无论是在合同诉讼、侵权行为或其他方面,* 与本软件或本软件的使用或其他交易有关。*/
// 引入一些必要的头文件
#include <linux/errno.h>    // 错误码定义
#include <linux/pci.h>      // PCI总线相关函数和结构体
#include <linux/delay.h>    // 延时相关的函数
#include <linux/slab.h>     // 内核内存分配相关
#include <linux/jiffies.h>  // 内核时间管理相关
#include "mlx4.h"           // Mellanox设备相关头文件
// 定义一个重置函数
int mlx4_reset(struct mlx4_dev *dev)
{void __iomem *reset;           // 映射设备寄存器的指针u32 *hca_header = NULL;        // 指向保存PCI头部数据的内存int pcie_cap;                  // PCI-E能力寄存器的位置u16 devctl, linkctl, vendor;   // 枚举一些PCI相关的变量unsigned long end;             // 用于记录超时时间u32 sem;                       // 记录半导体值int i;                         // 循环计数器int err = 0;                   // 错误码// 定义重置相关的常量
#define MLX4_RESET_BASE        0xf0000               // 重置寄存器的基础地址
#define MLX4_RESET_SIZE        0x400                 // 重置寄存器的大小
#define MLX4_SEM_OFFSET        0x3fc                 // 硬件信号量的偏移量
#define MLX4_RESET_OFFSET    0x10                  // 重置命令的偏移量
#define MLX4_RESET_VALUE    swab32(1)             // 重置命令的值// 定义超时的常数
#define MLX4_SEM_TIMEOUT_JIFFIES    (10 * HZ)  // 获取硬件信号量的超时时长
#define MLX4_RESET_TIMEOUT_JIFFIES    (2 * HZ)   // 重置等待超时的时长/** 重置芯片。这个过程有点不优雅,因为我们必须在重置之前保存PCI头部,* 然后在芯片重启后恢复它。我们跳过配置空间偏移量22和23,因为它们有特殊含义。*/// 是否需要保存完整的4K PCIe头?先分配空间以保存头部信息。hca_header = kmalloc(256, GFP_KERNEL);if (!hca_header) {err = -ENOMEM;  // 内存分配失败设置错误码mlx4_err(dev, "Couldn't allocate memory to save HCA PCI header, aborting\n");goto out;       // 跳转到函数的结束部分}// 获取PCI-E Capability的偏移量。pcie_cap = pci_pcie_cap(dev->persist->pdev);// 保存PCI配置空间的前256字节,跳过22和23偏移量的保存。for (i = 0; i < 64; ++i) {if (i == 22 || i == 23) continue;// 读取PCI配置空间的每一个DWORD,并保存起来if (pci_read_config_dword(dev->persist->pdev, i * 4, hca_header + i)) {err = -ENODEV;  // 读取失败则设定错误码mlx4_err(dev, "Couldn't save HCA PCI header, aborting\n");goto out;}}// 映射重置寄存器的物理地址到虚拟地址空间reset = ioremap(pci_resource_start(dev->persist->pdev, 0) + MLX4_RESET_BASE, MLX4_RESET_SIZE);if (!reset) {err = -ENOMEM;  // 映射失败设置错误码mlx4_err(dev, "Couldn't map HCA reset register, aborting\n");goto out;}// 锁定硬件信号量,以阻止闪存更新end = jiffies + MLX4_SEM_TIMEOUT_JIFFIES;do {sem = readl(reset + MLX4_SEM_OFFSET);if (!sem)break;msleep(1);} while (time_before(jiffies, end));if (sem) {mlx4_err(dev, "Failed to obtain HW semaphore, aborting\n");err = -EAGAIN;  // 获取信号量失败设置错误码iounmap(reset);goto out;}// 实际进行重置操作writel(MLX4_RESET_VALUE, reset + MLX4_RESET_OFFSET);iounmap(reset);  // 解除映射// 文档指出在设备访问前等待一秒钟msleep(1000);// 等待设备复位完毕end = jiffies + MLX4_RESET_TIMEOUT_JIFFIES;do {if (!pci_read_config_word(dev->persist->pdev, PCI_VENDOR_ID, &vendor) && vendor != 0xffff)break;msleep(1);} while (time_before(jiffies, end));if (vendor == 0xffff) {err = -ENODEV;  // 设备重置后未恢复,则设置错误码mlx4_err(dev, "PCI device did not come back after reset, aborting\n");goto out;}// 恢复PCI头部信息if (pcie_cap) {devctl = hca_header[(pcie_cap + PCI_EXP_DEVCTL) / 4];if (pcie_capability_write_word(dev->persist->pdev, PCI_EXP_DEVCTL, devctl)) {err = -ENODEV;  // 恢复失败则设置错误码mlx4_err(dev, "Couldn't restore HCA PCI Express Device Control register, aborting\n");goto out;}linkctl = hca_header[(pcie_cap + PCI_EXP_LNKCTL) / 4];if (pcie_capability_write_word(dev->persist->pdev, PCI_EXP_LNKCTL, linkctl)) {err = -ENODEV;  // 恢复失败则设置错误码mlx4_err(dev, "Couldn't restore HCA PCI Express Link control register, aborting\n");goto out;}}for (i = 0; i < 16; ++i) {if (i * 4 == PCI_COMMAND)continue;if (pci_write_config_dword(dev->persist->pdev, i * 4, hca_header[i])) {err = -ENODEV;  // 写入失败则设置错误码mlx4_err(dev, "Couldn't restore HCA reg %x, aborting\n", i);goto out;}}

继续从最后一个`for`循环。

    // 最后恢复PCI_COMMAND寄存器。if (pci_write_config_dword(dev->persist->pdev, PCI_COMMAND, hca_header[PCI_COMMAND / 4])) {err = -ENODEV;  // 写入失败则设置错误码mlx4_err(dev, "Couldn't restore HCA COMMAND, aborting\n");goto out;}out:kfree(hca_header);  // 释放之前保存PCI头部信息的内存空间return err;  // 返回错误码,0代表成功,非0代表失败
}

这部分代码主要执行了芯片的重置过程,并且在重置前后操作了PCI头部信息的保存与恢复,以确保设备能够在重置之后正常工作。需要注意的是代码中充满了错误检查和处理的步骤,以保证万一出现不可预见的问题时能够安全退出,并且反馈给调用者相应的错误信息。

二、讲解

这段代码来自Linux内核的Mellanox网络驱动mlx4,位于文件`reset.c`中。这个文件负责处理`mlx4`设备的重置逻辑。以下是代码中各个部分的中文解释:
代码开始于一个版权声明,声明代码属于Cisco Systems, Inc. 和 Mellanox Technologies所有,许可证可选为GPL(GNU通用公共许可证)版本2或OpenIB.org的BSD(伯克利软件许可证)。
接着引入了一些所需的Linux内核头文件。这些文件允许代码访问一些内核提供的功能,如内存分配和时间相关功能。
声明了`mlx4_reset`函数,它负责重置`mlx4_dev`指向的设备。
在函数中,定义了一些宏和它们的值:这些宏用于方便地引用重置流程中所需要的一些硬件寄存器的偏移量和相关的值。
函数`mlx4_reset`的实现分几个步骤:
1. 保存PCI配置头信息:在重置设备之前,需要保存PCI配置头的内容,以便后面可以恢复它。这是因为重置操作会导致这些信息丢失。作者注意到需要略过配置空间偏移量22和23,因为它们有特殊含义。
2. 映射重置寄存器:代码通过`ioremap`函数映射了设备的重置寄存器区域到内核地址空间,以便可以对它们进行读写操作。
3. 获取硬件信号量:在重置前需要获得一个硬件信号量,防止在重置时对闪存发生写操作。这里使用了循环,并通过读取信号量寄存器来判断是否获取成功。
4. 执行重置:通过向某个特定的寄存器写入特定值来触发硬件重置。
5. 等待设备初始化:硬件文档建议在访问设备之前等待一秒钟。
6. 恢复PCI头信息:重置完成后,PCI配置头信息需要从之前保存的内容恢复。
7. 清理:释放分配的内存空间,并返回执行结果。
整个函数的逻辑层次清晰,分为了准备、执行和清理三个主要部分。出错处理(错误标记`err`)也涵盖了各种可能的失败情况,例如内存分配失败、映射失败、信号量获取失败等,并在最后正确地释放资源和返回相应的错误代码。

ChatGPT

mlx4_reset是一种在InfiniBand驱动中遇到的函数,它一般是Mellanox InfiniBand驱动的一部分。这个函数用来将Mellanox InfiniBand硬件设备恢复到其初始状态。在Linux内核中,Mellanox的mlx4驱动负责管理InfiniBand设备。这个驱动通常与某些InfiniBand芯片组一起使用,比如那些被用在高性能计算集群中的芯片组。
由于InfiniBand驱动的代码是专业领域的内容,且通常需要深入了解硬件规格和内核编程,一个典型的`mlx4_reset`函数可能涉及复杂的硬件操作,包括直接与硬件交互的函数调用,内核同步机制,可能还有对硬件寄存器的读写。
在具体的C代码层面上,`mlx4_reset`函数的实现依赖于Mellanox硬件和驱动的具体版本,因此会根据不同的设备模型和驱动版本有所差异。但是,其大致框架可能如下所示:

int mlx4_reset(struct mlx4_dev *dev)
{// 获取设备上下文或设备特定的数据结构的引用struct mlx4_priv *priv = mlx4_priv(dev);// 执行硬件复位序列,可能包括:// - 禁用中断// - 停止设备端口// - 重置设备的某些区域或寄存器// - 将硬件状态置为已知的初始状态/* 示例代码,非真实代码 */mlx4_disable_interrupts(dev);mlx4_stop_device_ports(dev);// 实际的重置操作可以涉及对硬件寄存器的操作writel(value, priv->reset_reg); // 写入复位寄存器以重置设备// 检查设备是否已经重置完毕并且准备好重新初始化if (!mlx4_device_ready(dev)) {// 如果设备未能恢复到初始状态,则返回错误return -EIO;}// 重置成功后的操作,可能会重新初始化设备// 硬件复位后的设备必须重新初始化以便再次使用mlx4_enable_interrupts(dev);mlx4_start_device(dev);// 复位成功,返回0表示成功return 0;
}

请注意,上面的代码只是一个示意性的伪代码。实际的`mlx4_reset`函数包含更详细的步骤,包括对硬件规格的复杂操作,错误处理和设备状态的检查。要获得真实的源代码,您很可能需要访问Linux内核的源代码,具体到drivers/net/ethernet/mellanox/mlx4/目录中,您可以在该目录下找到与mlx4相关的实现。

通义灵码

在InfiniBand驱动程序中,mlx4_reset 函数通常用于复位 Mellanox InfiniBand Host Channel Adapter (HCA) 的硬件状态或某个特定功能模块。这个函数的作用是在遇到硬件错误、超时或其他需要恢复正常操作状态的情况时,重新初始化和同步HCA的硬件资源。

在C语言实现的Mellanox mlx4系列驱动中,mlx4_reset 函数可能执行如下步骤:

  1. 停止活动

    • 暂停所有正在进行的传输操作,包括清理发送和接收队列中的未完成请求(Work Requests, WRs)。
    • 中断任何正在使用的DMA传输。
  2. 硬件复位

    • 向HCA发送复位命令,使其进入已知初始状态。
    • 根据硬件规格书,通过相应的寄存器操作来触发和监控复位过程。
  3. 资源重置

    • 释放之前分配的硬件资源,如队列对、内存窗口、保护域等。
    • 重新初始化这些资源,确保它们处于有效可用的状态。
  4. 重新配置

    • 在硬件复位完成后,根据设备配置重新设置寄存器值和内部参数。
    • 重新建立硬件队列、中断处理机制和其他关键的硬件上下文。
  5. 启动服务

    • 重新启动必要的服务,比如恢复中断服务例程(Interrupt Service Routines, ISRs)。
    • 通知上层协议栈和应用程序复位已完成,可以继续进行新的通信操作。

请注意,上述流程是基于一般理解推测出的,具体实现细节会因不同版本的mlx4驱动而异,查看源代码是最准确的理解方式。

这篇关于【C语言】Infiniband驱动mlx4_reset的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/852121

相关文章

Go 语言中的 Struct Tag 的用法详解

《Go语言中的StructTag的用法详解》在Go语言中,结构体字段标签(StructTag)是一种用于给字段添加元信息(metadata)的机制,常用于序列化(如JSON、XML)、ORM映... 目录一、结构体标签的基本语法二、json:"token"的具体含义三、常见的标签格式变体四、使用示例五、使用

Go语言使用slices包轻松实现排序功能

《Go语言使用slices包轻松实现排序功能》在Go语言开发中,对数据进行排序是常见的需求,Go1.18版本引入的slices包提供了简洁高效的排序解决方案,支持内置类型和用户自定义类型的排序操作,本... 目录一、内置类型排序:字符串与整数的应用1. 字符串切片排序2. 整数切片排序二、检查切片排序状态:

基于Go语言实现Base62编码的三种方式以及对比分析

《基于Go语言实现Base62编码的三种方式以及对比分析》Base62编码是一种在字符编码中使用62个字符的编码方式,在计算机科学中,,Go语言是一种静态类型、编译型语言,它由Google开发并开源,... 目录一、标准库现状与解决方案1. 标准库对比表2. 解决方案完整实现代码(含边界处理)二、关键实现细

如何合理管控Java语言的异常

《如何合理管控Java语言的异常》:本文主要介绍如何合理管控Java语言的异常问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、介绍2、Thorwable类3、Error4、Exception类4.1、检查异常4.2、运行时异常5、处理方式5.1. 捕获异常

C语言中的常见进制转换详解(从二进制到十六进制)

《C语言中的常见进制转换详解(从二进制到十六进制)》进制转换是计算机编程中的一个常见任务,特别是在处理低级别的数据操作时,C语言作为一门底层编程语言,在进制转换方面提供了灵活的操作方式,今天,我们将深... 目录1、进制基础2、C语言中的进制转换2.1 从十进制转换为其他进制十进制转二进制十进制转八进制十进

如何在Ubuntu上安装NVIDIA显卡驱动? Ubuntu安装英伟达显卡驱动教程

《如何在Ubuntu上安装NVIDIA显卡驱动?Ubuntu安装英伟达显卡驱动教程》Windows系统不同,Linux系统通常不会自动安装专有显卡驱动,今天我们就来看看Ubuntu系统安装英伟达显卡... 对于使用NVIDIA显卡的Ubuntu用户来说,正确安装显卡驱动是获得最佳图形性能的关键。与Windo

$在R语言中的作用示例小结

《$在R语言中的作用示例小结》在R语言中,$是一个非常重要的操作符,主要用于访问对象的成员或组件,它的用途非常广泛,不仅限于数据框(dataframe),还可以用于列表(list)、环境(enviro... 目录1. 访问数据框(data frame)中的列2. 访问列表(list)中的元素3. 访问jav

嵌入式Linux之使用设备树驱动GPIO的实现方式

《嵌入式Linux之使用设备树驱动GPIO的实现方式》:本文主要介绍嵌入式Linux之使用设备树驱动GPIO的实现方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录一、设备树配置1.1 添加 pinctrl 节点1.2 添加 LED 设备节点二、编写驱动程序2.1

嵌入式Linux驱动中的异步通知机制详解

《嵌入式Linux驱动中的异步通知机制详解》:本文主要介绍嵌入式Linux驱动中的异步通知机制,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录前言一、异步通知的核心概念1. 什么是异步通知2. 异步通知的关键组件二、异步通知的实现原理三、代码示例分析1. 设备结构

C语言中位操作的实际应用举例

《C语言中位操作的实际应用举例》:本文主要介绍C语言中位操作的实际应用,总结了位操作的使用场景,并指出了需要注意的问题,如可读性、平台依赖性和溢出风险,文中通过代码介绍的非常详细,需要的朋友可以参... 目录1. 嵌入式系统与硬件寄存器操作2. 网络协议解析3. 图像处理与颜色编码4. 高效处理布尔标志集合