【C语言】Infiniband驱动mlx4_init_one

一、注释

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

cpp 复制代码
// 驱动的主结构体,包含了供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_H
    struct devlink *devlink;
#endif
    struct 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);
#else
    priv = kzalloc(sizeof(*priv), GFP_KERNEL);  // 分配内存
    if (!priv)
        return -ENOMEM;  // 如果内存分配失败,则返回错误
#endif
    dev       = &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的设置和注册
    ...
#endif

    ret =  __mlx4_init_one(pdev, id->driver_data, priv);  // 调用内部初始化函数
#ifdef HAVE_DEVLINK_H
    // 处理可能的devlink初始化错误,并注册devlink参数
    ...
#endif

    if (ret) {
        // 如果有错误发生,进行清理工作
        ...
    } else {
        pci_save_state(pdev);  // 保存PCI设备状态
    }

#ifdef HAVE_DEVLINK_H
    // 如果支持devlink,处理错误时的清理工作
    ...
#endif
    return ret;  // 返回初始化结果,成功或失败的错误码
}

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

完整代码的中文注释:

cpp 复制代码
// 驱动的主结构体,包含了供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_H
    struct devlink *devlink;
#endif
    struct 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_PARAMS
    devlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv), &pdev->dev);
#else
    devlink = devlink_alloc(&mlx4_devlink_ops, sizeof(*priv));
#endif
    if (!devlink)
        return -ENOMEM;   // 内存分配失败时返回错误
    priv = devlink_priv(devlink); // 从devlink得到私有结构体的引用
#else
    priv = kzalloc(sizeof(*priv), GFP_KERNEL); // 在没有devlink的情况下分配内存
    if (!priv)
        return -ENOMEM;   // 内存分配失败时返回错误
#endif
    dev = &priv->dev;
    dev->persist = kzalloc(sizeof(*dev->persist), GFP_KERNEL); // 分配持久化设备结构
    if (!dev->persist) {
#ifdef HAVE_DEVLINK_H
        ret = -ENOMEM;
        goto err_devlink_free;   // 在devlink设置中,如果内存分配失败则跳转到相应清理环节
#else
        kfree(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;    // 设置设备特定的数据
    // 初始化多个mutex
    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实例
#ifdef HAVE_DEVLINK_REGISTER_GET_1_PARAMS
    devlink_register(devlink);
#else
    ret = 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
#endif
    ret =  __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_PUBLISHED
    devlink_params_publish(devlink);
#endif
#ifdef HAVE_DEVLINK_RELOAD_ENABLE
    // 如果支持devlink重载,使能devlink的重载操作
    devlink_reload_enable(devlink);
#endif
#endif
#endif
    pci_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:
#endif
    kfree(dev->persist);  // 释放持久化设备结构内存
    // 如果devlink分配失败,则执行以下代码来释放内存
err_devlink_free:
    devlink_free(devlink);  // 释放devlink结构体所占用的内存
#endif
    return 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设备驱动初始化流程的一部分,包括资源的分配和初始化,以及设备在系统内的注册。如果在初始化过程中遇到任何错误,它会回滚已经执行的步骤,释放资源,确保系统稳定。

相关推荐
朱小弟cs67 小时前
Orange的运维学习日记--41.Ansible基础入门
linux·运维·学习·ci/cd·自动化·ansible·devops
CIb0la7 小时前
kali linux 2025.2安装WPS并设置无报错的详细步骤
linux·运维·wps
醉方休8 小时前
Node.js 精选:50 款文件处理与开发环境工具库
linux·运维·node.js
代码老y9 小时前
从裸机到云原生:Linux 操作系统实战进阶的“四维跃迁”
linux·运维·云原生
CMCST9 小时前
CentOS 7.9 升级 GLibc 2.34
linux·运维·centos
xiep143833351010 小时前
Rocky Linux 10 部署 Kafka 集群
linux·运维·kafka
笨鸟要努力13 小时前
Ubuntu 全盘备份
linux·运维·ubuntu
ChironW13 小时前
Ubuntu 22.04 离线环境下完整安装 Anaconda、CUDA 12.1、NVIDIA 驱动及 cuDNN 8.9.3 教程
linux·运维·人工智能·深度学习·yolo·ubuntu
轻松Ai享生活15 小时前
linux 日志详解
linux
小白的代码日记15 小时前
Linux常用指令
linux·运维·服务器