- 第三章:SR-IOV
- 9.0 SR-IOV的基础知识
- 9.1 SR-IOV总览
- 9.2 SR-IOV初始化和资源分配
- 9.3 Configuration(重点)
- 9.3.1 SR-IOV Configuration Overview
- 9.3.2 Configuration Space
- 9.3.3 SR-IOV Extended Capability (重点关注)
- 9.3.3.1 SR-IOV Extended Capability Header __(Offset 00h)
- 9.3.3.2 SR-IOV Capabilities Register __(04h)
- 9.3.3.3 SR-IOV Control Register __(Offset 08h)
- 9.3.3.4 SR-IOV Status Register __(Offset 0Ah)
- 9.3.3.5 InitialVFs __(Offset 0Ch)
- 9.3.3.6 TotalVFs __(Offset 0Eh)
- 9.3.3.7 NumVFs __(Offset 10h)
- 9.3.3.8 Function Dependency Link __(Offset 12h)
- 9.3.3.9 First VF Offset __(Offset 14h)
- 9.3.3.10 VF Stride __(Offset 16h)
- 9.3.3.11 VF Device ID __(Offset 1Ah)
- 9.3.3.12 Supported Page Sizes __(Offset 1Ch)
- 9.3.3.13 System Page Size __(Offset 20h)
- 9.3.3.14 VF BARn
- 9.3.3.15 VF Migration State Array Offset __(Offset 3Ch)-暂不关注
- 9.3 SR-IOV Interrupts
- 代码
本章相关参考:
- 《PCI+Express体系结构》13.3章
- PcieTech1-3篇
- 《PCI Express Base_r5_1》9章
- PCI入门-第七章
- SR-IOV及linux驱动浅析
下载方式:
链接:https://pan.baidu.com/s/1zzWWt9ujVTr9oJSaJNP_mA
提取码:ahax
本章要解决得问题:
- SR-IOV是什么,Spec怎么描述得,在代码中得使用
注:本文不涉及到如何开启SR-IOV,只研究下支持SR-IOV的实现原理和配置
第三章:SR-IOV
注:为什么下边章节以9.x开始呢,因为 在pcie base r5中关于SR-IOV的描述在第九章,便于对应。
因为SR-IOV的知识比较新和好玩,文章主要描述了PCIE的SR-IOV软件支持。
主要参考:PCIE 5.0规范:PCI Express Base_r5_1.pdf 第九章
本文主要是为了后期的SR-IOV真实场景使用。
SR-IOV的技术的主要作用: 将一个物理的PCIE设备模拟成多个虚拟设备,其中每一个虚拟设备可以和指定的虚拟机绑定,从而便于多个虚拟机同时访问同一个物理PCIE设备。
支持SR-IOV的设备由多个物理子设备PF,且每一个物理子设备PF支持多个虚拟子设备VF。
其中,每个PF都有自己的唯一的配置空间,且PF关联的VF组共享这个配置空间。但每一个VF都有自己独立的BAR空间。(问题:VF和PF共享配置空间??? 那么VF的Vendor ID和Devcice ID之类的怎么来? 没描述清楚。)
9.0 SR-IOV的基础知识
9.0.1 PCIE的扩展空间
首先,PCIE支持0x100-0xFFF的配置空间(总共4K),SR-IOV的配置就在这个扩展配置空间中。
这玩意和0x40-0xFF一样,配置组成了个单向链表。如果这里边有 实际意义的配置,那么0x100-链表头必须在;
注:如果PCIE不支持这个扩展空间,那么0x100指向的结构,Capability ID=0xFFFF,所以软件端通过这种方式,判断是否支持扩展空间。 如下边代码:
pci_setup_device
dev->cfg_size = pci_cfg_space_size(dev);
if (pci_is_pcie(dev))
return pci_cfg_space_size_ext(dev);
/*So we try
* reading the dword at 0x100 which must either be 0 or a valid extended
* 尝试去读下0x100字节,判断是否正确返回
*/
pci_cfg_space_size_ext
if (pci_read_config_dword(dev, pos, &status) != PCIBIOS_SUCCESSFUL)
return PCI_CFG_SPACE_SIZE;
if (status == 0xffffffff || pci_ext_cfg_is_aliased(dev))
return PCI_CFG_SPACE_SIZE;
return PCI_CFG_SPACE_EXP_SIZE;
问题: 在sriov_init 中 我们看到PCI访问扩展配置空间,仍然用的是: pci_read_config_word, 这部分是怎么转到MMCONFIG上的???
9.1 SR-IOV总览
虚拟化下的PCIE
VI(在虚拟化中叫VM,后边统一叫VM)仅拥有底层硬件的所有权。 VM使用了超出本规范范围的各种方法,对硬件进行了抽象处理,为每个SI(后边叫Domain)提供了自己的虚拟系统。
每个Domain可用的实际硬件资源可能会根据工作负载或特定于客户的策略而有所不同。尽管此方法在许多环境中都很好用,但是I / O密集型工作负载可能会导致性能严重下降。 VM必须拦截和处理每个I / O操作,这会增加大量平台资源的开销。
SR-IOV提供了减少这些平台资源开销的工具。 SR-IOV的好处是:
- 消除VM参与主要数据移动动作(DMA,内存空间访问,中断处理等)的能力。消除VM拦截和每个I / O操作的处理可以显着改善应用程序和平台性能
通过SR-PCI管理器(SR-PCIM) 控制SR-IOV资源配置和管理的标准化方法。
注:由于实现的多种多样-系统固件,VM,操作系统,I / O驱动程序等。SR-PCIM中协议不描述。
通过在设备中预配置大量I / O功能来降低硬件要求和相关成本的能力
- 能够将SR-IOV与其他I / O虚拟化技术集成在一起,例如地址转换服务(ATS),地址转换和保护能力(ATPT)技术以及中断重新映射技术,以创建强大而完整的I / O虚拟化解决方案。(下章重点研究)
SR-IOV的组成部分:
- SR-PCIM-负责配置SR-IOV扩展功能,管理物理功能和虚拟功能以及处理相关的错误事件和整体设备控制(例如电源管理和热插拔服务)的软件。
- 可选支持 Optional Translation Agent (TA):
- 可选支持 Optional Address Translation and Protection T able (ATPT):
- 可选支持 Optional Address Translation Cache (ATC)
- 可选支持 Optional Access Control Services (ACS)
- PF:PF支持SR-IOV 扩展空间,有权限访问 SR-PCIM,VMM和客户机
- VF:可被SI直接访问的 light-weight pcie功能
注:与PF关联的所有VF必须具有与PF相同的设备类型(例如,相同的网络设备类型或相同的存储设备类型)。
Optional Translation Agent(TA):TA是硬件或硬件和软件的组合,负责将PCIe事务中的地址转换为关联的平台物理地址。 TA可以包含地址转换缓存(ATC),以加速转换表的访问。 TA还可以支持地址转换服务(ATS),该地址转换服务使PCIe功能能够获得对相关存储器进行DMA访问的先验地址转换。 ATS下一章研究。
Optional Address Translation and Protection T able (ATPT):ATPT包含由TA访问以处理PCIe请求(DMA读取,DMA写入或中断请求)的地址转换集。 详情参考ATS。
Optional Address Translation Cache (ATC):ATC可以存在于平台内的两个位置-可以集成在RC中或位于RC上方的TA中 , 也可以存在于PCIe设备内。 在RC中,ATC可以加快翻译查找的速度。 在设备内,通过ATS技术填充ATC。
指示其包含已转换地址的PCIe事务可能会绕过平台的ATC,以提高性能而不会损害与ATPT技术相关的利益。 详情参考ATS。
Optional Access Control Services (ACS): ACS在PCI Express拓扑中定义了一组控制点,以确定是否应正常路由,阻止或重定向TLP。 在支持SR-IOV的系统中,ACS可用于防止分配给VI或不同SI的设备功能相互通信或与对等设备通信。 重定向可以允许 TA 在做出对等转发决定之前转换上游内存TLP地址。 可选的ACS P2P出口控制可以提供选择性阻止。 ACS需与ATS进行交互。
PCIe设备与具有PCIe SR-IOV的设备进行对比:
Multi-PCIE Device:
PCIE设备共用一路PCIe Link,被Func0管理,最多支持256个Functions。使用 Alternative Routing Identifier(ARI)路由寻址。
SR-IOV Single Capable Device:
每个VF与PF共享许多公共配置空间字段。(即,这些字段适用于所有VF并通过单个PF控制。共享减少了实现每个VF的硬件资源要求。)
- VF使用与PF相同的配置机制和type-header类型
- 与给定PF关联的所有VF共享一个VF BAR集(请参阅第9.3.3.14节),并共享SR-IOV扩展功能中的一个VF存储器空间使能(MSE)位(请参阅第9.3.3.3.4节),该功能控制访问权限。 VF内存空间。 也就是说,如果VF MSE位为Clear,则禁用为所有VF分配的内存映射空间。
- InitialVFs和TotalVFs字段(请参见第9.3.3.5节和第9.3.3.6节)用于发现可以与PF关联的VF的最大数量。
- 如果不支持 VF migration(MR-IOV),那么InitialVFs=TotalVFs;MR-IOV暂时不考虑。
为每个功能PF和VF分配了唯一的路由ID。 每个PF的路由ID是根据2.2.4.2节构造的。
每个VF的路由ID是使用其关联PF的路由ID和该PF的SR-IOV扩展功能中的字段确定的。
假定所有PCIe和SR-IOV配置访问都是通过 a trusted software component (如VM或SR-PCIM)进行的。
- 内部路由特定于实现。(等于没说)
- 每个VF包含一组非共享的物理资源,这些资源是提供特定于功能的服务所需的,(例如,工作队列,数据缓冲区等资源)Domain 可以直接访问这些资源,而无需VM或SR-PCIM干预
- 尽管存在关于PF操作的许多方法,但常见的使用方法是 使用PF在VM的控制下引导设备或平台 。
一旦配置了SR-IOV扩展功能,就可以将VF分配给各个Domain,则PF将承担更多的监督作用。 例如,PF可用于管理设备特定的功能,例如对每个VF的内部资源分配,对共享资源(例如PCIe链接或功能特定的链接,例如网络或存储链接)的VF仲裁等。
暂时了解下,Multi-PCIE的SR-IOV
**
9.2 SR-IOV初始化和资源分配
9.2.1 SR-IOV Resource Discovery
本章 描述软件如何发现设备具有SR-IOV功能,随后通过虚拟功能配置空间识别VF资源
9.2.1.1 Configuring SR-IOV Capabilities
pcienablesriov的重点:
PF’s VF Enable bit // bit[0] 使能SR-IOV, SR-IOV Control Register (Offset 08h)
Memory Space Enable // bit[3] 使能SR-IOV存储空间, SR-IOV Control Register __(Offset 08h)
NumVFs (Offset 10h) // 获取 VFs支持的个数。
// SR-IOV初始化代码如下: _
pci_scan_single_device
pci_device_add(dev, bus);
pci_init_capabilities(dev);
pci_iov_init(dev); /* Single Root I/O Virtualization */
pci_iov_init
pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); // SR-IOV在扩展空间的ID=0X10
if (pos)
return sriov_init(dev, pos);
sriov_init
// 1. struct pci_dev *dev->iov的初始化;
// 2. 读取PCI_SRIOV_TOTAL_VF 获取VF个数
sriov_enable
iov->ctrl |= PCI_SRIOV_CTRL_VFE | PCI_SRIOV_CTRL_MSE;
pci_write_config_word(dev, iov->pos + PCI_SRIOV_CTRL, iov->ctrl);
9.2.1.1.1 VF BAR配置
本节介绍如何将VF BAR配置为映射内存空间。 VF不支持I / O空间,因此VF BAR不应指示I / O空间
注:VF BAR只支持Memory Space,不支持I/O Space
System Page Size : 定义系统将用于映射VF的PCIe内存地址的页面大小
VF BAR的行为与普通PCI内存空间BAR相同(请参见第7.5.1.2.1节),不同之处在于VF BAR描述了每个VF的映射范围(aperture 孔径,我觉得映射范围更容易看懂),而PCI BAR描述了单个功能的映射范围。
如果实现了VF BAR中某些位的属性,则受VF可调整大小的BAR扩展功能的影响(请参见9.3.7.5节 VF Resizable BAR Extended Capability)。
如何获取BAR空间的大小?
Spec 7.5.1.2 中的PF Bar和VF Bar获取大小方式一样,写全1即可获取。
问题:64位是否要在两个bar写全1?全写ffffffff之后如何恢复地址?
注:参考《PCIE代码导读》,64-bit需要全部写1,在获取完长度前,需要先获取地址,然后获取长度后,将地址数据写回到BAR中。
**
VF BAR和BAR之间的区别在于:对于每个VF BAR,与第二个和更高VF相关联的存储空间是从第一个VF的起始地址和每一个VF的映射范围确定**(其实就是这几个VF BAR地址连续,next_bar_addr = current_bar_addr + size)**。
与第二个和更高VF相关联的存储空间是从第一个VF的起始地址和映射范围中得出的。 对于任何给定的VF v, 对于任何已实现的BAR b),其内存空间孔径的起始地址均根据以下公式计算:
BARb _VF_v _starting address = VF BAR_b + (v - 1) x (VF BARb aperture size)
问题:是否每一个VF的映射大小都一致??? 是的,参考后边9.3.14部分,有这部分描述
VF BAR 映射大小取决于 by the usual BAR probing algorithm- 9.3.3.14
**
https://blog.csdn.net/hx_op/article/details/104029386 简单描述下系统上电资源分配和使用过程
9.2.1.2 VF Discover
路由ID和总线分配(简单描述PCIE的路由寻址)
参考:SR-IOV及linux驱动浅析
首先回顾一下Routing ID,Routing ID就是BDF number,即采用Bus Number、Device Number和Function Number来确定目标设备的位置的id。
SR-IOV Extended Capability中用FirstVF Offset和VF Stride来标记VF的Routing ID。VF的Routing ID是以PF的Routing ID值为参考来计算的。
- FirstVF Offset:第一个VF相对PF的Routing ID的偏移量
- VF Stride: 相邻VF之间的Routing ID的偏移量(步进值)
- PF的Routing ID在PF枚举之后就已经分配好了。PF的驱动程序通过配置SR-IOV Extended Capability,打开这个PF关联的VF之后,通过FirstVF Offset和VF Stride就能计算出VF们的Routing ID
pci_iov_add_virtfn
pci_iov_virtfn_bus(dev, id); //获取virt得总线号,是pf得总线号+步进+偏移
int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)
{
if (!dev->is_physfn)
return -EINVAL;
return dev->bus->number + ((dev->devfn + dev->sriov->offset +
dev->sriov->stride * vf_id) >> 8); // x>>8 == x/256,因为每个bus下256个号
}
int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)
{
if (!dev->is_physfn)
return -EINVAL;
return (dev->devfn + dev->sriov->offset +
dev->sriov->stride * vf_id) & 0xff;
}
9.3 Configuration(重点)
9.3.1 SR-IOV Configuration Overview
PF肯定要支持SR-IOV Extended Capability配置。
PF用于发现,配置和管理与PF相关的VF,以及用于本规范中描述的其他内容。
9.3.2 Configuration Space
支持SR-IOV的PF必须实现以下各节中定义的SR-IOV扩展功能。
VF必须实现以下部分中定义的配置空间字段和功能。
9.3.3 SR-IOV Extended Capability (重点关注)
下边这个Capability结构描述PF的SR-IOV功能
注:Multi-Function Devices,每一个PF都需要实现自己内部的SR-IOV Capability结构。(有的设备可以例化出多个PF,每个PF都有自己的VF)
对应软件中的SR-IOV描述字段:
/* Single Root I/O Virtualization */
struct pci_sriov {
int pos; /* Capability position */
int nres; /* Number of resources */
u32 cap; /* SR-IOV Capabilities */
u16 ctrl; /* SR-IOV Control */
u16 total_VFs; /* Total VFs associated with the PF */
u16 initial_VFs; /* Initial VFs associated with the PF */
u16 num_VFs; /* Number of VFs available */
u16 offset; /* First VF Routing ID offset */
u16 stride; /* Following VF stride */
u16 vf_device; /* VF device ID */
u32 pgsz; /* Page size for BAR alignment */
u8 link; /* Function Dependency Link */
u8 max_VF_buses; /* Max buses consumed by VFs */
u16 driver_max_VFs; /* Max num VFs driver supports */
struct pci_dev *dev; /* Lowest numbered PF */
struct pci_dev *self; /* This PF */
u32 class; /* VF device */
u8 hdr_type; /* VF header type */
u16 subsystem_vendor; /* VF subsystem vendor */
u16 subsystem_device; /* VF subsystem device */
resource_size_t barsz[PCI_SRIOV_NUM_BARS]; /* VF BAR size */
bool drivers_autoprobe; /* Auto probing of VFs by driver */
};
下边会描述每个字段的意思
270: 10 00 01 00 02 00 00 00 00 00 00 00 20 00 20 00
280: 00 00 00 00 00 01 00 01 00 00 cd ab 53 05 00 00
290: 01 00 00 00 0c 00 00 f0 bf 39 00 00 0c 00 00 d0
2a0: bd 39 00 00 08 00 20 e1 08 00 00 e1 00 00 00 00
9.3.3.1 SR-IOV Extended Capability Header __(Offset 00h)
这个要符合扩展空间的通用结构,且均为只读(对软件来说),唯一要注意的是:SR-IOV的Capability ID == 0010H
软件中 通过 pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); 来定位SR-IOV的配置,9.2.1.1 有关于这部分描述
9.3.3.2 SR-IOV Capabilities Register __(04h)
TBD: Migration 关于MR-IOV的后期再看
sriov_enable // 软件好像也就sriov_enable用了下,iov->cap在 sriov_init 读取这个寄存器赋值
// 禁止迁移时,InitialVFs= totalVFs,后边看
if (initial > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
return -EIO;
// 在禁止迁移时,VF启用数量小于最大数
if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
9.3.3.3 SR-IOV Control Register __(Offset 08h)
基本使用方法参考 9.2.1.1,至少需要使能VF Enable 和 VF MSE,VF才可以看到和使能。
Migration 这部分后边再看
sriov_init
if (pci_ari_enabled(dev->bus))
ctrl |= PCI_SRIOV_CTRL_ARI;
pci_init_capabilities
pci_configure_ari 会去对设备进行ARI使能
所以 ARI是否打开看的不是设备,而是上游总线是否支持
9.3.3.4 SR-IOV Status Register __(Offset 0Ah)
VF迁移状态(VF Migration Enabel才有用)-表示MR-PCIM已发出VF迁移或迁移请求。 为了确定事件的原因,软件可以扫描VF状态阵列(9.3.3.15)。 默认值为0b, RW1C属性
9.3.3.5 InitialVFs __(Offset 0Ch)
InitialVF向SR-PCIM指示最初与PF关联的VF的数量。 InitialVFs的最小值为0。
对于SR-IOV来说, InitialVFs=TotalVFs;
对于MR-IOV来说,VF disable时,可通过MR-PCIM修改。暂时不考虑。
sriov_enable // 函数中会判断这个值 必须等于 total_VFs, 9.3.3.2 讲了这部分
pci_read_config_word(dev, iov->pos + PCI_SRIOV_INITIAL_VF, &initial);
// 禁止迁移时,InitialVFs= totalVFs,
if (initial > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (initial != iov->total_VFs)))
return -EIO;
// 在禁止迁移时,VF启用数量小于最大数
if (nr_virtfn < 0 || nr_virtfn > iov->total_VFs ||
(!(iov->cap & PCI_SRIOV_CAP_VFM) && (nr_virtfn > initial)))
return -EINVAL;
9.3.3.6 TotalVFs __(Offset 0Eh)
PF可支持的最大VF数量。
SR-IOV模式,TotalVFs == Initial VFs
9.3.3.7 NumVFs __(Offset 10h)
sriov_enable
pci_iov_set_numvfs
// 软件启用指定个数的VF
pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
9.3.3.8 Function Dependency Link __(Offset 12h)
8bit, 这个字段描述了Function的依赖性;
该字段描述PF之间的依赖性。VF依赖关系与其关联的PF的依赖关系相同。
// TBD ??? 一脸懵逼
9.3.3.9 First VF Offset __(Offset 14h)
First VF Offset 是一个常数: 它定义与包含此功能结构的PF关联的第一个VF的路由ID偏移量。
第一个VF的16位路由ID是通过使用无符号16位算术将该字段的内容添加到包含该字段的PF的路由ID(忽略任何进位)来计算的。
VF不应位于在数字上小于其关联PF的总线号上。
当编号最小的PF的ARI能力层次结构值更改或此PF的NumVFs值更改时,此字段可能会更改值。
注意:如果NumVFs为0,则第一个VF偏移量未使用。如果NumVFs大于0,则第一个VF偏移量不得为零。
这边代码可以看出:NumVFs更改后,VF的Offset和Stride都会更新。
/*
* Per SR-IOV spec sec 3.3.10 and 3.3.11, First VF Offset and VF Stride may
* change when NumVFs changes. ***
*
* Update iov->offset and iov->stride when NumVFs is written.
*/
static inline void pci_iov_set_numvfs(struct pci_dev *dev, int nr_virtfn)
{
struct pci_sriov *iov = dev->sriov;
pci_write_config_word(dev, iov->pos + PCI_SRIOV_NUM_VF, nr_virtfn);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_OFFSET, &iov->offset);
pci_read_config_word(dev, iov->pos + PCI_SRIOV_VF_STRIDE, &iov->stride);
}
TBD:这个计算关系是怎么计算的???参考9.1.2
9.3.3.10 VF Stride __(Offset 16h)
VF Stride为与包含此功能结构的PF关联的所有VF定义从一个VF到下一个VF的路由ID偏移。
下一个VF的16位路由ID是通过使用无符号16位算术将此字段的内容添加到当前VF的路由ID中而忽略任何进位来计算的。
同First VF Offset 一样,修改NumVFs后,软件也需要重新获取下这个值。
// TBD:同样这个关系是怎么计算的???参考9.1.2
注:_First VF Offset 和 VF Stride 对软件都是RO属性的。 lspci -vvv中看到的属性就是这部分
270: 10 00 01 00 02 00 00 00 00 00 00 00 20 00 20 00
280: 00 00 00 00 00 01 00 01 00 00 cd ab 53 05 00 00
290: 01 00 00 00 0c 00 00 f0 bf 39 00 00 0c 00 00 d0
2a0: bd 39 00 00 08 00 20 e1 08 00 00 e1 00 00 00 00
2b0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2c0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2d0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
2e0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
Capabilities: [270 v1] Single Root I/O Virtualization (SR-IOV)
IOVCap: Migration-, Interrupt Message Number: 000
IOVCtl: Enable- Migration- Interrupt- MSE- ARIHierarchy-
IOVSta: Migration-
Initial VFs: 32, Total VFs: 32, Number of VFs: 0, Function Dependency Link: 00
VF offset: 256, stride: 256, Device ID: abcd # device == 0 ??? VF是否可以???
Supported Page Size: 00000553, System Page Size: 00000001
Region 0: Memory at 000039bff0000000 (64-bit, prefetchable)
Region 2: Memory at 000039bdd0000000 (64-bit, prefetchable)
Region 4: Memory at e1200000 (32-bit, prefetchable)
Region 5: Memory at e1000000 (32-bit, prefetchable)
VF Migration: offset: 00000000, BIR: 0
Kernel modules: a7evm
9.3.3.11 VF Device ID __(Offset 1Ah)
就是VF的设备ID。由FPGA 人员分配,系统软件驱动适配。
9.3.3.12 Supported Page Sizes __(Offset 1Ch)
描述PF支持的Page Size = 2(n+12) n==0时,PFs支持4K,8K,64K,256K, 1M和4M页大小。
这个值影响了:VF BAR的资源的最低对齐限制。
注:这个值是按照bit计算得,比如,配置成 111b,那么可以支持4K,8K,16K等。
9.3.3.13 System Page Size __(Offset 20h)
上边描述的是:支持的页大小,这部分描述的是 软件配置系统页大小; default value:00000001h
#define PAGE_SHIFT 12
sriov_init // 中
pci_read_config_dword(dev, pos + PCI_SRIOV_SUP_PGSIZE, &pgsz);
i = PAGE_SHIFT > 12 ? PAGE_SHIFT - 12 : 0; // i==0
pgsz &= ~((1 << i) - 1); // pgsz &= ~0 ==>pgsz值不变
if (!pgsz) // 说明:pgsz默认值不能为0
return -EIO;
pgsz &= ~(pgsz - 1); //
pci_write_config_dword(dev, pos + PCI_SRIOV_SYS_PGSIZE, pgsz);
对于软件来说,这部分要注意的是: 配置System Page Size得时候,VF EN必须为0-Disable。
pgsz默认值不能为0(2),默认值n=1时,8K对齐, 这里就写1就好。
_
9.3.3.14 VF BARn
VF BARn在哪?VF BARn怎么使用?VF BARn的属性
VF BARn的属性:
- 这些字段必须定义VF的基址寄存器(BAR)。 这些字段的行为与普通的PCI BAR相同
- 每个BAR解码的地址空间量应为系统页面大小的整数倍。
- VF BARn只能是32bit或64bit存储域,不能为IO域
VF BARn在哪?
我们看Capability中,只有6个BAR,是否有疑问:只有6个bar,但可以支持很多个VF,这些资源怎么使用?
需要了解下:
首先,每个VF的BARn大小必须一致,且地址连续。
其次,每个VF的BARn的属性也一致。
最后,我们给BAR写入全1的时候,获取的长度是单个VF对应BAR的长度。
sriov_init 扩展空间初始化时
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) { // PCI_SRIOV_NUM_BARS=6
// PF的dev->resource在支持IOV时,长度不止基础BAR空间+ROM空间,还有6个IOV空间
res = &dev->resource[i + PCI_IOV_RESOURCES];
// 读取对应BAR空间,解析,获取资源地址
bar64 = __pci_read_base(dev, pci_bar_unknown, res,
pos + PCI_SRIOV_BAR + i * 4);
iov->barsz[i] = resource_size(res); // 当前BAR空间大小,所有VF BAR空间大小一致
// *** 资源的总大小为:单个BAR空间大小 * 所支持VF的个数
res->end = res->start + resource_size(res) * total - 1;
}
sriov_enable
sriov_add_vfs(dev, initial);
pci_iov_add_virtfn ; // loop num_vfs
for (i = 0; i < num_vfs; i++) { // 启用所支持的num_vfs个数
pci_iov_add_virtfn(dev, i);
}
pci_iov_add_virtfn // 函数创建了VF的struct pci_dev
......
for (i = 0; i < PCI_SRIOV_NUM_BARS; i++) {
res = &dev->resource[i + PCI_IOV_RESOURCES]; // PF结构中关于VF资源描述
// 这个就是初始化时 iov->barsz[i],所有VF在BARn时大小相同,
size = pci_iov_resource_size(dev, i + PCI_IOV_RESOURCES);
// 这就是前边说的:所有VF的BARn地址连续,大小相等。
virtfn->resource[i].start = res->start + size * id
virtfn->resource[i].end = virtfn->resource[i].start + size - 1;
}
9.3.3.15 VF Migration State Array Offset __(Offset 3Ch)-暂不关注
9.3 SR-IOV Interrupts
PF可以实现INTx。 VF不得实现INT x。 如果请求中断资源,PF和VF必须实现MSI或MSI-X或两者。
每个PF和VF必须实现自己的独特中断功能。
Address Range Isolation地址范围隔离
如果为MSI-X Table或MSI-X PBA映射地址空间的BAR也映射了与MSI-X结构不相关的其他可用地址空间,则在其他地址空间中使用的位置(例如,用于CSR的地址)一定不能 与任一MSI-X结构所在的地址共享任何自然对齐的“系统页面大小”地址范围。
MSI-X T能力和MSI-X PBA可以共存于自然对齐的系统页面大小地址范围内,尽管它们不能相互重叠。
代码
pci_scan_child_bus_extend
...
sriov_init->
compute_max_vf_buses
iov->max_VF_buses = busnr; // 这个函数会计算占用的VF buses个数
...
/* Reserve buses for SR-IOV capability */
used_buses = pci_iov_bus_range(bus); // 预留VF的bus个数
max += used_buses;