由于研究学习的需要,要对intel XL710网卡的驱动代码进行分析,主要分析其VF的相关代码,整个代码量相当大,有数万行。当然我也是从头开始学,一点一点分析并记录在51cto的博客中,如果大家在阅读过程中发现有错误,请及时提出,另外我会专门开一个专栏,用来记录对每个函数的分析,有些可能会对每行代码进行分析。
代码在github上有共享链接在这:i40e 大家如有需要可自行下载。
那么今天就从i40e的驱动中i40e_driver的这个结构体出发,一点一点抽丝剥茧来看来分析。
首先一个设备的驱动程序,一般来说都有一个接口,因为一个硬件插入到主机当中,一定是要通过驱动来讲这个设备注册到系统里,通过初始化模块来注册,基本每个驱动都会包含这样一个结构体:
在驱动i40e_main.c函数中的大致18386行的位置。也可以搜索这个名字找到。
上图是710代码中的i40e驱动,其中包含了name、probe、 remove(移除设备)、shutdown(关闭设备) 等等还有一些是不同驱动的不同特性,这个结构体的主要作用就是用来想操作系统注册该设备。
我们在这段代码中主要关心两个一个是i40e_probe,这是个函数,用以对驱动中模块的初始化。
还有个是sriov_configure,sriov就是虚拟化,也就是vf。这个定义是由pcie标准中提出的。简单理解就是像操作系统中有虚拟机一样,给网卡设备也增加了类似虚拟机的功能,称之为VF,他们相互隔离,共用一个硬件设备。提高网卡的性能。
大概的介绍完毕了,我们来具体的看一看这个结构体:
首先
.name = i40e_driver_name,
其中的name是驱动设备的名称
i40e_driver_name
我们可以点进去。是一个char类型的数组:
char i40e_driver_name[] = "i40e";
也就是驱动程序的名称:i40e。
紧接着第二个:
.id_table = i40e_pci_tbl,
id_table指的是PCI设备的ID表,指向i40e_pci_tbl,用于匹配驱动程序和PCI设备。
详细内容如下:
static const struct pci_device_id i40e_pci_tbl[] = {
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_XL710), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QEMU), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_C), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_5G_BASE_T_BC), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T4), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_BC), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_SFP), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_20G_KR2_A), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_X710_N3000), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_XXV710_N3000), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_KX_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_QSFP_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_1G_BASE_T_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_10G_BASE_T_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_SFP_I_X722), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_B), 0},
{PCI_VDEVICE(INTEL, I40E_DEV_ID_25G_SFP28), 0},
/* required last entry */
{0, }
};
第三个:设备的探针(探测函数)
.probe = i40e_probe,
用于pci设备被发现时调用。
第四个:设备移除函数
.remove = i40e_remove,
指向了i40e_remove函数,用于从操作系统中移除该驱动程序。
其中有个宏:
#ifdef HAVE_CONFIG_HOTPLUG
.remove = __devexit_p(i40e_remove),
#else
.remove = i40e_remove,
#endif
该宏指的是是否支持热插拔,根据宏判断选择执行哪个移除函数。
第五个:关机函数
.shutdown = i40e_shutdown,
指向设备关机函数 i40e_shutdown,在系统关机时调用。
第六个:错误处理函数
.err_handler = &i40e_err_handler,
如果系统支持 PCI 错误恢复(PCI Error Recovery System),则指向错误处理函数 i40e_err_handler。
第七个:虚拟化配置
.sriov_configure = i40e_pci_sriov_configure,
如果系统支持 SR-IOV(Single Root I/O Virtualization),则指向 SR-IOV 配置函数 i40e_pci_sriov_configure。
其中还有个COFIG_PM,这个是电源管理
#ifdef CONFIG_PM
#ifdef USE_LEGACY_PM_SUPPORT
.suspend = i40e_legacy_suspend,
.resume = i40e_legacy_resume,
#else /* USE_LEGACY_PM_SUPPORT */
.driver = {
.pm = &i40e_pm_ops,
},
#endif /* !USE_LEGACY_PM_SUPPORT */
#endif /* CONFIG_PM */
如果系统支持电源管理(CONFIG_PM 定义),则指向电源管理操作结构 i40e_pm_ops。这是用于支持设备的挂起和恢复操作。
suspend 和 resume:如果系统不使用遗留的电源管理支持,而是使用新的 PM runtime 框架,那么会使用这两个回调函数。
最后有两个:
#ifdef HAVE_RHEL6_SRIOV_CONFIGURE
.rh_reserved = &i40e_driver_rh,
#endif
#ifdef HAVE_RHEL7_PCI_DRIVER_RH
.pci_driver_rh = &i40e_driver_rh,
#endif
rh_reserved、pci_driver_rh:这些属性可能与 Red Hat Enterprise Linux 特定的配置有关,用于指定一些特殊的配置或回调。这两个我们不作关系。
总体而言,这个结构体描述了 i40e 驱动程序的一些基本信息和操作回调函数,用于注册到 PCI 子系统中,方便后续驱动的执行。
这个结构体一般是用在以下这个函数中:
static int __init i40e_init_module(void)
{
pr_info("%s: %s - version %s\n", i40e_driver_name,
i40e_driver_string, i40e_driver_version_str);
pr_info("%s: %s\n", i40e_driver_name, i40e_copyright);
i40e_wq = alloc_workqueue("%s", 0, 0, i40e_driver_name);
if (!i40e_wq) {
pr_err("%s: Failed to create workqueue\n", i40e_driver_name);
return -ENOMEM;
}
#ifdef HAVE_RHEL7_PCI_DRIVER_RH
#endif
i40e_dbg_init();
return pci_register_driver(&i40e_driver);
}
module_init(i40e_init_module);
其中module_init是个linux内核函数,用来加载模块,加载的模块就是上面 i40e_init_module,这个函数实际意义就是要执行上面的那个结构体。通过调用pci_register_driver来执行。
那么以上就是i40e_driver的一些基本内容分析以及模块注册。
后续我会继续分析710这个驱动,当然主要是跟虚拟化VF有关的代码。