پیاده سازی حسابداری حافظه DMABUF و GPU در اندروید 12

این صفحه، پیشرفت‌های مختلف در زمینه‌ی محاسبه‌ی حافظه که در اندروید ۱۲ معرفی شده‌اند را شرح می‌دهد.

آمار DMA-BUF در sysfs

در اندروید ۱۱ و اندروید ۱۲، debugfs نمی‌توانند در نسخه‌های کاربری (User builds) نصب شوند. بنابراین آمار DMA-BUF در اندروید ۱۲ به sysfs در دایرکتوری /sys/kernel/dmabuf/buffers اضافه شده است.

مسیر توضیحات
/sys/kernel/dmabuf/buffers دایرکتوری /sys/kernel/dmabuf/buffers شامل یک تصویر لحظه‌ای از وضعیت داخلی هر DMA-BUF است. /sys/kernel/dmabuf/buffers/<inode_number> شامل آمار مربوط به DMA-BUF با شماره inode منحصر به فرد <inode_number> .
/sys/kernel/dmabuf/buffers/<inode_number>/exporter_name این فایل فقط خواندنی حاوی نام صادرکننده‌ی DMA-BUF است.
/sys/kernel/dmabuf/buffers/<inode_number>/size این فایل فقط خواندنی، اندازه DMA-BUF را بر حسب بایت مشخص می‌کند.

رابط برنامه‌نویسی کاربردی libdmabufinfo ، آمار sysfs DMA-BUF را تجزیه و تحلیل می‌کند تا آمار مربوط به هر صادرکننده و هر بافر را نمایش دهد.

درایورهای هسته که DMA-BUFها را صادر می‌کنند، باید قبل از فراخوانی API dma_buf_export() برای ایجاد DMA-BUF، فیلد exp_name از struct dma_buf_export_info را به درستی روی نام صادرکننده تنظیم کنند. این کار برای libdmabufinfo و ابزار dmabuf_dump جهت استخراج آمار مربوط به هر صادرکننده که سپس در bugreport نمایش داده می‌شوند، ضروری است.

ابزار dmabuf_dump اصلاح شده است تا این اطلاعات را با آرگومان جدید -b نمایش دهد.

آمار مربوط به چارچوب هیپ‌های DMA-BUF

ION در GKI 2.0 به نفع چارچوب heaps DMA-BUF که بخشی از هسته لینوکس بالادستی است، منسوخ شده است.

آمار جهانی ION در اندروید ۱۱ به شرح زیر است:

  • اندازه کل DMA-BUF های صادر شده توسط هر هیپ ION
  • حجم کل حافظه از پیش تخصیص‌یافته استفاده نشده که توسط هر هیپ ION ذخیره شده است

هیچ رابطی برای نمایش آمار هر هیپ به ازای هر ION در اندروید ۱۱ وجود ندارد.

جدول زیر رابط‌های آماری ION را با نمونه‌های مشابه آنها برای دستگاه‌هایی که از چارچوب هیپ DMA-BUF در اندروید ۱۲ استفاده می‌کنند، مقایسه می‌کند.

اندروید ۱۱ یا دستگاه‌هایی که با پشتیبانی از ION در اندروید ۱۲ عرضه می‌شوند دستگاه‌هایی که با هیپ‌های DMA-BUF در اندروید ۱۲ عرضه می‌شوند
آمار ION به ازای هر هیپ هیچکدام تجزیه‌شده از آمار سیستم DMA-BUF
حجم کل DMA-BUF های صادر شده /sys/kernel/ion/total_heap_size_kb
(اندازه DMA-BUF های صادر شده توسط صادرکنندگان غیر ION را شامل نمی‌شود)
تجزیه‌شده از آمار سیستم DMA-BUF
(شامل اندازه تمام DMA-BUF های صادر شده)
کل حافظه‌ای که توسط پشته‌ها جمع‌آوری شده است /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

بهبود دقت محاسبه رم از دست رفته

قبلاً محاسبه رم از دست رفته به شرح زیر انجام می‌شد:

آخرین Long lostRAM = memInfo.getTotalSizeKb( ) - ( totalPss - totalSwapPss )

- memInfo.getFreeSizeKb() - memInfo.getCachedSizeKb()

- kernelUsed - memInfo.getZramTotalSizeKb() ;

مؤلفه totalPss شامل میزان استفاده از حافظه GPU (که توسط رابط getMemory() مربوط به Memtrack HAL برگردانده می‌شود) بود. مؤلفه kernelUsed شامل کل استفاده از حافظه DMA-BUF بود. با این حال، برای دستگاه‌های اندروید، حافظه GPU از موارد زیر حاصل می‌شد:

  • تخصیص‌های مستقیم انجام شده توسط درایور GPU با استفاده از تخصیص‌دهنده صفحه فیزیکی
  • DMA-BUF ها در فضای آدرس GPU نگاشت شده اند

