ใน Android 12 นั้น GKI 2.0 จะใช้กอง DMA-BUF แทนตัวจัดสรร ION ด้วยเหตุผลต่อไปนี้
- ความปลอดภัย: เนื่องจากฮีป DMA-BUF แต่ละรายการเป็นอุปกรณ์อักขระที่แยกจากกัน การเข้าถึงฮีปแต่ละรายการจึงควบคุมแยกกันได้ด้วย Sepolicy ซึ่ง ION ทำไม่ได้เนื่องจากการจัดสรรจากกองใดก็ตามต้องเข้าถึงอุปกรณ์
/dev/ion
เท่านั้น - ความเสถียรของ ABI: อินเทอร์เฟซ IOCTL ของเฟรมเวิร์กฮีป DMA-BUF ต่างจาก ION เพราะมี ABI ที่เสถียรเนื่องจากมีการบำรุงรักษาในเคอร์เนลของอัปสตรีม Linux
- การกำหนดมาตรฐาน: เฟรมเวิร์กฮีป 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 ที่อิงตามกอง
- ทั้งสองช่วยให้แต่ละกองกำหนดตัวจัดสรรและการดำเนินการ DMA-BUF ของตนเองได้
- ประสิทธิภาพการจัดสรรคล้ายกันเนื่องจากทั้ง 2 แผนต้องใช้ IOCTL เดียวสำหรับการจัดสรร
ความแตกต่างระหว่างเฟรมเวิร์กกอง ION กับ DMA-BUF
ฮีป ION | กอง DMA-BUF |
---|---|
การจัดสรร ION ทั้งหมดจะดำเนินการด้วย /dev/ion
|
กอง DMA-BUF แต่ละกองเป็นอุปกรณ์อักขระที่แสดงอยู่ที่ /dev/dma_heap/<heap_name>
|
ION รองรับฮีป Flag | กอง DMA-BUF ไม่รองรับ Flag ส่วนตัวของกอง การจัดสรรแต่ละประเภทจะดำเนินการจากกองที่แตกต่างกัน ตัวอย่างเช่น ตัวแปรฮีปของระบบที่แคชและไม่ได้แคชจะเป็นฮีปแยกต่างหากที่ /dev/dma_heap/system และ /dev/dma_heap/system_uncached
|
ต้องระบุรหัส/มาสก์กองและ Flag สำหรับการกําหนด | ชื่อฮีปจะใช้สำหรับการจัดสรร |
ส่วนต่อไปนี้จะแสดงรายการคอมโพเนนต์ที่จัดการกับ ION และอธิบายวิธีเปลี่ยนไปใช้เฟรมเวิร์กกอง DMA-BUF
เปลี่ยนไดรเวอร์เคอร์เนลจากกอง ION เป็น DMA-BUF
ไดรเวอร์เคอร์เนลที่ใช้กอง ION
ทั้งกอง ION และ DMA-BUF ช่วยให้กองแต่ละกองใช้ตัวจัดสรรและการดำเนินการ DMA-BUF ของตนเองได้ คุณจึงเปลี่ยนจากการใช้งานกอง ION ไปใช้การใช้งานกอง DMA-BUF ได้โดยใช้ชุด API อื่นเพื่อลงทะเบียนกอง ตารางนี้แสดง API การลงทะเบียนฮีป 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 ไม่รองรับ Flag ส่วนตัวของกอง ดังนั้นจึงต้องลงทะเบียนตัวแปรแต่ละรายการของกองโดยใช้ dma_heap_add()
API แยกกัน เราขอแนะนำให้ลงทะเบียนตัวแปรทั้งหมดของกองเดียวกันภายในไดรเวอร์เดียวกันเพื่อให้แชร์โค้ดได้ง่ายขึ้น
ตัวอย่าง dma-buf: system_heap นี้แสดงการใช้งานเวอร์ชันที่แคชไว้และไม่ได้แคชของฮีประบบ
ใช้ dma-buf: ฮีป: เทมเพลตตัวอย่าง เพื่อสร้างฮีป DMA-BUF ใหม่ตั้งแต่ต้น
โปรแกรมควบคุมเคอร์เนลที่จัดสรรจากกอง ION โดยตรง
เฟรมเวิร์กกอง DMA-BUF ยังมีอินเทอร์เฟซการจัดสรรสำหรับไคลเอ็นต์ในเคอร์เนลด้วย อินเทอร์เฟซที่นำเสนอโดยกอง DMA-BUF จะรับชื่อกองเป็นอินพุตแทนการระบุมาสก์กองและ Flag เพื่อเลือกประเภทการจัดสรร
ต่อไปนี้แสดง API การจัดสรร ION ในเคอร์เนลและ API การจัดสรรกอง heap ของ 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)
|
|
โปรแกรมควบคุมเคอร์เนลที่ใช้ DMA-BUF
คุณไม่ต้องทำการเปลี่ยนแปลงใดๆ สำหรับไดรเวอร์ที่นำเข้าเฉพาะ DMA-BUF เนื่องจากบัฟเฟอร์ที่จัดสรรจากฮีป ION ทำงานเหมือนกับบัฟเฟอร์ที่จัดสรรจากฮีป DMA-BUF ที่เทียบเท่า
เปลี่ยนไคลเอ็นต์พื้นที่ผู้ใช้ของ ION เป็นฮีป DMA-BUF
เรามีไลบรารีนามธรรมที่เรียกว่า libdmabufheap
ไว้ให้ใช้งานเพื่อให้ลูกค้าพื้นที่ผู้ใช้ของ ION เปลี่ยนผ่านได้อย่างง่ายดาย libdmabufheap
รองรับการจัดสรรในกอง DMA-BUF และกอง ION โดยระบบจะตรวจสอบก่อนว่ามีกอง DMA-BUF ที่มีชื่อที่ระบุหรือไม่ หากไม่มี ระบบจะเปลี่ยนไปใช้กอง ION ที่เทียบเท่า หากมี
ไคลเอ็นต์ควรเริ่มต้นBufferAllocator
ออบเจ็กต์ระหว่างการเริ่มต้นแทนที่จะเปิด /dev/ion using
ion_open()
เนื่องจากตัวระบุไฟล์ที่สร้างโดยการเปิด /dev/ion
และ /dev/dma_heap/<heap_name>
ได้รับการจัดการโดยออบเจ็กต์ BufferAllocator
ภายใน
หากต้องการเปลี่ยนจาก libion
เป็น libdmabufheap
ให้แก้ไขลักษณะการทํางานของลูกค้าดังนี้
- ติดตามชื่อฮีปที่จะใช้สำหรับการจัดสรร แทนที่จะเป็นรหัสส่วนหัว/มาสก์และฮีปแฟล็ก
- แทนที่
ion_alloc_fd()
API ซึ่งรับฮีปมาสก์และอาร์กิวเมนต์แฟล็ก ด้วย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 (ชื่อกองหรือมาสก์และ Flag) เพื่อให้อินเทอร์เฟซเหล่านั้นใช้การจัดสรรตามชื่อได้ เพื่อรองรับการอัปเกรดอุปกรณ์ ลองดูตัวอย่างการจัดสรร
ตามชื่อ
เอกสารประกอบสําหรับ API ทั้งหมดที่ libdmabufheap
แสดงมีให้ใช้งาน ไลบรารียังแสดงไฟล์ส่วนหัวสำหรับใช้โดยไคลเอ็นต์ C ด้วย
การใช้งาน Gralloc อ้างอิง
การใช้งาน gralloc ของ Hikey960 ใช้ libdmabufheap
คุณจึงใช้เป็นการการใช้งานอ้างอิงได้
การเพิ่มเติมที่จำเป็น
สำหรับฮีป DMA-BUF เฉพาะอุปกรณ์ใหม่ที่สร้างขึ้น ให้เพิ่มรายการใหม่ลงในไฟล์ ueventd.rc
ของอุปกรณ์
การตั้งค่าที่เกิดขึ้นให้รองรับฮีป 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) จากพื้นที่ของผู้ใช้ได้
ประเด็นข้างต้นยังใช้กับตัวแปรที่ไม่มีการแคชของกองขยะระบบด้วย แม้ว่าอุปกรณ์ที่ทำงานแบบ IO อย่างสอดคล้องกันโดยสมบูรณ์ไม่จำเป็นต้องมีตัวแปรนี้
กองที่จะจัดสรรจากหน่วยความจําที่ได้รับการคุ้มครอง
การใช้งานกองข้อมูลที่ปลอดภัยต้องเจาะจงผู้ให้บริการ เนื่องจากเคอร์เนล Android Common ไม่รองรับการใช้งานกองข้อมูลที่ปลอดภัยทั่วไป
- ลงทะเบียนการใช้งานที่เจาะจงผู้ให้บริการเป็น
/dev/dma_heap/system-secure<vendor-suffix>
- การใช้กองเหล่านี้เป็นตัวเลือกที่ไม่บังคับ
- หากมีกองอยู่ การทดสอบ VTS จะตรวจสอบว่าสามารถจัดสรรจากกองได้
- คอมโพเนนต์เฟรมเวิร์กมีสิทธิ์เข้าถึงกองเหล่านี้เพื่อให้สามารถเปิดใช้การใช้งานกองผ่าน HAL ของ Codec2/HAL ที่อยู่ในกระบวนการเดียวกันซึ่งไม่ได้เป็น Binderized อย่างไรก็ตาม ฟีเจอร์ทั่วไปของเฟรมเวิร์ก Android จะขึ้นอยู่กับฟีเจอร์ดังกล่าวไม่ได้เนื่องจากรายละเอียดการใช้งานมีความแตกต่างกัน หากมีการเพิ่มการใช้งานกองข้อมูลที่ปลอดภัยทั่วไปลงในเคอร์เนลทั่วไปของ Android ในอนาคต การใช้งานดังกล่าวต้องใช้ ABI อื่นเพื่อหลีกเลี่ยงความขัดแย้งกับการอัปเกรดอุปกรณ์
ตัวจัดสรรตัวแปลงรหัส 2 สำหรับฮีป DMA-BUF
ตัวจัดสรร codec2 สำหรับอินเทอร์เฟซกอง DMA-BUF มีอยู่ใน AOSP
อินเทอร์เฟซของที่เก็บคอมโพเนนต์ที่อนุญาตให้ระบุพารามิเตอร์กองจาก C2 HAL พร้อมใช้งานกับตัวจัดสรรกอง C2 DMA-BUF
ตัวอย่างโฟลว์การเปลี่ยนสำหรับฮีป ION
libdmabufheap
อนุญาตให้สลับกองข้อมูลได้ครั้งละ 1 กองเพื่อให้การเปลี่ยนจากกอง ION ไปเป็นกอง DMA-BUF เป็นไปอย่างราบรื่น ขั้นตอนต่อไปนี้สาธิตเวิร์กโฟลว์ที่แนะนำสำหรับการเปลี่ยนฮีป ION ที่ไม่ใช่เวอร์ชันเดิมที่ชื่อ my_heap
ซึ่งรองรับแฟล็กเดียว ION_FLAG_MY_FLAG
ขั้นตอนที่ 1: สร้างรายการที่เทียบเท่าของกอง ION ในเฟรมเวิร์ก DMA-BUF ในตัวอย่างนี้ เนื่องจากกอง ION my_heap
รองรับ Flag ION_FLAG_MY_FLAG
เราจึงลงทะเบียนกอง DMA-BUF 2 กอง ดังนี้
- ลักษณะการทํางานของ
my_heap
ตรงกับลักษณะการทํางานของกอง ION ที่ปิดใช้ FlagION_FLAG_MY_FLAG
- ลักษณะการทํางานของ
my_heap_special
ตรงกับลักษณะการทํางานของกอง ION ทั้งหมดเมื่อเปิดใช้ FlagION_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
ให้แก้ไขไฟล์ make ให้ลิงก์กับ 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()
โดยใช้ชื่อกองที่เหมาะสม
ประเภทการจัดสรร | libion | Libdmabufheap |
---|---|---|
การจัดสรรจาก my_heap ที่มี Flag
ION_FLAG_MY_FLAG ไม่ได้ตั้งค่า
|
ion_alloc_fd(ionfd, size, 0, ION_HEAP_MY_HEAP, 0, &fd)
|
allocator->Alloc("my_heap", size)
|
การจัดสรรจาก my_heap ที่มีการตั้งค่า Flag
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()
ออกได้เช่นกัน