天天看點

display:Integrating the ION memory allocator-2013

作為2013 Linux Plumbers Conference大會上Android + Graphics會議的一部分,我們将讨論ION記憶體配置設定器及其功能如何上傳到主流核心。由于時間有限,我想建立一些背景文檔,以提供我們将在會議上讨論并試圖解決的問題的背景。

ION overview

Android ION子系統的主要目标是允許在硬體裝置和使用者空間之間配置設定和共享緩沖區,以便在裝置之間實作零拷貝記憶體共享。這聽起來很簡單,但實際上是一個困難的問題。在片上系統(SoC)硬體上,通常有許多不同的裝置具有直接記憶體通路(DMA)。然而,這些裝置可能具有不同的功能,并且可以在不同的限制條件下檢視和通路記憶體。例如,一些裝置可能處理分散聚集的清單,而另一些裝置可能隻能通路記憶體中實體上相鄰的頁面。一些裝置可以通路所有的記憶體,而另一些裝置隻能通路記憶體的一小部分。最後,一些裝置可能位于I/O記憶體管理單元(IOMMU)後面,這可能需要配置來讓裝置通路記憶體中的特定頁面。

如果你有一個緩沖區,你想與一個裝置共享,并且緩沖區沒有被配置設定到裝置可以通路的記憶體中,你必須使用反彈緩沖區複制該記憶體的内容到其他裝置可以通路它的位置。這可能是昂貴的,并極大地損害性能。是以,在所有使用緩沖區的裝置都可通路的位置配置設定緩沖區的能力是很重要的。

是以,ION提供了一個接口,允許集中配置設定不同“類型”的記憶體(或“堆”)。在目前沒有ION的核心中,如果您試圖在DRM圖形裝置和video4linux (V4L)錄影機之間共享記憶體,則需要確定使用管理最受限裝置的子系統來配置設定記憶體。是以,如果相機是最受限制的裝置,則需要通過V4L核心接口進行配置設定,而如果圖形是最受限制的裝置,你就必須通過圖形執行管理器(GEM)接口進行配置設定。相反,ION提供了一個單一的集中接口,允許應用程式配置設定滿足所需限制的記憶體。

但是,ION沒有提供一種方法來确定哪種類型的記憶體滿足相關硬體的限制。這是一個留給特定裝置的使用者空間實作配置設定的問題(“Gralloc”,在Android的情況下)。這種寫死的限制解決方案并不理想,但對于GEM和V4L配置設定緩沖區,沒有更好的主流解決方案了。使用者空間隻需要知道什麼是最受限制的裝置。在大多數靜态硬體裝置上,如手機和平闆電腦,這些資訊是預先知道的,但這種限制使得ION不太适合以目前的形式在上遊應用。

為了共享這些緩沖區,ION導出了一個連結到特定緩沖區的檔案描述符。然後,這些檔案描述符可以在應用程式之間傳遞,也可以傳遞給支援ION的驅動程式。最初這些是特定于ION的檔案描述符,但是ION已經被重新設計,以利用dma-buf結構來共享。需要注意的是,雖然ION可以export dma-bufs,但它不會從其他驅動器import dma-bufs。

ION cache management

ION作為中央緩沖區配置設定器和管理器的另一個主要角色是處理DMA的緩存維護。由于許多裝置維護它們自己的記憶體緩存,是以在序列化裝置和CPU通路共享記憶體時,這些裝置和CPU在讓其他裝置通路緩沖區之前重新整理它們的私有緩存是很重要的。提供關于緩存的完整背景已經超出了本文的範圍,是以如果讀者有興趣了解更多,我将向他們推薦這篇LWN文章https://lwn.net/Articles/282250/。

