انتقال از ION به پشته های DMA-BUF (فقط هسته 5.4)

در اندروید ۱۲، GKI 2.0 به دلایل زیر، تخصیص‌دهنده ION را با هیپ‌های DMA-BUF جایگزین می‌کند:

  • امنیت: از آنجا که هر هیپ DMA-BUF یک دستگاه کاراکتری جداگانه است، دسترسی به هر هیپ را می‌توان با seppolicy به طور جداگانه کنترل کرد. این امر با ION امکان‌پذیر نبود زیرا تخصیص از هر هیپ فقط نیاز به دسترسی به دستگاه /dev/ion داشت.
  • پایداری ABI: برخلاف ION، رابط IOCTL چارچوب هیپ‌های DMA-BUF از نظر ABI پایدار است زیرا در هسته لینوکس بالادستی نگهداری می‌شود.
  • استانداردسازی: چارچوب هیپ‌های DMA-BUF یک UAPI کاملاً تعریف‌شده ارائه می‌دهد. ION امکان استفاده از پرچم‌ها و شناسه‌های هیپ سفارشی را فراهم می‌کرد که مانع از توسعه یک چارچوب تست مشترک می‌شد، زیرا پیاده‌سازی ION هر دستگاه می‌توانست رفتار متفاوتی داشته باشد.

شاخه android12-5.10 از هسته مشترک اندروید، CONFIG_ION در اول مارس 2021 غیرفعال کرد.

پیشینه

در ادامه مقایسه‌ای مختصر بین هیپ‌های ION و DMA-BUF ارائه شده است.

شباهت‌های بین چارچوب هیپ‌های ION و DMA-BUF

  • چارچوب‌های هیپ ION و DMA-BUF هر دو صادرکننده‌های DMA-BUF مبتنی بر هیپ هستند.
  • هر دو به هر هیپ اجازه می‌دهند تخصیص‌دهنده و عملیات DMA-BUF خود را تعریف کند.
  • عملکرد تخصیص مشابه است زیرا هر دو طرح برای تخصیص به یک IOCTL واحد نیاز دارند.

تفاوت‌های بین چارچوب هیپ‌های ION و DMA-BUF

هیپ‌های 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 قرار دارند.
شناسه/ماسک هیپ و پرچم‌ها برای تخصیص باید مشخص شوند. نام هیپ برای تخصیص استفاده می‌شود.

بخش‌های زیر اجزایی را که با ION سروکار دارند فهرست می‌کنند و نحوه‌ی انتقال آنها به چارچوب هیپ‌های DMA-BUF را شرح می‌دهند.

انتقال درایورهای هسته از هیپ‌های ION به هیپ‌های DMA-BUF

درایورهای هسته که هیپ‌های ION را پیاده‌سازی می‌کنند

هر دو هیپ ION و DMA-BUF به هر هیپ اجازه می‌دهند تخصیص‌دهنده‌ها و عملیات DMA-BUF خود را پیاده‌سازی کند. بنابراین می‌توانید با استفاده از مجموعه‌ای متفاوت از APIها برای ثبت هیپ، از پیاده‌سازی هیپ ION به پیاده‌سازی هیپ DMA-BUF تغییر دهید. این جدول APIهای ثبت هیپ ION و APIهای هیپ DMA-BUF معادل آنها را نشان می‌دهد.

هیپ‌های 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 ثبت شود. برای تسهیل اشتراک‌گذاری کد، توصیه می‌شود همه انواع هیپ مشابه را در یک درایور ثبت کنید. این مثال dma-buf: system_heap پیاده‌سازی انواع کش‌شده و کش‌نشده هیپ سیستم را نشان می‌دهد.

از این الگوی نمونه dma-buf:heaps: برای ایجاد یک هیپ DMA-BUF از ابتدا استفاده کنید.

درایورهای هسته مستقیماً از پشته‌های ION تخصیص می‌یابند

