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

ใน 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)

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

เรามีไลบรารีนามธรรมที่เรียกว่า 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 หมวดหมู่ที่ต้องเข้าถึงจากโค้ดเฟรมเวิร์กตามความคิดเห็นที่ได้รับจากพาร์ทเนอร์ ดังนี้

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

กองข้อมูลที่อิงตามกองข้อมูลของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะอุปกรณ์หรือ 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 ที่ปิดใช้ Flag ION_FLAG_MY_FLAG
  • ลักษณะการทํางานของ my_heap_special ตรงกับลักษณะการทํางานของกอง ION ทั้งหมดเมื่อเปิดใช้ Flag 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 ให้แก้ไขไฟล์ 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() ออกได้เช่นกัน