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

ב-Android 12, GKI 2.0 מחליף את מנהל הקצאת ה-ION ב-DMA-BUF heaps מהסיבות הבאות:

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

ההסתעפות android12-5.10 של הליבה המשותפת של Android הושבתה ב-CONFIG_ION ב-1 במרץ 2021.

רקע

בהמשך מופיעה השוואה קצרה בין ערמות ION לבין ערמות DMA-BUF.

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

  • מסגרות הערימה (heaps) ION ו-DMA-BUF הן יצואנים של אזורי DMA-BUF שמבוססים על ערימה.
  • בשתי השיטות, כל אשכול יכול להגדיר את המקצה ואת הפעולות של 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

מנהלי ליבה (kernel) שמטמיעים ערימות של ION

גם אשכולות ION וגם אשכולות DMA-BUF מאפשרים לכל אשכול להטמיע את המקצים והפעולות שלו ב-DMA-BUF. כך תוכלו לעבור מהטמעה של אשכול ION להטמעה של אשכול DMA-BUF באמצעות קבוצה אחרת של ממשקי API כדי לרשום את האשכול. בטבלה הזו מפורטים ממשקי ה-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

מסגרת ה-DMA-BUF heaps כוללת גם ממשק הקצאה ללקוחות בתוך הליבה. במקום לציין את המסכה והדגלים של האשפה כדי לבחור את סוג ההקצאה, הממשק שמוצג על ידי אשפה של 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)

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 מתנהג בדיוק כמו מאגר של מאגר 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, משנים את ההתנהגות של הלקוחות באופן הבא:

  • חשוב לעקוב אחרי שם הערימה לשימוש לצורך הקצאה, במקום לפי מזהה/מסכת הראש ודגל הערימה.
  • מחליפים את ה-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)
הקצאה שלא נשמרה במטמון מערימה של המערכת 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, כך שאפשר להשתמש בה כהטמעת עזר.

תוספות נדרשות

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

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

הוספת הרשאות של מדיניות אבטחה (sepolicy) כדי לאפשר ללקוח במרחב המשתמש לגשת למחסנית DMA-BUF חדשה. בדוגמה הזו להוספת ההרשאות הנדרשות מוצגות ההרשאות של מדיניות האבטחה שנוצרו ללקוחות שונים כדי לגשת לאוסף המערכת של DMA-BUF.

גישה לערימות של ספקים מקוד ה-framework

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

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

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

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

כדי לתמוך בתרחיש לדוגמה הזה, אפשר לבטל את הטמעת הערימה של מערכת הערימה (heap) שמוגדרת כברירת מחדל מסוג DMA-BUF.

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

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

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

הטמעות של אשכול מאובטח חייבות להיות ספציפיות לספק, כי ל-Android Common Kernel אין תמיכה בהטמעה של אשכול מאובטח כללי.

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

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

אפשר להשתמש במקצית קודק2 של ממשק הערימה (heaps) של 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() באמצעות שם האשפה המתאים.

סוג ההקצאה 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().