1. 程式人生 > >QEMU的Vhost-User特性及Snabbswitch的Vhost-User應用

QEMU的Vhost-User特性及Snabbswitch的Vhost-User應用

此文旨在向讀者介紹Virtual Open Systems公司為QEMU開發的vhost-user特性在Snabbswitch軟交換機中的使用。vhost-user的架構和Vapp軟體也會涉及到。讀者可跟隨本文構建具有vhost-user特性的QEMU軟體,並且同Vapp的參考實現進行測試。

Vhost-User的產生

基於QEMU/KVM的虛擬機器訪問外部網路的一種方法是通過virtio_net所實現的半虛擬化網路驅動。根據virtio架構標準,virtio_pci驅動實現了相符合的virtio ring,virtio_net等據此來實現具體的半虛擬化驅動。相應的QEMU模擬一個虛擬的PCI裝置,為x86虛擬機器中的virtio ring提供傳輸機制。除去virtio架構的機器相關特性外,virtio驅動通常使用通用API來建立virtqueues虛擬佇列結構,當virtqueues中被填入了裝有新資料的快取時,其將得到kicked通知事件。

Virtio_net是基於virtio機制實現的網路驅動。執行virtio_net驅動的客戶機與其所依託的QEMU程序之間共享一定數量的virtqueues。據此QEMU程序就可接收到客戶機發出的網路流量,並轉發到宿主機的網路中。然而這意味著,所有的客戶機流量必須先被QEMU程序處理,之後才能進入宿主機的網路棧。

針對此問題產生了vhost解決方案,它允許使用者空間程序與核心驅動之間共享一定數量的virtqueues。此時的傳輸就變為核心直接訪問使用者空間應用的記憶體,外加ioeventfs和irqfs來完成通知機制。但是客戶機仍然需要QEMU模擬的PCI裝置,因為所有的控制資訊還是由QEMU處理,當virtqueue建立完成後,就可使用vhost的API將virtqueue的控制交由核心驅動。在此模型中,驅動vhost_net在核心層面直接將客戶機的網路流量傳給TUN裝置,很大程度上提供了效能。但是這樣對於完全執行在使用者空間的虛擬軟體交換機Snabbswitch來說,就不是完美的解決方案了。Snabbswitch證明直接在使用者空間驅動物理乙太網卡裝置可獲得很高的網路效能。Snabbswitch中的虛擬機器虛擬介面要想達到與之匹配的效能,需要能夠直接訪問QEMU/KVM虛擬機器的virtio相關介面,減少中間軟體的處理,包括核心。據此提出了vhost-user解決方案,其為vhost方案在使用者層面的實現,對於像Snabbswitch這樣執行在使用者空間的經常,在與虛擬機器互動資料時,不需要核心在中間參與,直接在Snabbswitch中實現vhost介面。

Vhost-user概觀

vhost-user的目標是實現一個Virtio傳輸機制,使其能足夠直接的使用vhost方案中的共享記憶體、ioeventfds和irqfds。兩個使用者空間程序使用本地UNIX套介面通訊,為共享Vrings建立共享記憶體資源。並且,配置所需的eventfds,使其在Vring收到另一端發出的kick事件時傳送訊號。

QEMU通過一系列的補丁實現了vhost-user,可通過將virtio_net的Vrings直接傳給其它使用者空間程序,在QEMU之外實現一個virtio_net的後端程式。這樣,Snabbswitch程序與QEMU客戶機virtio_net驅動間的直接通訊就可實現了。QEMU已經實現了一個vhost介面零拷貝客戶機流量到核心資料平面。此介面的配置依賴於一系列的控制平面ioctls呼叫。此實現中,QEMU的網路後端使用的為一個TAP裝置。通常的建立命令如下:

$ qemu -netdev type=tap,script=/etc/kvm/kvm-ifup,id=net0,vhost=on \
                                            -device virtio-net-pci,netdev=net0

示意圖圖如下:

QEMU的vhost-user補丁旨在提供使用者空間的vhost介面實現和邏輯。增加的基本元件有:

    增加-mem-path選項分配客戶機可與其它程序共享的記憶體     使用UNIX套介面在QEMU和使用者空間vhost實現程序間通訊     使用者空間程序將接收預分配的客戶機共享記憶體的檔案描述符,它可直接存取客戶機這部分記憶體空間中相關的vrings。

vhost-user架構如下圖:

目前實現中,vhost客戶端在QEMU中,後端位於Snabbswitch中。

編譯與使用

QEMU編譯

擁有最新vhost-user補丁的QEMU版本可在Virtual Open Systems公司的程式碼庫中取到:https://github.com/virtualopensystems/qemu.git, 分支:vhost-user-v5.

$ git clone -b vhost-user-v5 https://github.com/virtualopensystems/qemu.git

編譯:

$ mkdir qemu/obj
$ cd qemu/obj/
$ ../configure --target-list=x86_64-softmmu
$ make -j