چارچوب هیپ‌های DMA-BUF همچنین یک رابط تخصیص برای کلاینت‌های درون هسته ارائه می‌دهد. به جای مشخص کردن ماسک هیپ و پرچم‌ها برای انتخاب نوع تخصیص، رابط ارائه شده توسط هیپ‌های DMA-BUF نام هیپ را به عنوان ورودی دریافت می‌کند.

شکل زیر API تخصیص ION درون هسته و APIهای تخصیص هیپ DMA-BUF معادل آن را نشان می‌دهد. درایورهای هسته می‌توانند از API dma_heap_find() برای پرس‌وجو در مورد وجود یک هیپ استفاده کنند. این API یک اشاره‌گر به نمونه‌ای از struct dma_heap برمی‌گرداند که می‌تواند به عنوان آرگومان به API dma_heap_buffer_alloc() ارسال شود.

هیپ‌های 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-BUF استفاده می‌کنند

برای درایورهایی که فقط DMA-BUFها را وارد می‌کنند، نیازی به تغییر نیست، زیرا بافری که از یک هیپ ION اختصاص داده شده است، دقیقاً مانند بافری که از یک هیپ DMA-BUF معادل اختصاص داده شده است، رفتار می‌کند.

انتقال کلاینت‌های فضای کاربری ION به هیپ‌های DMA-BUF

برای آسان‌تر کردن انتقال برای کلاینت‌های فضای کاربری ION، یک کتابخانه انتزاعی به نام libdmabufheap در دسترس است. libdmabufheap از تخصیص در هیپ‌های DMA-BUF و هیپ‌های ION پشتیبانی می‌کند. ابتدا بررسی می‌کند که آیا هیپ DMA-BUF با نام مشخص شده وجود دارد یا خیر و در صورت وجود، به هیپ ION معادل برمی‌گردد.

کلاینت‌ها باید در طول مقداردهی اولیه خود، به جای باز کردن /dev/ion using ion_open() یک شیء BufferAllocator مقداردهی اولیه کنند. دلیل این امر آن است که توصیف‌گرهای فایل ایجاد شده با باز کردن /dev/ion و /dev/dma_heap/<heap_name> به صورت داخلی توسط شیء BufferAllocator مدیریت می‌شوند.

برای تغییر از libion ​​به libdmabufheap ، رفتار کلاینت‌ها را به صورت زیر تغییر دهید:

  • به جای شناسه/ماسک سرپوش و پرچم پشته، نام پشته را برای تخصیص پیگیری کنید.
  • API مربوط به ion_alloc_fd() که یک ماسک هیپ و آرگومان پرچم می‌گیرد را با API BufferAllocator::Alloc() که یک نام هیپ می‌گیرد، جایگزین کنید.

این جدول با نشان دادن نحوه‌ی تخصیص حافظه‌ی پنهان نشده‌ی سیستم توسط 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)

نوع بدون حافظه پنهان پشته سیستم در انتظار تأیید از بالادست است، اما در حال حاضر بخشی از شاخه android12-5.10 است.

برای پشتیبانی از ارتقاء دستگاه‌ها، API مربوط به MapNameToIonHeap() امکان نگاشت نام یک هیپ به پارامترهای هیپ ION (نام هیپ یا ماسک و پرچم‌ها) را فراهم می‌کند تا به آن رابط‌ها اجازه دهد از تخصیص‌های مبتنی بر نام استفاده کنند. در اینجا یک مثال تخصیص مبتنی بر نام آورده شده است.

مستندات هر API که توسط libdmabufheap ارائه می‌شود، موجود است. این کتابخانه همچنین یک فایل هدر برای استفاده توسط کلاینت‌های C ارائه می‌دهد.

پیاده‌سازی مرجع Gralloc

پیاده‌سازی gralloc در Hikey960 از libdmabufheap استفاده می‌کند، بنابراین می‌توانید از آن به عنوان یک پیاده‌سازی مرجع استفاده کنید.

