הטמעת ניהול זיכרון של 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 בבייטים.

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

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

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

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

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

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

  • הגודל הכולל של DMA-BUFs שמיוצאים על ידי כל ION heap
  • הגודל הכולל של זיכרון שהוקצה מראש ולא נעשה בו שימוש, שמאוחסן על ידי כל ערימת 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.
  • הפתרון GPU memory tracepoint/eBPF שנוסף ב-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 שנמצא בשימוש.

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