מעבר מ-ION למקבצים של DMA-BUF (ליבה 5.4 בלבד)

ב-Android 12, ‏ GKI 2.0 מחליף את מקצה הזיכרון ION בערימות DMA-BUF מהסיבות הבאות:

  • אבטחה: כל ערימת DMA-BUF היא מכשיר נפרד, ולכן אפשר לשלוט בגישה לכל ערימה בנפרד באמצעות sepolicy. זה לא היה אפשרי עם ION כי הקצאה מכל ערימה דרשה רק גישה למכשיר /dev/ion.
  • יציבות של ABI: בניגוד ל-ION, ממשק ה-IOCTL של מסגרת ה-DMA-BUF heaps הוא יציב מבחינת ABI כי הוא מתוחזק בקרנל Linux במעלה הזרם.
  • סטנדרטיזציה: מסגרת הערימות של DMA-BUF מציעה UAPI מוגדר היטב. ‫ION אפשר דגלים מותאמים אישית ומזהי ערימה שמנעו פיתוח של מסגרת בדיקה משותפת, כי ההטמעה של ION בכל מכשיר יכולה להתנהג בצורה שונה.

הענף android12-5.10 של ליבת Android Common Kernel הושבת ב-1 במרץ 2021.CONFIG_ION

רקע

הטבלה הבאה מציגה השוואה קצרה בין ION לבין DMA-BUF heaps.

קווי דמיון בין מסגרת הערימות של ION ו-DMA-BUF

  • המסגרות ION ו-DMA-BUF הן שתיהן יצואנים של DMA-BUF שמבוססים על heap.
  • בשניהם אפשר להגדיר לכל ערימה מקצה זיכרון משלה ופעולות DMA-BUF.
  • ביצועי ההקצאה דומים כי שתי הסכימות צריכות IOCTL אחד להקצאה.

ההבדלים בין מסגרת הערימות של ION לבין מסגרת הערימות של DMA-BUF

ערימות (heaps) של ION ערימות DMA-BUF
כל ההקצאות של ION מתבצעות באמצעות /dev/ion. כל ערימת DMA-BUF היא מכשיר תווים שמופיע ב-/dev/dma_heap/<heap_name>.
‫ION תומך בדגלים פרטיים של ערימה. ערימות DMA-BUF לא תומכות בדגלים פרטיים של ערימות. במקום זאת, כל סוג הקצאה מתבצע מערימה אחרת. לדוגמה, גרסאות ה-heap של המערכת שנשמרו במטמון ושלא נשמרו במטמון הן heaps נפרדים שנמצאים בכתובות /dev/dma_heap/system ו-/dev/dma_heap/system_uncached.
צריך לציין הקצאה של מזהה/מסכה ודגלים של Heap. השם של ה-heap משמש להקצאה.

בקטעים הבאים מפורטים הרכיבים שקשורים ל-ION, ומוסבר איך להעביר אותם למסגרת של ערימות DMA-BUF.

מעבר של מנהלי התקנים של ליבת המערכת מ-ION ל-DMA-BUF heaps

מנהלי התקנים של הליבה שמטמיעים ערימות ION

גם ב-ION וגם ב-DMA-BUF, כל ערימה יכולה להטמיע הקצאות משלה ופעולות DMA-BUF. לכן, אפשר לעבור מהטמעה של ערימת ION להטמעה של ערימת DMA-BUF באמצעות קבוצה אחרת של ממשקי API לרישום הערימה. בטבלה הזו מפורטים ממשקי ה-API של ION heap registration וממשקי ה-API המקבילים של DMA-BUF heap.

ערימות (heaps) של ION ערימות DMA-BUF
void ion_device_add_heap(struct ion_heap *heap) struct dma_heap *dma_heap_add(const struct dma_heap_export_info *exp_info);
void ion_device_remove_heap(struct ion_heap *heap) void dma_heap_put(struct dma_heap *heap);

