انتقال از ION به DMA-BUF Heaps

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

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

شاخه android12-5.10 هسته مشترک Android CONFIG_ION در 1 مارس 2021 غیرفعال کرد.

زمینه

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

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

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

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

پشته های یون پشته های DMA-BUF
تمام تخصیص های ION با /dev/ion انجام می شود. هر پشته DMA-BUF یک دستگاه کاراکتری است که در /dev/dma_heap/<heap_name> موجود است.
ION از پرچم های خصوصی heap پشتیبانی می کند. پشته‌های DMA-BUF از پرچم‌های خصوصی پشته‌ای پشتیبانی نمی‌کنند. هر نوع تخصیص متفاوت در عوض از پشته متفاوتی انجام می شود. به عنوان مثال، انواع هیپ سیستم حافظه پنهان و ذخیره نشده، پشته های جداگانه ای هستند که در /dev/dma_heap/system و /dev/dma_heap/system_uncached قرار دارند.
شناسه/ماسک پشته و پرچم ها باید برای تخصیص مشخص شوند. از نام پشته برای تخصیص استفاده می شود.

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

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

درایورهای هسته در حال پیاده سازی پشته های ION

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

پشته های یون پشته های 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 از پرچم‌های خصوصی پشته‌ای پشتیبانی نمی‌کنند. بنابراین هر گونه از heap باید به صورت جداگانه با استفاده از dma_heap_add() API ثبت شود. برای تسهیل اشتراک‌گذاری کد، توصیه می‌شود همه انواع یک پشته را در یک درایور ثبت کنید. این مثال dma-buf: system_heap اجرای انواع کش و غیر کش سیستم 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 برمی گرداند، که سپس می تواند به عنوان آرگومان به dma_heap_buffer_alloc() API ارسال شود.

پشته های یون پشته های 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 با نام مشخص‌شده وجود دارد یا نه، اگر وجود داشته باشد، به یک پشته یون معادل برمی‌گردد.

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

برای تغییر از libion ​​به libdmabufheap ، رفتار مشتریان را به صورت زیر تغییر دهید:

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

این جدول این تغییرات را با نشان دادن اینکه چگونه 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 است.

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

اسناد مربوط به هر API که توسط libdmabufheap در معرض دید قرار می گیرد در دسترس است. این کتابخانه همچنین یک فایل هدر را برای استفاده توسط کلاینت های C در معرض دید قرار می دهد.

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

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

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

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

ضمیمه های سیاست گذاری مورد نیاز

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

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

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

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

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

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

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

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

نکات پیشین در مورد نوع ذخیره نشده سیستم heap نیز صادق است، اگرچه وجود آن برای دستگاه های کاملاً منسجم IO اجباری نیست.

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

پیاده سازی Secure Heap باید مختص فروشنده باشد، زیرا Android Common Kernel از اجرای Heap ایمن عمومی پشتیبانی نمی کند.

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

تخصیص دهنده کدک 2 برای پشته های DMA-BUF

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

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

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

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

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

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

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

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

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

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

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