一、注释
这个 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和23
for (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
函数可能执行如下步骤:
-
停止活动:
- 暂停所有正在进行的传输操作,包括清理发送和接收队列中的未完成请求(Work Requests, WRs)。
- 中断任何正在使用的DMA传输。
-
硬件复位:
- 向HCA发送复位命令,使其进入已知初始状态。
- 根据硬件规格书,通过相应的寄存器操作来触发和监控复位过程。
-
资源重置:
- 释放之前分配的硬件资源,如队列对、内存窗口、保护域等。
- 重新初始化这些资源,确保它们处于有效可用的状态。
-
重新配置:
- 在硬件复位完成后,根据设备配置重新设置寄存器值和内部参数。
- 重新建立硬件队列、中断处理机制和其他关键的硬件上下文。
-
启动服务:
- 重新启动必要的服务,比如恢复中断服务例程(Interrupt Service Routines, ISRs)。
- 通知上层协议栈和应用程序复位已完成,可以继续进行新的通信操作。
请注意,上述流程是基于一般理解推测出的,具体实现细节会因不同版本的mlx4驱动而异,查看源代码是最准确的理解方式。