【C语言】Infiniband驱动mlx4_init_one

2024-03-27 19:04

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

一、注释

这是Linux内核中Mellanox Ethernet网卡驱动程序mlx4模块的一部分代码,主要用于初始化一个PCI设备。以下是其注释:

// 驱动的主结构体,包含了供PCI核心使用的勾子(hooks)
static struct pci_driver mlx4_driver = {.name        = DRV_NAME,                 // 驱动名称.id_table    = mlx4_pci_table,          // 支持的PCI设备ID表.probe        = mlx4_init_one,           // 初始化一个PCI设备时调用的函数.shutdown    = mlx4_shutdown,           // 关闭设备时调用的函数.remove        = mlx4_remove_one,         // 移除设备时调用的函数.suspend    = mlx4_suspend,            // 暂停设备时调用的函数.resume        = mlx4_resume,             // 恢复设备时调用的函数.err_handler    = &mlx4_err_handler,      // 错误处理程序(一些PCI错误处理函数)
};// 初始化PCI设备的函数
static int mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{// 下面的宏定义是用来处理不同内核版本之间的兼容性问题#ifdef HAVE_DEVLINK_Hstruct devlink *devlink;
#endifstruct mlx4_priv *priv;struct mlx4_dev *dev;int ret;printk_once(KERN_INFO "%s", mlx4_version);  // 打印驱动版本信息#ifdef HAVE_DEVLINK_H// devlink相关的初始化。如果支持devlink,则使用devlink相应的API分配内存以及初始化...priv = devlink_priv(devlink);
#elsepriv = kzalloc(sizeof(*priv), GFP_KERNEL);  // 分配内存if (!priv)return -ENOMEM;  // 如果内存分配失败,则返回错误
#endifdev       = &priv->dev;dev->persist = kzalloc(sizeof(*dev->persist), GFP_KERNEL);  // 分配持久化的设备结构if (!dev->persist) {// 如果分配失败,执行相应的清理工作然后返回错误...}dev->persist->pdev = pdev;dev->persist->dev = dev;pci_set_drvdata(pdev, dev->persist);  // 设置PCI设备的驱动程序数据priv->pci_dev_data = id->driver_data;  // 设置设备私有数据// 初始化互斥锁mutex_init(&dev->persist->device_state_mutex);mutex_init(&dev->persist->interface_state_mutex);mutex_init(&dev->persist->pci_status_mutex);#ifdef HAVE_DEVLINK_H// 如果支持devlink,则继续进行devlink的设置和注册...
#endifret =  __mlx4_init_one(pdev, id->driver_data, priv);  // 调用内部初始化函数
#ifdef HAVE_DEVLINK_H// 处理可能的devlink初始化错误,并注册devlink参数...
#endifif (ret) {// 如果有错误发生,进行清理工作...} else {pci_save_state(pdev);  // 保存PCI设备状态}#ifdef HAVE_DEVLINK_H// 如果支持devlink,处理错误时的清理工作...
#endifreturn ret;  // 返回初始化结果,成功或失败的错误码
}

以上代码是在内核代码中的一个PCI驱动程序的初始化函数。在该函数中,驱动程序初始化PCI设备并为其分配必要的资源(比如内存)。如果启用了`devlink`配置选项(用于一些设备的配置和监控功能),它会额外处理`devlink`相关的注册和初始化。该函数最后将返回一个错误码,表示初始化操作的成功或失败状态。

完整代码的中文注释:

