Chuyển đổi từ vùng nhớ khối xếp ION sang DMA-BUF (chỉ dành cho nhân 5.4)

Trong Android 12, GKI 2.0 thay thế trình phân bổ ION bằng các vùng nhớ DMA-BUF vì những lý do sau:

  • Bảo mật: Vì mỗi vùng nhớ DMA-BUF là một thiết bị ký tự riêng biệt, nên quyền truy cập vào mỗi vùng nhớ có thể được kiểm soát riêng biệt bằng sepolicy. Điều này không thể thực hiện được với ION vì việc phân bổ từ bất kỳ vùng nhớ nào chỉ yêu cầu quyền truy cập vào thiết bị /dev/ion.
  • Tính ổn định của ABI: Không giống như ION, giao diện IOCTL của khung đống DMA-BUF có ABI ổn định vì được duy trì trong nhân Linux nguồn mở.
  • Tiêu chuẩn hoá: Khung nhóm DMA-BUF cung cấp một UAPI được xác định rõ ràng. ION cho phép các cờ tuỳ chỉnh và mã nhận dạng vùng nhớ heap ngăn chặn việc phát triển một khung kiểm thử chung vì mỗi cách triển khai ION của thiết bị có thể hoạt động theo cách khác nhau.

Nhánh android12-5.10 của Nhân chung Android đã bị vô hiệu hoá CONFIG_ION vào ngày 1 tháng 3 năm 2021.

Thông tin khái quát

Sau đây là nội dung so sánh ngắn gọn giữa các vùng nhớ ION và DMA-BUF.

Điểm tương đồng giữa khung ION và DMA-BUF

  • Cả khung ION và DMA-BUF đều là trình xuất DMA-BUF dựa trên heap.
  • Cả hai đều cho phép mỗi vùng nhớ xác định trình phân bổ và các thao tác DMA-BUF riêng.
  • Hiệu suất phân bổ tương tự nhau vì cả hai lược đồ đều cần một IOCTL duy nhất để phân bổ.

Sự khác biệt giữa khung ION và DMA-BUF

Vùng nhớ khối xếp ION Nhóm DMA-BUF
Tất cả các hoạt động phân bổ ION đều được thực hiện bằng /dev/ion. Mỗi vùng nhớ DMA-BUF là một thiết bị ký tự có ở /dev/dma_heap/<heap_name>.
ION hỗ trợ các cờ riêng tư của heap. Các vùng nhớ đệm DMA-BUF không hỗ trợ cờ riêng tư của vùng nhớ đệm. Thay vào đó, mỗi loại phân bổ khác nhau được thực hiện từ một vùng nhớ heap khác. Ví dụ: các biến thể của vùng nhớ heap hệ thống được lưu vào bộ nhớ đệm và không được lưu vào bộ nhớ đệm là các vùng nhớ heap riêng biệt nằm ở /dev/dma_heap/system/dev/dma_heap/system_uncached.
Bạn cần chỉ định cờ và mặt nạ/mã nhận dạng vùng nhớ heap để phân bổ. Tên vùng nhớ heap được dùng để phân bổ.

Các phần sau đây liệt kê những thành phần liên quan đến ION và mô tả cách chuyển các thành phần đó sang khung heap DMA-BUF.

Chuyển trình điều khiển nhân từ ION sang các vùng nhớ DMA-BUF

Trình điều khiển nhân triển khai các vùng nhớ ION

Cả ION và DMA-BUF đều cho phép mỗi vùng nhớ triển khai các trình phân bổ và thao tác DMA-BUF riêng. Vì vậy, bạn có thể chuyển từ một cách triển khai vùng nhớ heap ION sang một cách triển khai vùng nhớ heap DMA-BUF bằng cách sử dụng một nhóm API khác để đăng ký vùng nhớ heap. Bảng này cho thấy các API đăng ký vùng nhớ heap ION và các API vùng nhớ heap DMA-BUF tương đương.

Vùng nhớ khối xếp ION Nhóm 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);

