การเปลี่ยนจาก ION เป็น DMA-BUF Heaps

ใน Android 12 GKI 2.0 จะแทนที่ ION allocator ด้วย DMA-BUF heaps ด้วยเหตุผลดังต่อไปนี้:

  • ความปลอดภัย: เนื่องจากฮีป DMA-BUF แต่ละฮีปเป็นอุปกรณ์อักขระที่แยกจากกัน การเข้าถึงฮีปแต่ละฮีปจึงสามารถควบคุมแยกกันได้ด้วยการแยกตัวออกจากกัน สิ่งนี้เป็นไปไม่ได้ด้วย ION เนื่องจากการจัดสรรจากฮีปใดๆ จำเป็นต้องเข้าถึงอุปกรณ์ /dev/ion เท่านั้น
  • ความเสถียรของ ABI: อินเทอร์เฟซ IOCTL ของ DMA-BUF heaps framework ต่างจาก ION รับประกันว่า ABI เสถียรเพราะได้รับการดูแลในเคอร์เนลอัปสตรีม
  • การกำหนดมาตรฐาน: เฟรมเวิร์ก DMA-BUF heaps นำเสนอ UAPI ที่กำหนดไว้อย่างดี ION อนุญาตแฟล็กที่กำหนดเองและ ID ฮีปที่ขัดขวางการพัฒนาเฟรมเวิร์กการทดสอบทั่วไป เนื่องจากการใช้งาน ION ของอุปกรณ์แต่ละเครื่องอาจมีการทำงานแตกต่างกัน

สาขา android12-5.10 ของ Android Common Kernel ปิดใช้งาน CONFIG_ION เมื่อ วันที่ 1 มีนาคม 2021

พื้นหลัง

ต่อไปนี้คือการเปรียบเทียบสั้นๆ ระหว่างฮีป ION และ DMA-BUF

ความคล้ายคลึงกันระหว่างเฟรมเวิร์กฮีป ION และ DMA-BUF

  • ION และ DMA-BUF ฮีปเฟรมเวิร์กเป็นทั้งผู้ส่งออก DMA-BUF แบบอิงฮีป
  • พวกเขาทั้งสองปล่อยให้แต่ละฮีปกำหนดตัวจัดสรรของตัวเองและ DMA-BUF ops
  • ประสิทธิภาพการจัดสรรมีความคล้ายคลึงกันเนื่องจากทั้งสองแผนต้องมี IOCTL เดียวสำหรับการจัดสรร

ความแตกต่างระหว่างเฟรมเวิร์กฮีป ION และ DMA-BUF

ไอออนฮีป 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 ID/มาสก์และแฟล็กเพื่อการจัดสรร ชื่อฮีปใช้สำหรับการจัดสรร

ส่วนต่อไปนี้แสดงรายการส่วนประกอบที่เกี่ยวข้องกับ ION และอธิบายวิธีสลับไปยังเฟรมเวิร์กฮีป DMA-BUF

การเปลี่ยนไดรเวอร์เคอร์เนลจาก ION เป็น DMA-BUF heaps

ไดรเวอร์เคอร์เนลที่ใช้ ION heaps

ทั้งฮีป ION และ DMA-BUF อนุญาตให้แต่ละฮีปใช้ตัวจัดสรรของตัวเองและ DMA-BUF ops ดังนั้น คุณจึงสามารถเปลี่ยนจากการนำ ION heap ไปใช้งาน DMA-BUF heap ได้โดยใช้ API ชุดอื่นเพื่อลงทะเบียน heap ตารางนี้แสดง API การลงทะเบียน ION heap และ DMA-BUF heap API ที่เทียบเท่ากัน

ไอออนฮีป 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 heap ตั้งแต่เริ่มต้น

ไดรเวอร์เคอร์เนลที่จัดสรรโดยตรงจาก ION heaps

เฟรมเวิร์กฮีป DMA-BUF ยังมี อินเทอร์เฟซการจัดสรร สำหรับไคลเอ็นต์ในเคอร์เนล แทนที่จะระบุฮีพมาสก์และแฟล็กเพื่อเลือกประเภทของการจัดสรร อินเทอร์เฟซที่เสนอโดยฮีป DMA-BUF จะใช้ชื่อฮีพเป็นอินพุต

ต่อไปนี้แสดง in-kernel ION allocation API และ DMA-BUF heap allocation API ที่เทียบเท่ากัน ไดรเวอร์เคอร์เนลสามารถใช้ dma_heap_find() API เพื่อสอบถามการมีอยู่ของฮีป 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-BUFs

ไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ สำหรับไดรเวอร์ที่นำเข้าเฉพาะ DMA-BUF เนื่องจากบัฟเฟอร์ที่จัดสรรจากฮีป ION จะทำงานเหมือนกับบัฟเฟอร์ที่จัดสรรจากฮีป DMA-BUF ที่เทียบเท่ากันทุกประการ