編譯生成的QEMU位於qemu/obj/x86_64-softmmu/qemu-system-x86_64。

使用vhost-user功能的QEMU

QEMU要搭配vhost-user後端執行,需要首先提供一個命名的UNIX套介面,並且此套介面要已經由後端開啟。

$ qemu -m 1024 -mem-path /hugetlbfs,prealloc=on,share=on \
-netdev type=vhost-user,id=net0,file=/path/to/socket \
-device virtio-net-pci,netdev=net0

Vapp參考實現

Vapp為一套vhost-user的客戶端與後端的參考實現程式碼。可使用其測試vhost-user協議,或者據其實現一套新的客戶端或者服務端程式。Vapp的原始碼位於Virtual Open Systems公司的github倉庫中:

$ git clone https://github.com/virtualopensystems/vapp.git

編譯:

$ cd vapp
$ make

vhost-user參考後端如下啟動:

$ ./vhost -s ./vhost.sock

參考客戶端如下啟動:

$ ./vhost -q ./vhost.sock

Snabbswitch中的vhost-user支援

Snabbswitch的vhost-user功能支援有SnabbCo維護。程式碼可由官方的Snabbswitch庫中的vhostuser分支獲取:

$ git clone -b vhostuser --recursive https://github.com/SnabbCo/snabbswitch.git
$ cd snabbswitch
$ make

如下測試:

$ sudo src/snabbswitch -t apps.vhost.vhost_user

設計與架構

通用的使用者空間之間virtio架構

當前virtio傳輸僅考慮了客戶機作業系統驅動同運行於Hypervisor中的virtio後端的通訊。對於QEMU/KVM,僅有兩種情況:

  • 客戶機的virtio驅動到QEMU中執行的virtio後端。這是最普遍的情況。在x86平臺virtio_pci傳輸被用來交換Vrings(virtio的基本資料集)和新資料的通知訊息(kicks)。實際的資料有QEMU直接存取,因為其可訪問到客戶機的整個記憶體空間。
  •  客戶機的virtio驅動到Linux核心中的vhost後端。此優化可使宿主機的核心直接處理客戶機的流量不需要通過使用者空間的QEMU。此時,virtio_pci仍然被用來做配置使用,QEMU將建立eventfds來處理客戶機發出的通知(kicks),配置核心vhost驅動使其可直接處理通知事件。

很明顯我們需要第三組選擇,即客戶機的virtio驅動到使用者空間程序中的virtio後端。儘管KVM的基礎建構可將virtio的事件(kicks)匹配到eventfds,我們仍然需要一個標準介面將virtio事件的控制處理交由宿主機的使用者空間程序。此使用者程序需要能夠存取客戶機與宿主機交換快取或者資料結構的任何記憶體空間。

此介面基於共享記憶體和eventfds實現,與已得到驗證的核心vhost介面類似。

Vapp參考實現

儘管與已存在核心vhost架構類似,但是畢竟是一種新的virtio機制,需要一個測試案例以驗證使用者空間程序的virtio後端程式碼以及概念。

我們稱此測試實現為Vapp,本質上有兩個主要元件,其一Vapp客戶端代表虛擬機器的角色;其二Vapp服務端程式碼virtio後端的角色。在測試案例中,客戶端中執行的程式建立一個數據包,放置到Vapp客戶端的Vring中。Vapp客戶端與服務端共享一段記憶體,其中儲存了Vrings和資料快取,兩端通過eventfds通訊。建立過程使用了簡單的UNIX套介面,使用了類似vhost核心實現的機制。

Vapp的客戶端服務端架構如下:

服務端Vring實現

Vring的架構足夠通用,以便其可在QEMU中實現(客戶端),也可在Snabbswitch中實現(服務端)。儘管如此,在下一節將要討論的現實世界中,Vring是執行在客戶機作業系統中而不是QEMU中。QEMU僅僅建立起客戶機virtio(利用virtio_pci)與目標使用者空間程序的連線。

這就要求服務端實現的Vring必須一字不差的準確遵照virtio規範。另外,virtio_pci驅動可使用KVM建立的eventfds(ioeventfd和irqfd)互動,也可通過訪問客戶機的地址空間的方式去獲取virtio驅動產生(或消耗)的包含有資料的共享快取。對於Vapp服務端要成功於客戶機通訊並且與其中的virtio_pci驅動互動資料,需要至少滿足以下幾點:

  • 能夠訪問客戶機的地址空間,其中存放有資料快取;
  • 處理響應(或產生)virtio事件的eventfds;
  • 能夠共享訪問virtio資料結構,包括最重要的Vrings。