ערימות DMA-BUF לא תומכות בדגלים פרטיים של ערימות. לכן צריך לרשום כל וריאציה של הערימה בנפרד באמצעות dma_heap_add()API. כדי להקל על שיתוף קוד, מומלץ לרשום את כל הווריאציות של אותו heap באותו מנהל התקן. בדוגמה הזו של dma-buf: system_heap מוצגת ההטמעה של הגרסאות שנשמרו במטמון ושלא נשמרו במטמון של system heap.

אפשר להשתמש בתבנית לדוגמה של dma-buf: heaps כדי ליצור ערימת DMA-BUF מאפס.

מנהלי התקנים של ליבת המערכת שמקצים ישירות מ-ION heaps

מסגרת הערימות של DMA-BUF מציעה גם ממשק הקצאה ללקוחות בתוך הליבה. במקום לציין את המסיכה והדגלים של הערימה כדי לבחור את סוג ההקצאה, הממשק שמציעות ערימות DMA-BUF מקבל שם של ערימה כקלט.

בדוגמה הבאה מוצג ה-API להקצאת ION בתוך הליבה, וממשקי ה-API המקבילים להקצאת DMA-BUF heap. מנהלי התקנים של ליבת מערכת ההפעלה יכולים להשתמש ב-API‏ dma_heap_find() כדי לשלוח שאילתה לגבי קיום של ערימה. ה-API מחזיר מצביע למופע של struct dma_heap, שאפשר להעביר אותו כארגומנט ל-API‏ dma_heap_buffer_alloc().

ערימות (heaps) של ION ערימות DMA-BUF
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)

struct dma_heap *dma_heap_find(const char *name)

struct dma_buf *struct dma_buf *dma_heap_buffer_alloc(struct dma_heap *heap, size_t len, unsigned int fd_flags, unsigned int heap_flags)

מנהלי התקנים של ליבת מערכת ההפעלה שמשתמשים ב-DMA-BUFs

נהגים שמייבאים רק DMA-BUFs לא צריכים לבצע שינויים, כי מאגר שהוקצה מ-ION heap מתנהג בדיוק כמו מאגר שהוקצה מ-DMA-BUF heap מקביל.

העברה של לקוחות במרחב המשתמשים של ION לערימות DMA-BUF

כדי להקל על המעבר ללקוחות במרחב המשתמש של ION, זמינה ספריית הפשטה בשם libdmabufheap. ‫libdmabufheap תומך בהקצאה ב-DMA-BUF heaps וב-ION heaps. הפונקציה בודקת קודם אם קיים DMA-BUF heap עם השם שצוין, ואם לא, היא חוזרת ל-ION heap מקביל, אם קיים כזה.

במהלך האתחול, הלקוחות צריכים לאתחל אובייקט BufferAllocator במקום לפתוח את /dev/ion using ion_open(). הסיבה לכך היא שמתארים של קבצים שנוצרו על ידי פתיחת /dev/ion ו-/dev/dma_heap/<heap_name> מנוהלים באופן פנימי על ידי אובייקט BufferAllocator.

כדי לעבור מ-libion ל-libdmabufheap, צריך לשנות את ההתנהגות של הלקוחות באופן הבא:

  • כדי להקצות זיכרון, צריך לעקוב אחרי שם ה-heap במקום אחרי מזהה/מסכת ה-head ודגל ה-heap.
  • מחליפים את ה-API‏ ion_alloc_fd(), שמקבל מסכת ערימה וארגומנט של דגל, ב-API‏ BufferAllocator::Alloc(), שמקבל שם ערימה במקום זאת.

בטבלה הזו מוצגים השינויים האלה, ומוסבר איך libion ו-libdmabufheap מקצים זיכרון בערימה של המערכת ללא שמירה במטמון.

סוג ההקצאה libion libdmabufheap
הקצאה במטמון מזיכרון הערימה של המערכת ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd) allocator->Alloc("system", size)
הקצאה שלא נשמרה במטמון מ-System heap ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd) allocator->Alloc("system-uncached", size)