// 驱动的主结构体,包含了供PCI核心使用的勾子(hooks)
static struct pci_driver mlx4_driver = {.name        = DRV_NAME,                   // 驱动名称.id_table    = mlx4_pci_table,             // 支持的PCI设备ID表.probe        = mlx4_init_one,              // 初始化一个PCI设备时调用的函数.shutdown    = mlx4_shutdown,              // 关闭设备时调用的函数.remove        = mlx4_remove_one,            // 移除设备时调用的函数.suspend    = mlx4_suspend,               // 暂停设备时调用的函数.resume        = mlx4_resume,                // 恢复设备时调用的函数.err_handler    = &mlx4_err_handler,         // 错误处理程序(一些PCI错误处理函数)
};
// 初始化一个PCI设备的函数
static int mlx4_init_one(struct pci_dev *pdev, const struct pci_device_id *id)
{
#ifdef HAVE_DEVLINK_Hstruct devlink *devlink;
#endifstruct mlx4_priv *priv;struct mlx4_dev *dev;int ret;// 仅打印一次驱动版本信息printk_once(KERN_INFO "%s", mlx4_version);
#ifdef HAVE_DEVLINK_H// 根据编译时的设置,以不同的方式分配并初始化devlink
#ifdef HAVE_DEVLINK_ALLOC_GET_3_PARAMSdevlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv), &pdev->dev);
#elsedevlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv));
#endifif (!devlink)return -ENOMEM;   // 内存分配失败时返回错误priv = devlink_priv(devlink); // 从devlink得到私有结构体的引用
#elsepriv = kzalloc(sizeof(*priv), GFP_KERNEL); // 在没有devlink的情况下分配内存if (!priv)return -ENOMEM;   // 内存分配失败时返回错误
#endifdev = &priv->dev;dev->persist = kzalloc(sizeof(*dev->persist), GFP_KERNEL); // 分配持久化设备结构if (!dev->persist) {
#ifdef HAVE_DEVLINK_Hret = -ENOMEM;goto err_devlink_free;   // 在devlink设置中,如果内存分配失败则跳转到相应清理环节
#elsekfree(priv);            // 在非devlink设置中清理分配的内存return -ENOMEM;
#endif}// 初始化pdev和dev之间的引用dev->persist->pdev = pdev;dev->persist->dev = dev;pci_set_drvdata(pdev, dev->persist);     // 将持久化的dev结构与pci设备关联起来priv->pci_dev_data = id->driver_data;    // 设置设备特定的数据// 初始化多个mutexmutex_init(&dev->persist->device_state_mutex);mutex_init(&dev->persist->interface_state_mutex);mutex_init(&dev->persist->pci_status_mutex);
#ifdef HAVE_DEVLINK_H// 如果支持devlink,根据编译时的配置注册devlink实例
#ifdef HAVE_DEVLINK_REGISTER_GET_1_PARAMSdevlink_register(devlink);
#elseret = devlink_register(devlink, &pdev->dev);if (ret)goto err_persist_free;  // 注册失败时清理并返回
#endif
#if defined(HAVE_DEVLINK_PARAM) && defined(HAVE_DEVLINK_PARAMS_PUBLISHED)// 如果支持devlink参数,注册设备的参数ret = devlink_params_register(devlink, mlx4_devlink_params,ARRAY_SIZE(mlx4_devlink_params));if (ret)goto err_devlink_unregister;  // 注册失败时清理并返回// 设置初始化时的参数值mlx4_devlink_set_params_init_values(devlink);
#endif
#endifret =  __mlx4_init_one(pdev, id->driver_data, priv);  // 调用内部的初始化函数if (ret) {goto err_handling;  // 如果初始化失败,处理错误}
#ifdef HAVE_DEVLINK_H
#if defined(HAVE_DEVLINK_PARAM) && defined(HAVE_DEVLINK_PARAMS_PUBLISHED)// 如果支持devlink参数并且已经发布,发布参数设置
#ifdef HAVE_DEVLINK_PARAMS_PUBLISHEDdevlink_params_publish(devlink);
#endif
#ifdef HAVE_DEVLINK_RELOAD_ENABLE// 如果支持devlink重载,使能devlink的重载操作devlink_reload_enable(devlink);
#endif
#endif
#endifpci_save_state(pdev);   // 保存当前的PCI设备状态,这样在需要恢复设备时(比如从挂起状态恢复时)可以用到return 0;               // 返回0表示成功
#ifdef HAVE_DEVLINK_H
#if defined(HAVE_DEVLINK_PARAM) && defined(HAVE_DEVLINK_PARAMS_PUBLISHED)// 如果支持devlink参数和发布功能,以下是失败时的清理过程
err_params_unregister:// 取消注册之前注册的devlink参数devlink_params_unregister(devlink, mlx4_devlink_params,ARRAY_SIZE(mlx4_devlink_params));
#endif// 错误处理部分:如果devlink注册失败,则取消注册
err_devlink_unregister:devlink_unregister(devlink);
#ifndef HAVE_DEVLINK_REGISTER_GET_1_PARAMS// 如果出现错误且版本不支持单参数devlink注册,则需要释放持久化设备结构内存
err_persist_free:
#endifkfree(dev->persist);  // 释放持久化设备结构内存// 如果devlink分配失败,则执行以下代码来释放内存
err_devlink_free:devlink_free(devlink);  // 释放devlink结构体所占用的内存
#endifreturn ret;  // 返回错误码。如果初始化过程中的某个步骤失败,则返回相应的错误码
}

