Chuyển đổi từ vùng ION sang vùng DMA-BUF

Trong Android 12, GKI 2.0 thay thế bộ cấp phát ION bằng vùng heap 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 từng vùng nhớ có thể được kiểm soát riêng biệt bằng chính sách riêng biệt. Đ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ớ heap nào chỉ yêu cầu quyền truy cập vào thiết bị /dev/ion .
  • Độ ổn định của ABI: Không giống như ION, giao diện IOCTL của khung heap DMA-BUF được đảm bảo ổn định ABI vì nó được duy trì trong nhân Linux ngược dòng.
  • Tiêu chuẩn hóa: Khung heap DMA-BUF cung cấp UAPI được xác định rõ ràng. ION cho phép các cờ tùy chỉnh và mã vùng nhớ ngăn cản việc phát triển một khung thử nghiệm chung vì việc triển khai ION của mỗi thiết bị có thể hoạt động khác nhau.

Nhánh android12-5.10 của Android Common Kernel đã vô hiệu hóa CONFIG_ION vào ngày 1 tháng 3 năm 2021 .

Lý lịch

Sau đây là so sánh ngắn gọn giữa đống ION và DMA-BUF.

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

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

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

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

Các phần sau liệt kê các thành phần xử lý ION và mô tả cách chuyển chúng sang khung heap DMA-BUF.

Chuyển đổi trình điều khiển hạt nhân từ vùng ION sang vùng DMA-BUF

Trình điều khiển hạt nhân triển khai vùng ION

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

đống ION Đống 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);

Vùng lưu trữ DMA-BUF không hỗ trợ cờ riêng của vùng lưu trữ. Vì vậy, mỗi biến thể của vùng heap phải được đăng ký riêng lẻ bằng API dma_heap_add() . Để tạo điều kiện thuận lợi cho việc 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 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 trong bộ nhớ đệm và không được lưu trong bộ nhớ đệm của vùng nhớ hệ thống.

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

Trình điều khiển hạt nhân phân bổ trực tiếp từ đống ION

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

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

đống ION Đống 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 hạt nhân sử dụng DMA-BUF

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

Chuyển đổi các máy khách trong không gian người dùng của ION sang vùng lưu trữ DMA-BUF

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

Khách hàng nên khởi tạo đố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 khách hàng như sau:

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

Bảng này minh họa những thay đổi này bằng cách hiển thị cách libion ​​và libdmabufheap thực hiện phân bổ vùng heap hệ thống không được lưu vào bộ đệm.

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

Biến thể vùng nhớ hệ thống không được lưu vào bộ nhớ đệm đang chờ phê duyệt ngược dòng 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 vùng nhớ heap tới các tham số vùng nhớ heap ION (tên vùng nhớ/mặt nạ và cờ) để cho phép các giao diện đó cũng sử dụng phân bổ dựa trên tên. Đây là một ví dụ phân bổ dựa trên tên .

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

Tham khảo triển khai Gralloc

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 khảo .

Các bổ sung sự kiện bắt buộc

Đố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ục nhập mới vào tệp ueventd.rc của thiết bị. Ví dụ về sự kiện Thiết lập để hỗ trợ vùng nhớ DMA-BUF này minh họa cách thực hiện điều này đối với vùng nhớ hệ thống DMA-BUF.

Yêu cầu bổ sung chính sách riêng biệt

Thêm quyền riêng biệt để cho phép ứng dụng khách trong không gian người dùng truy cập vào vùng lưu trữ DMA-BUF mới. Ví dụ về quyền bổ sung bắt buộc này hiển thị các quyền riêng biệt được tạo cho nhiều máy khách khác nhau để truy cập vào vùng nhớ hệ thống DMA-BUF.

Truy cập đống 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 vùng nhớ của nhà cung cấp đã được phê duyệt trước.

Dựa trên phản hồi nhận được từ các đối tác, Google đã xác định hai loại vùng nhớ của nhà cung cấp phải được truy cập từ mã khung:

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

Heap dựa trên heap hệ thống với các tối ưu hóa 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, việc triển khai vùng nhớ khối của hệ thống vùng nhớ khối DMA-BUF mặc định có thể bị ghi đè.

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

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

Heap để phân bổ từ bộ nhớ được bảo vệ

Việc triển khai vùng nhớ khối bảo mật phải dành riêng cho nhà cung cấp vì Hạt nhân chung của Android không hỗ trợ việc triển khai vùng nhớ vùng nhớ bảo mật chung.

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

Bộ cấp phát Codec 2 cho đống DMA-BUF

Bộ cấp phát codec2 cho giao diện heap DMA-BUF có sẵn trong AOSP.

Giao diện lưu trữ thành phần cho phép chỉ định các tham số heap từ C2 HAL có sẵn với bộ cấp phát heap C2 DMA-BUF.

Luồng chuyển tiếp mẫu cho vùng ION

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

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

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

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

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

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

Loại phân bổ sư tử libdmabufheap
Phân bổ từ my_heap với cờ ION_FLAG_MY_FLAG không đượ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 với 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, máy khách đã hoạt động nhưng vẫn được phân bổ từ vùng nhớ heap ION vì nó không có các quyền tách biệt cần thiết để mở vùng nhớ heap DMA-BUF.

Bước 5: Tạo các quyền riêng biệt cần thiết để máy khách truy cập vào vùng lưu trữ DMA-BUF mới. Máy khách hiện đã được trang bị đầy đủ để phân bổ từ vùng DMA-BUF mới.

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

Bước 7: Vô hiệu hóa ION heap my_heap trong kernel. Nếu mã máy khách không cần hỗ trợ nâng cấp thiết bị (nhân của chúng chỉ có thể hỗ trợ vùng nhớ ION), bạn cũng có thể xóa lệnh gọi MapNameToIonHeap() .