SR-IOV虚拟化简解

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: 不管风吹浪打,胜似闲庭信步, 加油, 朋友们!!!

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

来杯清咖_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值