Các vùng nhớ đệm DMA-BUF không hỗ trợ cờ riêng tư của vùng nhớ đệm. Vì vậy, mỗi biến thể của vùng nhớ heap phải được đăng ký riêng bằng cách sử dụng API dma_heap_add(). Để tạo điều kiện chia sẻ mã, bạn nên đăng ký tất cả các biến thể của cùng một vùng nhớ trong cùng một trình điều khiển. Ví dụ dma-buf: system_heap này cho thấy việc triển khai các biến thể được lưu vào bộ nhớ đệm và không được lưu vào bộ nhớ đệm của vùng nhớ hệ thống.

Sử dụng dma-buf: heaps: example template (dma-buf: heaps: mẫu ví dụ) này để tạo một vùng nhớ đệm DMA-BUF từ đầu.

Trình điều khiển nhân phân bổ trực tiếp từ các vùng nhớ ION

Khung heap DMA-BUF cũng cung cấp một giao diện phân bổ cho các ứng dụng trong nhân. Thay vì chỉ định mặt nạ và cờ heap để chọn loại phân bổ, giao diện do các heap DMA-BUF cung cấp sẽ lấy tên heap làm đầu vào.

Sau đây là API phân bổ ION trong nhân và các API phân bổ vùng nhớ DMA-BUF tương đương. Trình điều khiển nhân có thể sử dụng API dma_heap_find() để truy vấn sự tồn tại của một vùng nhớ. API này trả về một con trỏ đến một phiên bản của struct dma_heap, sau đó có thể được truyền dưới dạng một đối số đến API dma_heap_buffer_alloc().

Vùng nhớ khối xếp ION Nhóm 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)

Trình điều khiển nhân sử dụng DMA-BUF

Không cần thay đổi đối với những trình điều khiển chỉ nhập DMA-BUF, vì vùng đệm được phân bổ từ một nhóm ION hoạt động giống hệt như vùng đệm được phân bổ từ một nhóm DMA-BUF tương đương.

Chuyển đổi các ứng dụng không gian người dùng của ION sang các vùng nhớ DMA-BUF

Để giúp các ứng dụng không gian người dùng của ION dễ dàng chuyển đổi, một thư viện trừu tượng có tên là libdmabufheap có sẵn. libdmabufheap hỗ trợ việc phân bổ trong các vùng nhớ DMA-BUF và ION. Trước tiên, nó sẽ kiểm tra xem có tồn tại một vùng nhớ DMA-BUF có tên được chỉ định hay không. Nếu không, nó sẽ quay lại một vùng nhớ ION tương đương, nếu có.

Ứng dụng khách nên khởi tạo một đối tượng BufferAllocator trong quá trình khởi tạo thay vì mở /dev/ion using ion_open(). Điều này là do các bộ mô tả tệp được tạo bằng cách mở /dev/ion/dev/dma_heap/<heap_name> được đối tượng BufferAllocator quản lý nội bộ.

Để chuyển từ libion sang libdmabufheap, hãy sửa đổi hành vi của các ứng dụng như sau:

  • Theo dõi tên vùng nhớ heap để dùng cho việc phân bổ, thay vì mã nhận dạng/mặt nạ đầu và cờ vùng nhớ heap.
  • Thay thế API ion_alloc_fd() (nhận đối số cờ và mặt nạ heap) bằng API BufferAllocator::Alloc() (thay vào đó nhận tên heap).

Bảng này minh hoạ những thay đổi này bằng cách cho biết cách libionlibdmabufheap thực hiện việc phân bổ heap hệ thống chưa lưu vào bộ nhớ đệm.

Loại phân bổ libion libdmabufheap
Phân bổ được lưu vào bộ nhớ đệm từ vùng nhớ heap của hệ thống ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
Phân bổ chưa lưu vào bộ nhớ đệm từ vùng nhớ khối xếp hệ thống ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

Biến thể heap hệ thống chưa lưu vào bộ nhớ đệm đang chờ phê duyệt ở nguồn trên nhưng đã là một phần của nhánh android12-5.10.

Để hỗ trợ nâng cấp thiết bị, API MapNameToIonHeap() cho phép ánh xạ tên heap với các tham số heap ION (tên heap hoặc mặt nạ và cờ) để cho phép các giao diện đó sử dụng các hoạt động phân bổ dựa trên tên. Sau đây là một ví dụ về việc phân bổ dựa trên tên.

