הטמעת ניהול חשבונות זיכרון של DMABUF ו-GPU ב-Android 12

בדף הזה מתוארות השיפורים השונים בדיווח על שימוש בזיכרון שנוספו ב-Android 12.

נתונים סטטיסטיים של DMA-BUF ב-sysfs

ב-Android 11 וב-Android 12 אי אפשר לטעון את debugfs ב-builds של משתמשים. אז הנתונים הסטטיסטיים של DMA-BUF נוספו ל-sysfs בספרייה /sys/kernel/dmabuf/buffers ב-Android 12.

נתיב תיאור
/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 בבייטים.

ה-API של libdmabufinfo מנתח את הנתונים הסטטיסטיים של DMA-BUF sysfs כדי לחשוף נתונים סטטיסטיים לכל מייצא ולכל מאגר נתונים זמני.

שימו לב שמנהלי התקנים של ליבה שמייצאים קובצי DMA-BUF חייבים להגדיר בצורה נכונה את השדה exp_name של struct dma_buf_export_info לשם המייצא לפני שמפעילים את ה-API dma_buf_export() כדי ליצור DMA-BUF. הפעולה הזו נדרשת כדי ש-libdmabufinfo והכלי dmabuf_dump יוכלו להפיק נתונים סטטיסטיים לכל יצואן, שנחשפים לאחר מכן בדוח הבאגים.

הכלי dmabuf_dump השתנה כך שיפיק את המידע הזה עם ארגומנט חדש, -b.

נתונים סטטיסטיים לגבי מסגרת ה-heaps של DMA-BUF

ION ב-GKI 2.0 הוצא משימוש והוחלף במסגרת של DMA-BUF heaps, שמהווה חלק מליבת Linux במקור.

ב-Android 11 מתבצע מעקב אחרי הנתונים הסטטיסטיים הגלובליים הבאים של ION:

  • הגודל הכולל של DMA-BUF שיוצאו מכל ערימה של ION
  • הגודל הכולל של הזיכרון שהוקצה מראש ולא נמצא בשימוש, שנשמר בכל ערימה של ION

אין ממשק זמין לחשוף נתונים סטטיסטיים של תמונת מצב של הזיכרון לכל ION ב-Android 11.

בטבלה הבאה מוצגת השוואה בין ממשקי הנתונים הסטטיסטיים של ION לבין הממשקים המקבילים במכשירים שמשתמשים במסגרת הערימה (heap framework) של DMA-BUF ב-Android 12.

Android 11 או מכשירים שהושקעו עם תמיכה ב-ION ב-Android 12 מכשירי Android 12 שהושקעו עם אשכולות DMA-BUF
נתונים סטטיסטיים של ION לכל אשכול ללא בוצע ניתוח מנתונים סטטיסטיים של sysfs DMA-BUF
הגודל הכולל של DMA-BUFs שמיוצאים /sys/kernel/ion/total_heap_size_kb
(לא כולל את גודל אזורי השוק הייעודיים (DMA-BUF) שיוצאו על ידי יצואנים שהם לא ION)
ניתוח מנתוני sysfs של DMA-BUF
(כולל את הגודל של כל DMA-BUFs שיוצאו).
סך כל הזיכרון שנאסף בערימה /sys/kernel/ion/total_pool_size_kb /sys/kernel/dma_heap/total_pool_size_kb

שיפור הדיוק של חישוב ה-RAM שאבד

בעבר, החישוב של נפח ה-RAM שאבד בוצע באופן הבא:

האורך האחרון lostRAM = memInfo.getTotalSizeKb() - (totalPss - totalSwapPss)

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

- kernelUsed - memInfo.getZramTotalSizeKb();

הרכיב totalPss כלל את השימוש בזיכרון ה-GPU (החזיר את הממשק getMemory()‎ של Memtrack HAL). הרכיב kernelUsed כלל את השימוש הכולל בזיכרון DMA-BUF. עם זאת, במכשירי Android, זיכרון ה-GPU הגיע מהמקורות הבאים:

  • הקצאות ישירות שבוצעו על ידי מנהל ההתקן של ה-GPU באמצעות מנהל הקצאות דפים פיזיים
  • אזורי DMA-BUF ממופים לשטח הכתובות של GPU

