انتقال از ION به توده‌های DMA-BUF (فقط هسته ۵.۴)

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

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

شاخه android12-5.10 از «هسته مشترک Android» در تاریخ ۱ مارس ۲۰۲۱ غیرفعال شد CONFIG_ION.

پس‌زمینه

در زیر مقایسه مختصری بین پشته‌های 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 بروید. این جدول میاناهای برنامه‌سازی کاربردی ثبت پشته ION و میاناهای برنامه‌سازی کاربردی پشته 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: example template برای ایجاد توده DMA-BUF از ابتدا استفاده کنید.

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

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

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

پشته‌های 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، رفتار مشتریان را به‌صورت زیر تغییر دهید:

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

این جدول این تغییرات را با نشان دادن نحوه انجام تخصیص پشته سیستم بدون حافظه نهان توسط libion و libdmabufheap نشان می‌دهد.

نوع تخصیص لیبیون 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 است.

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

مستندات همه میاناهای برنامه‌سازی کاربردی نمایان‌شده توسط libdmabufheap دردسترس است. کتابخانه همچنین یک فایل سرصفحه برای استفاده توسط مشتریان C ارائه می‌دهد.

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

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

افزوده‌های الزامی ueventd

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

افزوده‌های الزامی sepolicy

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

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

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

براساس بازخوردهای دریافتی از شرکا، Google دو دسته از انبوهه‌های فروشنده را شناسایی کرده است که باید از کد چارچوب به آن‌ها دسترسی داشت:

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

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

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

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

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

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

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

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

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

واگذارکننده کدک۲ برای رابط توده‌های DMA-BUF در AOSP دردسترس است.

میانای فروشگاه مؤلفه که به پارامترهای پشته اجازه می‌دهد از لایه انتزاع سخت‌افزار C2 مشخص شوند با تخصیص‌دهنده پشته 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 با غیرفعال بودن پرچم ION_FLAG_MY_FLAG مطابقت دارد.
  • رفتار my_heap_special دقیقاً با رفتار پشته ION با پرچم 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 شیء نمونه‌سازی کنید و از MapNameToIonHeap() API برای تخصیص دادن ترکیب <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 با پارامترهای نام و پرچم، می‌توانید با تنظیم پارامتر نام پشته ION روی خالی، نگاشت را از <ION heap mask, flag> به نام‌های پشته DMA-BUF معادل ایجاد کنید

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

نوع تخصیص لیبیون 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 ندارد.

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

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

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