SR-IOV逻辑梳理

PCIE端口虚拟化

SR-IOV 全称 Single Root I/O Virtualization ,是 Intel 在 2007年提出的一种基于硬件的虚拟化解决方案。
在虚拟机中,一切皆虚拟,比如网卡,虚拟机看起来好像有一个真实网卡,但是这个网卡是宿主机虚拟出来的硬件,没有真实的硬件;
在虚拟化场景中,CPU 与内存是最先解决的,但是 I/O 设备一直没有很好的解决办法,Intel 有 VT-d(Virtualization Technology for Directed I/O)可以将物理服务器的 PCIe 设备直接提供给虚拟机使用,也就是我们常说的“直通”(passthrough),但是直通面临一个问题是 PCIe 设备只能给一个虚拟机使用,其他虚拟机就只能干瞪眼,这肯定是不行的,所以有了 SR-IOV,一个物理设备可以虚拟出多个虚拟设备给虚拟机使用。
SR-IOV 是一种规范,使得单根端口下的单个快速外围组件互连 (PCIe) 物理设备显示为管理程序或客户机操作系统的多个单独的物理设备,既有直通设备的性能优势,又可以支持多个虚拟机,一举两得。

SR-IOV是虚拟化的一个重要功能。启用SR-IOV的这个功能,将大大减轻宿主机的CPU负荷,提高网络性能,降低网络延时等。

UEFI下SR-IOV逻辑

SR-IOV分配总线

默认大家对PCIE扫描逻辑都很清楚, 假设一张支持虚拟化网卡设备B挂载在P2P桥A下:
当我们PciScan到桥A时, 发现存在设备, 并给下游设备分配总线号, 然后去扫描对应总线(当然我们最初扫描是从Bus0开始), 当扫描这条总线时确认挂载的是设备B, 然后扫描这个设备(假设是多功能2(MULT_FUNC)设备), 我们在扫描第一个function后, 发现SrIovCapabilityOffset(来自设备的扩展配置空间)存在数据, 就说明该设备支持SR-IOV(网卡),

检测是否支持SR-IOV,为VF预留总线号: 当给IOV设备预留出一条总线后, 将总线号给到Pci桥的Header的Subordinate Bus Number, 用来记录下属的最后一条总线号.

 1270         //                                                                                      1271         // It is device. Check PCI IOV for Bus reservation                                      1272         // Go through each function, just reserve the MAX ReservedBusNum for one device         1273         //                                                                                      1274         if (PcdGetBool (PcdSrIovSupport) && PciDevice->SrIovCapabilityOffset != 0) {            1275           if (TempReservedBusNum < PciDevice->ReservedBusNum) {                                 1276                                                                                                 1277             Status = PciAllocateBusNumber (PciDevice, *SubBusNumber, (UINT8) (PciDevice-              >ReservedBusNum - TempReservedBusNum), SubBusNumber);                                           1278             if (EFI_ERROR (Status)) {                                                           1279               return Status;                                                                    1280             }                                                                                   1281             TempReservedBusNum = PciDevice->ReservedBusNum;                                     1282                                                                                                 1283             if (Func == 0) {                                                                    1284               DEBUG ((EFI_D_INFO, "PCI-IOV ScanBus - SubBusNumber - 0x%x\n", *SubBusNumber));   1285             } else {                                                                            1286               DEBUG ((EFI_D_INFO, "PCI-IOV ScanBus - SubBusNumber - 0x%x (Update)\n", *               SubBusNumber));                                                                                 1287             }                                                                                   1288           }                                                                                     1289         }

当然在扫描Function时发现支持SR-IOV, 会将VFBAR的Offset(来自于扩展配置空间)也扫描出来, 然后填写到维护的Device数据结构中, 包括VfBar类型与大小等:

  533   //                                                                                            534   // Parse the SR-IOV VF bars                                                                   535   //                                                                                            536   if (PcdGetBool (PcdSrIovSupport) && PciIoDevice->SrIovCapabilityOffset != 0) {                537     for (Offset = PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR0,             BarIndex = 0;                                                                                   538          Offset <= PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_BAR5;      539          BarIndex++) {                                                                          540                                                                                                 541       ASSERT (BarIndex < PCI_MAX_BAR);                                                          542       Offset = PciIovParseVfBar (PciIoDevice, Offset, BarIndex);                                543     }                                                                                           544   }

