之前寫過DPDK相關內存管理的代碼分析,但是隨著DPDK的版本迭代在內存管理方面也在進行著不斷的演進。這里結合一些參考文章再對DPDK的內存使用方式和發展變化做一個階段性的總結。
大頁
DPDK通常是使用大頁(hugepage)內存的,無論是2M的大頁還是1G的大頁,本質上都是為了減少TLB miss,通過更大的page size來提升TLB的命中率,而TLB就是用來緩存頁表的高速緩存。
我們知道計算機的設備,如網卡硬件是不能處理用戶空間的虛擬地址(只有CPU通過頁表轉換MMU才能識別虛擬地址),因為它不能感知任何用戶態的進程和其所分配到的用戶空間虛擬地址。相反,它只能訪問真實的物理地址上的內存。
出于對效率的考量,現代硬件幾乎總是使用直接內存存取(DMA)事務。通常,為了執行一個DMA事務,內核需要參與創建一個支持DMA的存儲區域,將進程內虛擬地址轉換成硬件能夠理解的真實物理地址,并啟動DMA事務。這是大多數現代操作系統中輸入輸出的工作方式;然而,這是一個耗時的過程,需要上下文切換、轉換和查找操作,這不利于高性能輸入/輸出。
DPDK的內存管理以一種簡單的方式解決了這個問題。每當一個內存區域可供DPDK使用時,DPDK就通過詢問內核來計算它的物理地址,即DPDK維護了虛擬地址和物理地址的一個映射關系,由于DPDK使用鎖定(如vfio_pin_map_dma)內存(防止物理內存和虛擬內存的映射關系變化),來使底層內存區域的物理地址不會改變,因此硬件可以依賴這些物理地址始終有效,即使內存本身有一段時間沒有使用。然后,DPDK會在準備由硬件完成的輸入/輸出事務時使用這些物理地址,并以允許硬件自己啟動DMA事務的方式配置硬件。這使DPDK避免不必要的開銷,并且完全從用戶空間執行輸入/輸出。
IOMMU和IOVA
默認情況下,任何硬件都可以訪問整個系統的物理內存,因此它可以在任何地方執行DMA事務。這有許多安全隱患。例如,流氓和/或不可信進程(包括在VM (虛擬機)內運行的進程)可能使用硬件設備來讀寫內核空間,和幾乎其他任何存儲位置。為了解決這個問題,現代系統配備了輸入輸出內存管理單元(IOMMU)。這是一種硬件設備,提供DMA地址轉換和設備隔離功能,因此只允許特定設備執行進出特定內存區域(由IOMMU指定)的DMA事務,而不能訪問指定訪問之外的系統內存地址空間。
由于IOMMU的參與,硬件使用的物理地址可能不是真實的物理地址,而是IOMMU分配給硬件的(完全任意的)輸入輸出虛擬地址(IOVA)。一般來說,DPDK社區可以互換使用物理地址和IOVA這兩個術語,但是根據上下文,這兩者之間的區別可能很重要。例如,DPDK 17.11和更新的DPDK長期支持(LTS)版本在某些情況下可能根本不使用實際的物理地址,而是使用用戶空間虛擬地址來實現DMA。IOMMU負責地址轉換,因此硬件永遠不會注意到兩者之間的差異。
如上圖所示,P1和P2進程分別用虛擬地址P進行DMA,由于IOMMU將虛擬地址P映射到不同的物理地址,所以這并不會沖突,而P3訪問的地址P并沒有在IOMMU中建立映射關系,所以DMA訪問會導致失敗。
IO虛擬地址(IOVA)模式
DPDK是一個用戶態應用框架,使用DPDK的軟件可以像其他軟件一樣使用常規虛擬地址。但除此之外,DPDK還提供了用戶態PMD和一組API,以實現完全以用戶態執行IO操作。前文也已經提到過,硬件不能識別用戶空間虛擬地址;它使用的是IO地址——物理地址(PA)或IO虛擬地址(IOVA)。
DPDK API對物理和IO虛擬地址不作區分,即使不是由IO內存管理單元(IOMMU)提供VA部分,也都以IOVA來代表兩種地址。但DPDK卻會區分物理地址作為IOVA的情況,和用戶空間虛擬地址作為IOVA的情況。它們在DPDK API中被稱為IOVA模式,可分為兩種:PA作為IOVA模式,和VA作為IOVA模式。
PA作為IOVA模式
PA作為IOVA的模式下,分配到整個DPDK存儲區的IOVA地址都是實際的物理地址,而虛擬內存的分配與物理內存的分配相匹配。該模式的一大優點就是它很簡單:它適用于所有硬件(也就是說,不需要IOMMU),并且它適用于內核空間(將真實物理地址轉換為內核空間地址的開銷是微不足道的)。實際上,這就是DPDK長期以來的運作方式,在很多方面它都被認為是默認的選項。
然而,作為PA的IOVA模式也存在一些缺點。其中一個就是它需要根用戶特權——如果無法訪問系統的頁面映射(因為DPDK要維護虛擬地址和物理地址IOVA的對應關系),DPDK就無法獲取內存區域的真實物理地址。因此,如果系統中沒有root權限,就無法以該模式運行。
PA作為IOVA的模式
PA作為IOVA的模式還有另外一個值得一提的限制——虛擬內存分配要遵循物理內存分配。這意味著如果物理內存空間被分段(被分成許多小段而不是幾個大段)時,虛擬內存空間也要遵循同樣的分段。極端情況下,分段可能過于嚴重,導致被分割出來物理上連續的片段數量過多,耗盡DPDK用于存儲這些片段相關信息的內部數據結構,就會讓DPDK初始化失敗。
作為PA的IOVA模式下PA分段示例。
應對這些問題,DPDK社區提出了解決方法。舉例來說,一種減少分段影響的方式是使用更大的分頁——問題雖然沒被解決,但是單獨的1千兆字節(GB)段比獨立的2兆字節(MB)段能大幅度減小分段的數量。另外一種廣泛使用的解決方式則是在啟動時引導系統并保留大頁,而不是在運行時。但上述的解決方法都不能根本地解決問題,而且整個DPDK社區都習慣了要去解決這些問題,每個DPDK用戶(有意或無意)在使用時都會采取相同的思維模式——“我需要X MB內存,但以防萬一,我要保留X + Y MB!”
VA作為IOVA模式
相比之下,VA作為IOVA的模式不需遵循底層物理內存的分布。而是重新分配物理內存,與虛擬內存的分配匹配。DPDK EAL依靠內核基礎設施來實現這一點。內核基礎設施又反過來使用IOMMU重新映射物理內存。
作為VA的IOVA模式。
這種方式的優點顯而易見:VA作為IOVA的模式下,所有內存都是VA和IOVA連續的。這意味著所有需要大量IOVA連續內存的內存分配更有可能成功,因為對硬件來說,即使底層物理內存可能不存在,內存看上去還是IOVA連續的。由于重新映射,IOVA空間片段化的問題就變得無關緊要。不管物理內存被分段得多么嚴重,它總能被重新映射為IOVA-連續的大塊內存。
作為VA的IOVA模式下的分段示例。
VA作為IOVA的模式還有另一個優點,它不需要任何權限。這是因為它不需要訪問系統頁面映射。這樣就可以允許以非root用戶身份運行DPDK,而且在特權訪問不受歡迎的環境中,如云原生環境就可以更加容易地使用DPDK。
當然,作為VA的IOVA模式也有一個缺點。出于各種原因,有時候可能不能選擇使用IOMMU。這種情況可能包括:
1)硬件不支持IOMMU;
2)平臺可能本身就沒有IOMMU(比如沒有IOMMU模擬的VM);
3)軟件設備(例如,DPDK的內核網絡接口(KNI)PMD)不支持作為VA的IOVA模式;
4)一些IOMMU(通常是模擬的IOMMU)的地址寬度可能有限,雖然這不妨礙用作VA的IOVA模式,但限制了其有效性;
5)在非Linux *的操作系統上使用DPDK;
但是,這些情況還是相對較少,絕大多數情況下,VA作為IOVA的模式都可以正常工作。
IOVA模式的選擇
很多情況下,DPDK默認選擇作為PA的IOVA模式,因為從硬件角度這是最安全的模式。所有給定的硬件(或軟件)PMD至少都可以保證支持作為PA的IOVA模式。盡管如此,如果條件允許,還是強烈建議所有DPDK用戶使用作為VA的IOVA模式,畢竟此模式具有不可否認的優勢。
但是,用戶不必非要在兩者中選擇一個。可以自動檢測出最合適的IOVA模式,而且默認選項絕對適用于大多數情況,因此不需要用戶來做此選擇。如果默認選項并不合適,用戶可以使用--iova-mode EAL命令行參數嘗試使用EAL標志(適用于DPDK 17.11及更高版本)來代替IOVA模式:
1./app --iova-mode=pa # use IOVA as PA mode
2./app --iova-mode=va # use IOVA as VA mode
大多數情況下,VA和PA模式不會互相排斥,可以使用任一模式,但在某些情況下,PA作為IOVA的模式是唯一可用的選擇。當不能使用作為VA模式的IOVA時,即使EAL參數要求使用作為VA模式的IOVA,DPDK也會自動切換為作為PA模式的IOVA。DPDK還提供了一個API,可查詢運行時正在使用的IOVA模式,但通常這不會在用戶應用中使用,因為只有像是DPDK PMD和總線驅動程序才會要求獲取這種信息。
UIO和VFIO對IOVA的支持
lIgb_uio
DPDK代碼庫中最早的內核驅動程序是igb_uio驅動程序。在DPDK最初的發展階段,這個驅動程序就已經存在了,因此它是DPDK開發人員使用最廣泛也是最熟悉的驅動程序。
此驅動程序依賴內核用戶空間IO(UIO)基礎結構運作,并為所有中斷類型(INT、消息信號中斷(MSI)和MSI-X)提供支持,以及創建虛擬功能。它還公開硬件設備通過/dev/uio文件系統注冊和中斷句柄,然后DPDK EAL將它們用于將它們映射到用戶空間并使它們可用于DPDK PMD。
gb_uio驅動程序非常簡單,能做的也并不多,因此它不支持使用IOMMU。或者,更確切地說,它確實支持IOMMU,但僅在傳輸模式下,它在IOVA和物理內存地址之間建立1:1映射。igb_uio不支持使用完整的IOMMU模式。因此,igb_uio驅動程序僅支持PA作為IOVA模式,并且根本無法在IOVA中作為VA模式工作。
類似于igb_uio的驅動程序在內核中可用:uio_pci_generic。它的工作方式與igb_uio非常相似,只是它的功能更加有限。例如,igb_uio支持所有中斷類型(傳統,MSI和MSI -X),而uio_pci_generic只支持傳統中斷。更重要的是,igb_uio可以創建虛擬函數(Virtual Function, VF),而uio_pci_generic則不能;因此,如果在使用DPDK物理函數(Physical Function, PF)驅動程序時創建VF是必需的一步,igb_uio是唯一的選擇。
因此,在大多數情況下,igb_uio與uio_pci_generic相同或更可取。關于使用IOMMU的所有限制同樣適用于igb_uio和uio_pci_generic驅動程序-它們不能使用完整的IOMMU功能,因此僅支持IOVA作為PA模式。
lvfio
vfio-pci驅動是虛擬功能I / O(VFIO)內核基礎結構的一部分,并在Linux 3.6版中引入。VFIO使設備寄存器和設備中斷可供用戶空間應用程序使用,并可使用IOMMU設置IOVA映射以從用戶空間執行IO。后一部分至關重要,此驅動程序專為與IOMMU一起使用而開發,在較舊的內核上,如果沒有啟用IOMMU,它甚至都無法工作。
與直觀看法相反,使用VFIO驅動程序允許使用PA作為IOVA和VA作為IOVA模式。這是因為,雖然建議使用IOVA作為VA模式來利用該模式的所有好處,但沒有什么能阻止DPDK的設置IOMMU映射的EAL以遵循物理內存布局1:1的方式;畢竟IOVA映射是任意的。在這種情況下,即使使用IOMMU,DPDK也可以在IOVA中作為PA模式工作,從而允許DPDK KNI等工作。但是,仍然需要root權限才能將IOVA用作PA模式。
在更新的內核(4.5+,向后移植到一些舊版本)上,有一enable_unsafe_noiommu_mode選項,允許在沒有IOMMU的情況下使用VFIO。這種模式適用于與基于UIO的驅動程序相同的所有意圖和目的,并具有所有相同的優點與限制。
17.11及早期版本
使用DPDK庫17.11版本或更早版本的任何應用程序必須事先知道其內存要求。這是因為,對于這些版本的DPDK,在初始化之后不可能再申請額外的大頁內存,或者將其釋放回系統。因此,DPDK應用程序可能使用的任何內存都必須在應用程序初始化時預留,并在應用程序的整個生命周期都由DPDK保留。
在決定要保留的內存量時,留出一些余量通常是個好主意。在各種內部分配中,某些DPDK內存將在不同的內部分配中被“浪費”,數量會因您的配置而異(DPDK將使用的設備數量,啟用功能等)。
此外,DPDK17.11中的大多數API都需要大量的IOVA連續內存。這是因為在DPDK 17.11中,虛擬內存布局總是與物理內存布局相匹配。換句話說,如果使用PA作為IOVA,則要求PA是連續的。這是DPDK17.11內存管理中眾所周知的問題之一:實際上很少有應用程序需要PA連續內存,由于缺少足夠的IOVA連續內存,分配大量內存可能會失敗。
IOVA模式的比較
上述限制當然僅適用于作為物理地址(PA)模式的IOVA,因為在該模式下,DPDK的虛擬地址(VA)空間遵循PA空間的布局。在PA模式的IOVA中,可用IOVA連續內存量取決于DPDK控制之外的許多因素,盡管DPDK將嘗試保留盡可能多的IOVA連續內存,具體取決于可用內存量和系統配置,可能沒有足夠的IOVA連續內存來滿足所有分配。
在VA模式的IOVA中,這不是問題,因為在這種情況下,IOVA空間布局將與VA空間的布局相匹配(而不是相反),并且所有物理內存都被重新映射為IOVA連續到硬件。
18.11及之后版本
動態內存管理
DPDK 18.11的最大變化是可以在運行時增加和減少內存使用量,從而消除了DPDK內存映射為靜態的情況,這帶來了許多可用性方面的改進。
在DPDK 17.11中,運行沒有任何環境抽象層(EAL)參數的DPDK應用會保留所有可用的大頁內存供其使用,且不會為其他應用程序或其他DPDK實例留下任何大頁內存。對于DPDK18.11而言情況有所不同,DPDK僅保留應用運行所必須的內存量。在這種意義上,DPDK現在性能更好,并且使DPDK與其他應用程序完美配合所需的工作也更少。
同樣,不再需要事先知道應用程序的內存需求,DPDK的內存映射可以動態增加和減少,因此DPDK內存子系統可以根據需要自動增加其內存使用量,并在不再需要時將內存返回給系統。這意味著部署DPDK應用程序所需的工作更少,因為現在DPDK可以自行管理其內存需求。
DPDK 18.11和IOVA連續性
DPDK 18.11中的一項基本后臺更改是,不能保證虛擬地址(VA)連續內存是IOVA連續的;兩者之間不再有任何關聯。在VA作為IOVA的模式下,IOVA布局仍然像以前一樣遵循VA布局,但是在PA作為IOVA的模式下,PA布局是任意的(物理頁面要向后映射并不罕見)。
DPDK版本之間的IOVA(PA模式)布局比較
這種改變并不像看起來那樣具有顛覆性,因為實際上沒有多少數據結構需要IOVA連續內存。所有軟件數據結構(環,內存池,哈希表等)僅需要VA連續內存,而不在意底層物理內存布局。這使得在早期DPDK版本上工作的大多數用戶應用程序可以無縫過渡到新版本,而無需更改代碼。
盡管如此,某些數據結構確實需要IOVA連續內存(例如,硬件隊列結構),對于這些情況,引入了新的分配器標志。使用此標志,可以使memzone分配器嘗試分配IOVA連續的內存。
單個文件段
較舊的DPDK版本在hugetlbfs文件系統中的每個大頁上存儲一個文件,這適用于大多數用例,但有時會出現問題,特別是,vhost-user后端的Virtio將與后端共享文件,并且有可共享文件描述符數量的硬性限制。當使用大頁(例如1 GB的頁面)時,它可以很好地工作,但是在頁面大小較小的情況下,文件數量會很快超過文件描述符限制。
為了解決此問題,版本18.11中引入了一種新模式,即單文件段模式,該模式通過--single-file-segmentsEAL命令行標志啟用,這使得EAL在hugetlbfs中創建的文件更少,并且使具有vhost-user后端的Virtio甚至可以在最小頁面大小下工作。
-
寄存器
+關注
關注
31文章
5325瀏覽量
120052 -
dma
+關注
關注
3文章
559瀏覽量
100447 -
動態內存管理
+關注
關注
0文章
5瀏覽量
6606 -
物理內存
+關注
關注
0文章
11瀏覽量
8450 -
DPDK
+關注
關注
0文章
13瀏覽量
1721
發布評論請先 登錄
相關推薦
評論