Triển khai tính năng kế toán bộ nhớ DMABUF và GPU trong Android 12

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

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

Trong Android 11 và Android 12, bạn không thể gắn debugfs trong 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 trên Android 12.

Đường dẫn Mô tả
/sys/kernel/dmabuf/buffers Thư mục /sys/kernel/dmabuf/buffers chứa ảnh chụp nhanh 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 có số inode duy nhất <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ê sysfs DMA-BUF để hiển thị số liệu thống kê cho mỗi trình xuất và mỗi vùng đệm.

Trình điều khiển nhân xuất DMA-BUF phải đặt trường exp_name của struct dma_buf_export_info một cách chính xác thành tên của 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 đối với libdmabufinfo và công cụ dmabuf_dump để lấy số liệu thống kê cho mỗi trình xuất, sau đó được 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, -b.

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

ION trong GKI 2.0 đang bị ngừng sử dụng để chuyển sang khung heap DMA-BUF, đây là một phần của nhân Linux nguồn mở.

Các số liệu thống kê ION chung sau đây được theo dõi trong Android 11:

  • Tổng kích thước của DMA-BUF do mọi vùng nhớ ION xuất
  • Tổng dung lượng bộ nhớ được phân bổ trước nhưng chưa dùng do mọi vùng nhớ ION lưu trữ

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

Bảng sau đây so sánh các giao diện thống kê ION với các giao diện tương ứng cho những thiết bị sử dụng khung heap 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ị ra mắt cùng với các nhóm DMA-BUF trong Android 12
Số liệu thống kê về ION cho 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ê sysfs DMA-BUF
Tổng kích thước của DMA-BUF đã xuất /sys/kernel/ion/total_heap_size_kb
(Không bao gồm kích thước của DMA-BUF do các trình xuất không phải ION xuất)
Được phân tích cú pháp từ số liệu thống kê sysfs DMA-BUF
(bao gồm kích thước của tất cả DMA-BUF đã xuất).
Tổng bộ nhớ được nhóm 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, cách tính RAM bị mất 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 (do 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 các thiết bị Android, bộ nhớ GPU đến từ những nguồn sau:

  • Các hoạt động phân bổ trực tiếp do trình điều khiển GPU thực hiện bằng cách sử dụng trình phân bổ trang thực
  • DMA-BUF được liên kết 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 sẽ bị trừ hai lần khi tính toán 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à chỉ được tính một lần trong phép tính RAM bị mất.

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

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

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

Bộ nhớ MemtrackType::GRAPHICS sẽ 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ạ trong mã sau:

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 sẽ bị xoá khỏi totalPss và được thay thế bằng bộ nhớ GPU riêng tư (gpuPrivateUsage) trong phép tính lostRAM trong ActivityManagerService.java như minh hoạ trong đoạn mã sau:

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 cách tính 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 nằm trong kernelUsed + totalPss, được xoá khỏi lostRAM. Điều này giúp loại bỏ cả việc tính hai lần và sự biến thiên Memtrack khỏi phép tính 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ả

Các kiểm thử VTS thực thi quy tắc rằng những thiết bị chạy Android 12 có nhân Linux phiên bản 5.4 trở lên sẽ hỗ trợ API getGpuDeviceInfo().

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

Điều này giúp việc hạch toán bộ nhớ và khả năng hiển thị vùng đệm DMA cũng như mức sử dụng bộ nhớ GPU trở nên hiệu quả hơn. Triển khai HAL AIDL memtrack để cải thiện khả năng kế toán RAM và bộ nhớ bị mất. 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ó trong mã dưới dạng nhận xét. Tất cả HAL HIDL đều dự kiến sẽ được chuyển đổi sang 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();

Để xác minh rằng phiên bản của bạn hoạt động như dự kiế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 AIDL memtrack HAL getMemory() để 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::GLMemtrackRecord::FLAG_SMAPS_UNACCOUNTED.