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 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 ảnh chụp 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 <inode_number>.
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name Tệp chỉ có thể đọ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ó thể đọ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.

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. Điều này là bắt buộc đối với libdmabufinfo và công cụ dmabuf_dump để lấy số liệu thống kê theo trình xuất, sau đó 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 không còn được dùng nữa mà thay vào đó là khung vùng nhớ khối xếp DMA-BUF, là một phần của nhân Linux ngược dòng.

Android 11 theo dõi các số liệu thống kê ION toàn cầu sau:

  • Tổng kích thước của DMA-BUF do mọi vùng nhớ khối xếp ION xuất
  • 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 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 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 Thiết bị khởi chạy 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ê ION theo 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 được 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 được xuất).
Tổng bộ nhớ được 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 (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 lượt 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
  • DMA-BUF được ánh xạ vào không gian địa chỉ GPU

Do đó, DMA-BUF được ánh xạ bộ nhớ vào không gian địa chỉ GPU đã 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à 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:

  • API Memtrack HAL 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, đối với MemtrackType::GL và MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • getMemory() khi được gọi bằng PID 0 cho MemtrackType khác với GL 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 bộ nhớ GPU/eBPF được thêm vào Android 12 tính tổng bộ nhớ GPU. Việc trừ tổng bộ nhớ riêng của GPU khỏi tổng bộ nhớ GPU sẽ cho biết kích thước của DMA-BUF được ánh xạ vào không gian địa chỉ GPU. Sau đó, bạn có thể sử dụng giá trị này để cải thiện độ chính xác của phép tính 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 hoạt động triển khai HAL Memtrack, do đó, bạn phải loại bỏ trùng lặp trước khi xoá bộ nhớ này 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á tính 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 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 loại bỏ cả việc tính hai lần và sự biến động của Memtrack từ việc tính toán 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 quy trình kiểm thử VTS thực thi quy tắc rằng các thiết bị chạy Android 12 với hạt nhân Linux phiên bản 5.4 trở lên sẽ 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 đề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();

Để đả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.