Triển khai DMABUF và việc tính toán bộ nhớ GPU trong Android 12

Trang này mô tả nhiều điểm cải tiến về việc tính toán bộ nhớ được giới thiệu trong Android 12.

Số liệu thống kê DMA-BUF trong sysfs

Trong Android 11 và Android 12, bạn không thể gắn debugfs trong các bản dựng Người dùng. Vì vậy, số liệu thống kê DMA-BUF đã được thêm vào sysfs trong thư mục /sys/kernel/dmabuf/buffers trong Android 12.

Đường dẫn Mô tả
/sys/kernel/dmabuf/buffers Thư mục /sys/kernel/dmabuf/buffers chứa thông tin tổng quan nhanh về trạng thái nội bộ của mỗi DMA-BUF. /sys/kernel/dmabuf/buffers/<inode_number> chứa số liệu thống kê cho DMA-BUF với số inode duy nhất là <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Tệp chỉ đọc này chứa tên của trình xuất DMA-BUF.
/sys/kernel/dmabuf/buffers/<inode_number>/size Tệp chỉ đọc này chỉ định kích thước của DMA-BUF tính bằng byte.

API libdmabufinfo phân tích cú pháp số liệu thống kê DMA-BUF sysfs để hiển thị số liệu thống kê theo từng trình xuất và mỗi vùng đệm.

Xin lưu ý rằng các trình điều khiển hạt nhân xuất DMA-BUF phải đặt trường exp_name của struct dma_buf_export_info chính xác thành tên trình xuất trước khi gọi API dma_buf_export() để tạo DMA-BUF. Đây là yêu cầu bắt buộc để libdmabufinfo và công cụ dmabuf_dump lấy số liệu thống kê theo từng trình xuất, sau đó sẽ hiển thị trong báo cáo lỗi.

Công cụ dmabuf_dump đã được sửa đổi để xuất thông tin này bằng một đối số mới là -b.

Số liệu thống kê cho khung vùng nhớ khối xếp DMA-BUF

ION trong GKI 2.0 sẽ không được dùng nữa và thay vào đó là khung vùng nhớ khối xếp DMA-BUF, một phần của nhân hệ điều hành linux ngược dòng.

Số liệu thống kê toàn cầu sau đây về ION được theo dõi trong Android 11:

  • Tổng kích thước của DMA-BUF được xuất theo mỗi vùng nhớ khối xếp ION
  • Tổng dung lượng bộ nhớ được phân bổ trước không sử dụng được lưu trữ bởi mọi vùng nhớ khối xếp ION

Không có giao diện nào để hiển thị số liệu thống kê về vùng nhớ khối xếp cho mỗi ION trong Android 11.

Bảng sau đây so sánh giao diện thống kê ION với các giao diện tương ứng cho các thiết bị sử dụng khung vùng nhớ khối xếp DMA-BUF trong Android 12.

Android 11 hoặc các thiết bị ra mắt có hỗ trợ ION trong Android 12 Thiết bị khởi chạy bằng vùng nhớ khối xếp DMA-BUF trong Android 12
Số liệu thống kê về ION trên mỗi vùng nhớ khối xếp Không có Được phân tích cú pháp từ số liệu thống kê hệ thống theo DMA-BUF
Tổng kích thước của các DMA-BUF đã xuất /sys/kernel/ion/total_heap_size_kb
(Không bao gồm quy mô của DMA-BUF do các nhà xuất khẩu không phải ION xuất ra)
Được phân tích cú pháp từ số liệu thống kê hệ thống theo DMA-BUF
(bao gồm kích thước của tất cả các DMA-BUF đã xuất).
Tổng bộ nhớ gộp theo vùng nhớ khối xếp /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

Cải thiện độ chính xác của việc tính toán RAM bị mất

Trước đây, việc tính toán RAM bị mất được thực hiện như sau:

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)

memInfo.getFreeSizeKb()memInfo.getCachedSizeKb()

kernelUsedmemInfo.getZramTotalSizeKb();

Thành phần totalPss bao gồm mức sử dụng bộ nhớ GPU (được giao diện getMemory() của Memtrack HAL trả về). Thành phần kernelUsed bao gồm tổng mức sử dụng bộ nhớ DMA-BUF. Tuy nhiên, đối với thiết bị Android, bộ nhớ GPU bắt nguồn từ các nguồn sau:

  • Phân bổ trực tiếp do trình điều khiển GPU thực hiện bằng trình phân bổ trang thực tế
  • DMA-BUF được ánh xạ vào không gian địa chỉ GPU

Do đó, các DMA-BUF được ánh xạ bộ nhớ vào không gian địa chỉ GPU bị trừ 2 lần khi tính toán lượng RAM bị mất. Android 12 triển khai một giải pháp để tính toán kích thước của DMA-BUF được ánh xạ vào không gian địa chỉ GPU, tức là kích thước này chỉ được tính một lần trong quá trình tính toán RAM bị mất.

