ใน Android 12, GKI 2.0 จะแทนที่ตัวจัดสรร ION ด้วยฮีป DMA-BUF ด้วยเหตุผลต่อไปนี้
- ความปลอดภัย: เนื่องจากฮีป DMA-BUF แต่ละรายการเป็นอุปกรณ์อักขระแยกกัน จึงสามารถควบคุมการเข้าถึงฮีปแต่ละรายการแยกกันได้ด้วย sepolicy ซึ่งทำไม่ได้ใน ION เนื่องจากการจัดสรรจากฮีปใดก็ตามต้องใช้สิทธิ์เข้าถึงอุปกรณ์
/dev/ionเท่านั้น - ความเสถียรของ ABI: อินเทอร์เฟซ IOCTL ของเฟรมเวิร์กฮีป DMA-BUF มีความเสถียรของ ABI เนื่องจากมีการดูแลรักษาในเคอร์เนล Linux ต้นน้ำ ซึ่งแตกต่างจาก ION
- การกำหนดมาตรฐาน: เฟรมเวิร์กฮีป DMA-BUF มี UAPI ที่กำหนดไว้อย่างชัดเจน ION อนุญาตให้ใช้แฟล็กและรหัสฮีปที่กำหนดเอง ซึ่งขัดขวางการพัฒนาเฟรมเวิร์กการทดสอบทั่วไป เนื่องจากการใช้งาน ION ของอุปกรณ์แต่ละเครื่องอาจทำงานแตกต่างกัน
สาขา android12-5.10 ของ Android Common Kernel ปิดใช้ CONFIG_ION ในวันที่ 1 มีนาคม 2021
ฉากหลัง
ต่อไปนี้คือการเปรียบเทียบโดยย่อระหว่าง ION กับฮีป DMA-BUF
ความคล้ายคลึงระหว่างเฟรมเวิร์ก ION กับเฟรมเวิร์กฮีป DMA-BUF
- ทั้งเฟรมเวิร์ก ION และเฟรมเวิร์กฮีป DMA-BUF เป็นตัวส่งออก DMA-BUF ที่อิงตามฮีป
- ทั้ง 2 เฟรมเวิร์กอนุญาตให้ฮีปแต่ละรายการกำหนดตัวจัดสรรและ DMA-BUF Ops ของตัวเองได้
- ประสิทธิภาพการจัดสรรคล้ายกันเนื่องจากทั้ง 2 สคีมาต้องใช้ 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 Ops ของตัวเองได้ ดังนั้นคุณจึงเปลี่ยนจากการใช้งานฮีป ION ไปเป็นการใช้งานฮีป DMA-BUF ได้โดยใช้ชุด API อื่นเพื่อลงทะเบียนฮีป ตารางนี้แสดง 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 ซึ่งสามารถส่งผ่านเป็นอาร์กิวเมนต์ไปยัง
dma_heap_buffer_alloc() API ได้
| ฮีป ION | ฮีป DMA-BUF |
|---|---|
struct dma_buf *ion_alloc(size_t len, unsigned int heap_id_mask, unsigned int flags)
|
|
ไดรเวอร์เคอร์เนลที่ใช้ DMA-BUF
ไดรเวอร์ที่นำเข้าเฉพาะ DMA-BUF ไม่จำเป็นต้องทำการเปลี่ยนแปลงใดๆ เนื่องจากบัฟเฟอร์ที่จัดสรรจากฮีป ION จะทำงานเหมือนกับบัฟเฟอร์ที่จัดสรรจากฮีป DMA-BUF ที่เทียบเท่าทุกประการ
เปลี่ยนไคลเอ็นต์พื้นที่ผู้ใช้ของ ION เป็นฮีป DMA-BUF
เรามีไลบรารีการแยกส่วนที่เรียกว่า
libdmabufheap
เพื่อให้ไคลเอ็นต์พื้นที่ผู้ใช้ของ ION เปลี่ยนไปใช้ฮีป DMA-BUF ได้ง่าย libdmabufheap รองรับการจัดสรรในฮีป DMA-BUF และฮีป ION โดยจะตรวจสอบก่อนว่ามีฮีป DMA-BUF ที่มีชื่อที่ระบุหรือไม่ หากไม่มี ระบบจะกลับไปใช้ฮีป ION ที่เทียบเท่า (หากมี)
ไคลเอ็นต์ควรเริ่มต้นออบเจ็กต์
BufferAllocator
ระหว่างการเริ่มต้นใช้งานแทนที่จะเปิด /dev/ion using
ion_open() เนื่องจากตัวอธิบายไฟล์ที่สร้างขึ้นโดยการเปิด
/dev/ion และ /dev/dma_heap/<heap_name> จะได้รับการจัดการ
ภายในโดยออบเจ็กต์ BufferAllocator
หากต้องการเปลี่ยนจาก libion เป็น libdmabufheap ให้แก้ไขลักษณะการทำงานของไคลเอ็นต์ดังนี้
- ติดตามชื่อฮีปที่จะใช้สำหรับการจัดสรร แทนที่จะเป็นรหัส/มาสก์ของฮีปและแฟล็กของฮีป
- แทนที่ API
ion_alloc_fd()ซึ่งใช้มาสก์และแฟล็กของฮีปเป็นอาร์กิวเมนต์ด้วย APIBufferAllocator::Alloc()ซึ่งใช้ชื่อฮีปแทน
ตารางนี้แสดงการเปลี่ยนแปลงเหล่านี้โดยแสดงวิธีที่ 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
สาขาแล้ว
API MapNameToIonHeap() อนุญาตให้แมปชื่อฮีปกับพารามิเตอร์ฮีป ION (ชื่อหรือมาสก์และแฟล็กของฮีป) เพื่อให้อินเทอร์เฟซเหล่านั้นใช้การจัดสรรตามชื่อได้ เพื่อรองรับการอัปเกรดอุปกรณ์ ต่อไปนี้คือตัวอย่างการจัดสรรตามชื่อ
เอกสารประกอบสำหรับ ทุก API ที่
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 ได้ระบุฮีปของผู้ให้บริการ 2 หมวดหมู่ที่ต้องเข้าถึงจากโค้ดเฟรมเวิร์ก โดยอิงตามความคิดเห็นที่ได้รับจากพาร์ทเนอร์
- ฮีปที่อิงตามฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะอุปกรณ์หรือ SoC
- ฮีปที่จะจัดสรรจากหน่วยความจำที่ได้รับการป้องกัน
ฮีปที่อิงตามฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะอุปกรณ์หรือ SoC
การใช้งานฮีปของระบบฮีป DMA-BUF เริ่มต้นสามารถลบล้างได้เพื่อรองรับกรณีการใช้งานนี้
CONFIG_DMABUF_HEAPS_SYSTEMปิดอยู่ในgki_defconfigเพื่อให้เป็นโมดูลของผู้ให้บริการ- การทดสอบการปฏิบัติตามข้อกำหนด VTS ช่วยให้มั่นใจว่าฮีปอยู่ใน
/dev/dma_heap/systemนอกจากนี้ การทดสอบยังยืนยันว่าสามารถจัดสรรจากฮีปได้ และตัวบอกไฟล์ (fd) ที่ส่งคืนสามารถแมปหน่วยความจำ (mmapped) จากพื้นที่ผู้ใช้ได้
จุดข้างต้นยังใช้ได้กับตัวแปรฮีปของระบบที่ไม่แคชด้วย แม้ว่าการมีอยู่ของตัวแปรนี้จะไม่บังคับสำหรับอุปกรณ์ที่สอดคล้องกับ I/O อย่างสมบูรณ์
ฮีปที่จะจัดสรรจากหน่วยความจำที่ได้รับการป้องกัน
การใช้งานฮีปที่ปลอดภัยต้องเป็นแบบเฉพาะของผู้ให้บริการ เนื่องจาก Android Common Kernel ไม่รองรับการใช้งานฮีปที่ปลอดภัยทั่วไป
- ลงทะเบียนการใช้งานที่เฉพาะเจาะจงของผู้ให้บริการเป็น
/dev/dma_heap/system-secure<vendor-suffix> - การใช้งานฮีปเหล่านี้ไม่บังคับ
- หากมีฮีป การทดสอบ VTS จะช่วยให้มั่นใจว่าสามารถจัดสรรจากฮีปได้
- คอมโพเนนต์เฟรมเวิร์กจะได้รับสิทธิ์เข้าถึงฮีปเหล่านี้เพื่อให้สามารถเปิดใช้การใช้งานฮีปผ่าน Codec2 HAL/non-binderized, HAL ในกระบวนการเดียวกัน อย่างไรก็ตาม ฟีเจอร์เฟรมเวิร์ก Android ทั่วไปไม่สามารถขึ้นอยู่กับฮีปเหล่านี้ได้เนื่องจากรายละเอียดการใช้งานที่แตกต่างกัน หากมีการเพิ่มการใช้งานฮีปที่ปลอดภัยทั่วไปลงใน Android Common Kernel ในอนาคต การใช้งานดังกล่าวต้องใช้ ABI อื่นเพื่อหลีกเลี่ยงความขัดแย้งกับอุปกรณ์ที่อัปเกรด
ตัวจัดสรร Codec 2 สำหรับฮีป DMA-BUF
ตัวจัดสรร codec2 สำหรับอินเทอร์เฟซ ฮีป DMA-BUF มีให้บริการใน AOSP
อินเทอร์เฟซที่เก็บคอมโพเนนต์ที่อนุญาตให้ระบุพารามิเตอร์ฮีปจาก C2 HAL มีให้บริการพร้อมกับตัวจัดสรรฮีป DMA-BUF ของ C2
ขั้นตอนการเปลี่ยนตัวอย่างสำหรับฮีป ION
libdmabufheap อนุญาตให้เปลี่ยนฮีปทีละรายการเพื่อทำให้การเปลี่ยนจาก ION เป็นฮีป DMA-BUF เป็นไปอย่างราบรื่น ขั้นตอนต่อไปนี้แสดงเวิร์กโฟลว์ที่แนะนำสำหรับการเปลี่ยนฮีป ION ที่ไม่ใช่ฮีปเดิมชื่อ my_heap ซึ่งรองรับแฟล็กเดียวคือ ION_FLAG_MY_FLAG
ขั้นตอนที่ 1: สร้างฮีป ION ที่เทียบเท่าในเฟรมเวิร์ก DMA-BUF ในตัวอย่างนี้ เนื่องจากฮีป ION my_heap รองรับแฟล็ก ION_FLAG_MY_FLAG เราจึงลงทะเบียนฮีป DMA-BUF 2 รายการดังนี้
my_heapมีลักษณะการทำงานตรงกับลักษณะการทำงานของฮีป ION ที่ปิดใช้แฟล็กION_FLAG_MY_FLAGทุกประการmy_heap_specialมีลักษณะการทำงานตรงกับลักษณะการทำงานของฮีป ION ที่เปิดใช้แฟล็กION_FLAG_MY_FLAGทุกประการ
ขั้นตอนที่ 2: สร้างการเปลี่ยนแปลง ueventd สำหรับฮีป DMA-BUF my_heap และ
my_heap_special ใหม่ ตอนนี้ฮีปจะแสดงเป็น
/dev/dma_heap/my_heap และ /dev/dma_heap/my_heap_special พร้อม
สิทธิ์ที่ต้องการ
ขั้นตอนที่ 3: สำหรับไคลเอ็นต์ที่จัดสรรจาก my_heap ให้แก้ไขไฟล์ Make เพื่อลิงก์กับ 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 */ )
คุณสามารถสร้างการแมปจาก<ION heap mask, flag> ไปยังชื่อฮีป DMA-BUF ที่เทียบเท่าได้โดยตั้งค่าพารามิเตอร์ชื่อฮีป ION เป็นค่าว่าง แทนที่จะใช้ API MapNameToIonHeap() กับพารามิเตอร์ชื่อและแฟล็ก
ขั้นตอนที่ 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)
|
ตอนนี้ไคลเอ็นต์ทำงานได้แล้ว แต่ยังคงจัดสรรจากฮีป ION เนื่องจากไม่มีสิทธิ์ sepolicy ที่จำเป็นในการเปิดฮีป DMA-BUF
ขั้นตอนที่ 5: สร้างสิทธิ์ sepolicy ที่จำเป็นเพื่อให้ไคลเอ็นต์เข้าถึงฮีป DMA-BUF ใหม่ได้ ตอนนี้ไคลเอ็นต์พร้อมที่จะจัดสรรจากฮีป DMA-BUF ใหม่แล้ว
ขั้นตอนที่ 6: ตรวจสอบว่าการจัดสรรเกิดขึ้นจากฮีป DMA-BUF ใหม่ โดยตรวจสอบ logcat
ขั้นตอนที่ 7: ปิดใช้ฮีป ION my_heap ในเคอร์เนล หากโค้ดไคลเอ็นต์ไม่จำเป็นต้องรองรับอุปกรณ์ที่อัปเกรด (ซึ่งเคอร์เนลอาจรองรับเฉพาะฮีป ION) คุณก็สามารถนำการเรียกใช้ MapNameToIonHeap() ออกได้ด้วย