اضافات مورد نیاز

برای هر هیپ DMA-BUF جدید مختص دستگاه که ایجاد شده است، یک ورودی جدید به فایل ueventd.rc دستگاه اضافه کنید. این مثال راه‌اندازی ueventd برای پشتیبانی از هیپ‌های DMA-BUF نحوه انجام این کار را برای هیپ سیستم DMA-BUF نشان می‌دهد.

اضافات مورد نیاز برای سیاست‌گذاری عمومی

مجوزهای sepolicy را اضافه کنید تا یک کلاینت فضای کاربری بتواند به یک هیپ DMA-BUF جدید دسترسی پیدا کند. این مثال از افزودن مجوزهای مورد نیاز، مجوزهای sepolicy ایجاد شده برای کلاینت‌های مختلف جهت دسترسی به هیپ سیستم DMA-BUF را نشان می‌دهد.

دسترسی به هیپ‌های فروشنده از کد فریم‌ورک

برای اطمینان از انطباق با Treble، کد چارچوب فقط می‌تواند از دسته‌های از پیش تأیید شده‌ی پشته‌های فروشندگان تخصیص یابد.

بر اساس بازخورد دریافتی از شرکا، گوگل دو دسته از هیپ‌های فروشندگان را شناسایی کرد که باید از طریق کد فریم‌ورک به آنها دسترسی داشت:

  1. هیپ‌هایی که مبتنی بر هیپ سیستم با بهینه‌سازی‌های عملکرد خاص دستگاه یا SoC هستند.
  2. هیپ‌هایی برای تخصیص از حافظه محافظت‌شده.

هیپ‌ها بر اساس هیپ سیستم با بهینه‌سازی‌های عملکرد مختص دستگاه یا SoC

برای پشتیبانی از این مورد استفاده، می‌توان پیاده‌سازی هیپ سیستم هیپ پیش‌فرض DMA-BUF را لغو کرد.

  • CONFIG_DMABUF_HEAPS_SYSTEM در gki_defconfig غیرفعال است تا به عنوان یک ماژول فروشنده باقی بماند.
  • تست‌های انطباق با VTS تضمین می‌کنند که هیپ در /dev/dma_heap/system وجود دارد. این تست‌ها همچنین تأیید می‌کنند که هیپ می‌تواند از فضای کاربر تخصیص داده شود و توصیفگر فایل برگشتی ( fd ) می‌تواند از فضای کاربر نگاشت حافظه (mmapped) شود.

نکات قبلی در مورد نوع بدون حافظه پنهان پشته سیستم نیز صادق است، اگرچه وجود آن برای دستگاه‌های کاملاً منسجم IO الزامی نیست.

هیپ‌هایی برای تخصیص از حافظه محافظت‌شده

پیاده‌سازی‌های پشته امن باید مختص فروشنده باشند، زیرا هسته مشترک اندروید از پیاده‌سازی عمومی پشته امن پشتیبانی نمی‌کند.

  • پیاده‌سازی‌های مختص فروشنده‌ی خود را در /dev/dma_heap/system-secure<vendor-suffix> ثبت کنید.
  • این پیاده‌سازی‌های هیپ اختیاری هستند.
  • اگر هیپ‌ها وجود داشته باشند، تست‌های VTS تضمین می‌کنند که تخصیص‌ها می‌توانند از روی آنها انجام شوند.
  • اجزای چارچوب به این هیپ‌ها دسترسی دارند تا بتوانند از طریق HALهای Codec2/non-binderized و با فرآیند یکسان، استفاده از هیپ‌ها را فعال کنند. با این حال، ویژگی‌های عمومی چارچوب اندروید به دلیل تنوع در جزئیات پیاده‌سازی آنها، نمی‌توانند به آنها وابسته باشند. اگر در آینده یک پیاده‌سازی هیپ امن عمومی به هسته مشترک اندروید اضافه شود، باید از ABI متفاوتی استفاده کند تا از تداخل با دستگاه‌های در حال ارتقا جلوگیری شود.