การเปลี่ยนไคลเอ็นต์พื้นที่ผู้ใช้ของ ION เป็น DMA-BUF heaps

เพื่อให้การเปลี่ยนแปลงเป็นเรื่องง่ายสำหรับไคลเอ็นต์พื้นที่ผู้ใช้ของ 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 ให้แก้ไขพฤติกรรมของไคลเอ็นต์ดังนี้:

  • ติดตามชื่อฮีปที่จะใช้สำหรับการจัดสรร แทนที่จะเป็น ID ส่วนหัว/มาสก์และแฟล็กฮีป
  • แทนที่ ion_alloc_fd() API ซึ่งใช้ heap mask และแฟล็กอาร์กิวเมนต์ด้วย BufferAllocator::Alloc() API ซึ่งใช้ชื่อฮีปแทน

ตารางนี้แสดงการเปลี่ยนแปลงเหล่านี้โดยแสดงให้เห็นว่า 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)

ฮีปของระบบ ที่ไม่ได้แคชกำลังรอการอนุมัติจากอัปสตรีม แต่เป็นส่วนหนึ่งของสาขา android12-5.10 แล้ว

เพื่อรองรับการอัปเกรดอุปกรณ์ MapNameToIonHeap() API อนุญาตให้ทำการแมปชื่อฮีปกับพารามิเตอร์ฮีป ION (ชื่อฮีป/มาสก์และแฟล็ก) เพื่อให้อินเทอร์เฟซเหล่านั้นใช้การจัดสรรตามชื่อได้ด้วย นี่คือ ตัวอย่างการจัดสรรตามชื่อ

เอกสารประกอบสำหรับ API ทุกตัวที่เปิดเผยโดย libdmabufheap มีให้ใช้งาน ไลบรารี ยังเปิดเผยไฟล์ส่วนหัวสำหรับใช้งานโดยไคลเอนต์ C

การอ้างอิงการใช้งาน Gralloc

การใช้งาน Hikey960 gralloc ใช้ libdmabufheap ดังนั้นคุณจึงสามารถใช้เป็น ข้อมูลอ้างอิง ได้

การเพิ่ม ueventd ที่จำเป็น

สำหรับฮีป DMA-BUF เฉพาะอุปกรณ์ใหม่ที่สร้างขึ้น ให้เพิ่มรายการใหม่ในไฟล์ ueventd.rc ของอุปกรณ์ การ ติดตั้งนี้สนับสนุนตัวอย่าง DMA-BUF heaps สาธิตวิธีการดำเนินการนี้สำหรับฮีประบบ DMA-BUF

การเพิ่มการแยกดินแดนที่จำเป็น

เพิ่มการอนุญาต sepolicy เพื่อเปิดใช้งานไคลเอ็นต์ userspace เพื่อเข้าถึงฮีป DMA-BUF ใหม่ ตัวอย่าง สิทธิ์ที่จำเป็นของการเพิ่ม นี้แสดงการอนุญาตการแยกตัวที่สร้างขึ้นสำหรับไคลเอนต์ต่างๆ เพื่อเข้าถึงฮีประบบ DMA-BUF

การเข้าถึงผู้ขายฮีปจากเฟรมเวิร์กโค้ด

เพื่อให้แน่ใจว่าเป็นไปตามข้อกำหนดของ Treble โค้ดเฟรมเวิร์กสามารถจัดสรรจากหมวดหมู่ฮีปของผู้ขายที่ได้รับการอนุมัติล่วงหน้าเท่านั้น

ตามความคิดเห็นที่ได้รับจากพันธมิตร Google ระบุกลุ่มผู้ขายสองประเภทที่ต้องเข้าถึงจากรหัสเฟรมเวิร์ก:

  1. ฮีปที่ยึดตามฮีปของระบบด้วยอุปกรณ์หรือการปรับแต่งประสิทธิภาพเฉพาะ SoC
  2. ฮีปเพื่อจัดสรรจากหน่วยความจำที่ได้รับการป้องกัน

ฮีปตามฮีปของระบบที่มีอุปกรณ์หรือการปรับแต่งประสิทธิภาพเฉพาะ SoC

เพื่อสนับสนุนกรณีการใช้งานนี้ การนำฮีปของระบบฮีป DMA-BUF เริ่มต้นไปใช้สามารถถูกแทนที่ได้

  • CONFIG_DMABUF_HEAPS_SYSTEM ถูกปิดใน gki_defconfig เพื่อให้เป็นโมดูลผู้ขาย
  • การทดสอบความสอดคล้องของ VTS ช่วยให้มั่นใจว่ามีฮีปอยู่ที่ /dev/dma_heap/system การทดสอบยังตรวจสอบว่าสามารถจัดสรรฮีปได้ และไฟล์ descriptor ที่ส่งคืน ( fd ) สามารถจับคู่หน่วยความจำ (mmapped) จากพื้นที่ผู้ใช้