SR-IOV Initialization

关于SR-IOV的Capability的初始化操作如下:

 402 #define EFI_PCIE_CAPABILITY_ID_SRIOV_CAPABILITIES               0x04                                                           403 #define EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL                    0x08                                                           404 #define EFI_PCIE_CAPABILITY_ID_SRIOV_STATUS                     0x0A                                                           405 #define EFI_PCIE_CAPABILITY_ID_SRIOV_INITIALVFS                 0x0C                                                           406 #define EFI_PCIE_CAPABILITY_ID_SRIOV_TOTALVFS                   0x0E                                                           407 #define EFI_PCIE_CAPABILITY_ID_SRIOV_NUMVFS                     0x10                                                           408 #define EFI_PCIE_CAPABILITY_ID_SRIOV_FUNCTION_DEPENDENCY_LINK   0x12                                                           409 #define EFI_PCIE_CAPABILITY_ID_SRIOV_FIRSTVF                    0x14                                                           410 #define EFI_PCIE_CAPABILITY_ID_SRIOV_VFSTRIDE                   0x16                                                           411 #define EFI_PCIE_CAPABILITY_ID_SRIOV_VFDEVICEID                 0x1A                                                           412 #define EFI_PCIE_CAPABILITY_ID_SRIOV_SUPPORTED_PAGE_SIZE        0x1C                                                           413 #define EFI_PCIE_CAPABILITY_ID_SRIOV_SYSTEM_PAGE_SIZE           0x20                                                           414 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR0                       0x24                                                           415 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR1                       0x28                                                           416 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR2                       0x2C                                                           417 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR3                       0x30                                                           418 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR4                       0x34                                                           419 #define EFI_PCIE_CAPABILITY_ID_SRIOV_BAR5                       0x38                                                           420 #define EFI_PCIE_CAPABILITY_ID_SRIOV_VF_MIGRATION_STATE         0x3C 2253   //                                                                                                                          2254   // Initialization for SR-IOV                                                                                                2255   //                                                                                                                          2256                                                                                                                               2257   if (PcdGetBool (PcdSrIovSupport)) {                                                                                         2258     Status = LocatePciExpressCapabilityRegBlock (                                                                             2259                PciIoDevice,                                                                                                   2260                EFI_PCIE_CAPABILITY_ID_SRIOV,                                                                                  2261                &PciIoDevice->SrIovCapabilityOffset,                                                                           2262                NULL                                                                                                           2263                );                                                                                                             2264     if (!EFI_ERROR (Status)) {                                                                                                2265       UINT32    SupportedPageSize;                                                                                            2266       UINT16    VFStride;                                                                                                     2267       UINT16    FirstVFOffset;                                                                                                2268       UINT16    Data16;                                                                                                       2269       UINT32    PFRid;                                                                                                        2270       UINT32    LastVF;                                                                                                       2271                                                                                                                               2272       //                                                                                                                      2273       // If the SR-IOV device is an ARI device, then Set ARI Capable Hierarchy for the device.                                2274       //                                                                                                                      2275       if (PcdGetBool (PcdAriSupport) && PciIoDevice->AriCapabilityOffset != 0) {                                              2276         PciIo->Pci.Read (                                                                                                     2277                      PciIo,                                                                                                   2278                      EfiPciIoWidthUint16,                                                                                     2279                      PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL,                               2280                      1,                                                                                                       2281                      &Data16                                                                                                  2282                      );                                                                                                       2283         Data16 |= EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL_ARI_HIERARCHY;                                                         2284         PciIo->Pci.Write (                                                                                                    2285                      PciIo,                                                                                                   2286                      EfiPciIoWidthUint16,                                                                                     2287                      PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_CONTROL,                               2288                      1,                                                                                                       2289                      &Data16                                                                                                  2290                      );                                                                                                       2291       }                                                                                                                       2292                                                                                                                               2293       //                                                                                                                      2294       // Calculate SystemPageSize                                                                                             2295       //                                                                                                                      2296                                                                                                                               2297       PciIo->Pci.Read (                                                                                                       2298                    PciIo,                                                                                                     2299                    EfiPciIoWidthUint32,                                                                                       2300                    PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SUPPORTED_PAGE_SIZE,                     2301                    1,                                                                                                         2302                    &SupportedPageSize                                                                                         2303                    );                                                                                                         2304       PciIoDevice->SystemPageSize = (PcdGet32 (PcdSrIovSystemPageSize) & SupportedPageSize);                                  2305       ASSERT (PciIoDevice->SystemPageSize != 0);                                                                              2306                                                                                                                                2307       PciIo->Pci.Write (                                                                                                      2308                    PciIo,                                                                                                     2309                    EfiPciIoWidthUint32,                                                                                       2310                    PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_SYSTEM_PAGE_SIZE,                        2311                    1,                                                                                                         2312                    &PciIoDevice->SystemPageSize                                                                               2313                    );                                                                                                         2314       //                                                                                                                      2315       // Adjust SystemPageSize for Alignment usage later                                                                      2316       //                                                                                                                      2317       PciIoDevice->SystemPageSize <<= 12;                                                                                     2318                                                                                                                               2319       //                                                                                                                      2320       // Calculate BusReservation for PCI IOV                                                                                 2321       //                                                                                                                      2322                                                                                                                               2323       //                                                                                                                      2324       // Read First FirstVFOffset, InitialVFs, and VFStride                                                                   2325       //                                                                                                                      2326       PciIo->Pci.Read (                                                                                                       2327                    PciIo,                                                                                                     2328                    EfiPciIoWidthUint16,                                                                                       2329                    PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_FIRSTVF,                                 2330                    1,                                                                                                         2331                    &FirstVFOffset                                                                                             2332                    );                                                                                                         2333       PciIo->Pci.Read (                                                                                                       2334                    PciIo,                                                                                                     2335                    EfiPciIoWidthUint16,                                                                                       2336                    PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_INITIALVFS,                              2337                    1,                                                                                                         2338                    &PciIoDevice->InitialVFs                                                                                   2339                    );                                                                                                         2340       PciIo->Pci.Read (                                                                                                       2341                    PciIo,                                                                                                     2342                    EfiPciIoWidthUint16,                                                                                       2343                    PciIoDevice->SrIovCapabilityOffset + EFI_PCIE_CAPABILITY_ID_SRIOV_VFSTRIDE,                                2344                    1,                                                                                                         2345                    &VFStride                                                                                                  2346                    );                                                                                                         2347       //                                                                                                                      2348       // Calculate LastVF                                                                                                     2349       //                                                                                                                      2350       PFRid = EFI_PCI_RID(Bus, Device, Func);                                                                                 2351       LastVF = PFRid + FirstVFOffset + (PciIoDevice->InitialVFs - 1) * VFStride;                                              2352                                                                                                                               2353       //                                                                                                                      2354       // Calculate ReservedBusNum for this PF                                                                                 2355       //                                                                                                                      2356       PciIoDevice->ReservedBusNum = (UINT16)(EFI_PCI_BUS_OF_RID (LastVF) - Bus + 1);                                          2357      2358       DEBUG ((                                                                                                                2359         EFI_D_INFO,                                                                                                           2360         " SR-IOV: SupportedPageSize = 0x%x; SystemPageSize = 0x%x; FirstVFOffset = 0x%x;\n",                                  2361         SupportedPageSize, PciIoDevice->SystemPageSize >> 12, FirstVFOffset                                                   2362         ));                                                                                                                   2363       DEBUG ((                                                                                                                2364         EFI_D_INFO,                                                                                                           2365         "         InitialVFs = 0x%x; ReservedBusNum = 0x%x; CapOffset = 0x%x\n",                                              2366         PciIoDevice->InitialVFs, PciIoDevice->ReservedBusNum, PciIoDevice->SrIovCapabilityOffset                              2367         ));                                                                                                                   2368     }                                                                                                                         2369   }
  • 以上填写修改的主要有: SystemPageSize; (FirstVFOffset; InitialVFs; VFStride)->ReservedBusNum: 计算需要预留的Bus号, 以备内核可以正常分配正常大小个数的虚拟设备.

