7.1 虛擬機器直接IO原理與架構
第5與第6章分別講解了qemu/kvm的硬體輔助IO模擬虛擬化與virtio半虛擬化.
模擬I/O裝置方式的優點是對硬體平臺依賴性較低、可以方便模擬一些流行的和較老久的裝置、不需要宿主機和客戶機的額外支援,故相容性高;而其缺點是I/O路徑較長、VM-Exit次數很多,故效能較差。一般適用於對I/O效能要求不高的場景。 Virtio半虛擬化裝置方式的優點是實現了VIRTIO API,減少了VM-Exit次數,提高了客戶機I/O執行效率,比普通模擬I/O的效率高很多;而其缺點是需要客戶機中virtio相關驅動的支援(較老的系統預設沒有自帶這些驅動,Windows系統中需要額外安裝virtio驅動),故相容性較差,而且I/O頻繁時的CPU使用率較高。 而本節方式叫做PCI裝置直接分配(Device Assignment,或者PCI pass-through),它允許將宿主機中的物理PCI(或PCI-E)裝置直接分配給客戶機完全使用.
其工作架構如下圖所示:
左側為直接IO, 右側為模擬IO虛擬化.
VM虛擬機器支援將宿主機中的PCI、PCI-E裝置附加到虛擬化的客戶機中,從而讓客戶機以獨佔方式訪問這個PCI(或PCI-E)裝置.在客戶機看來,裝置是物理上連線在其PCI(或PCI-E)總線上的,客戶機對該裝置的I/O互動操作和實際的物理裝置操作完全一樣,這不需要(或者很少需要)VMM的參與.
但我們知道,硬體虛擬化需要考慮dma傳輸與中斷的注入. 由於guest os的gpa不是直接實體記憶體, 裝置不能直接使用gpa進行dma傳輸. 其二, 裝置產生的中斷也不能直接傳遞給guest os. 為此intel定義了I/O虛擬化技術規範為“Intel® Virtualization Technology forDirected I/O”(VT-d), 來解決這些問題.
裝置直接分配讓客戶機完全佔有PCI裝置,在執行I/O操作時大量地減少了(甚至避免)了VM-Exit陷入到Hypervisor中,極大地提高了I/O效能,可以達到和Native系統中幾乎一樣的效能。儘管Virtio的效能也不錯,但VT-d克服了其相容性不夠好和CPU使用率較高的問題。不過,VT-d也有自己的缺點,一臺伺服器主機板上的空間比較有限,允許新增的PCI和PCI-E裝置是有限的,如果一個宿主機上有較多數量的客戶機,則很難給每個客戶機都獨立分配VT-d的裝置。另外,大量使用VT-d獨立分配裝置給客戶機,讓硬體裝置數量增加,故增加了硬體投資成本。為了避免這兩個缺點,可以考有兩種解決方案: (1)在一個物理宿主機上,僅給少數的對I/O(如網路)效能要求較高的客戶機使用VT-d直接分配裝置(如網絡卡),而其餘的客戶機使用硬體輔助模擬(emulated)或使用Virtio以
達到多個客戶機共享同一個裝置的目的。二是,對於網路I/O的解決方法,可以選擇SR-IOV讓一個網絡卡產生多個獨立的虛擬網絡卡,將每個虛擬網絡卡分別分配給一個客戶機使用. 本文將只討論第一種方案.
7.1.2 Intel VT-D概述
Intel VT-d技術是一種基於North Bridge北橋晶片(或者按照較新的說法:MCH)的硬體輔助虛擬化技術,通過在北橋中內建提供DMA虛擬化和IRQ虛擬化硬體,實現了新型的I/O虛擬化方式,Intel VT-d能夠在虛擬環境中大大地提升 I/O 的可靠性、靈活性與效能。
傳統的IOMMUs(I/O memorymanagement units,I/O記憶體管理單元)提供了一種集中的方式管理所有的DMA——除了傳統的內部DMA,還包括如AGPGART、TPT、RDMA over TCP/IP等這些特別的DMA,它通過在記憶體地址範圍來區別裝置,因此容易實現,卻不容易實現DMA隔離,因此VT-d通過更新設計的IOMMU架構,實現了多個DMA保護區域的存在,最終實現了DMA虛擬化。這個技術也叫做DMA Remapping。
I/O裝置會產生非常多的中斷請求,I/O虛擬化必須正確地分離這些請求,並路由到不同的虛擬機器上。傳統裝置的中斷請求可以具有兩種方式:一種將通過I/O中斷控制器路由,一種是通過DMA寫請求直接傳送出去的MSI(message signaled interrupts,訊息中斷),由於需要在DMA請求內嵌入目標記憶體地址,因此這個架構須要完全訪問所有的記憶體地址,並不能實現中斷隔離。 VT-d實現的中斷重對映(interrupt-remapping)架構通過重新定義MSI的格式來解決這個問題,新的MSI仍然是一個DMA寫請求的形式,不過並不嵌入目標記憶體地址,取而代之的是一個訊息ID,通過維護一個表結構,硬體可以通過不同的訊息ID辨認不同的虛擬機器區域。VT-d實現的中斷重對映可以支援所有的I/O源,包括IOAPICs,以及所有的中斷型別,如通常的MSI以及擴充套件的MSI-X。
(1) 裝置對映資料結構
a. 源標識。 出現在地址硬體地址轉換上的DMA和中斷都需要標示一個請求的發起端。 對pci裝置而言用(匯流排號,裝置號,功能好)標示。
b. 根條目: rootentry作為裝置直接分配的頂層分配結構,它把特定的pci匯流排對映到對應的guest os.它包含當前標誌(用來表明根條目是否已經存在和初始化) 和上下文條目
每個根條目對應一條pci匯流排,所有根條目組成一張根條目表;每個條目表最多256個根條目。下圖表明瞭根條目表與上下文條目表的對應關係:
上下文條目(context-entry)將pci總線上的某個裝置對映到某個guest os.它為分配該裝置的guest os對映到地址轉換結構。它包含如下內容:
a. 域標識: 它標識出哪些源標示符的裝置被分配給guest os.
b. 轉換型別: 用來轉換一個DMAoperation
c. 地址寬度: 被分配的guest的地址寬度
(2)IO的對映表
它與MMU表相似,下圖是三級io對映表
(3) IOTLB
與TLB相似, IOTLB用來快取DMA地址轉換中的對映條目。它也支援invalidate,
a) 全域性invalidate,清空整個iotlb
b) guest選擇invalidate, 這選擇某個guest的清空
c) guest頁面選擇invalidate: 這清空某個guest的某些條目
7.1.3 直接IO的使用
要在LInux kvm/qeum使用直接IO 需要滿足以下條件:
(1) CPU 支援 VT-D
(2) Host Linux OS enable IOMMU, DMAR
(3) Host Linux Enable PCI_STUB driver.
使用方法:
(1) 用pci-stub在linux host os 上遮蔽相關pci 裝置
echo -n “vendorid:deviceid” >/sys/bus/pci/drivers/pci-stub/new_id
echo “xxxx:yy:zz.v” > /sys/bus/pci/devices/“xxxx:yy:zz.v”/driver/unbind
echo “xxxx:yy:zz.v” >/sys/bus/pci/devices/pci-stub/binds
新增如下引數啟動kvm
pci-assign,?kvm-pci-assign.host=“bus.dev.func”
Pci-stub driver實際上是一個空實現的pcidriver,VM host使用它就不會主動訪問該裝置了,
其程式碼位於:drivers\pci\pci-stub.c.
KVM/QEMU依賴於IOMMU,下一節將從分析qemu pci-assign模組開始分析.