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

ใน 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 ถูกปิดใช้ CONFIG_ION ในวันที่ 1 มีนาคม 2021

ฉากหลัง

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

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

  • ทั้งเฟรมเวิร์ก ION และ DMA-BUF heaps เป็นเครื่องมือส่งออก 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 ของตัวเอง ดังนั้นคุณจึงเปลี่ยนจากการใช้งานฮีป 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: example template นี้เพื่อสร้างฮีป DMA-BUF ตั้งแต่ต้น

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

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

ต่อไปนี้คือ API การจัดสรร ION ในเคอร์เนลและ API การจัดสรรฮีป DMA-BUF ที่เทียบเท่า ไดรเวอร์เคอร์เนลสามารถใช้ dma_heap_find() API เพื่อค้นหา การมีอยู่ของฮีปได้ API จะแสดงตัวชี้ไปยังอินสแตนซ์ของ struct dma_heap ซึ่งสามารถส่งเป็นอาร์กิวเมนต์ไปยัง API ของ dma_heap_buffer_alloc() ได้

ฮีป 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 (ชื่อฮีปหรือมาสก์และแฟล็ก) เพื่อให้อินเทอร์เฟซเหล่านั้นใช้การจัดสรรตามชื่อได้ ต่อไปนี้คือตัวอย่างการจัดสรรตามชื่อ

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

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

การติดตั้งใช้งาน Gralloc ของ Hikey960 ใช้ libdmabufheap คุณจึงใช้เป็นการติดตั้งใช้งานอ้างอิงได้

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

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

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

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

เข้าถึงกองผู้ให้บริการจากโค้ดเฟรมเวิร์ก

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

จากความคิดเห็นที่ได้รับจากพาร์ทเนอร์ Google ได้ระบุฮีปของผู้ให้บริการ 2 หมวดหมู่ ที่ต้องเข้าถึงจากโค้ดเฟรมเวิร์ก

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

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

หากต้องการรองรับกรณีการใช้งานนี้ คุณสามารถลบล้างการใช้งานฮีปของฮีป DMA-BUF เริ่มต้น ของระบบได้

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

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

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

การใช้งานฮีปที่ปลอดภัยต้องเป็นของผู้ให้บริการรายใดรายหนึ่ง เนื่องจาก Android Common Kernel ไม่รองรับการใช้งานฮีปที่ปลอดภัยทั่วไป

  • ลงทะเบียนการใช้งานเฉพาะผู้ให้บริการเป็น /dev/dma_heap/system-secure<vendor-suffix>
  • การใช้งานฮีปเหล่านี้เป็นตัวเลือก
  • หากมีฮีปอยู่ การทดสอบ VTS จะช่วยให้มั่นใจว่าสามารถจัดสรรจากฮีปเหล่านั้นได้
  • คอมโพเนนต์เฟรมเวิร์กมีสิทธิ์เข้าถึงฮีปเหล่านี้เพื่อให้สามารถ เปิดใช้การใช้งานฮีปผ่าน HAL ของ Codec2/HAL ที่ไม่ใช่ Binderized และ HAL ในกระบวนการเดียวกัน อย่างไรก็ตาม ฟีเจอร์ของเฟรมเวิร์ก Android ทั่วไปไม่สามารถขึ้นอยู่กับฟีเจอร์เหล่านี้ได้เนื่องจาก รายละเอียดการใช้งานมีความหลากหลาย หากมีการเพิ่มการใช้งานฮีปที่ปลอดภัยทั่วไป ลงในเคอร์เนลทั่วไปของ Android ในอนาคต จะต้องใช้ 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 สำหรับฮีป my_heap และ my_heap_special DMA-BUF ใหม่ ในตอนนี้ ฮีปจะปรากฏเป็น /dev/dma_heap/my_heap และ /dev/dma_heap/my_heap_special โดยมี สิทธิ์ที่ต้องการ

ขั้นตอนที่ 3: สำหรับไคลเอ็นต์ที่จัดสรรจาก my_heap ให้แก้ไขไฟล์ Makefile เพื่อลิงก์ไปยัง 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 เป็นค่าว่าง แทนการใช้ MapNameToIonHeap() API กับพารามิเตอร์ชื่อและแฟล็ก

ขั้นตอนที่ 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() ออกได้เช่นกัน