关于PciScan逻辑可点击查看Segment的实现中PciScan的扫描.

内核下SR-IOV逻辑

目录: drivers/pci/iov.c

Linux系统下开启SR-IOV

  • echo 4 > /sys/bus/pci/devices/0000:04:00.0/sriov_numvfs(其中04为网卡设备物理总线):
  • lspci查看到Bus5为虚拟网卡, echo 4 为虚拟出4个Function, 其中ixgbevf为Intel网卡的虚拟万兆网卡的驱动,需要我们内核编译时添加(可参考链接内核编译):
  • ifconfig 查看虚拟网口:

内核逻辑

在Linux系统使用下我们知道了具体使用操作, 就是向sriov_numvfs 中添加虚拟网卡个数, 其中会调用内核逻辑:

 803 /**                                                                                                                                                                                                             804  * pci_sriov_set_totalvfs -- reduce the TotalVFs available                                                                                                                                                      805  * @dev: the PCI PF device                                                                                                                                                                                      806  * @numvfs: number that should be used for TotalVFs supported                                                                                                                                                   807  *                                                                                                                                                                                                              808  * Should be called from PF driver's probe routine with                                                                                                                                                         809  * device's mutex held.                                                                                                                                                                                         810  *                                                                                                                                                                                                              811  * Returns 0 if PF is an SRIOV-capable device and                                                                                                                                                               812  * value of numvfs valid. If not a PF return -ENOSYS;                                                                                                                                                           813  * if numvfs is invalid return -EINVAL;                                                                                                                                                                         814  * if VFs already enabled, return -EBUSY.                                                                                                                                                                       815  */                                                                                                                                                                                                             816 int pci_sriov_set_totalvfs(struct pci_dev *dev, u16 numvfs)                                                                                                                                                     817 {                                                                                                                                                                                                               818   if (!dev->is_physfn)                                                                                                                                                                                          819     return -ENOSYS;                                                                                                                                                                                             820                                                                                                                                                                                                                 821   if (numvfs > dev->sriov->total_VFs)                                                                                                                                                                           822     return -EINVAL;                                                                                                                                                                                             823                                                                                                                                                                                                                 824   /* Shouldn't change if VFs already enabled */                                                                                                                                                                 825   if (dev->sriov->ctrl & PCI_SRIOV_CTRL_VFE)                                                                                                                                                                    826     return -EBUSY;                                                                                                                                                                                              827                                                                                                                                                                                                                 828   dev->sriov->driver_max_VFs = numvfs;                                                                                                                                                                          829   return 0;                                                                                                                                                                                                     830 }                                                                                                                                                                                                               831 EXPORT_SYMBOL_GPL(pci_sriov_set_totalvfs);                                                                                                                                                                      832