Tài liệu cho mọi API do libdmabufheap cung cấp đều có sẵn. Thư viện này cũng hiển thị một tệp tiêu đề để các ứng dụng C sử dụng.

Triển khai Gralloc tham chiếu

Việc triển khai gralloc Hikey960 sử dụng libdmabufheap, vì vậy, bạn có thể sử dụng nó làm triển khai tham chiếu.

Các mục bổ sung bắt buộc cho ueventd

Đối với mọi vùng nhớ DMA-BUF dành riêng cho thiết bị mới được tạo, hãy thêm một mục mới vào tệp ueventd.rc của thiết bị. Ví dụ Thiết lập ueventd để hỗ trợ các vùng nhớ đống DMA-BUF này minh hoạ cách thực hiện cho vùng nhớ đống hệ thống DMA-BUF.

Các nội dung bổ sung bắt buộc cho sepolicy

Thêm các quyền sepolicy để cho phép một ứng dụng không gian người dùng truy cập vào một vùng nhớ DMA-BUF mới. Ví dụ thêm các quyền bắt buộc này cho thấy các quyền sepolicy được tạo cho nhiều ứng dụng để truy cập vào vùng nhớ hệ thống DMA-BUF.

Truy cập vào các heap của nhà cung cấp từ mã khung

Để đảm bảo tuân thủ Treble, mã khung chỉ có thể phân bổ từ các danh mục được phê duyệt trước của các heap nhà cung cấp.

Dựa trên ý kiến phản hồi của các đối tác, Google xác định 2 danh mục heap của nhà cung cấp mà bạn phải truy cập từ mã khung:

  1. Các vùng nhớ dựa trên vùng nhớ hệ thống với các chế độ tối ưu hoá hiệu suất dành riêng cho thiết bị hoặc SoC.
  2. Các vùng nhớ cần phân bổ từ bộ nhớ được bảo vệ.

Các vùng nhớ dựa trên vùng nhớ hệ thống với các chế độ tối ưu hoá hiệu suất dành riêng cho thiết bị hoặc SoC

Để hỗ trợ trường hợp sử dụng này, bạn có thể ghi đè việc triển khai vùng nhớ heap của hệ thống vùng nhớ heap DMA-BUF mặc định.

  • CONFIG_DMABUF_HEAPS_SYSTEM bị tắt trong gki_defconfig để cho phép CONFIG_DMABUF_HEAPS_SYSTEM là một mô-đun nhà cung cấp.
  • Các bài kiểm tra tuân thủ VTS đảm bảo rằng heap tồn tại ở /dev/dma_heap/system. Các kiểm thử này cũng xác minh rằng có thể phân bổ vùng nhớ từ vùng nhớ và rằng có thể ánh xạ bộ nhớ (mmapped) cho bộ mô tả tệp (fd) được trả về từ không gian người dùng.

Các điểm nêu trên cũng đúng với biến thể không được lưu vào bộ nhớ đệm của vùng nhớ heap hệ thống, mặc dù sự tồn tại của biến thể này không bắt buộc đối với các thiết bị hoàn toàn nhất quán về IO.

Các vùng nhớ cần phân bổ từ bộ nhớ được bảo vệ

Các quy trình triển khai heap bảo mật phải dành riêng cho nhà cung cấp vì Nhân Android chung không hỗ trợ quy trình triển khai heap bảo mật chung.

  • Đăng ký các phương thức triển khai dành riêng cho nhà cung cấp dưới dạng /dev/dma_heap/system-secure<vendor-suffix>.
  • Bạn không bắt buộc phải triển khai các heap này.
  • Nếu có các vùng nhớ này, các bài kiểm thử VTS sẽ đảm bảo rằng bạn có thể phân bổ từ các vùng nhớ đó.
  • Các thành phần khung được cấp quyền truy cập vào các vùng nhớ này để có thể cho phép sử dụng vùng nhớ thông qua Codec2 HAL/HAL không liên kết, HAL cùng quy trình. Tuy nhiên, các tính năng chung của khung Android không thể phụ thuộc vào các tính năng này do sự khác biệt về thông tin chi tiết triển khai. Nếu một chế độ triển khai heap bảo mật chung được thêm vào Nhân chung Android trong tương lai, thì chế độ này phải sử dụng một ABI khác để tránh xung đột với các thiết bị nâng cấp.