Sau đây là thông tin chi tiết về giải pháp này:

  • API Memtrack HAL getMemory() khi được gọi bằng PID 0 phải báo cáo tổng bộ nhớ riêng tư của GPU chung đối với MemtrackType::GL và MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() khi được gọi bằng PID 0 cho MemtrackType khác GL sẽ không bị lỗi. Thay vào đó, giá trị này phải trả về 0.
  • Giải pháp điểm theo dõi bộ nhớ GPU/eBPF được thêm vào Android 12 tính tổng bộ nhớ GPU. Lấy tổng bộ nhớ GPU trừ đi tổng bộ nhớ GPU sẽ cung cấp kích thước của DMA-BUF được ánh xạ vào không gian địa chỉ GPU. Sau đó, giá trị này có thể được dùng để cải thiện độ chính xác của các phép tính dung lượng RAM bị mất bằng cách tính chính xác mức sử dụng bộ nhớ GPU.
  • Bộ nhớ GPU riêng tư được đưa vào totalPss trong hầu hết các quá trình triển khai HAL Memtrack, do đó, phải được loại bỏ trùng lặp trước khi xoá khỏi lostRAM.

Giải pháp đã triển khai được trình bày chi tiết trong phần tiếp theo.

Xoá sự biến động của Memtrack khỏi RAM bị mất

Vì cách triển khai HAL Memtrack có thể khác nhau giữa các đối tác, nên bộ nhớ GPU có trong totalPSS từ HAL không phải lúc nào cũng nhất quán. Để xoá sự biến thiên khỏi lostRAM, bộ nhớ được tính trong MemtrackType::GRAPHICSMemtrackType::GL sẽ bị xoá khỏi totalPss trong quá trình tính toán lostRAM.

Bộ nhớ MemtrackType::GRAPHICS bị xoá khỏi totalPss và được thay thế bằng bộ nhớ totalExportedDmabuf trong phép tính lostRAM trong ActivityManagerService.java như minh hoạ dưới đây:

final long totalExportedDmabuf = Debug.getDmabufTotalExportedKb();

. . .

final long dmabufUnmapped = totalExportedDmabuf - dmabufMapped;

. . .

// Account unmapped dmabufs as part of the kernel memory allocations
kernelUsed += dmabufUnmapped;

// Replace Memtrack HAL reported Graphics category with mapped dmabufs
totalPss -= totalMemtrackGraphics;
totalPss += dmabufMapped;

Bộ nhớ MemtrackType::GL bị xoá khỏi totalPss và được thay thế bằng bộ nhớ GPU riêng (gpuPrivateUsage) trong phép tính lostRAM trong ActivityManagerService.java như minh hoạ dưới đây:

final long gpuUsage = Debug.getGpuTotalUsageKb();

. . .

final long gpuPrivateUsage = Debug.getGpuPrivateMemoryKb();

. . .

// Replace the Memtrack HAL-reported GL category with private GPU allocations.
// Count it as part of the kernel memory allocations.
totalPss -= totalMemtrackGl;
kernelUsed += gpuPrivateUsage;

Đã cập nhật phép tính dung lượng RAM bị mất

Cả tổng bộ nhớ GPU riêng tư và tổng bộ nhớ vùng đệm DMA đã xuất đều có trong kernelUsed + totalPss và sẽ bị xoá khỏi lostRAM. Điều này giúp loại bỏ cả biến thiên tính hai lần và Memtrack do phép tính dung lượng RAM bị mất.

final long lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)
- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()
- kernelUsed - memInfo.getZramTotalSizeKb();

Xác nhận kết quả

Hoạt động kiểm thử VTS sẽ thực thi quy tắc mà các thiết bị chạy trong Android 12 có nhân hệ điều hành Linux phiên bản 5.4 trở lên hỗ trợ API getGpuDeviceInfo().

API HAL Memtrack mới getGpuDeviceInfo() phải trả về thông tin về thiết bị GPU đang sử dụng.

Điều này giúp tính toán bộ nhớ và nắm rõ hơn về mức sử dụng bộ nhớ GPU và vùng đệm DMA. Triển khai HAL memtrack AIDL để tính toán bộ nhớ và RAM bị mất hiệu quả hơn. Tính năng này không phụ thuộc vào các dịch vụ của Google.

Triển khai

Tính năng này phụ thuộc vào AIDL Memtrack HAL và hướng dẫn triển khai tính năng này trong Android 12 được đưa vào mã dưới dạng nhận xét.

Tất cả HAL HIDL theo kế hoạch sẽ được chuyển đổi thành AIDL trong các bản phát hành sau này.

Các API sau đây đã được thêm vào core/java/android/os/Debug.java:

   /**
     * Return total memory size in kilobytes for exported DMA-BUFs or -1 if
     * the DMA-BUF sysfs stats at /sys/kernel/dmabuf/buffers could not be read.
     *
     * @hide
     */
    public static native long getDmabufTotalExportedKb();

   /**
     * Return memory size in kilobytes allocated for DMA-BUF heap pools or -1 if
     * /sys/kernel/dma_heap/total_pools_kb could not be read.
     *
     * @hide
     */
    public static native long getDmabufHeapPoolsSizeKb();

Để đảm bảo phiên bản của bạn hoạt động như mong muốn, hãy tích hợp các điểm theo dõi trong trình điều khiển GPU và triển khai API getMemory() HAL memtrack AIDL để trả về chính xác tổng bộ nhớ riêng của GPU trên toàn cầu khi được gọi bằng PID 0 cho MemtrackType::GL và MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.