לכן, ערכי DMA-BUF שמופו זיכרון לשטח הכתובות של ה-GPU ירדו פעמיים כשחושב נפח ה-RAM שאבד. ב-Android 12 מוטמעת פתרון לחישוב הגודל של DMA-BUFs שממופים למרחב הכתובות של ה-GPU, כלומר הוא נספר רק פעם אחת בחישוב של זיכרון RAM שאבד.

פרטי הפתרון הם:

  • כשמתבצעת קריאה ל-Memtrack HAL API getMemory(), כשמתבצעת קריאה אליו באמצעות PID 0, צריך לדווח על סך הזיכרון הגלובלי של GPU-private, עבור MemtrackType::GL ו-MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • קריאה לפונקציה getMemory()‎ עם PID 0 עבור MemtrackType שאינו GL לא יכולה להיכשל. במקום זאת, הוא צריך להחזיר 0.
  • הפתרון GPU memory tracepoint/eBPF שנוסף ב-Android 12 מתייחס לזיכרון ה-GPU הכולל. מחסירים את הזיכרון הפרטי הכולל של ה-GPU מהזיכרון הכולל של ה-GPU, ומקבלים את הגודל של ה-DMA-BUFs שממופים למרחב הכתובות של ה-GPU. לאחר מכן ניתן להשתמש בערך כדי לשפר את הדיוק של חישובי RAM שאבדו על ידי התייחסות נכונה לשימוש בזיכרון של ה-GPU.
  • זיכרון ה-GPU הפרטי נכלל ב-totalPss ברוב הטמעות ה-HAL של Memtrack, ולכן צריך לבטל את הכפילויות שלו לפני שמסירים אותו מ-lostRAM.

הפתרון שהוטמע מפורט בקטע הבא.

הסרת השונות של Memtrack מזיכרון RAM שאבד

מכיוון שהטמעות של Memtrack HAL עשויות להשתנות בין שותפים, זיכרון ה-GPU שכלול ב-totalPSS מה-HAL לא תמיד עקבי. כדי להסיר את התנודתיות מ-lostRAM, הזיכרון שמשוקלל ב-MemtrackType::GRAPHICS וב-MemtrackType::GL מוסר מ-totalPss במהלך החישוב של lostRAM.

הזיכרון של 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;

עדכון החישוב של אובדן זיכרון RAM

גם נפח הזיכרון הפרטי הכולל של ה-GPU וגם נפח הזיכרון הכולל של מאגר ה-DMA שמיוצאים נכללים ב-kernelUsed + totalPss, שמוחרג מ-lostRAM. כך אפשר למנוע ספירה כפולה ותנודות ב-Memtrack במסגרת החישוב של זיכרון RAM שאבד.

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

אימות

בדיקות VTS אוכפות את הכלל שמכשירים שמופעלים ב-Android 12 עם ליבה של Linux בגרסה 5.4 ואילך תומכים ב-API‏ getGpuDeviceInfo().

גרסה חדשה של Memtrack HAL API getGpuDeviceInfo() חייבת להחזיר מידע על מכשיר ה-GPU שנמצא בשימוש.

כך אפשר לקבל חשבון זיכרון טוב יותר ולראות את השימוש בזיכרון של מאגר ה-DMA ושל ה-GPU. הטמעת memtrack AIDL HAL לצורך ניהול יעיל יותר של זיכרון RAM שאבד. התכונה הזו לא תלויה בשירותי Google.

הטמעה

התכונה תלויה ב-AIDL Memtrack HAL, וההוראות להטמעה ב-Android 12 נכללות בקוד כתגובות.

כל ממשקי ה-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() כדי להחזיר בצורה תקינה את הזיכרון הגלובלי הכולל של GPU-private כאשר קוראים לו באמצעות PID 0 עבור MemtrackType::GL ו-MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.