其中计算虚拟设备的Bus与Devfunc的逻辑与固件类似:

  21 int pci_iov_virtfn_bus(struct pci_dev *dev, int vf_id)                                                                                                                                                          22 {                                                                                                                                                                                                               23   if (!dev->is_physfn)                                                                                                                                                                                          24     return -EINVAL;                                                                                                                                                                                             25   return dev->bus->number + ((dev->devfn + dev->sriov->offset +                                                                                                                                                 26             dev->sriov->stride * vf_id) >> 8);                                                                                                                                                                  27 }                                                                                                                                                                                                               28                                                                                                                                                                                                                 29 int pci_iov_virtfn_devfn(struct pci_dev *dev, int vf_id)                                                                                                                                                        30 {                                                                                                                                                                                                               31   if (!dev->is_physfn)                                                                                                                                                                                          32     return -EINVAL;                                                                                                                                                                                             33   return (dev->devfn + dev->sriov->offset +                                                                                                                                                                     34     dev->sriov->stride * vf_id) & 0xff;                                                                                                                                                                         35 }                                                                                                                                                                                                               36

最终: 内核会给每一个虚拟设备都分配一个配置空间, 这样在软件层次我们就可以分别访问,认为每个设备都是独立存在的, 互不影响.

Tip: 不管风吹浪打,胜似闲庭信步, 加油, 朋友们!!!

