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

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

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

בגרסאות Android 11 ו-Android 12, לא ניתן להשתמש ב-debugfs טעונה בגרסאות build של משתמשים. לכן, הנתונים הסטטיסטיים של DMA-BUF נוספו לקובץ sysfs בתיקייה /sys/kernel/dmabuf/buffers ב-Android 12.

נתיב תיאור
/sys/kernel/dmabuf/buffers הספרייה /sys/kernel/dmabuf/buffers מכילה קובץ snapshot של המצב הפנימי של כל 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 כדי לחשוף נתונים סטטיסטיים לכל ייצוא ולכל מאגר.

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

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

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

אנחנו מוציאים משימוש את ION ב-GKI 2.0 לטובת מסגרת הערימה (heaps framework) של DMA-BUF. שהיא חלק מהליבה של Linux ב-upstream.

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

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

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

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

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

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

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

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

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

- kernelUsed - memInfo.getZramTotalSizeKb();

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

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

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

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

  • כשמפעילים את Memtrack HAL API‏ getMemory() עם PID 0, הוא צריך לדווח על נפח הזיכרון הכולל ברמת ה-GPU, עבור MemtrackType::GL ו-MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.
  • כשנשלחת קריאה ל-getMemory() עם PID 0 עבור MemtrackType שאינו GL, אסור שהיא תיכשל. במקום זאת, הוא צריך להחזיר את הערך 0.
  • פתרון trackingpoint/eBPF של הזיכרון של GPU שנוספו לחשבונות Android 12 כדי ליהנות מהזיכרון הכולל של ה-GPU. מחסרים את סך הזיכרון הפרטי של GPU מהזיכרון הכולל של ה-GPU, הגודל של מזהי DMA-BUF שממופים למרחב הכתובות של ה-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().

ממשק API חדש של Memtrack HAL‏ getGpuDeviceInfo() חייב להחזיר מידע על מכשיר ה-GPU שבשימוש.

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

הטמעה

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

אנחנו מתכננים להמיר את כל שיעורי HIDL של HAL ב-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();

כדי לוודא שהגרסה פועלת כמצופה, צריך לשלב את נקודות המעקב (tracepoints) בנהגים של ה-GPU ולהטמיע את ה-API של memtrack HAL getMemory() ב-AIDL כדי להחזיר בצורה נכונה את הזיכרון הכולל הגלובלי של GPU פרטי כשמבצעים קריאה עם PID 0 עבור MemtrackType::GL ו-MemtrackRecord::FLAG_SMAPS_UNACCOUNTED.