uncached system heap variant ממתין לאישור במעלה הזרם, אבל הוא כבר חלק מהסניף android12-5.10.

כדי לתמוך בשדרוג מכשירים, MapNameToIonHeap() API מאפשר מיפוי של שם heap לפרמטרים של ION heap (שם או מסכה של heap ודגלים) כדי לאפשר לממשקים האלה להשתמש בהקצאות מבוססות-שם. דוגמה להקצאה לפי שם

התיעוד של כל ממשק API שנחשף על ידי libdmabufheap זמין. הספרייה גם חושפת קובץ כותרת לשימוש על ידי לקוחות C.

הטמעה לדוגמה של Gralloc

ההטמעה של Hikey960 gralloc משתמשת ב-libdmabufheap, כך שאפשר להשתמש בה כהטמעה לדוגמה.

תוספות נדרשות ל-ueventd

לכל ערימת DMA-BUF חדשה שנוצרה עבור מכשיר ספציפי, מוסיפים רשומה חדשה לקובץ ueventd.rc של המכשיר. בדוגמה הזו להגדרת ueventd לתמיכה ב-DMA-BUF heaps מוסבר איך עושים את זה עבור מערכת ה-DMA-BUF heap.

תוספות נדרשות למדיניות האבטחה

הוספת הרשאות sepolicy כדי לאפשר ללקוח במרחב המשתמשים לגשת ל-DMA-BUF heap חדש. בדוגמה הבאה של add required permissions מוצגות הרשאות sepolicy שנוצרו עבור לקוחות שונים כדי לגשת ל-DMA-BUF system heap.

גישה ל-heaps של ספקים מקוד של framework

כדי להבטיח תאימות ל-Treble, קוד המסגרת יכול להקצות רק מקטגוריות שאושרו מראש של ערימות ספקים.

על סמך משוב שהתקבל משותפים, Google זיהתה שתי קטגוריות של ערימות ספקים שצריך לגשת אליהן מקוד המסגרת:

  1. ערימות שמבוססות על ערימת המערכת עם אופטימיזציות של ביצועים שספציפיות למכשיר או ל-SoC.
  2. ערימות להקצאה מזיכרון מוגן.

ערימות שמבוססות על ערימת המערכת עם אופטימיזציות של הביצועים שספציפיות למכשיר או ל-SoC

כדי לתמוך בתרחיש השימוש הזה, אפשר לשנות את ההטמעה של הערימה של מערכת ברירת המחדל של DMA-BUF.

  • האפשרות CONFIG_DMABUF_HEAPS_SYSTEM מושבתת ב-gki_defconfig כדי שהיא תוכל להיות מודול ספק.
  • בדיקות התאימות של VTS מוודאות שה-heap קיים בכתובת /dev/dma_heap/system. בנוסף, הבדיקות מאמתות שאפשר להקצות את הערימה, ושאפשר למפות את מתאר הקובץ המוחזר (fd) ממרחב המשתמש לזיכרון (mmapped).

הנקודות הקודמות נכונות גם לגבי גרסת ה-heap של המערכת שלא נשמרה במטמון, אבל קיומה לא חובה במכשירים עם קלט/פלט קוהרנטי לחלוטין.

ערימות להקצאה מזיכרון מוגן

הטמעות מאובטחות של ערימה חייבות להיות ספציפיות לספק, כי ליבת Android Common לא תומכת בהטמעה גנרית מאובטחת של ערימה.

  • צריך לרשום את ההטמעות הספציפיות לספק כ-/dev/dma_heap/system-secure<vendor-suffix>.
  • ההטמעות האלה של הערימה הן אופציונליות.
  • אם הערימות קיימות, בדיקות VTS מוודאות שאפשר להקצות מהן זיכרון.
  • רכיבי המסגרת מקבלים גישה למחסני הנתונים האלה כדי שיוכלו להשתמש בהם דרך Codec2 HAL/non-binderized, ‏ HALs באותו תהליך. עם זאת, תכונות כלליות של מסגרת Android לא יכולות להיות תלויות בהן בגלל השונות בפרטי ההטמעה שלהן. אם בעתיד תתווסף הטמעה גנרית של ערימה מאובטחת לליבת Android Common Kernel, היא תצטרך להשתמש ב-ABI שונה כדי למנוע התנגשויות עם שדרוג מכשירים.