بنابراین، DMA-BUFهایی که در فضای آدرس GPU نگاشت حافظه شده بودند، هنگام محاسبه‌ی رم از دست رفته، دو بار کم شدند. اندروید ۱۲ راهکاری را برای محاسبه‌ی اندازه‌ی DMA-BUFهای نگاشت شده در فضای آدرس GPU پیاده‌سازی کرده است، به این معنی که فقط یک بار در محاسبه‌ی رم از دست رفته در نظر گرفته می‌شود.

جزئیات راهکار به شرح زیر است:

  • تابع Memtrack HAL API getMemory() ‎ هنگام فراخوانی با PID 0 باید کل حافظه خصوصی GPU را برای MemtrackType::GL و MemtrackRecord::FLAG_SMAPS_UNACCOUNTED گزارش دهد.
  • getMemory() وقتی با PID 0 برای MemtrackType غیر از GL فراخوانی شود، نباید با شکست مواجه شود. در عوض باید مقدار 0 را برگرداند.
  • راهکار ردیابی/eBPF حافظه GPU که در اندروید ۱۲ اضافه شده است، کل حافظه GPU را در نظر می‌گیرد. کم کردن کل حافظه خصوصی GPU از کل حافظه GPU، اندازه DMA-BUF های نگاشت شده در فضای آدرس GPU را به دست می‌دهد. سپس می‌توان از این مقدار برای بهبود دقت محاسبات Lost RAM با در نظر گرفتن صحیح میزان استفاده از حافظه GPU استفاده کرد.
  • حافظه خصوصی پردازنده گرافیکی (GPU) در اکثر پیاده‌سازی‌های Memtrack HAL در totalPss گنجانده شده است و بنابراین باید قبل از حذف آن از lostRAM داده‌های تکراری (deduplicate) حذف شوند.

راهکار پیاده‌سازی‌شده در بخش بعدی به‌طور مفصل شرح داده شده است.

حذف تغییرات Memtrack از حافظه رم از دست رفته

از آنجایی که پیاده‌سازی‌های Memtrack HAL می‌تواند در بین شرکا متفاوت باشد، حافظه GPU موجود در totalPSS از HAL همیشه ثابت نیست. برای حذف تغییرپذیری از lostRAM ، حافظه‌ای که در MemtrackType::GRAPHICS و MemtrackType::GL در نظر گرفته شده است، در طول محاسبه lostRAM از totalPss حذف می‌شود.

حافظه MemtrackType::GRAPHICS از totalPss حذف شده و با حافظه totalExportedDmabuf در محاسبه lostRAM در ActivityManagerService.java جایگزین می‌شود، همانطور که در کد زیر نشان داده شده است:

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;

حافظه MemtrackType::GL از totalPss حذف شده و با حافظه خصوصی GPU ( gpuPrivateUsage ) در محاسبه lostRAM در ActivityManagerService.java جایگزین می‌شود، همانطور که در کد زیر نشان داده شده است:

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;

محاسبه رم از دست رفته به‌روزرسانی شد

هم کل حافظه اختصاصی پردازنده گرافیکی (GPU) و هم کل حافظه بافر DMA خروجی در kernelUsed + totalPss قرار دارند که از lostRAM حذف می‌شود. این کار هم شمارش مضاعف و هم تغییرپذیری Memtrack را از محاسبه lost RAM حذف می‌کند.

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

اعتبارسنجی

آزمایش‌های VTS این قانون را اعمال می‌کنند که دستگاه‌هایی که با اندروید ۱۲ و هسته لینوکس نسخه ۵.۴ یا بالاتر راه‌اندازی می‌شوند، از API getGpuDeviceInfo() پشتیبانی کنند.

یک Memtrack HAL API جدید getGpuDeviceInfo() باید اطلاعات مربوط به دستگاه GPU مورد استفاده را برگرداند.

این امر امکان حسابداری بهتر حافظه و مشاهده‌ی میزان استفاده از بافر DMA و حافظه‌ی GPU را فراهم می‌کند. پیاده‌سازی memtrack AIDL HAL برای حسابداری بهتر رم و حافظه‌ی از دست رفته. این ویژگی به سرویس‌های گوگل وابسته نیست.

پیاده‌سازی

این ویژگی به AIDL Memtrack HAL بستگی دارد و دستورالعمل‌های پیاده‌سازی آن در اندروید ۱۲ به صورت کامنت در کد گنجانده شده است. قرار است همه HALهای HIDL در نسخه‌های آینده به AIDL تبدیل شوند.

API های زیر به 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();

برای تأیید اینکه نسخه شما طبق انتظار کار می‌کند، نقاط ردیابی را در درایورهای GPU خود ادغام کنید و API مربوط به AIDL memtrack HAL getMemory() را پیاده‌سازی کنید تا هنگام فراخوانی با PID 0 برای MemtrackType::GL و MemtrackRecord::FLAG_SMAPS_UNACCOUNTED ، کل حافظه خصوصی GPU را به درستی برگرداند.