ION允許緩沖區使用者設定一個标志,該标志描述配置設定時所需的緩存行為。這樣,這些使用者可以指定是否應使用ION進行緩存維護來緩存到該緩沖區的映射,是否要取消緩存但使用寫合并(有關詳細資訊,請參閱這篇文章https://lwn.net/Articles/440221/),或者是否要取消緩存和顯式管理緩沖區通過ION的同步ioctl()。相反,它提供了一個錯誤處理程式,以便僅在通路頁面時才将其映射進來。此方法允許ION跟蹤已更改的頁面,并且隻重新整理實際接觸的頁面。

在緩沖區已經緩存并且ION執行緩存維護的情況下,ION進一步嘗試通過在mmap()時延遲映射的建立來達到優化的目的。它提供了一個錯誤處理程式,是以僅在通路頁面時才映射頁面。此方法使ION可以跟蹤已更改的頁面,并且隻重新整理實際接觸的頁面。

此外,當ION為未緩存的緩沖區配置設定記憶體時,它正在管理尚未映射到核心空間的實體頁面。由于這些緩沖區可能在它們被映射到核心空間之前被DMA使用,是以在映射時重新整理它們是不正确的;這可能會導緻資料損壞。是以,在配置設定這些緩沖區時,必須為DMA預先重新整理它們。為此,ION提供的另一個性能優化是為DMA預重新整理頁面池。在一些系統上,在頻繁的小緩沖區配置設定上為DMA重新整理記憶體是一個主要的性能損失。是以,ION使用了一個頁面池,它允許預先配置設定大量未緩存的頁面并一次性重新整理,然後當進行較小的配置設定時,它們隻從池中挑選頁面。

不幸的是,從上遊的角度來看,這兩種優化都有些問題。

  • 延遲的映射建立是有問題的,因為DMA API要麼使用分散聚集的清單,要麼使用更大的連續DMA區域;沒有用于重新整理單個頁面的通用接口。正因為如此,當ION試圖僅重新整理已碰過的頁面時,它最終會使用特定于arm的__dma_page_cpu_to_dev()函數,因為周遊分散聚集的清單來找到有問題的頁面代價太高。這個接口的使用使得ION隻能在32位的ARM系統上建構。
  • 預重新整理的頁面池也有問題:因為這些記憶體池是提前配置設定的,是以并不一定清楚哪個裝置将使用它們。通常,當為DMA重新整理頁面時,必須指定下一個将通路記憶體的裝置,是以,在IOMMU後面的裝置的情況下,可以設定IOMMU,以便裝置可以通路這些頁面。ION通過使用32位arm特有的__dma_page_cpu_to_dev()接口再次解決了這個問題,該接口不接受裝置參數。是以,這進一步限制了ION在更常見的IOMMUs環境中發揮作用的能力。

對于Android的使用來說,這一限制并不成問題。32位ARM是它的主要目标,而且,在英特爾系統上有一緻的記憶體和較少的裝置特定的限制,是以在那裡不需要ION。此外,對于Android的用例,IOMMUs可以靜态配置到特定的堆(例如,啟動時預留的分離記憶體),是以沒有必要動态地重新配置IOMMUs。但是這些限制對于上遊ION來說是有問題的。問題是,如果沒有這些優化,性能損失将會太大,是以Android不太可能使用更多上遊友好的方法而忽略這些優化。

Other ION details

由于ION是一個集中式配置設定器,它必須具有一定的靈活性,以便處理所有類型的硬體。是以,ION允許實作定義自己的堆,而不是預設提供的公共堆。另外,由于許多裝置可能有古怪的配置設定規則,例如在特定的記憶體庫上配置設定,ION允許由堆實作定義一些配置設定标志。它還提供了一個ION_IOC_CUSTOM ioctl()多路複用器,允許ION實作自己的緩沖區操作,比如更細粒度的緩存管理或特殊的配置設定器。然而,這樣做的缺點是,它使ION界面實際上是相當特定的硬體-在某些情況下,特定的裝置需要對ION核心進行相當大的改變。是以,必須對使用ION接口的使用者空間應用程式進行定制,使其為運作在其上的硬體使用特定的ION實作。同樣,這對于核心和使用者空間一起傳遞的嵌入式裝置來說并不是一個真正的問題,是以不需要嚴格的ABI一緻性,但對于上遊合并來說是一個問題。

ION的這種特定于硬體和實作的特性也給ION使用的集中式配置設定器方法的可行性帶來了問題。為了支援所有不同硬體的各種特性,它基本上具有特定于硬體的接口,進而迫使編寫特定于硬體的使用者空間應用程式。這就消除了使用集中式配置設定器而不是使用特定于裝置的配置設定器的一些概念上的好處。然而,Android開發人員認為,ION是一個集中的記憶體管理器,它們可以減少每個裝置驅動程式的複雜代碼的必須實作,并且允許優化,而不是一遍又一遍實作各種不同的驅動程式。

總結一下圍繞ION的問題:

  • 它沒有提供發現裝置限制的方法。
  • 該接口向使用者空間公開特定于硬體的堆id。
  • 集中式接口并不是對所有裝置都足夠通用,是以它為特定于裝置的選項公開了ioctl()多路複用器。
  • ION隻從自身進口dma-bufs。
  • 它沒有正确地使用DMA API,在為DMA重新整理緩存時未能指定裝置。
  • ION隻建立在32位ARM系統上。

ION compared to current upstream solutions

在某些方面,GEM是一個類似的記憶體配置設定和共享系統。它提供了一個用于配置設定圖形緩沖區的API,應用程式可以使用該緩沖區與圖形驅動程式通信。另外,GEM為應用程式提供了一種将已配置設定的緩沖區傳遞給另一個程序的方法。為此,需要使用DRM_IOCTL_GEM_FLINK操作,該操作提供了特定于gem的引用,在概念上類似于可以通過套接字傳遞給另一個程序的檔案描述符。這樣做的一個缺點是,這些特定于gem的“flink”引用隻是一個32位的全局值,是以可以被本來不應該通路它們的應用程式猜測,進而進行hack操作。GEM配置設定緩沖區的另一個問題是,它們是特定于它們被配置設定的裝置的。是以,雖然GEM緩沖區可以在應用程式之間共享,但沒有辦法在不同的裝置之間共享GEM緩沖區。

随着混合圖形實作的出現(通常是獨立的NVIDIA gpu與內建的Intel gpu相結合),裝置之間共享緩沖區的需求出現了,于是建立了dma-bufs和PRIME(用于裝置之間共享緩沖區的gem特定機制)。

在大多數情況下,dma-bufs可以被認為是緩沖區的封送結構。dma-buf系統不提供任何配置設定方法,但提供了一個通用結構,可用于在許多不同的裝置和應用程式之間共享緩沖區。dma-buf結構使用檔案描述符共享給使用者空間,這避免了GEM flink id的潛在安全問題。

DRM PRIME基礎設施允許驅動程式通過dma-bufs共享GEM緩沖區,進而使Nouveau驅動程式能夠直接渲染到英特爾驅動程式将顯示在螢幕上的緩沖區中。通過這種方式,GEM和PRIME一起提供了類似于ION的功能,允許ION在更傳統的桌面機器上的soc上實作的緩沖區共享類型(都利用dma-bufs)。然而,PRIME并不處理裝置可以通路的記憶體類型的任何資訊,它隻是允許GEM驅動程式利用dma-buf共享,前提是所有共享緩沖區的裝置都可以通路它。

V4L子系統用于錄影機和視訊錄像機,也具有內建的dma-buf功能,允許相機緩沖器與圖形卡和其他裝置共享。它提供了自己的配置設定接口,但是,像GEM一樣,這些配置設定接口隻確定被配置設定的緩沖區與驅動程式管理的裝置一起工作,而不知道緩沖區可能與其他驅動程式共享的限制。

是以,在目前的上遊方法中,為了在裝置之間共享緩沖區,使用者空間必須知道哪些裝置将共享緩沖區,以及哪些裝置有最嚴格的限制;然後它必須使用最受限制的驅動程式使用的API來配置設定緩沖區。同樣,就像在ION的情況下,使用者空間沒有可用的方法來确定哪個裝置是最受限制的。

是以,上遊問題可以這樣總結:.

  • 沒有現有的解決方案來解決裝置之間共享緩沖區的限制。
  • 對于不同的裝置有不同的配置設定API,是以,一旦使用者确定了最受限制的裝置,他們就必須使用該裝置的比對API進行配置設定。
  • IOMMU 和 DMA API 接口目前不允許在 ION 中使用的 DMA 優化。

Possible solutions

以前,在社群讨論 ION 時,提出了一些可能的方法。以下是我所知的。

一個想法是嘗試将一個集中式的ION式配置設定器上遊合并,保持相似的界面。為了解決有問題的限制發現問題,裝置将通過sysfs和/或通過ioctl()導出不透明的堆cookie,這取決于裝置的需求(裝置可能有不同的需求,取決于裝置特定的配置)。位的含義不會被定義到使用者空間,而是可以被使用者空間應用程式和在一起并傳遞給配置設定器,就像堆掩碼目前是ION的一樣。這為使用者空間提供了一種解決限制的方法,但避免了将堆類型固定到ABI中的問題;它還允許核心定義給定機器的哪個位代表哪個堆,使接口更加靈活和可擴充。然而,對于使用者空間來說,這是一個更複雜的界面,許多人不喜歡将限制資訊公開給使用者空間的想法,即使是以不透明的cookie的形式。

另一個可能的解決方案是允許dma-buf導出器不立即配置設定支援緩沖區。這将允許多個驅動程式在配置設定之前附加到dma-buf上。然後,當緩沖區第一次被使用時,配置設定完成;此時,配置設定器可以掃描附加的驅動程式清單,并能夠确定附加裝置的限制并相應地配置設定記憶體。這将允許使用者空間不必處理任何限制問題。

雖然在最初設計dma-bufs時就計劃了這種方法,但仍然缺少急需的基礎設施,而且目前還沒有使用這種解決方案的驅動程式。Android開發者擔心這種延遲配置設定可能會導緻應用程式熱路徑的不确定性延遲,但是,在沒有實作的情況下,這還沒有被量化.另一個缺點是,并非所有dma-buf導出器都需要這種延遲配置設定,是以它隻适用于實際實作該特性的驅動程式。然而存在一種可能性,并不是所有的驅動想分享一個緩沖區都支援延遲配置設定,應用程式必須以某種方式檢測功能,確定配置設定記憶體共享使用dma-buf出口者支援這個延遲配置設定功能。這種方法還要求導出驅動程式配置設定器分别處理這個限制問題(雖然可以提供一些常見的輔助函數)。

另一種可能的方法是使用通用的dma-buf導出器來原型dma-buf後期配置設定限制解決方案。這在某種程度上有點像ION,因為它是一個集中的導出器,但不會向使用者空間公開堆id。然後,緩沖區将被附加到各種硬體驅動程式上,并且,在第一次使用時,導出程式将确定附加的限制并配置設定緩沖區。這将為上面的延遲配置設定方法提供一個試驗場,同時在概念上與ION有一些相似之處。這種方法的缺點是,集中的接口可能無法處理可能需要的更複雜的特定于硬體的配置設定标記。

最後,這些建議都沒有解決非通用緩存優化的使用,是以這些問題還需要進一步讨論。

Conference Discussion

我懷疑在Linux Plumbers Android + Graphics 會議上,我們不會找到一個神奇或簡單的解決方案來讓Android的ION功能上遊。但我希望,通過關鍵開發人員從Android團隊和上遊核心社群能夠讨論他們的需求和限制,能夠傾聽對方,我們可以了解子問題必須得到解決,我們可以采取什麼方向前進。為此,我提出了幾個問題供大家思考和讨論,希望我們能在讨論過程中找到答案:

  • 目前上遊的dma-buf共享使用,比如PRIME,似乎集中在x86用例上(比如兩個裝置共享緩沖區)。這些接口真的能以通用的方式擴充到arm風格的用例(許多裝置共享緩沖區)嗎?由于非集中式配置設定要求導出器管理更多的邏輯并了解裝置限制,是以這種方法似乎有可能最終無法維護。
  • ION的集中式配置設定風格在許多情況下都有問題,但也提供了顯著的性能提高。這是一個太大的僵局還是有辦法前進?
  • 如果集中式的dma-buf配置設定API是前進的方向,那麼什麼是最好的方法(比如:堆-cookie, vs後附加配置設定)?
  • 還有哪些潛在的解決方案還沒有被考慮?
  • 有沒有辦法實作一些緩存優化使用的方式,也是更普遍适用的?可能延長IOMMU / DMA-API嗎?
  • 考慮到Android的需求,下一步該怎麼做才能集中到一個解決方案上呢?我們如何測試附加時間解決方案是否适用于Android開發者?ION還能提供什麼呢?
  • Android開發者打算如何處理IOMMUs和非32位ARM架構問題?

Some general reference links follow:

  • General graphics overview [PDF]
  • Caching overview
  • Write combining
  • ION overviews: 1, 2, 3
  • DRM overview
  • GEM: 1, 2
  • DMA-buf
  • PRIME

Credits

Thanks to Laurent Pinchart, Jesse Barker, Benjamin Gaignard and Dave Hansen for reviewing and providing feedback on early drafts of this document, and many thanks to Jon Corbet for his careful editing.

https://lwn.net/Articles/565469/

繼續閱讀