הקצאת קודק 2 עבור ערימות DMA-BUF

ב-AOSP זמין מקצה זיכרון codec2 לממשק של ערימות DMA-BUF.

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

דוגמה לזרימת מעבר עבור ערימת ION

כדי להקל על המעבר מ-ION לערימות DMA-BUF, ‏ libdmabufheap מאפשר מעבר בין ערימות אחת בכל פעם. השלבים הבאים מדגימים תהליך עבודה מומלץ למעבר מערימת ION שאינה מדור קודם בשם my_heap שתומכת בדגל אחד, ION_FLAG_MY_FLAG.

שלב 1: יוצרים מקבילות של ערימת ION במסגרת DMA-BUF. בדוגמה הזו, מכיוון ש-ION heap my_heap תומך בדגל ION_FLAG_MY_FLAG, אנחנו רושמים שני DMA-BUF heaps:

  • ההתנהגות של my_heap זהה בדיוק להתנהגות של ION heap כשהדגל ION_FLAG_MY_FLAG מושבת.
  • ההתנהגות של my_heap_special זהה בדיוק להתנהגות של ה-heap של ION עם הסימון ION_FLAG_MY_FLAG מופעל.

שלב 2: יוצרים את השינויים ב-ueventd עבור הערימות החדשות של my_heap ו-my_heap_special DMA-BUF. בשלב הזה, הערימות מוצגות כ-/dev/dma_heap/my_heap ו-/dev/dma_heap/my_heap_special, עם ההרשאות הרצויות.

שלב 3: לקוחות שמקצים מ-my_heap, משנים את קובצי ה-Makefile שלהם כדי לקשר ל-libdmabufheap. במהלך אתחול הלקוח, יוצרים מופע של אובייקט BufferAllocator ומשתמשים ב-API‏ MapNameToIonHeap() כדי למפות את השילוב <ION heap name/mask, flag> לשמות שווי ערך של ערימות DMA-BUF.

לדוגמה:

allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )

במקום להשתמש ב-MapNameToIonHeap() API עם הפרמטרים name ו-flag, אפשר ליצור את המיפוי מ-<ION heap mask, flag> לשמות שווי ערך של ערימת DMA-BUF על ידי הגדרת הפרמטר ION heap name כריק.

שלב 4: מחליפים את הקריאות ל-ion_alloc_fd() ב-BufferAllocator::Alloc() באמצעות שם הערימה המתאים.

סוג ההקצאה libion libdmabufheap
הקצאה מ-my_heap עם הדגל ION_FLAG_MY_FLAG לא מוגדר ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size)
הקצאה מ-my_heap עם הדגל ION_FLAG_MY_FLAG מוגדר ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

בשלב הזה, הלקוח פועל אבל עדיין מקצה זיכרון מה-heap של ION כי אין לו את ההרשאות הנדרשות של sepolicy כדי לפתוח את ה-heap של DMA-BUF.

שלב 5: יוצרים את ההרשאות של sepolicy שנדרשות ללקוח כדי לגשת ל-DMA-BUF heaps החדשים. הלקוח מצויד עכשיו באופן מלא להקצאה מה-heap החדש של DMA-BUF.

שלב 6: בודקים שההקצאות מתבצעות מה-heap החדש של DMA-BUF על ידי בדיקה של logcat.

שלב 7: משביתים את ה-ION heap‏ my_heap בקרנל. אם קוד הלקוח לא צריך לתמוך בשדרוג מכשירים (שהקרנלים שלהם עשויים לתמוך רק ב-ION heaps), אפשר גם להסיר את הקריאות ל-MapNameToIonHeap().