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

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

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

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

רקע

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

קווי דמיון בין ION לבין DMA-BUF heaps framework

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

בקטעים הבאים מפורטים הרכיבים שקשורים ל-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 מוצגת ההטמעה של גרסאות עם מטמון וגרסאות ללא מטמון של ערימת המערכת.

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

תוספות נדרשות למדיניות sepolicy

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

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

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

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

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

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

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

  • האפשרות 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 שונה כדי למנוע התנגשויות בשדרוג מכשירים.

מקצה זיכרון Codec 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 זהה בדיוק להתנהגות של ION heap עם הדגל 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 */ )

במקום להשתמש ב-API‏ MapNameToIonHeap() עם הפרמטרים name ו-flag, אפשר ליצור את המיפוי מ-<ION heap mask, flag> לשמות שווי ערך של DMA-BUF heap על ידי הגדרת הפרמטר 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: משביתים את ה-heap של ION‏ my_heap בקרנל. אם קוד הלקוח לא צריך לתמוך בשדרוג מכשירים (שהקרנלים שלהם עשויים לתמוך רק ב-ION heaps), אפשר גם להסיר את הקריאות ל-MapNameToIonHeap().