จุดก่อนหน้านั้นเป็นจริงเช่นกันสำหรับฮีประบบที่ไม่ได้เก็บแคชไว้ แม้ว่าการมีอยู่ของมันจะไม่บังคับสำหรับอุปกรณ์ที่สอดคล้อง IO อย่างสมบูรณ์ก็ตาม

กองที่จะจัดสรรจากหน่วยความจำที่ได้รับการป้องกัน

การใช้งานฮีปที่ปลอดภัยจะต้องเป็นแบบเฉพาะของผู้ขาย เนื่องจากเคอร์เนลทั่วไปของ Android ไม่รองรับการใช้งานฮีปที่ปลอดภัยทั่วไป

  • ลงทะเบียนการใช้งานเฉพาะผู้จำหน่ายของคุณเป็น /dev/dma_heap/system-secure<vendor-suffix>
  • การใช้งานฮีปเหล่านี้เป็นทางเลือก
  • หากมีฮีป การทดสอบ VTS จะช่วยให้แน่ใจว่าการจัดสรรนั้นสามารถทำได้
  • คอมโพเนนต์เฟรมเวิร์กได้รับการเข้าถึงฮีปเหล่านี้เพื่อให้สามารถเปิดใช้งานการใช้ฮีปผ่าน Codec2 HAL/ไม่ผูกมัด HAL กระบวนการเดียวกัน อย่างไรก็ตาม คุณลักษณะทั่วไปของกรอบงาน Android ไม่สามารถพึ่งพาได้เนื่องจากความแปรปรวนในรายละเอียดการนำไปใช้งาน หากมีการเพิ่มการใช้งานฮีปความปลอดภัยทั่วไปในเคอร์เนลทั่วไปของ Android ในอนาคต จะต้องใช้ ABI อื่นเพื่อหลีกเลี่ยงข้อขัดแย้งกับการอัปเกรดอุปกรณ์

ตัวจัดสรรตัวแปลงสัญญาณ 2 สำหรับ DMA-BUF heaps

ตัวจัดสรรตัวแปลงสัญญาณ2 สำหรับอินเทอร์เฟซฮีป DMA-BUF มีอยู่ใน AOSP

อินเทอร์เฟซที่เก็บส่วนประกอบที่อนุญาตให้ระบุพารามิเตอร์ฮีปจาก C2 HAL พร้อมใช้งานกับตัวจัดสรรฮีป C2 DMA-BUF

ตัวอย่างโฟลว์การเปลี่ยนภาพสำหรับ ION heap

เพื่อให้การเปลี่ยนจากฮีป 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 behavior ตรงกับพฤติกรรมของ 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 ให้แก้ไข makefiles เพื่อลิงก์ไปยัง 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 heap name เป็นค่าว่าง

ขั้นตอนที่ 4: แทนที่การเรียกใช้ ion_alloc_fd() ด้วย BufferAllocator::Alloc() โดยใช้ชื่อฮีปที่เหมาะสม

ประเภทการจัดสรร libion libdmabufheap
การจัดสรรจาก my_heap พร้อมแฟ ION_FLAG_MY_FLAG unset ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd) allocator->Alloc("my_heap", size
การจัดสรรจาก my_heap พร้อมแฟ ION_FLAG_MY_FLAG set ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, ION_FLAG_MY_FLAG, &fd) allocator->Alloc("my_heap_special", size)

ณ จุดนี้ ไคลเอ็นต์ทำงานได้ แต่ยังคงจัดสรรจาก ION heap เนื่องจากไม่มีสิทธิ์การแยกตัวที่จำเป็นในการเปิด DMA-BUF heap

ขั้นตอนที่ 5: สร้างสิทธิ์การแยกตัวที่จำเป็นสำหรับไคลเอนต์เพื่อเข้าถึงฮีป DMA-BUF ใหม่ ขณะนี้ไคลเอ็นต์มีอุปกรณ์ครบครันในการจัดสรรจากฮีป DMA-BUF ใหม่

ขั้นตอนที่ 6: ตรวจสอบว่าการจัดสรรเกิดขึ้นจากฮีป DMA-BUF ใหม่โดยตรวจสอบ logcat

ขั้นตอนที่ 7: ปิดใช้งาน ION heap my_heap ในเคอร์เนล หากรหัสไคลเอนต์ไม่จำเป็นต้องรองรับการอัปเกรดอุปกรณ์ (ซึ่งเมล็ดอาจรองรับเฉพาะฮีป ION) คุณยังสามารถลบการเรียกใช้ MapNameToIonHeap() ได้