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: 不管风吹浪打,胜似闲庭信步, 加油, 朋友们!!!
SR-IOV虚拟化简解相关推荐
- 最新版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中使用,只 ...
- 最新版FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用简解(提供博客园本地下载)
来源:cleocn.com 最新版FreeTextBox(版本3.1.6)在ASP.Net 2.0中使用简解(提供博客园本地下载) 简介:对于FreeTextBox(版本3.1.6)在ASP.Net ...
- 爬虫5-BeautifulSoup模块简解2
1.BeautifulSoup简解2 from bs4 import BeautifulSoup import re file = open("./baidu.html",'rb' ...
- github-markdown-css 使用简解,markdown文案格式优化(笔记)
github-markdown-css 使用简解,markdown文案格式优化 1.npm 安装 $ npm install github-markdown-css 2.使用 导入github-mar ...
- Cisco AP1240多SSID配置简解
Cisco AP1240多SSID配置简解 AP1#show run <?xml:namespace prefix = o ns = "urn:schemas-microsoft-co ...
- 0904 SRTP SR 包文详解
0904 SRTP SR 包文详解 RC(report content) 指整个SR包文的Receive Report Block 块的个数 SSRC of sender 发送者的SRC,以便让接收者 ...
- KVM虚拟化详解以及如何创建KVM虚拟机
一.何为虚拟化 虚拟化是云计算的基础,是通过虚拟化技术将一台计算机虚拟为多台逻辑计算机,在一台计算机上同时运行多个逻辑计算机,同时每个逻辑计算机可运行不同的操作系统,应用程序都可以在相互独立的空间内运 ...
- (转)FFMPEG解码H264拼帧简解
http://blog.csdn.net/ikevin/article/details/7649095 H264的I帧通常 0x00 0x00 0x00 0x01 0x67 开始,到下一个帧头开始之前 ...
- QuickSort简解(分治思想) By ACReaper
快排又称为快速排序算法 快熟排序主要思想为:递归而分,重于分而治,而简与合. 治: int partition(int A[],int st,int ed){int key = A[st];//把第一 ...
最新文章
- 谁偷走了程序员的时间??
- HTTP状态码--含义
- 大牛书单 | 搜索大牛都读什么书?
- 谈谈HashMap线程不安全的体现
- 信息图:程序员/开发人员实际在用哪些工具
- 解决:关于Git无法提交 index.lock File exists的问题
- vscode 头文件包含问题_使用clangd替代c/c++配置vscode c++项目
- windows 10右键项添加Notepad++ 和插件管理
- python获取网页元素坐标_html网页元素在屏幕上的坐标获取
- MongoDB的角色作用(2)
- Sql注入基础_mysql注入
- 环信即时通讯在工程中的安装——Nusen_Liu
- 极客大学产品经理训练营 产品思维和产品意识(下) 第5课总结
- java冒泡排序计算学生学号_java冒泡法排序
- 文件上传利器SWFUpload入门简易教程(转)
- function函数
- 阅兵方阵 蓝桥杯 第九届JavaA
- linux系统中ulimit命查看/设置堆栈空间大小
- CentOS后门入侵检测工具
- BH1750光照传感器
热门文章
- LTE关键技术之二:多天线技术
- MySQL--学习笔记
- 知识图谱本体建模工具Protege使用教程
- W ndows7怎么进入BlOS,Windows7旗舰版进不了bios界面的详解教程
- 计算机基础算术加法,计算机基础第二讲.ppt
- DevOps相关知识点
- bugku 你必须让他停下
- SqlNullValueException: Data is Null. This method or property cannot be called on Null values.
- Android 图文混排 异步加载图片
- 【机器学习】特征编码OneHot与Dummy的区别与联系