การเปลี่ยนจากกอง ION เป็น DMA-BUF (เคอร์เนล 5.4 เท่านั้น)

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

  • ความปลอดภัย: เนื่องจากกอง DMA-BUF แต่ละกองเป็นอุปกรณ์อักขระแยกต่างหาก คุณจึงควบคุมการเข้าถึงกองแต่ละกองแยกกันได้ด้วย sepolicy ซึ่ง ION ทำไม่ได้เนื่องจากการจัดสรรจากกองใดก็ตามต้องเข้าถึงอุปกรณ์ /dev/ion เท่านั้น
  • ความเสถียรของ ABI: อินเทอร์เฟซ IOCTL ของเฟรมเวิร์กกอง DMA-BUF มีความเสถียรของ ABI ต่างจาก ION เนื่องจากได้รับการดูแลรักษาในเคอร์เนล Linux เวอร์ชันที่พัฒนาขึ้น
  • มาตรฐาน: เฟรมเวิร์กกอง DMA-BUF มี UAPI ที่กําหนดไว้อย่างชัดเจน ION อนุญาตให้ใช้ Flag และรหัสกองที่กําหนดเองซึ่งทําให้พัฒนาเฟรมเวิร์กการทดสอบทั่วไปไม่ได้ เนื่องจากการติดตั้งใช้งาน 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 ส่วนตัวของกอง heap กอง 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 และ 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 ไม่รองรับ Flag ส่วนตัวของกอง ดังนั้นแต่ละตัวแปรของฮีปจะต้องลงทะเบียนแยกกันโดยใช้ dma_heap_add() API เราขอแนะนำให้ลงทะเบียนตัวแปรทั้งหมดของกองเดียวกันภายในไดรเวอร์เดียวกันเพื่อให้แชร์โค้ดได้ง่ายขึ้น ตัวอย่าง dma-buf: system_heap นี้แสดงการใช้งานตัวแปรที่แคชและไม่แคชของ heap ระบบ

ใช้dma-buf: heaps: example template นี้เพื่อสร้างกอง 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)

ไดรเวอร์ Kernel ที่ใช้ 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 ให้แก้ไขลักษณะการทํางานของลูกค้าดังนี้

  • ติดตามชื่อกองที่จะใช้สำหรับการจัดสรรแทนรหัส/มาสก์ส่วนหัวและ Flag กอง
  • แทนที่ ion_alloc_fd() API ซึ่งใช้อาร์กิวเมนต์เฮมพ์มาสก์และ Flag ด้วย 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)

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

MapNameToIonHeap() API จะอนุญาตให้แมปชื่อฮีปกับพารามิเตอร์ฮีปของ 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 หมวดหมู่ที่ต้องเข้าถึงจากโค้ดเฟรมเวิร์กตามความคิดเห็นที่ได้รับจากพาร์ทเนอร์ ดังนี้

  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 ทั่วไปต้องไม่ใช้ Firebase เนื่องด้วยรายละเอียดการใช้งานที่หลากหลาย หากมีการเพิ่มการใช้งานกองข้อมูลที่ปลอดภัยทั่วไปลงในเคอร์เนลทั่วไปของ Android ในอนาคต การใช้งานดังกล่าวต้องใช้ ABI อื่นเพื่อหลีกเลี่ยงความขัดแย้งกับการอัปเกรดอุปกรณ์

ตัวจัดสรรตัวแปลงรหัส 2 สำหรับฮีป DMA-BUF

ตัวแปลงรหัสตัวแปลงรหัส 2 สำหรับอินเทอร์เฟซฮีป DMA-BUF มีให้ใช้งานใน AOSP

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

ตัวอย่างขั้นตอนการเปลี่ยนสำหรับกอง ION

libdmabufheap อนุญาตให้สลับกองข้อมูลได้ครั้งละ 1 กองเพื่อให้การเปลี่ยนจากกอง ION ไปเป็นกอง DMA-BUF เป็นไปอย่างราบรื่น ขั้นตอนต่อไปนี้แสดงเวิร์กโฟลว์ที่แนะนําสําหรับการเปลี่ยนกอง ION ที่ไม่ใช่เดิมชื่อ my_heap ซึ่งรองรับ Flag เดียว ION_FLAG_MY_FLAG

ขั้นตอนที่ 1: สร้างฮีปที่เทียบเท่ากับ ION ในเฟรมเวิร์ก DMA-BUF ในตัวอย่างนี้ เนื่องจากกอง ION my_heap รองรับ Flag ION_FLAG_MY_FLAG เราจึงลงทะเบียนกอง DMA-BUF 2 กอง ดังนี้

  • ลักษณะการทำงานของ 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 ให้แก้ไขไฟล์ 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 ที่มีพารามิเตอร์ชื่อและ Flag คุณสามารถสร้างการแมปจาก <ION heap mask, flag> ไปยังชื่อกอง heap DMA-BUF ที่เทียบเท่าได้ด้วยการกําหนดค่าพารามิเตอร์ชื่อกอง heap 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() ออกได้เช่นกัน