การเปลี่ยนจากกอง 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 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)

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

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

ฮีปที่อิงตามฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะอุปกรณ์หรือ 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() ออกได้ด้วย