VirtIO
VirtIO 由 Rusty Russell 開發(fā),最初是為了支持自己開發(fā)的 lguest Hypervisor,其設計目標是在虛擬化環(huán)境下提供與物理設備相近的 I/O 功能和性能,并且避免在虛擬機中安裝額外的驅動程序?;谶@一目標,后來通過開源的方式將 VirtIO 延伸為一種虛擬化設備接口標準,并廣泛地支持 KVM、QEMU、Xen 和 VMware 等虛擬化解決方案。
為了追求更高的性能和更低的開銷表現,VirtIO 采用了 Para-virtualizaiton(半虛擬化/準虛擬化)技術路線,本質區(qū)別于性能低下的 Full-virtualizaiton(e.g. QEMU I/O Emulation)。這也意味著使用 VirtIO 的 GuestOS 需要經過一定的代碼修改(安裝非原生的 Device Driver),這也是為什么需要將 VirtIO 定位于 “虛擬化設備接口標準“ 的原因,其整體的集成架構需要由 Front-end(GuestOS Kernel Device Driver)和 Back-end(VMM Provider)共同遵守統(tǒng)一的傳輸協(xié)議。
在應用 VirtIO 的場景中,除了 GuestOS 需要安裝額外的 VirtIO Front-end Device Driver 這一點不足之外(通常在制作 QCOW2 鏡像時會安裝好),相對地帶來了下列諸多好處:
高性能:VirtIO 省去了 Full-virtualizaiton 模式下的 Traps(操作捕獲)環(huán)節(jié),GuestOS 通過 VirtIO Interfaces 可以直接與 Hypervisor 中的 Device Emulation 進行交互。
低開銷:VirtIO 優(yōu)化了 CPU 在內核態(tài)和用戶態(tài)之間頻繁切換,以及 CPU 在 VM Exit 和 VM Entry 之間頻繁陷入陷出所帶來的性能開銷。
標準化:VirtIO 實現了統(tǒng)一的虛擬設備接口標準,可以應用在多種虛擬化解決方案中。
VirtIO 虛擬設備接口標準
為了抑制 Para-virtualizaiton 所具有的跨平臺兼容性缺點,VirtIO 明智地走上了開源之路,通過構建標準而統(tǒng)一的生態(tài)來爭取最小化的兼容性排斥問題,反而讓其逆轉成為了一種優(yōu)勢。
在 VirtIO 提出的虛擬化設備接口標準中,包括多個子系統(tǒng),每個子系統(tǒng)都定義了一組虛擬設備類型和協(xié)議。例如:
VirtIO-Block(塊設備):提供虛擬磁盤設備的接口。
VirtIO-Net(網絡設備):提供虛擬網絡設備的接口。
VirtIO-Serial(串口設備):提供虛擬串口設備的接口。
VirtIO-Memory(內存設備):提供虛擬內存設備的接口。
VirtIO-Input(輸入設備):提供虛擬輸入設備的接口。
此外還有 VirtIO-SCSI、VirtIO-NVMe、VirtIO-GPU、VirtIO-FS、VirtIO-VSock 等等虛擬設備類型和協(xié)議。
VirtIO 的前后端分層架構
VirtIO 虛擬設備接口標準的分層軟件架構如下圖所示:
Front-end:是一組通用的,安裝在 GuestOS Kernel 中的 VirtIO Device Driver,通過 Hyper-call 的方式對 Back-end 進行調用。
Back-end:是一組 Hypervisor 專用的,運行在 VMM 中的設備模擬程序,提供了 Hyper-call 調用接口。
Transport:是一組標準的傳輸層接口,基于環(huán)形隊列的方式來批量處理 I/O 請求。
VirtIO 的數控路徑分離架構
VirtIO 還定義了 “控制路徑” 和 “數據路徑” 的分離架構,兩者的側重各有不同,控制平面追求盡可能的靈活以兼容不用的設備和廠商,而數據平面則追求更高的轉發(fā)效率以快速的交換數據包。
控制路徑:控制建立或刪除 Front-end 和 Back-end 之間的數據路徑,提供可管理的靈活性。
數據路徑:在 Front-end 和 Back-end 之間進行數據傳輸,追求性能。
對應地,VirtIO 標準也可以分為兩個部分:
VirtIO Spec:定義了如何在 Front-end 和 Back-end 之間構建控制路徑和數據路徑的標準,例如:數據路徑規(guī)定采用環(huán)形隊列緩沖區(qū)布局。
Vhost Protocol:定義了如何降數據路徑的高性能實現標準,例如:基于 Kernel、DPDK、SmartNIC Offloading 的實現方式。
以 QEMU 為例,其根據 VirtIO Spec 實現了控制路徑,而數據路徑則可以 Bypass QEMU,使用 vhost-net、vhost-user 等實現。
VirtIO Networking
VirtIO Networking 是一種高功能、高性能、可擴展的虛擬化網絡設備,支持多種網絡功能和虛擬網絡技術,并可以通過多種實現方式進行部署。
網絡功能:MAC 地址、流量限制、流量過濾、多隊列收發(fā)等;
虛擬網絡技術:VLAN、GRE、VXLAN 等;
多種實現方式:Kernel、DPDK、硬件網卡等。
virtio-net Driver 和 virtio-net Device(由 QEMU 模擬的后端)
virtio-net 是 VirtIO Networking 的默認實現。其中,Front-end 是 virtio-net Driver(虛擬網卡驅動程序);Back-end 是由 QEMU 軟件模擬的 virtio-net Device。
QEMU 會在 VM 啟動時,為 VM 創(chuàng)建好相應的 virtio-net Device(虛擬設備),并在 VM 啟動后,通過在 GuestOS Kernel 中安裝的 virtio-net Driver 來 Probe(探知)到該 virtio-net Device。
virtio-net Driver 和 virtio-net Device 的軟件架構如下圖所示,我們需要關注 3 個方面:
Control Plane:virtio-net Driver 和 virtio-net Device 之間使用了 PCI 傳輸協(xié)議,如下圖中藍線部分。
Data Plane:virtio-net Driver 和 virtio-net Device 之間使用了 Virtqueues 和 Vrings,如下圖中紅虛線部分。而 virtio-net Device 和 Kernel Network Stack 之間使用了 Tap 虛擬網卡設備,作為 User space(QEMU)和 Kernel space(Network Stack)之間的數據傳輸通道,如下圖中紅實線部分。
Notification:作為 virtio-net Driver、virtio-net Device 和 KVM 之間交互方式,用于實現 “vCPU 硬中斷“ 的功能。
其中值得細究的就是 virtio-net Driver 和 virtio-net Device 之間的 Transport(傳輸層)實現。virtio-net Transport 采用了非常類似于物理網卡設備(NIC Rx/Tx Queues 和 Kernel Rx/Tx Rings)的設計思路。
Front-end 和 Back-end 之間存在 2 個 Virtqueues,對應到 NIC 的 Rx/Tx Queues。Virtqueues 的底層利用了 IPC 共享內存技術,在 HostOS 和 GuestOS 之間建立直接的傳輸通道,繞開了 VMM 的低效處理。
Receive Queue:是一塊 Buffer 內存空間,存放具體的由 Back-end 發(fā)送到 Front-end 數據。
Send Queue:是一塊 Buffer 內存空間,存放具體的由 Front-end 發(fā)送到 Back-end 數據。
同時,每個 Virtqueue 都具有 2 個 Vrings,對應到 Kernel 的 Rx/Tx Rings:
Available Ring:存放 Front-end 向 Back-end 發(fā)送的 Queue 中可用的 Buffer 內存地址。
Used Ring:存放 Back-end 向 Front-end 發(fā)送的 Queue 中已使用的 Buffer 內存地址。
當然,Front-end 和 Back-end 之間還需要一種雙向通知機制,對應到 NIC 和 CPU 之間的硬中斷信號:
Available Buffer Notification:Front-end 使用此信號通知 Back-end 存在待處理的緩沖區(qū)。
Used Buffer Notification:Back-end 使用此信號標志已完成處理某些緩沖區(qū)。
具體的,當 GuestOS 發(fā)送數據時,Front-end 將數據填充到 Send Queue 內存空間,然后將地址記錄到 Available Ring,并在適當的時候向 Back-end 發(fā)送 Available Buffer Notification;QEMU 接收到通知后,從 Available Ring 中獲取到數據的數據,完成數據接收,然后記錄 New Buffer Head Index 到 Used Ring,并在適當的時候向 Front-end 發(fā)送一個 Used Buffer Notification。Front-end 接收到通知后釋放對應的 Send Queue 內存空間,Available Ring 指針指向下一位。
當 GuestOS 接收數據時,Front-end 首先分配好 NULL 的 Receive Queue 內存空間,然后將地址記錄到 Available Ring,并在適當的時候向 Back-end 發(fā)送 Available Buffer Notification;QEMU 接收到通知后,從 Available Ring 中獲取到數據填充入口地址,完成數據填充,然后記錄 New Buffer Head Index 到 Used Ring,并在適當的時候向 Front-end 發(fā)送一個 Used Buffer Notification。Front-end 接收到通知后從 Receive Queue 中讀取數據,Available Ring 指針指向下一位。
值得注意的是,在 PCI Passthrough 場景中,可以使用 virtio-pci(更多的是使用 VFIO)。雖然此時依舊會沿用了 Available/Used buffer notification 這一控制面的雙向通知機制,但實際的數據面,則是由 DMA 和專用隊列來完成的,具有更高的性能。這體現了數控分離帶來的好處。
綜上所述,下述流程圖總結了 2 個關鍵流程:
virtio-net Device 初始化流程:包括 Device discovery 和 Device configuration。對于 virtio-net Driver 而言,virtio-net Device 就是一個 PCI 設備,遵守標準的 PCI 設備初始化流程,并且 Driver 和 Device 使用 PCI 協(xié)議進行通信。
virtio-net Driver 發(fā)包流程:
當 virtio-net Driver 發(fā)送數據包時,將數據包放入 Send Queue,將數據包的訪問地址放入 Available Ring,觸發(fā)一次 Available Buffer Notification 到 KVM,然后 VM Exit,由 QEMU 接管 CPU,執(zhí)行 I/O 控制邏輯,將此數據包傳遞到 virtio-net Device,然后 virtio-net Device 降數據包通過 Tap 設備傳入 Kernel Network Stack;
完成后,QEMU 將 New Buffer Head Index 放入到 Used Ring 中,同時 virtio-net Device 發(fā)出一個 vIRQ 中斷注入到 KVM,然后 KVM 發(fā)出一個 Used Buffer Notification 到 virtio-net Driver,此時 virtio-net Driver 就會從 Used Ring 中取出 Buffer Head Index;
至此一次發(fā)送操作就完成了。
另外,由于 Tap 設備支持 Tx/Rx 多隊列,所以實際上也會存在 N 個 Virtqueues Peer 進行一一映射。
vhost-net(由 Kernel 提供的后端)
由 QEMU 模擬的 virtio-net Device 顯然性能不佳,具有頻繁的模式和上下文切換、低效的數據拷貝、線程間同步等性能問題。于是,VirtIO 社區(qū)在 Kernel 中實現了一個新的 vhost-net 后端,以解決上述問題。
更低的延遲(latency):比普通 virtio-net 低 10%。
更高的吞吐量(throughput):比普通 virtio-net 高 8 倍,達到 7~8 Gigabits/Sec。
vhost-net 的核心思想就是將 Data Path Bypass QEMU,定義了一種新的 Data Path 傳輸方式,使得 GuestOS virtio-net Driver 和 HostOS Kernel vhost-net 可以直接通信。如下圖所示:
Control Plane:virtio-net Driver 和 virtio-net Device 之間使用了 PCI 傳輸協(xié)議,virtio-net Device 和 vhost-net 之間使用 ioctl() 接口,如下圖中藍線部分。
Data Plane:virtio-net Driver 和 vhost-net 直通,如下圖中虛線部分。而 vhost-net 和 Kernel Network Stack 之間使用了 Tap 虛擬網卡設備連接,如下圖中紅實線部分。
Notification:作為 virtio-net Driver、vhost-net 和 KVM 之間交互方式,用于實現 “vCPU 硬中斷“ 的功能。
vhost-net 的本質是一個 HostOS Kernel Module,實現了 vhost-net Protocol 標準,當 Kernel 加載 vhost-net 后,會暴露一個設備接口文件 /dev/vhost-net。
當 QEMU 在 vhost-net 模式下啟動時,QEMU(virtio-net Device)首先會 open() 該文件,并通過 ioctl() 與 vhost-net 進行交互,繼而完成 vhost-net 實例的 Setup(初始化)。初始化的核心是建立 virtio-net Driver 和 vhost-net 之間的 Data Path 以及 Notification,包括:
Hypervisor Memory Layout(虛擬化內存布局):用于 Data Path 傳輸,讓 vhost-net 可以在 KVM 的內存空間中找到特定 VM 的 Virtqueues 和 Vrings 的地址空間。
ioeventfd 和 irqfd(文件描述符):用于 Notification 傳輸,讓 vhost-net 和 KVM 之間可以在 Kernel space 中完成 Notification 的直接交互,不再需要通過 User space 中的 QEMU。
同時,在 vhost-net 實例初始化的過程中,vhost-net 會創(chuàng)建一個 Kernel Thread(vhost worker thread),名為 vhost-$pid(pid 是 QEMU 進程 PID),QEMU 進程就和 vhost-net 實例以此建立了聯(lián)系。vhost worker thread 用于處理 I/O Event(異步 I/O 模式),包括:
使用 ioeventfd 輪詢 Tap 事件和 Notify 事件,并轉發(fā)數據。
使用 irqfd 允許 vhost-net/KVM 通過對其進行寫入來將 vCPU 的 vIRQ 注入到 virtio-net Driver。
綜上,在 vhost-net 實例初始化完成之后,virtio-net Device 將不再負責數據包的處理(對 Virtqueues 和 Vrings 的讀/寫操作),取而代之的是 vhost-net。vhost-net 可以直接訪問 VM 的 Virtqueues 和 Vrings 內存空間,也可以通過 KVM 與 virtio-net Driver 進行 Notification 交互。
最后需要注意的是,vhost-net 在某些應用場景中的性能未必就會比 virtio-net Device 更好。例如:從 HostOS 傳輸到 GuestOS 的 UDP 流量,如果 GuestOS 接受 UDP 數據包的速度比 HostOS 發(fā)送的速度要慢,在這種情況下使用 vhost-net 將會導致 GuestOS UDP Socket Receive Buffer 更容易溢出,導致丟包,繼而使得 GuestOS 的整體性能出現下降。此時使用慢速的 virtio-net,讓 HostOS 和 GuestOS 之間的傳輸速度稍微慢一點,反而會提高整體的性能。
可見,在虛擬機場景中,全鏈路的性能均衡需要給予更加全面的考慮。
vhost-user(由 DPDK 實現的用戶態(tài)后端)
vhost-user 是一個基于 DPDK vhost-user Library 開發(fā)的后端,運行在 User space,應用了 DPDK 所提供的 CPU 親和性,大頁內存,輪詢模式驅動等數據面轉發(fā)加速技術。
區(qū)別于 vhost-net Protocol,vhost-user Library 實現了一種新的 vhost-user Protocol,兩者間最大的區(qū)別體現在通信信道的實現方式上。
vhost-net:使用 /dev/vhost-net 字符設備和 ioctl() 實現了 User Process(QEMU)和 Kernel(vhost-net)之間的 Control Plane 通信信道。
vhost-user:使用 Unix Socket 實現了 User Process(QEMU 和 DPDK App)之間的 Control Plane 通信信道。
QEMU 通過 Unix Socket 來完成對 vhost-user 的 Data Plane 配置,包括:
特性協(xié)商配置:QEMU(virtio-net Device)與 vhost-user 協(xié)商兩者間功能特性的交集,確認哪些功能特性是有效的。
內存區(qū)域配置:vhost-user 使用 mmap() 來映射分配給 QEMU 的內存區(qū)域,建立兩者間的直接數據傳輸通道。
Virtqueues/Vrings 配置:QEMU 將 Virtqueues/Vrings 的數量和訪問地址發(fā)送給 vhost-user,以便 vhost-user 訪問。
Notification 配置:vhost-user 仍然使用 ioeventfd 和 irqfd 來完成和 KVM 之間的通知交互。
目前最常見的 vhost-user DPDK App 就是 OVS-DPDK,除了支持 vhost-user Back-end 之外,還支持 virtio-pmd Front-end。在追求極致性能的 vhost-user / virtio-pmd 架構中,HostOS User space 和 GuestOS User space 都運行著 DPDK。
用戶可以自由選擇創(chuàng)建 vhost-user 類型的 vSwitch Port(對應一個 vhost-user 實例)。如下圖所示。
HW vDPA(使用硬件加速的后端)
目前性能最好的 Data Plane 無疑是 SR-IOV VF Passthrough,但原生的 SR-IOV 缺少控制面邏輯,在實際使用中多有不便。
HW vDPA(Hardware vhost Data Path Acceleration,硬件 vhost 數據面加速)就是一種使用 SR-IOV VF 來充當 Data Plane、使用 vDPA Framework 來充當控制面的軟硬件融合加速技術,能夠同時兼具軟件的可管理性、靈活性以及硬件的高性能。同時也解決了 VF Passthrough 虛擬機不支持熱遷移的難題。
NOTE:SW vDPA(e.g. vDPA on OVS-DPDK)不在本文討論范圍中。
下圖中展示了 OVS-DPDK 和 HW vDPA 的性能對比。
目前 vDPA Framework 的底層實現方式還尚未統(tǒng)一,從 QEMU 的角度來看,主要有 2 種方式:
適應性更好的 vhost-user + vDPA Driver:在 DPDK vhost-user Library 的基礎之上,再實現一個 vDPA Driver 適配層。由 vDPA Driver 完成與 HW Device(e.g. SmartNIC、DPU)之間的交互。從下圖中可以看到 QEMU 與 HW 的 Control Plane 通過 vDPA driver 進行傳遞,并配置好 HW 與 VM 之間的 Passthrough Data Plane。目前 Mellanox MLX5 和 Intel IFC 對應的 vDPA Driver 都已經合入到 DPDK 和 Kernel 社區(qū)。(注:IOMMU/VFIO 實現原理請瀏覽《虛擬化技術 — 硬件輔助的虛擬化技術》。)
架構更合理的 vhost-vdpa:實現一種新的 vhost-vdpa Protocol,QEMU 通過 ioctl() 調用 Kernel 中的 vDPA Module,再由 vDPA Module 通過 vDPA Driver 完成與 HW Device 之間的交互。
目前,隨著 SmartNIC / DPU 的快速發(fā)展,一種結合了 SW vDPA 和 HW vDPA 特性的 HW vDPA on OVS-DPDK 方案也逐漸成熟中,其實現會更加復雜、但功能也更加完備。值得期待。
審核編輯:劉清
-
SCSI接口
+關注
關注
0文章
19瀏覽量
10518 -
虛擬機
+關注
關注
1文章
919瀏覽量
28321 -
VLAN技術
+關注
關注
0文章
45瀏覽量
6400 -
VMM
+關注
關注
0文章
11瀏覽量
10087 -
qemu
+關注
關注
0文章
57瀏覽量
5363
原文標題:詳解:VirtIO Networking 虛擬網絡設備實現架構
文章出處:【微信號:SDNLAB,微信公眾號:SDNLAB】歡迎添加關注!文章轉載請注明出處。
發(fā)布評論請先 登錄
相關推薦
評論