SR-IOV虚拟化简解相关推荐

  1. 最新版freetextbox(版本3.1.6)在asp.net 2.0中使用简解

    最新版freetextbox(版本3.1.6)在asp.net 2.0中使用简解 2008-10-14 12:21 简介:对于FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用,只 ...

  2. 最新版FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用简解(提供博客园本地下载)

    来源:cleocn.com 最新版FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用简解(提供博客园本地下载) 简介:对于FreeTextBox(版本3.1.6)在ASP.Net ...

  3. 爬虫5-BeautifulSoup模块简解2

    1.BeautifulSoup简解2 from bs4 import BeautifulSoup import re file = open("./baidu.html",'rb' ...

  4. github-markdown-css 使用简解,markdown文案格式优化(笔记)

    github-markdown-css 使用简解,markdown文案格式优化 1.npm 安装 $ npm install github-markdown-css 2.使用 导入github-mar ...

  5. Cisco AP1240多SSID配置简解

    Cisco AP1240多SSID配置简解 AP1#show run <?xml:namespace prefix = o ns = "urn:schemas-microsoft-co ...

  6. 0904 SRTP SR 包文详解

    0904 SRTP SR 包文详解 RC(report content) 指整个SR包文的Receive Report Block 块的个数 SSRC of sender 发送者的SRC,以便让接收者 ...

  7. KVM虚拟化详解以及如何创建KVM虚拟机

    一.何为虚拟化 虚拟化是云计算的基础,是通过虚拟化技术将一台计算机虚拟为多台逻辑计算机,在一台计算机上同时运行多个逻辑计算机,同时每个逻辑计算机可运行不同的操作系统,应用程序都可以在相互独立的空间内运 ...

  8. (转)FFMPEG解码H264拼帧简解

    http://blog.csdn.net/ikevin/article/details/7649095 H264的I帧通常 0x00 0x00 0x00 0x01 0x67 开始,到下一个帧头开始之前 ...

  9. QuickSort简解(分治思想) By ACReaper

    快排又称为快速排序算法 快熟排序主要思想为:递归而分,重于分而治,而简与合. 治: int partition(int A[],int st,int ed){int key = A[st];//把第一 ...

最新文章

  1. 谁偷走了程序员的时间??
  2. HTTP状态码--含义
  3. 大牛书单 | 搜索大牛都读什么书?
  4. 谈谈HashMap线程不安全的体现
  5. 信息图:程序员/开发人员实际在用哪些工具
  6. 解决:关于Git无法提交 index.lock File exists的问题
  7. vscode 头文件包含问题_使用clangd替代c/c++配置vscode c++项目
  8. windows 10右键项添加Notepad++ 和插件管理
  9. python获取网页元素坐标_html网页元素在屏幕上的坐标获取
  10. MongoDB的角色作用(2)
  11. Sql注入基础_mysql注入
  12. 环信即时通讯在工程中的安装——Nusen_Liu
  13. 极客大学产品经理训练营 产品思维和产品意识(下) 第5课总结
  14. java冒泡排序计算学生学号_java冒泡法排序
  15. 文件上传利器SWFUpload入门简易教程(转)
  16. function函数
  17. 阅兵方阵 蓝桥杯 第九届JavaA
  18. linux系统中ulimit命查看/设置堆栈空间大小
  19. CentOS后门入侵检测工具
  20. BH1750光照传感器

热门文章

  1. LTE关键技术之二:多天线技术
  2. MySQL--学习笔记
  3. 知识图谱本体建模工具Protege使用教程
  4. W ndows7怎么进入BlOS,Windows7旗舰版进不了bios界面的详解教程
  5. 计算机基础算术加法,计算机基础第二讲.ppt
  6. DevOps相关知识点
  7. bugku 你必须让他停下
  8. SqlNullValueException: Data is Null. This method or property cannot be called on Null values.
  9. Android 图文混排 异步加载图片
  10. 【机器学习】特征编码OneHot与Dummy的区别与联系