本文描述了“vHost User NUMA感知”的概念,該特性的測試表現,以及該特性為ovs+dpdk帶來的效能提升。本文的目標受眾是那些希望瞭解ovs+dpdk底層細節的人,如果你正在使用ovs+dpdk在NUMA host上配置虛擬化平臺,使用vHost User型別的port作為guest的虛擬網路配置,那麼本文或許會給你一些優化效能的靈感。
vHost User NUMA感知
vHost User NUMA感知特性在DPDK 2.2版本時引入,該特性的引入是為了解決DPDK中的一個拖後腿的地方:在多NUMA節點的環境中使用DPDK,vHost的記憶體分配效率比較低。為了瞭解這個拖後腿的點,我們先必須瞭解vHost User裝置使用的三種記憶體:
- 由DPDK分配管理的記憶體,Device tracking memory
- 由OVS分配管理的記憶體,mbufs
- 由QEMU管理分配的記憶體,Guest memory(device and memory buffers)
在多NUMA節點環境中,顯然如果要優化效能,這三種記憶體應當分配在同一個NUMA節點上。但就這個小小的要求,在DPDK 2.2版本之前都是不可能達到的,因為在2.2版本之前,所有由DPDK分配管理的Device tracking memory記憶體,都來自同一個NUMA節點,即便使用這些記憶體的vHost User裝置被其它NUMA節點上跑著的虛擬機器使用著。這就會有一種尷尬的場景出現:一臺虛擬機器,QEMU為它分配的guest memory在節點A上,而DPDK的device tracking memory在另外一個節點B上。這種尷尬的場景會直接導致Intel QuickPath Interconnect(QPI)堵車,顯然也會有其它方面潛在的效能損耗。這個場景的示意圖如下:
在DPDK 2.2版本之後,DPDK中的vHost結構被優化成了動態的與QEMU管理的guest memory貼在一起。這時,當一個vHost裝置出生的時候,DPDK為它分配的記憶體不再固定,變得有點像一個臨時記憶體區,這個vHost裝置將在這個臨時記憶體區開心的活著,直到QEMU通知DPDK:“嘿,小同志,我需要一個vHost裝置”。當QEMU向DPDK索取一個vHost裝置的時候,顯然QEMU需要向DPDK傳送訊息,而DPDK就可以利用這個訊息去確定這個索要vHost裝置的虛擬機器位於哪個NUMA節點,之後,這個vHost裝置的記憶體也將遷移至這個NUMA節點上。
換句話說,vHost裝置出生時居住在一個臨時住所,直至QEMU前來領養它,之後它才有一個穩定的家。
現在我們解決了2/3的問題,還有一部分記憶體上文沒有提到,那就是由OVS分配管理的mbufs。這些記憶體由OVS分配管理,旨在提高datapath的執行效率,為了優化效能,顯然它們也應當與QEMU及DPDK管理分配的記憶體位於內一個NUMA節點上。目前,這個功能由DPDK向OVS傳送訊息實現,DPDK會向OVS傳送有關虛擬機器依存的NUMA節點資訊的訊息,之後OVS將把mbufs使用的記憶體分配在正確的NUMA節點上。在DPDK向OVS傳送這些訊息之前,mbufs的記憶體始終分配在DPDK master lcore所在的NUMA節點上。
現在三部分記憶體都位於同一個NUMA節點了,還剩下最後一個問題:PMD輪詢執行緒(poll mode driver threads)。
PMD輪詢執行緒是一些比較苦逼的執行緒,它們日夜不停馬不停蹄的輪詢input ports,對收到的包進行分類,並對包執行相應的actions。在“vHost User NUMA感知”特性出現之前,所有OVS中的PMD輪詢執行緒都住在同一個NUMA節點上,即是DPDK的master lcore所在的NUMA節點。終於,現在,社會解放了,好日子來了,PMD輪詢執行緒和mbufs、guest memory、device tracking memory呆在同一個NUMA節點了。
下圖展示了三塊記憶體及PMD輪詢執行緒位於同一個NUMA節點時的場景:
效能測試環境
測試環境需要一個至少有兩個NUMA節點的host。上面跑著ovs+dpdk,ovs-bridge上有兩個vHost User裝置,我們稱之分別為vhost0與vhost1。兩個虛擬機器跑在不同的NUMA節點上,分別稱之為vm0與vm1。vhost0與vm0是一對,vhost1與vm1是一對。
下面是測試環境的規格:
Processor E5-2695 v3
Kernel 4.2.8-200
OS Fedora* 22
QEMU* 2.6.0
DPDK 16.04
OVS 914403294be2
測試環境配置過程
在安裝DPDK與OVS之前,確保NUMA庫已安裝
sudo yum install numactl-libs sudo yum install numactl-devel
確保編譯DPDK時開啟了以下的配置項
CONFIG_RTE_LIBRTE_VHOST_NUMA=y
編譯DPDK
連結DPDK庫,編譯OVS
這些都沒啥可說的,畢竟裝了幾百回了,閉著眼睛也會做了。
配置ovs-bridge,就像上面說的那樣:建立一個ovs-bridge,在下面建立兩個ovs-port,型別為dpdkvhostuser或dpdkvhostuserclient。設定ovs的other_config:pmd-cpu-mask掩碼時,為兩個NUMA節點雨露均沾,平均分配。比如,在一個28個邏輯核心的機器上,0~13號核心在NUMA節點0上,14~17號核心在NUMA節點1上,那麼如下設定就是雨露均沾:
ovs-vsctl set Open_vSwitch . other_config:pmd-cpu-mask=10001
# 10001是16進位制,翻譯成二進位制是10000000000000001,即PMD輪詢執行緒的核心親合設定為0號核心與16號核心兩個核心,其中0號核心位於NUMA節點0,1號核心位於NUMA節點1 # 這篇文章比較奇怪,只有一個cpu socket,這個cpu型號,即E5 2695 v3是14核心28執行緒的,按理來說應該只有一個numa節點啊。難道用一個cpu socket也能組兩個numa節點?
在啟動虛擬機器之前,使用如下命令檢查一個pmd設定
ovs-appctl dpif-netdev/pmd-rxq-show
在啟動虛擬機器之前,QEMU還沒有分配記憶體,也肯定談不上發訊息給DPDK,DPDK就更談不上發訊息給OVS了,所以此時這時PMD執行緒將落在同一個NUMA節點上,顯示如下:
pmd thread numa_id 0 core_id 0: port: dpdkvhostuser1 queue-id: 0 port: dpdkvhostuser0 queue-id: 0
然後啟動虛擬機器,在兩個NUMA節點上分別啟動vm0與vm1,下面以qemu為例,為了確保兩個虛擬機器分別跑在兩個NUMA節點上,使用taskset命令,如下:
sudo taskset 0x2 qemu-system-x86_64 -name vm0 -cpu ... sudo taskset 0x2000 qemu-system-x86_64 -name vm1 -cpu ...
這時檢視虛擬機器的log,vm1會列印出如下的log:
VHOST_CONFIG: read message VHOST_USER_SET_VRING_ADDR VHOST_CONFIG: reallocate vq from 0 to 1 node VHOST_CONFIG: reallocate dev from 0 to 1 node
出現上面這樣的log就意味著DPDK的device tracking memory被從臨時住所挪到了正確的NUMA節點上
另外一個驗證方法是使用pmd-rxq-show工具,顯示如下:
pmd thread numa_id 1 core_id 20: port: dpdkvhostuser1 queue-id: 0 pmd thread numa_id 0 core_id 0: port: dpdkvhostuser0 queue-id: 0
dpdkvhostuser1現在被一個位於NUMA節點1上的執行緒服務著,這也正是vm1所在的NUMA節點