在 Android 12 中,GKI 2.0 會以 DMA-BUF 堆疊取代 ION 配置器,原因如下:
- 安全性:由於每個 DMA-BUF 堆積都是獨立的字元裝置,因此可以使用本政策個別控制每個堆積的存取權。這在 ION 中是不可能的,因為從任何堆疊中進行配置時,只需要存取
/dev/ion
裝置。 - ABI 穩定性:與 ION 不同,DMA-BUF 堆積架構的 IOCTL 介面是 ABI 穩定版,因為上游 Linux 核心會保留這個介面。
- 標準化:DMA-BUF 堆積架構提供定義明確的 UAPI。ION 允許自訂旗標和堆積 ID,這會導致無法開發通用測試架構,因為每部裝置的 ION 實作方式可能不同。
Android 通用核心的 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:堆疊:範例範本,從頭建立 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
檔案中新增項目。這個設定支援 DMA-BUF 堆積範例的設定示範瞭如何針對 DMA-BUF 系統堆積進行配置。
必要的 sepolicy 新增內容
新增 sepolicy 權限,讓使用者空間用戶端能夠存取新的 DMA-BUF 堆疊。這個新增必要權限範例顯示為各種用戶端建立的 sepolicy 權限,以便存取 DMA-BUF 系統堆疊。
從架構程式碼存取供應商堆疊
為確保符合 Treble 規定,架構程式碼只能從預先核准的供應商堆疊類別中進行配置。
Google 根據合作夥伴提供的意見回饋,識別出必須透過架構程式碼存取的兩種供應商堆積:
- 以系統堆積為基礎的堆積,可針對裝置或 SoC 進行效能最佳化。
- 要分配給受保護記憶體的堆積。
以系統堆疊為基礎的堆疊,並針對裝置或 SoC 進行效能最佳化
為了支援此用途,可以覆寫預設 DMA-BUF 堆積系統的堆積實作。
CONFIG_DMABUF_HEAPS_SYSTEM
會在gki_defconfig
中關閉,以便成為供應商模組。- VTS 相容性測試可確保堆積存在於
/dev/dma_heap/system
。這些測試也會驗證是否可從堆積分配堆積,以及傳回的檔案描述元 (fd
) 能否從使用者空間進行記憶體對應 (mmapped)。
上述要點也適用於系統堆積的未快取變化版本,但對於完全 IO 一致的裝置而言,這項變化版本並非必要。
從受保護記憶體分配的堆積
由於 Android 通用核心不支援一般安全堆疊實作,因此安全堆疊實作必須是特定供應商的。
- 將供應商專屬實作項目註冊為
/dev/dma_heap/system-secure<vendor-suffix>
。 - 這些堆積實作為選用項目。
- 如果堆積存在,VTS 測試會確保可從堆積進行配置。
- 架構元件會提供這些堆積的存取權,以便透過 Codec2 HAL/非繫結的相同程序 HAL 啟用堆積使用情形。不過,由於實作細節的變化,一般 Android 架構功能無法依賴這些功能。如果日後將一般安全堆疊實作項目新增至 Android 通用核心,則必須使用不同的 ABI,以免與升級裝置發生衝突。
DMA-BUF 堆積的轉碼器 2 分配器
Android 開放原始碼計畫提供 DMA-BUF 堆積介面的轉碼器 2 分配器。
元件存放區介面可讓您從 C2 HAL 指定堆積參數,並可搭配 C2 DMA-BUF 堆積分配器使用。
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 堆積建立 uevent 變更。此時,堆積會顯示為 /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()
叫用作業。