上面的 pci_save_state(pdev) 函数调用负责保存当前PCI设备的核心配置寄存器。这在系统休眠或PCI设备电源管理事件中是必要的,因为在这样的事件中,设备的PCI配置可能会丢失或被重置,系统在恢复时需要这信息重新对设备进行配置。

return 0; 表明函数执行成功,返回值0符合Linux内核中处理成功操作的返回值约定。在C语言中,返回状态通常使用整形返回,而0通常表示成功,非0值表示某种形式的错误。

备注:此代码段涉及到了错误处理和内存清理部分。在初始化一个PCI设备时,如果过程中遇到错误,代码会跳转到相应的错误处理标签进行清理。这些清理工作可能包括注销注册的devlink参数、释放分配的内存、取消注册devlink设备等,以确保在发生错误时不会泄露资源。一旦完成清理,函数将返回一个表示错误状态的负值错误码。如果初始化成功,代码则将保存PCI设备的状态,并且返回0表示初始化成功。

二、讲解

这段代码主体是一个`mlx4_init_one`函数,它是Mellanox公司网络设备驱动的初始化函数,用于对PCI网络设备进行初始化。这段代码来自Linux内核的Mellanox网络驱动,其中的`mlx4_driver`结构体是PCI驱动的注册信息。`mlx4_init_one`函数是在PCI设备开始初始化时由内核调用的。函数主体涉及资源分配、设备注册和错误处理。
代码以及注释的大致意思如下:
- mlx4_driver:一个PCI驱动结构体,提供驱动的基本信息和回调函数。
- mlx4_init_one:驱动的初始化函数,它会针对每个被发现的PCI设备被调用。
  - 输出一次版本信息到内核日志。
  - 分配`devlink`资源,作为网络设备到devlink层的接口。
  - 分配`mlx4_priv`结构体以存储私有数据。
  - 分配`mlx4_dev`结构体中的`persist`字段,存储设备持续性状态。
  - 设置PCI设备与驱动数据的关联。
  - 初始化一些互斥锁,保护设备状态和PCI状态。
  - 在devlink层注册该设备,以及设备参数。
  - 调用`__mlx4_init_one`进行进一步的设备初始化工作。
  - 最后,保存PCI设备的状态以便恢复时使用。
- 如果初始化失败,函数会逐步释放已分配的资源,并且返回错误代码。
这段代码展示了复杂PCI设备驱动初始化流程的一部分,包括资源的分配和初始化,以及设备在系统内的注册。如果在初始化过程中遇到任何错误,它会回滚已经执行的步骤,释放资源,确保系统稳定。

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



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

相关文章

C语言中的数据类型强制转换

《C语言中的数据类型强制转换》:本文主要介绍C语言中的数据类型强制转换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录C语言数据类型强制转换自动转换强制转换类型总结C语言数据类型强制转换强制类型转换:是通过类型转换运算来实现的,主要的数据类型转换分为自动转换

