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

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

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

ב-Android 11 וב-Android 12, אי אפשר לטעון את debugfs ב-User builds. לכן, נתונים סטטיסטיים של 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 כדי להציג נתונים סטטיסטיים לכל יצואן ולכל מאגר.

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

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

סטטיסטיקות של מסגרת ה-DMA-BUF heaps

התמיכה ב-ION ב-GKI 2.0 יוצאת משימוש לטובת מסגרת הערימות של DMA-BUF, שהיא חלק מליבת לינוקס במעלה הזרם.

הנתונים הסטטיסטיים הגלובליים הבאים של ION נמדדים ב-Android 11:

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

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

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

‫Android 11 או מכשירים שמופעלת בהם תמיכה ב-ION ב-Android 12 מכשירים שמופעלים עם ערימות DMA-BUF ב-Android 12
נתונים סטטיסטיים של ION לכל ערימה ללא מנותח מתוך נתונים סטטיסטיים של DMA-BUF sysfs
הגודל הכולל של קובצי DMA-BUF שיוצאו /sys/kernel/ion/total_heap_size_kb
(Doesn’t include the size of DMA-BUFs exported by non-ION exporters)
הניתוח מתבצע מנתוני הסטטיסטיקה של DMA-BUF sysfs
(כולל הגודל של כל DMA-BUF שיוצא).
סך הזיכרון שמוקצה לערימות /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()

‫- kernelUsedmemInfo.getZramTotalSizeKb().

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

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

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

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

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