Trình phân bổ Codec 2 cho các vùng nhớ DMA-BUF

Một trình phân bổ codec2 cho giao diện DMA-BUF heaps có trong AOSP.

Giao diện kho thành phần cho phép chỉ định các tham số heap từ C2 HAL có sẵn với trình phân bổ heap C2 DMA-BUF.

Luồng chuyển đổi mẫu cho một vùng nhớ ION

Để quá trình chuyển đổi từ ION sang DMA-BUF diễn ra suôn sẻ, libdmabufheap cho phép chuyển đổi từng nhóm tại một thời điểm. Các bước sau đây minh hoạ một quy trình đề xuất để chuyển đổi một vùng nhớ ION không phải cũ có tên là my_heap hỗ trợ một cờ, ION_FLAG_MY_FLAG.

Bước 1: Tạo các thành phần tương đương của vùng nhớ ION trong khung DMA-BUF. Trong ví dụ này, vì vùng nhớ ION my_heap hỗ trợ cờ ION_FLAG_MY_FLAG, nên chúng ta đăng ký 2 vùng nhớ DMA-BUF:

  • Hành vi my_heap khớp chính xác với hành vi của vùng nhớ ION khi cờ ION_FLAG_MY_FLAG bị tắt.
  • Hành vi của my_heap_special khớp chính xác với hành vi của ION heap khi bật cờ ION_FLAG_MY_FLAG.

Bước 2: Tạo các thay đổi ueventd cho my_heapmy_heap_special DMA-BUF heap mới. Tại thời điểm này, các vùng nhớ heap sẽ xuất hiện dưới dạng /dev/dma_heap/my_heap/dev/dma_heap/my_heap_special, với các quyền dự kiến.

Bước 3: Đối với những ứng dụng phân bổ từ my_heap, hãy sửa đổi tệp makefile để liên kết đến libdmabufheap. Trong quá trình khởi chạy ứng dụng, hãy tạo thực thể một đối tượng BufferAllocator và sử dụng API MapNameToIonHeap() để liên kết tổ hợp <ION heap name/mask, flag> với tên heap DMA-BUF tương đương.

Ví dụ:

allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )

Thay vì sử dụng API MapNameToIonHeap() với các tham số tên và cờ, bạn có thể tạo mối liên kết từ <ION heap mask, flag> đến các tên nhóm DMA-BUF tương đương bằng cách đặt tham số tên nhóm ION thành giá trị trống.

Bước 4: Thay thế các lệnh gọi ion_alloc_fd() bằng BufferAllocator::Alloc() bằng cách sử dụng tên vùng nhớ heap thích hợp.

Loại phân bổ libion libdmabufheap
Phân bổ từ my_heap có cờ ION_FLAG_MY_FLAG chưa được đặt ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
Phân bổ từ my_heap có cờ ION_FLAG_MY_FLAG được đặt ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

Tại thời điểm này, ứng dụng hoạt động bình thường nhưng vẫn phân bổ từ vùng nhớ ION vì không có các quyền sepolicy cần thiết để mở vùng nhớ DMA-BUF.

Bước 5: Tạo các quyền sepolicy cần thiết để ứng dụng có thể truy cập vào các vùng nhớ DMA-BUF mới. Giờ đây, ứng dụng đã được trang bị đầy đủ để phân bổ từ vùng nhớ heap DMA-BUF mới.

Bước 6: Xác minh rằng các hoạt động phân bổ đang diễn ra từ vùng nhớ heap DMA-BUF mới bằng cách kiểm tra logcat.

Bước 7: Tắt ION heap my_heap trong nhân. Nếu mã ứng dụng không cần hỗ trợ nâng cấp thiết bị (có thể chỉ hỗ trợ các vùng nhớ ION), bạn cũng có thể xoá các lệnh gọi MapNameToIonHeap().