此模型也被核心的vhost成功驗證,即宿主機核心空間中執行的Vring實現可以直接與客戶機的virtio_pci所實現的Vring成功互動。與核心vhost實現的主要區別:

  • 我們的virtio後端執行在使用者空間。我們使用UNIX套介面和共享記憶體介面實現,而不是ioctl介面;
  • 我們不需要TAP裝置。執行virtio後端的目標應用如何處理接收到的流量對於我們來說是不可知的。

建立機制

以上描述的virtio機制要工作,我們需要首先建立介面來初始化共享記憶體區域、互動事件檔案描述符。UNIX套介面實現的API可完成此功能。此套介面介面可用來初始化使用者空間virtio傳輸(vhost-user),包括:

  • 初始化時Vrings確定下來,並放置到兩個程序間的共享記憶體中;
  • 只有virtio事件(Vring kicks),我們使用eventfd對映到Vring事件。這使我們的實現與下一章描述的QEMU/KVM實現相相容。KVM執行我們將客戶機中virtio_pci發出的事件與eventfd(ioeventfd和irqfd)相匹配。

在兩個程序間共享檔案描述符和在程序與核心間共享時不同的。前者需要使用UNIX套介面的sendmsg系統呼叫設定SCM_RIGHTS值。

QEMU對Snabbswitch使用者空間virtio的支援

測試案例證明了Vapp的可行性並且驗證了使用者空間virtio(vhost-user)的實現程式碼,據此我們希望在真實的場景中為QEMU/KVM客戶機匹配執行一個位於單獨使用者程序中的virtio後端,即Snabbswitch交換機。我們將新增vhost-user支援到QEMU和Snabbswitch中,前者取代Vapp客戶端,後者取代Vapp服務端。

QEMU的vhost-user支援

在之前的章節我們已經討論了客戶機作業系統基於virtio_pci的virtio實現。當前情況下,需要由vhost-user客戶端去發起與vhost-user服務端的建立過程,此過程與virtio_pci的關係不大。客戶機中的virtio_pci產生的事件由vhost-user的服務端的eventfs所關聯。同時,服務端可通過共享記憶體介面訪問客戶機作業系統的記憶體。QEMU中對vhostvhost-user的支援如下圖:

考慮到客戶機並不知道它執行在何種virtio_net後端實現上;所有我們不能對客戶機中已實現的virtio_pci和virtio_net驅動增加新的限制條件。這就意味著需要意識到,客戶機有可能在它的記憶體空間中任意位置分配快取,進而請求virtio_net驅動將其指到宿主機可訪問的Vring中。

Snabbswitch中vhost-user服務端的實現

Snabbswitch要使用virtio機制需要進行擴充套件以實現一個完整的virtio_net後端。不像QEMU,Snabbswitch將要實現一個自身完整的virtio協議棧和virtio_net模組。基於Vapp的服務端介面和virtqueues可實現一個通用的virtio API。

無論從前端還是後端來看,virtio驅動的高層實現中,virtio Vrings都體現為一套基於virtqueues的更抽象的API介面;儘管本質上有著相同的功能,但是被高層的幫助函式進行了封裝。virtio驅動,包括virtio_net,就是基於封裝層實現的。目前介紹的元件能夠允許移植vhost-user服務端的介面部分,使得Snabbswitch中virtio_net的實現從virtio具體使用的傳輸機制中抽象出來。

訪問客戶機地址空間中的快取

對於執行在QEMU中的virtio_net後端而言,由於QEMU可以訪問客戶機全部的地址空間,所以客戶機作業系統可將任意位置的快取放入Vring中。所以,客戶機作業系統將有可能放置網路資料在任意其可訪問的快取位置。

保存於目前運行於客戶機作業系統的virtio_net驅動的相容,我們不能夠對客戶機放置資料的快取位置施加限制。所以當初始化Snabbswitch的vhost-user服務端,使用其中的virtio_net後端去對接客戶機中的virtio_net前端驅動時,我們需要Snabbswitch能夠訪問客戶機的全部實體地址空間。因此Snabbswitch有可能與客戶機的操作相干擾,Snabbswitch需要為一個可信任程序。

結論

本文描述了一種在兩個使用者空間程序間實現直接virtio傳輸的設計和架構。隨後將這種架構擴充套件到真實世界中一個執行virtio_net前端驅動的QEMU/KVM客戶機和一個執行virtio_net後端的Snabbswitch交換機獨立使用者程序間。此實現基於Linux核心的vhost,對協議做了擴充套件以支援使用者空間vhost後端(vhost-user)。

此項virtio技術可使得Snabbswitch接收執行與QEMU/KVM的客戶機作業系統的網路流量,同時獲得以下優勢:

  • 避免退出到使用者空間(QEMU)的消耗;
  • 網路資料包的處理不需要核心參與。一旦有客戶機exit事件,Snabbswitch通過eventfd能夠儘快接收到通知。

Snabbswitch完全在使用者空間處理資料流量,可獲得使用vhost-user架構直接與客戶機的virtio_net互動的益處,避免了經由QEMU的上下文切換和核心的參與。