在 Android 12 中,GKI 2.0 會以 DMA-BUF 堆積取代 ION 分配器,原因如下:
- 安全性:由於每個 DMA-BUF 堆積都是獨立的字元裝置,因此可使用 sepolicy 分別控管每個堆積的存取權。使用 ION 時無法這麼做,因為從任何堆積分配記憶體時,只需要存取
/dev/ion
裝置。 - ABI 穩定性:與 ION 不同,DMA-BUF 堆積架構的 IOCTL 介面是 ABI 穩定版,因為該介面是在上游 Linux 核心中維護。
- 標準化:DMA-BUF 堆積架構提供明確定義的 UAPI。ION 允許使用自訂標記和堆積 ID,但這會阻礙開發通用測試架構,因為每個裝置的 ION 實作方式可能不同。
Android Common Kernel 的 android12-5.10
分支版本已於 2021 年 3 月 1 日停用。CONFIG_ION
背景
以下簡要比較 ION 和 DMA-BUF 堆積。
ION 和 DMA-BUF 堆積架構的相似之處
- ION 和 DMA-BUF 堆積架構都是以堆積為基礎的 DMA-BUF 匯出工具。
- 兩者都允許每個堆積定義自己的分配器和 DMA-BUF 作業。
- 由於這兩種配置都需要單一 IOCTL 進行配置,因此配置效能相似。
ION 和 DMA-BUF 堆積框架之間的差異
ION 堆積 | DMA-BUF 堆積 |
---|---|
所有 ION 分配作業都透過 /dev/ion 完成。
|
每個 DMA-BUF 堆積都是位於 /dev/dma_heap/<heap_name> 的字元裝置。
|
ION 支援堆積私有旗標。 | DMA-BUF 堆積不支援堆積私有旗標。每種不同的配置類型都是從不同的堆積完成。舉例來說,快取和未快取的系統堆積變數是位於 /dev/dma_heap/system 和 /dev/dma_heap/system_uncached 的個別堆積。 |
必須指定堆積 ID/遮罩和旗標,才能進行分配。 | 堆積名稱用於分配。 |
以下各節列出處理 ION 的元件,並說明如何將這些元件切換至 DMA-BUF 堆積架構。
將核心驅動程式從 ION 轉換為 DMA-BUF 堆積
實作 ION 堆積的核心驅動程式
ION 和 DMA-BUF 堆積都會允許每個堆積實作自己的分配器和 DMA-BUF 作業。因此,您可以透過另一組 API 註冊堆積,從 ION 堆積實作切換至 DMA-BUF 堆積實作。下表列出 ION 堆積註冊 API,以及對應的 DMA-BUF 堆積 API。
ION 堆積 | DMA-BUF 堆積 |
---|---|
void ion_device_add_heap(struct ion_heap *heap)
|
struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
|
void ion_device_remove_heap(struct ion_heap *heap)
|
void dma_heap_put(struct dma_heap *heap);
|
DMA-BUF 堆積不支援堆積私有旗標。因此,您必須使用 dma_heap_add()
API 個別註冊每個堆積變體。為方便共用程式碼,建議在同一個驅動程式中,註冊同一個堆積的所有變體。這個 dma-buf: system_heap 範例顯示系統堆積的快取和未快取變數實作方式。
使用這個 dma-buf: heaps: example template 從頭建立 DMA-BUF 堆積。
核心驅動程式直接從 ION 堆積分配
DMA-BUF 堆積架構也為核心內用戶端提供分配介面。DMA-BUF 堆積提供的介面會將堆積名稱做為輸入內容,而不是指定堆積遮罩和旗標來選取配置類型。
以下顯示核心內 ION 分配 API,以及對應的 DMA-BUF 堆積分配 API。核心驅動程式可以使用 dma_heap_find()
API 查詢堆積是否存在。API 會傳回 struct dma_heap 執行個體的指標,然後可做為引數傳遞至 dma_heap_buffer_alloc()
API。
ION 堆積 | DMA-BUF 堆積 |
---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
使用 DMA-BUF 的核心驅動程式
如果驅動程式只匯入 DMA-BUF,則不需要進行任何變更,因為從 ION 堆積分配的緩衝區,行為與從同等 DMA-BUF 堆積分配的緩衝區完全相同。
將 ION 的使用者空間用戶端轉換為 DMA-BUF 堆積
為了方便 ION 的使用者空間用戶端進行轉換,我們提供名為 libdmabufheap
的抽象化程式庫。libdmabufheap
支援在 DMA-BUF 堆積和 ION 堆積中分配。系統會先檢查指定名稱的 DMA-BUF 堆積是否存在,如果不存在,則會回溯至對等的 ION 堆積 (如有)。
用戶端應在初始化期間初始化 BufferAllocator
物件,而非開啟 /dev/ion using
ion_open()
。這是因為開啟 /dev/ion
和 /dev/dma_heap/<heap_name>
所建立的檔案描述元,是由 BufferAllocator
物件在內部管理。
如要從 libion
切換至 libdmabufheap
,請按照下列方式修改用戶端行為:
- 追蹤要用於分配的堆積名稱,而非標頭 ID/遮罩和堆積標記。
- 將採用堆積遮罩和旗標引數的
ion_alloc_fd()
API,替換為採用堆積名稱的BufferAllocator::Alloc()
API。
下表說明這些變更,顯示 libion
和 libdmabufheap
如何執行未快取的系統堆積分配作業。
分配類型 | libion | libdmabufheap |
---|---|---|
系統堆積中快取的分配量 | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd)
|
allocator->Alloc("system", size)
|
系統堆積中未快取的配置 | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd)
|
allocator->Alloc("system-uncached", size)
|
未快取的系統堆積變體正在等待上游核准,但已是 android12-5.10
分支的一部分。
為支援裝置升級,MapNameToIonHeap()
API 可將堆積名稱對應至 ION 堆積參數 (堆積名稱或遮罩和旗標),讓這些介面使用以名稱為準的分配方式。以下是以名稱為準的分配範例。
您可參閱libdmabufheap
公開的每個 API 說明文件。程式庫也會公開標頭檔,供 C 用戶端使用。
參考 Gralloc 實作
Hikey960 gralloc 實作項目使用 libdmabufheap
,因此您可以將其做為參考實作項目。
必須新增 ueventd
針對建立的任何新裝置專屬 DMA-BUF 堆積,請在裝置的 ueventd.rc
檔案中新增項目。這個設定 ueventd 以支援 DMA-BUF 堆積的範例,說明如何為 DMA-BUF 系統堆積完成這項作業。
必須新增 sepolicy
新增 sepolicy 權限,讓使用者空間用戶端存取新的 DMA-BUF 堆積。這個新增必要權限範例顯示為各種用戶端建立的 sepolicy 權限,可存取 DMA-BUF 系統堆積。
透過架構程式碼存取供應商堆積
為確保符合 Treble 規範,架構程式碼只能從預先核准的供應商堆積類別中分配。
根據合作夥伴提供的意見回饋,Google 找出兩類必須從架構程式碼存取的供應商堆積:
- 以系統堆積為基礎,並針對裝置或 SoC 進行效能最佳化。
- 要從受保護的記憶體分配的堆積。
根據系統堆積,並針對裝置或 SoC 進行效能最佳化
如要支援這個使用情境,可以覆寫預設 DMA-BUF 堆積系統的堆積實作。
- 在
gki_defconfig
中關閉CONFIG_DMABUF_HEAPS_SYSTEM
,讓其成為供應商模組。 - VTS 遵循性測試可確保堆積存在於
/dev/dma_heap/system
。測試也會驗證堆積是否可從中分配,以及傳回的檔案描述元 (fd
) 是否可從使用者空間進行記憶體對應 (mmapped)。
系統堆積的未快取變體也適用上述要點,但對於完全 IO 一致的裝置而言,這並非必要。
要從受保護的記憶體配置的堆積
由於 Android Common Kernel 不支援一般安全堆積實作,因此安全堆積實作必須是供應商專屬。
- 將供應商專屬的實作項目註冊為
/dev/dma_heap/system-secure<vendor-suffix>
。 - 這些堆積實作項目為選用項目。
- 如果堆積存在,VTS 測試會確保可從中進行分配。
- 架構元件可存取這些堆積,因此能透過 Codec2 HAL/非繫結程序 HAL,啟用堆積使用情形。不過,由於實作細節各不相同,一般 Android 架構功能無法依附於這些功能。如果日後在 Android Common Kernel 中新增一般安全堆積實作項目,則必須使用不同的 ABI,以免與升級裝置發生衝突。
Codec 2 allocator for DMA-BUF heaps
Android 開放原始碼計畫 (AOSP) 提供 DMA-BUF 堆積介面的 codec2 分配器。
使用 C2 DMA-BUF 堆積分配器時,可透過元件商店介面指定 C2 HAL 的堆積參數。
ION 堆積的轉換流程範例
為順利從 ION 轉換至 DMA-BUF 堆積,libdmabufheap
允許一次切換一個堆積。以下步驟示範建議的工作流程,用於轉換名為 my_heap
的非舊版 ION 堆積,該堆積支援一個旗標 ION_FLAG_MY_FLAG
。
步驟 1:在 DMA-BUF 架構中建立 ION 堆積的對等項目。在本範例中,由於 ION 堆積 my_heap
支援旗標 ION_FLAG_MY_FLAG
,因此我們註冊了兩個 DMA-BUF 堆積:
my_heap
行為與停用ION_FLAG_MY_FLAG
旗標的 ION 堆積行為完全一致。my_heap_special
的行為與啟用ION_FLAG_MY_FLAG
旗標的 ION 堆積行為完全相同。
步驟 2:為新的 my_heap
和 my_heap_special
DMA-BUF 堆積建立 ueventd 變更。此時,堆積會顯示為 /dev/dma_heap/my_heap
和 /dev/dma_heap/my_heap_special
,並具有預期權限。
步驟 3:對於從 my_heap
分配的用戶端,請修改其 Makefile,連結至 libdmabufheap
。在用戶端初始化期間,請建立 BufferAllocator
物件的例項,並使用 MapNameToIonHeap()
API 將 <ION heap name/mask, flag>
組合對應至等效的 DMA-BUF 堆積名稱。
例如:
allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )
您可以將 ION 堆積名稱參數設為空白,藉此建立從 <ION heap mask, flag>
到對等 DMA-BUF 堆積名稱的對應,而不必使用 MapNameToIonHeap()
API 搭配名稱和旗標參數。
步驟 4:使用適當的堆積名稱,將 ion_alloc_fd()
呼叫替換為 BufferAllocator::Alloc()
。
分配類型 | libion | libdmabufheap |
---|---|---|
從 my_heap 分配,且旗標 ION_FLAG_MY_FLAG 未設定 |
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size)
|
從 my_heap 分配,並設定旗標
ION_FLAG_MY_FLAG
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP,
ION_FLAG_MY_FLAG, &fd)
|
allocator->Alloc("my_heap_special", size)
|
此時,用戶端可正常運作,但仍會從 ION 堆積分配,因為用戶端沒有開啟 DMA-BUF 堆積所需的 sepolicy 權限。
步驟 5:建立用戶端存取新 DMA-BUF 堆積所需的 sepolicy 權限。用戶端現在已完全具備從新的 DMA-BUF 堆積分配資源的能力。
步驟 6:檢查 logcat,確認分配作業是從新的 DMA-BUF 堆積進行。
步驟 7:在核心中停用 ION 堆積 my_heap
。如果用戶端程式碼不需要支援升級裝置 (核心可能只支援 ION 堆積),您也可以移除 MapNameToIonHeap()
呼叫。