從 ION 轉換成 DMA-BUF 堆積

在 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)

struct dma_heap *dma_heap_find(const char *name)

struct dma_buf *struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, unsigned int fd_flags, unsigned int heap_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。

下表說明這些變更,並顯示 libionlibdmabufheap 如何執行未快取的系統堆積配置。

分配類型 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 根據合作夥伴提供的意見回饋,識別出必須透過架構程式碼存取的兩種供應商堆積:

  1. 以系統堆積為基礎的堆積,可針對裝置或 SoC 進行效能最佳化。
  2. 要分配給受保護記憶體的堆積。

以系統堆疊為基礎的堆疊,並針對裝置或 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_heapmy_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() 叫用作業。