利用Go语言开发文件操作工具轻松处理所有文件

《利用Go语言开发文件操作工具轻松处理所有文件》在后端开发中,文件操作是一个非常常见但又容易出错的场景,本文小编要向大家介绍一个强大的Go语言文件操作工具库,它能帮你轻松处理各种文件操作场景... 目录为什么需要这个工具?核心功能详解1. 文件/目录存javascript在性检查2. 批量创建目录3. 文件

C语言实现两个变量值交换的三种方式

《C语言实现两个变量值交换的三种方式》两个变量值的交换是编程中最常见的问题之一,以下将介绍三种变量的交换方式,其中第一种方式是最常用也是最实用的,后两种方式一般只在特殊限制下使用,需要的朋友可以参考下... 目录1.使用临时变量(推荐)2.相加和相减的方式(值较大时可能丢失数据)3.按位异或运算1.使用临时

使用C语言实现交换整数的奇数位和偶数位

《使用C语言实现交换整数的奇数位和偶数位》在C语言中,要交换一个整数的二进制位中的奇数位和偶数位,重点需要理解位操作,当我们谈论二进制位的奇数位和偶数位时,我们是指从右到左数的位置,本文给大家介绍了使... 目录一、问题描述二、解决思路三、函数实现四、宏实现五、总结一、问题描述使用C语言代码实现:将一个整

C语言字符函数和字符串函数示例详解

《C语言字符函数和字符串函数示例详解》本文详细介绍了C语言中字符分类函数、字符转换函数及字符串操作函数的使用方法,并通过示例代码展示了如何实现这些功能,通过这些内容,读者可以深入理解并掌握C语言中的字... 目录一、字符分类函数二、字符转换函数三、strlen的使用和模拟实现3.1strlen函数3.2st

Go语言中最便捷的http请求包resty的使用详解

《Go语言中最便捷的http请求包resty的使用详解》go语言虽然自身就有net/http包,但是说实话用起来没那么好用,resty包是go语言中一个非常受欢迎的http请求处理包,下面我们一起来学... 目录安装一、一个简单的get二、带查询参数三、设置请求头、body四、设置表单数据五、处理响应六、超

C语言中的浮点数存储详解

《C语言中的浮点数存储详解》:本文主要介绍C语言中的浮点数存储详解,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录1、首先明确一个概念2、接下来,讲解C语言中浮点型数存储的规则2.1、可以将上述公式分为两部分来看2.2、问:十进制小数0.5该如何存储?2.3 浮点

基于Python实现多语言朗读与单词选择测验

《基于Python实现多语言朗读与单词选择测验》在数字化教育日益普及的今天,开发一款能够支持多语言朗读和单词选择测验的程序,对于语言学习者来说无疑是一个巨大的福音,下面我们就来用Python实现一个这... 目录一、项目概述二、环境准备三、实现朗读功能四、实现单词选择测验五、创建图形用户界面六、运行程序七、

使用Go语言开发一个命令行文件管理工具

《使用Go语言开发一个命令行文件管理工具》这篇文章主要为大家详细介绍了如何使用Go语言开发一款命令行文件管理工具,支持批量重命名,删除,创建,移动文件,需要的小伙伴可以了解下... 目录一、工具功能一览二、核心代码解析1. 主程序结构2. 批量重命名3. 批量删除4. 创建文件/目录5. 批量移动三、如何安

python使用fastapi实现多语言国际化的操作指南

《python使用fastapi实现多语言国际化的操作指南》本文介绍了使用Python和FastAPI实现多语言国际化的操作指南,包括多语言架构技术栈、翻译管理、前端本地化、语言切换机制以及常见陷阱和... 目录多语言国际化实现指南项目多语言架构技术栈目录结构翻译工作流1. 翻译数据存储2. 翻译生成脚本