تخصیص‌دهنده کدک ۲ برای هیپ‌های DMA-BUF

یک تخصیص‌دهنده codec2 برای رابط هیپ‌های DMA-BUF در AOSP موجود است.

رابط ذخیره‌سازی اجزا که امکان تعیین پارامترهای هیپ از C2 HAL را فراهم می‌کند، با تخصیص‌دهنده هیپ C2 DMA-BUF در دسترس است.

نمونه جریان انتقال برای یک هیپ ION

برای روان‌تر کردن انتقال از هیپ‌های ION به DMA-BUF، libdmabufheap امکان تغییر یک هیپ را در هر زمان فراهم می‌کند. مراحل زیر یک گردش کار پیشنهادی برای انتقال یک هیپ ION غیر قدیمی به نام my_heap را نشان می‌دهد که از یک پرچم، ION_FLAG_MY_FLAG ، پشتیبانی می‌کند.

مرحله ۱: معادل‌های هیپ ION را در چارچوب DMA-BUF ایجاد کنید. در این مثال، از آنجا که هیپ ION my_heap از پرچم ION_FLAG_MY_FLAG پشتیبانی می‌کند، دو هیپ DMA-BUF ثبت می‌کنیم:

  • رفتار my_heap دقیقاً با رفتار ION heap در حالتی که فلگ ION_FLAG_MY_FLAG غیرفعال باشد، مطابقت دارد.
  • رفتار my_heap_special دقیقاً با رفتار ION heap با پرچم ION_FLAG_MY_FLAG فعال مطابقت دارد.

مرحله ۲: تغییرات ueventd را برای هیپ‌های جدید my_heap و my_heap_special DMA-BUF ایجاد کنید. در این مرحله، هیپ‌ها با مجوزهای مورد نظر به صورت /dev/dma_heap/my_heap و /dev/dma_heap/my_heap_special قابل مشاهده هستند.

مرحله ۳: برای کلاینت‌هایی که از my_heap تخصیص می‌دهند، فایل‌های makefile آنها را طوری تغییر دهید که به libdmabufheap لینک شوند. در طول مقداردهی اولیه کلاینت، یک شیء BufferAllocator نمونه‌سازی کنید و از API مربوط به MapNameToIonHeap() برای نگاشت ترکیب <ION heap name/mask, flag> به نام‌های معادل heap مربوط به 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 روی مقدار empty، نگاشتی از <ION heap mask, flag> به نام‌های هیپ معادل DMA-BUF ایجاد کنید.

مرحله ۴: فراخوانی‌های ion_alloc_fd() را با BufferAllocator::Alloc() با استفاده از نام هیپ مناسب جایگزین کنید.

نوع تخصیص لیبیون لیبدمابوفهیپ
تخصیص از 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 تخصیص می‌دهد زیرا مجوزهای seppolicy لازم برای باز کردن هیپ DMA-BUF را ندارد.

مرحله ۵: مجوزهای جداگانه مورد نیاز برای کلاینت جهت دسترسی به هیپ‌های جدید DMA-BUF را ایجاد کنید. اکنون کلاینت کاملاً برای تخصیص از هیپ جدید DMA-BUF مجهز شده است.

مرحله ۶: با بررسی logcat، تأیید کنید که تخصیص‌ها از هیپ جدید DMA-BUF انجام می‌شوند.

مرحله 7: غیرفعال کردن ION heap my_heap در هسته. اگر کد کلاینت نیازی به پشتیبانی از دستگاه‌های در حال ارتقا ندارد (که هسته‌های آنها ممکن است فقط از ION heap پشتیبانی کنند)، می‌توانید فراخوانی‌های MapNameToIonHeap() را نیز حذف کنید.