前言
在710的这个专栏里,我上篇文章中主要分析了驱动代码中的注册函数以及注册所需的结构体,其中有很多内容,今天我们围绕i40e_probe这个探测函数进行分析,由于研究原因以及时间原因,对这个驱动代码的分析,还是紧紧围绕虚拟化这个部分来分析,也就是VF。
代码在github上有共享链接在这:i40e 大家如有需要可自行下载。
i40e_probe
该函数位于:i40e_main.c函数下,共计700多行。代码比较多其中包括了配置DCB,配置dma的高低地址,设置pci的链接,设置mac类型,初始化adminq(710自己定义的cmd命令函数),sw初始化,
配置SRIOV,配置二层VLAN等等等等,很多很多内容,我们主要关注就是配置SRIOV。
CONFIG_PCI_IOV
该函数中有关配置虚拟化的主要有以下两个部分:
两部分都是针对SR-IOV(Single Root I/O Virtualization)的支持
第一部分
#ifdef CONFIG_PCI_IOV
/* prep for VF support */
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
!test_bit(__I40E_BAD_EEPROM, pf->state)) {
if (pci_num_vf(pdev))
pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE)
else if (pf->num_req_vfs)
pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
#endif
}
#endif
#ifdef CONFIG_PCI_IOV
这是一个条件编译的预处理指令,用于判断是否启用了 PCI SR-IOV 支持。如果在内核配置中启用了 PCI SR-IOV,那么该段代码将被编译,否则将被忽略。
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
!test_bit(__I40E_BAD_EEPROM, pf->state))
件判断,检查是否满足启用 SR-IOV 的条件。具体条件包括:设备的标志中开启了 SR-IOV、开启了 MSI-X(Message Signaled Interrupts Extension),并且 EEPROM 中没有标记为坏的。
其中MSI-X也是PCIE标准中定义的中断类型,简单来说,pcie中定义了三种中断,分别为INTx中断,MSI中断,MSI-X中断,其中INTx是可选的,MSI/MSI-X是必须实现的。
MSI, message signal interrupt, 是PCI设备通过写一个特定消息到特定地址,从而触发一个CPU中断。MSI-X是对MSI中断的增强和扩展。(后续如果有机会或者有时间我会再开一个专栏分析pcie的标准,其实已经准备一部分了,只是因为时间关系,暂时可能没有时间去跟新PCIE这部分。)
if (pci_num_vf(pdev))
调用 pci_num_vf 函数,检查 PCIe 设备是否支持虚拟函数(VF)。如果支持,将设备标志 I40E_FLAG_VEB_MODE_ENABLED 置位。VF 是 SR-IOV 的一部分,用于创建多个虚拟设备,使得多个虚拟机可以直接访问硬件资源。
其中标志位I40E_FLAG_VEB_MODE_ENABLED 就是个寄存器的位,如果该位使能,则表明开启了VEB。VEB是什么呢?
VEB(Virtual Ethernet Bridge,虚拟以太网交换机)是虚拟机与服务器网络接入层之间的一个新的网络层。解决的是同一服务器中不同虚拟机如何通过同一张物理网卡与外部网络进行通信,以及这些虚拟机之间如何互相通信的问题。
当然上面如果理解不了也没关系,不重要,我们继续进行我们的代码分析即可。
#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE)
这是一个条件编译,当系统没有定义 HAVE_SRIOV_CONFIGURE 和 HAVE_RHEL6_SRIOV_CONFIGURE 时,执行下面的操作。
else if (pf->num_req_vfs)
pf->flags |= I40E_FLAG_VEB_MODE_ENABLED;
如果系统不支持 HAVE_SRIOV_CONFIGURE 和 HAVE_RHEL6_SRIOV_CONFIGURE,则检查是否有请求的 VF 数量大于零。如果是,同样将设备标志 I40E_FLAG_VEB_MODE_ENABLED 置位。这里的 num_req_vfs 表示期望启用的 VF 的数量。
那么以上就是第一部分,总体而言就是通过一系列的判断,如果都满足要求,就将寄存器中veb 模式的位置1,表示启用veb启用虚拟化vf。
第二部分
#ifdef CONFIG_PCI_IOV
/* prep for VF support */
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
!test_bit(__I40E_BAD_EEPROM, pf->state)) {
/* disable link interrupts for VFs */
val = rd32(hw, I40E_PFGEN_PORTMDIO_NUM);
val &= ~I40E_PFGEN_PORTMDIO_NUM_VFLINK_STAT_ENA_MASK;
wr32(hw, I40E_PFGEN_PORTMDIO_NUM, val);
i40e_flush(hw);
/* allocate memory for VFs mac list backup */
val = max_t(int, pci_num_vf(pdev), pf->num_req_vfs);
if (val) {
u32 index;
pf->mac_list = kcalloc(val,
sizeof(struct list_head),
GFP_KERNEL);
if (!pf->mac_list)
goto err_vsis;
for (index = 0; index < val; index++)
INIT_LIST_HEAD(&pf->mac_list[index]);
}
if (pci_num_vf(pdev)) {
dev_info(&pdev->dev,
"Active VFs found, allocating resources.\n");
err = i40e_alloc_vfs(pf, pci_num_vf(pdev));
if (err)
dev_info(&pdev->dev,
"Error %d allocating resources for existing VFs\n",
err);
#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE)
} else if (pf->num_req_vfs) {
err = i40e_alloc_vfs(pf, pf->num_req_vfs);
if (err) {
pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
dev_info(&pdev->dev,
"failed to alloc vfs: %d\n", err);
}
#endif /* HAVE_SRIOV_CONFIGURE */
}
}
#endif /* CONFIG_PCI_IOV */
先判断是否有宏CONFIG_PCI_IOV,有的话就继续执行下面的代码:
这部分跟我们上面分析第一部分的开头是一样的,不再过多赘述。
if ((pf->flags & I40E_FLAG_SRIOV_ENABLED) &&
(pf->flags & I40E_FLAG_MSIX_ENABLED) &&
!test_bit(__I40E_BAD_EEPROM, pf->state)) {
接下来:
val = rd32(hw, I40E_PFGEN_PORTMDIO_NUM);
val &= ~I40E_PFGEN_PORTMDIO_NUM_VFLINK_STAT_ENA_MASK;
wr32(hw, I40E_PFGEN_PORTMDIO_NUM, val);
i40e_flush(hw);
通过读取和修改寄存器的方式,禁用 VF 的链路中断。这是因为 VF 是虚拟设备,不需要链路状态的中断。然后调用函数i40e_flush(hw)刷新硬件。
val = max_t(int, pci_num_vf(pdev), pf->num_req_vfs);
if (val) {
u32 index;
pf->mac_list = kcalloc(val,
sizeof(struct list_head),
GFP_KERNEL);
if (!pf->mac_list)
goto err_vsis;
for (index = 0; index < val; index++)
INIT_LIST_HEAD(&pf->mac_list[index]);
}
为 VF 的 MAC 地址列表备份分配内存。根据 VF 的数量(val),使用 kcalloc 分配一个数组,每个元素是一个 struct list_head。这个数组被用于保存 VF 的 MAC 地址列表。
if (pci_num_vf(pdev)) {
dev_info(&pdev->dev,
"Active VFs found, allocating resources.\n");
err = i40e_alloc_vfs(pf, pci_num_vf(pdev));
if (err)
dev_info(&pdev->dev,
"Error %d allocating resources for existing VFs\n",
err);
检查 PCIe 设备是否支持 VF。如果支持,则输出调试信息,表示发现了活动的 VF,然后调用 i40e_alloc_vfs 分配 VF 的资源。
#if !defined(HAVE_SRIOV_CONFIGURE) && !defined(HAVE_RHEL6_SRIOV_CONFIGURE)
} else if (pf->num_req_vfs) {
err = i40e_alloc_vfs(pf, pf->num_req_vfs);
if (err) {
pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
dev_info(&pdev->dev,
"failed to alloc vfs: %d\n", err);
}
如果系统不支持 HAVE_SRIOV_CONFIGURE 和 HAVE_RHEL6_SRIOV_CONFIGURE,检查是否有请求的 VF 数量大于零。如果是,调用 i40e_alloc_vfs 分配 VF 的资源。
pf->flags &= ~I40E_FLAG_SRIOV_ENABLED;
如果分配 VF 的资源失败,清除设备该标志位 ,表示禁用SRIOV。
最后附上该函数中有关sriov的执行流程图:
标签:Intel710,I40E,VF,probe,i40e,FLAG,num,pf,SRIOV From: https://blog.51cto.com/u_16160587/8740148