ב-Android 12, GKI 2.0 מחליף את מנהל הקצאת ה-ION ב-DMA-BUF heaps מהסיבות הבאות:
- אבטחה: מאחר שכל אשכול DMA-BUF הוא מכשיר תווים נפרד, אפשר לשלוט בגישה לכל אשכול בנפרד באמצעות מדיניות האבטחה. לא ניתן היה לעשות זאת באמצעות ION כי הקצאה מכל אשכול דרשה רק גישה למכשיר
/dev/ion
. - יציבות ABI: בניגוד ל-ION, ממשק ה-IOCTL של מסגרת ה-DMA-BUF heaps יציב מבחינת ABI כי הוא מתוחזק בליבה של Linux במקור.
- סטנדרטיזציה: מסגרת ה-DMA-BUF heaps מציעה UAPI מוגדר היטב. ION אפשר להשתמש בדגלים מותאמים אישית ובמזהי אשכול, דבר שגרם לכך שלא ניתן היה לפתח מסגרת בדיקה משותפת, כי ההטמעה של ION בכל מכשיר יכולה להתנהג בצורה שונה.
ההסתעפות android12-5.10
של הליבה המשותפת של Android השביתה את CONFIG_ION
ב-1 במרץ 2021.
רקע
בהמשך מופיעה השוואה קצרה בין ערמות ION לבין ערמות DMA-BUF.
קווי דמיון בין מסגרת ה-heaps של ION לבין מסגרת ה-heaps של DMA-BUF
- מסגרות ה-heap של ION ו-DMA-BUF הן שתי מסגרות של ייצוא DMA-BUF שמבוססות על heap.
- בשתי השיטות, כל אשכול יכול להגדיר את המקצה ואת הפעולות של DMA-BUF שלו.
- ביצועי ההקצאה דומים כי בשתי הסכימות נדרש IOCTL יחיד להקצאה.
ההבדלים בין מסגרת ה-heaps של ION לבין מסגרת ה-heaps של DMA-BUF
ערמות (heaps) של ION | אשכולות DMA-BUF |
---|---|
כל ההקצאות של ION מתבצעות באמצעות /dev/ion .
|
כל אשכול DMA-BUF הוא מכשיר תווים שנמצא ב-/dev/dma_heap/<heap_name> .
|
ב-ION יש תמיכה בדגלים פרטיים של אשכול. | ערמות DMA-BUF לא תומכות בדגלים פרטיים של ערימה. במקום זאת, כל סוג הקצאה מתבצע מערימה אחרת. לדוגמה, וריאציות של אשכול המערכת שנשמרו במטמון ושל אשכול המערכת שלא נשמר במטמון הן אשכולות נפרדים שנמצאים ב-/dev/dma_heap/system וב-/dev/dma_heap/system_uncached .
|
כדי להקצות, צריך לציין את המזהה או המסכה של ה-heap ואת הדגלים. | השם של אשכול הנתונים משמש להקצאה. |
בקטעים הבאים מפורטים הרכיבים שקשורים ל-ION, ומוסבר איך להעביר אותם למסגרת של אשכולות DMA-BUF.
מעבר של מנהלי ליבה מ-ION למקבצים של DMA-BUF
מנהלי התקן של ליבה שמטמיעים ערמות ION
גם אשכול ION וגם אשכול DMA-BUF מאפשרים לכל אשכול להטמיע את המקצים והפעולות שלו ב-DMA-BUF. כך תוכלו לעבור מהטמעה של ION heap להטמעה של DMA-BUF heap באמצעות קבוצה אחרת של ממשקי API כדי לרשום את ה-heap. בטבלה הזו מפורטים ממשקי ה-API לרישום אשכול ION וממשקי ה-API המקבילים של אשכול DMA-BUF.
ערמות (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 לא תומכות בדגלים פרטיים של ערימה. לכן צריך לרשום כל וריאנט של האוסף בנפרד באמצעות ה-API dma_heap_add()
. כדי להקל על שיתוף הקוד, מומלץ לרשום את כל הווריאציות של אותו אשכול באותו מנהל.
בדוגמה הזו של dma-buf: system_heap מוצגת ההטמעה של הגרסאות השמורות במטמון והגרסאות שלא שמורות במטמון של אשכול המערכת.
אפשר להשתמש בתבנית לדוגמה של dma-buf: heaps כדי ליצור אשכול DMA-BUF מאפס.
מנהלי ליבה שמקצים ישירות מערמות ION
מסגרת ה-heaps של DMA-BUF כוללת גם ממשק הקצאה ללקוחות בתוך הליבה. במקום לציין את המסכה והדגלים של האשפה כדי לבחור את סוג ההקצאה, הממשק שמוצג על ידי אשפה של DMA-BUF מקבל שם של אשפה כקלט.
בהמשך מוצגים ממשקי ה-API להקצאת ION בליבה וממשקי ה-API המקבילים להקצאת אשכול DMA-BUF. מנהלי התקנים של הליבה יכולים להשתמש ב-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)
|
|
מנהלי ליבה שמשתמשים ב-DMA-BUFs
אין צורך לבצע שינויים בנהגים שמייבאים רק DMA-BUFs, כי מאגר של מאגר ION מתנהג בדיוק כמו מאגר של מאגר DMA-BUF מקביל.
מעבר של לקוחות ION במרחב המשתמש למקבצים של DMA-BUF
כדי להקל על המעבר ללקוחות ION במרחב המשתמש, קיימת ספריית הפשטה שנקראת libdmabufheap
. libdmabufheap
תומך בהקצאה ב-DMA-BUF heaps וב-ION heaps. קודם כול, המערכת בודקת אם יש אשכול DMA-BUF בשם שצוין. אם לא, היא עוברת לאשכול ION מקביל, אם יש כזה.
לקוחות צריכים לאתחל אובייקט BufferAllocator
במהלך האתחול שלהם במקום לפתוח את /dev/ion using
ion_open()
. הסיבה לכך היא שמאפייני הקבצים שנוצרים על ידי פתיחת /dev/ion
ו-/dev/dma_heap/<heap_name>
מנוהלים באופן פנימי על ידי האובייקט BufferAllocator
.
כדי לעבור מ-libion
ל-libdmabufheap
, משנים את ההתנהגות של הלקוחות באופן הבא:
- מעקב אחרי שם האשפה לשימוש בהקצאה, במקום אחרי המזהה/המסכה של ה-head ודגל האשפה.
- מחליפים את ה-API
ion_alloc_fd()
, שמקבל מסכה של אשכול וארגומנט דגל, ב-APIBufferAllocator::Alloc()
, שמקבל שם של אשכול במקום זאת.
בטבלה הזו מוצגים השינויים האלה באמצעות הדגמה של האופן שבו libion
ו-libdmabufheap
מבצעים הקצאה של אשכול מערכת ללא מטמון.
סוג ההקצאה | libion | libdmabufheap |
---|---|---|
הקצאה ששמורה במטמון מערימה של מערכת | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, ION_FLAG_CACHED, &fd)
|
allocator->Alloc("system", size)
|
הקצאה ללא שמירה במטמון מהמקבץ של המערכת | ion_alloc_fd(ionfd, size, 0, ION_HEAP_SYSTEM, 0, &fd)
|
allocator->Alloc("system-uncached", size)
|
הגרסה ללא מטמון של אשכול המערכת ממתינה לאישור ב-upstream, אבל כבר נכללת בהסתעפות android12-5.10
.
כדי לתמוך בשדרוג של מכשירים, ה-API של MapNameToIonHeap()
מאפשר למפות שם של אשכול לפרמטרים של אשכול ION (שם האשכול או מסכה ודגלים) כדי לאפשר לממשקים האלה להשתמש בהקצאות שמבוססות על שם. דוגמה להקצאה לפי שם
המסמכים של כל ממשק API שlibdmabufheap
חושף זמינים. הספרייה חושפת גם קובץ כותרת לשימוש של לקוחות C.
הפניה להטמעה של Gralloc
הטמעת gralloc של Hikey960 משתמשת ב-libdmabufheap
, כך שאפשר להשתמש בה כהטמעת עזר.
תוספות נדרשות ל-ueventd
לכל אשכול DMA-BUF חדש ספציפי למכשיר שנוצר, מוסיפים רשומה חדשה לקובץ ueventd.rc
של המכשיר.
בדוגמה הזו להגדרת ueventd לתמיכה ב-DMA-BUF heaps מוסבר איך עושים זאת ב-DMA-BUF system heap.
הוספות נדרשות למדיניות האבטחה
הוספת הרשאות של מדיניות אבטחה (sepolicy) כדי לאפשר ללקוח במרחב המשתמש לגשת למחסנית DMA-BUF חדשה. בדוגמה הזו להוספת ההרשאות הנדרשות מוצגות ההרשאות של מדיניות האבטחה שנוצרו ללקוחות שונים כדי לגשת לאוסף המערכת של DMA-BUF.
גישה לאשכולות של ספקים מקוד של מסגרת
כדי לוודא תאימות ל-Treble, קוד המסגרת יכול להקצות רק מקטגוריות שאושרו מראש של אשכולות של ספקים.
על סמך המשוב שקיבלנו מהשותפים, Google זיהתה שתי קטגוריות של אשכולות של ספקים שצריך לגשת אליהם מקוד המסגרת:
- אשכולות שמבוססים על אשכול המערכת עם אופטימיזציות ביצועים ספציפיות למכשיר או ל-SoC.
- אשכולות להקצאה מזיכרון מוגן.
אשכולות שמבוססים על אשכול המערכת עם אופטימיזציות ביצועים ספציפיות למכשיר או ל-SoC
כדי לתמוך בתרחיש לדוגמה הזה, אפשר לשנות את ההטמעה של אשכול מערכת ברירת המחדל של DMA-BUF.
CONFIG_DMABUF_HEAPS_SYSTEM
מושבת ב-gki_defconfig
כדי שהוא יוכל לשמש כמודול של ספק.- בדיקות התאימות של VTS מוודאות שהמקבץ קיים ב-
/dev/dma_heap/system
. בנוסף, הבדיקה מוודאת שאפשר להקצות מהמקבץ, ואפשר למפות את מתאר הקובץ המוחזר (fd
) בזיכרון (mmapped) ממרחב המשתמש.
הנקודות הקודמות נכונות גם לגרסה ללא מטמון של אשכול המערכת, אבל קיומה לא חובה במכשירים עם תאימות מלאה ל-IO.
אשכולות להקצאה מזיכרון מוגן
הטמעות של אשכול מאובטח צריכות להיות ספציפיות לספק, כי Android Common Kernel לא תומך בהטמעה כללית של אשכול מאובטח.
- רושמים את ההטמעות הספציפיות לספק כ-
/dev/dma_heap/system-secure<vendor-suffix>
. - ההטמעות האלה של אשכול הן אופציונליות.
- אם האשכולות קיימים, בדיקות VTS מוודאות שאפשר לבצע הקצאות מהם.
- לרכיבי המסגרת יש גישה לאשכולות האלה, כדי שיוכלו לאפשר שימוש באשכולות דרך Codec2 HAL או HAL ללא קישור באותו תהליך. עם זאת, תכונות כלליות של מסגרת Android לא יכולות להיות תלויות בהן בגלל השונות בפרטי ההטמעה שלהן. אם תתווסף בעתיד הטמעה של אשכול מאובטח גנרי ל-Android Common Kernel, היא תצטרך להשתמש ב-ABI שונה כדי למנוע התנגשויות עם מכשירי שדרוג.
מנהל הקצאות של Codec 2 למקבצים של DMA-BUF
מקצה codec2 לממשק של אשכולות DMA-BUF זמין ב-AOSP.
ממשק מאגר הרכיבים שמאפשר לציין פרמטרים של אשכול מ-C2 HAL זמין עם מנהל האשכולות C2 DMA-BUF.
דוגמה לתהליך מעבר של אשכול ION
כדי שהמעבר מ-ION למקבצים של DMA-BUF יהיה חלק, אפשר להשתמש ב-libdmabufheap
כדי לעבור למקבץ אחד בכל פעם. השלבים הבאים מדגימים תהליך עבודה מוצע למעבר של אשכול ION לא מדור קודם בשם my_heap
שתומך בדגל אחד, ION_FLAG_MY_FLAG
.
שלב 1: יוצרים מקבילות של ערמת ION במסגרת DMA-BUF. בדוגמה הזו, מכיוון שהערימה my_heap
של ION תומכת בדגל ION_FLAG_MY_FLAG
, אנחנו רושמים שתי ערימות DMA-BUF:
- ההתנהגות של
my_heap
תואמת בדיוק להתנהגות של אשכול ION כשהדגלION_FLAG_MY_FLAG
מושבת. - ההתנהגות של
my_heap_special
זהה לזו של אשכול ION כשהדגלION_FLAG_MY_FLAG
מופעל.
שלב 2: יוצרים את השינויים ב-ueventd עבור אשכולות ה-DMA-BUF החדשים my_heap
ו-my_heap_special
. בשלב הזה, האשכולות גלויים בתור /dev/dma_heap/my_heap
ו-/dev/dma_heap/my_heap_special
, עם ההרשאות המיועדות.
שלב 3: ללקוחות שמקצים מ-my_heap
, משנים את קובצי ה-makefile שלהם כדי לקשר ל-libdmabufheap
. במהלך האי initialization של הלקוח, יוצרים אובייקט BufferAllocator
ומשתמשים ב-API של MapNameToIonHeap()
כדי למפות את השילוב <ION heap name/mask, flag>
לשמות equivaent DMA-BUF heap.
לדוגמה:
allocator->MapNameToIonHeap("my_heap_special" /* name of DMA-BUF heap */, "my_heap" /* name of the ION heap */, ION_FLAG_MY_FLAG /* ion flags */ )
במקום להשתמש ב-API MapNameToIonHeap()
עם הפרמטרים name ו-flag, אפשר ליצור את המיפוי מ-<ION heap mask, flag>
לשמות equivaent DMA-BUF heap על ידי הגדרת הפרמטר name של ION heap כריק.
שלב 4: מחליפים את ההפעלות של ion_alloc_fd()
ב-BufferAllocator::Alloc()
באמצעות שם ה-heap המתאים.
סוג ההקצאה | 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)
|
בשלב הזה, הלקוח פועל אבל עדיין מקצה מהמקבץ של ION כי אין לו את הרשאות ה-sepolicy הנדרשות כדי לפתוח את המקבץ של DMA-BUF.
שלב 5: יוצרים את ההרשאות של מדיניות האבטחה הנדרשות כדי שהלקוח יוכל לגשת למקבצי ה-DMA-BUF החדשים. עכשיו הלקוח מוכן לחלוטין להקצות מהמקבץ החדש של DMA-BUF.
שלב 6: בודקים את logcat כדי לוודא שהקצאות מתבצעות מהמקבץ החדש של DMA-BUF.
שלב 7: משביתים את my_heap
של ION heap בליבה. אם קוד הלקוח לא צריך לתמוך בשדרוג של מכשירים (שליבת המעבד שלהם עשויה לתמוך רק ב-heaps של ION), אפשר גם להסיר את ההפעלות של MapNameToIonHeap()
.