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

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

  • ความปลอดภัย: เนื่องจากแต่ละฮีป DMA-BUF เป็นอุปกรณ์อักขระที่แยกจากกัน การเข้าถึงแต่ละฮีปจึงสามารถควบคุมแยกกันได้ด้วย sepolicy สิ่งนี้เป็นไปไม่ได้กับ ION เนื่องจากการจัดสรรจากฮีปใดๆ จำเป็นต้องเข้าถึงอุปกรณ์ /dev/ion เท่านั้น
  • ความเสถียรของ ABI: ไม่เหมือนกับ ION ตรงที่อินเทอร์เฟซ IOCTL ของเฟรมเวิร์กฮีป DMA-BUF รับประกันว่า ABI จะเสถียรเพราะได้รับการบำรุงรักษาในเคอร์เนลอัปสตรีม Linux
  • การกำหนดมาตรฐาน: เฟรมเวิร์กฮีป DMA-BUF นำเสนอ UAPI ที่กำหนดไว้อย่างดี ION อนุญาตให้ใช้แฟล็กที่กำหนดเองและรหัสฮีปที่ทำให้ไม่สามารถพัฒนากรอบการทดสอบทั่วไปได้ เนื่องจากการใช้งาน ION ของอุปกรณ์แต่ละเครื่องอาจมีพฤติกรรมแตกต่างกัน

สาขา android12-5.10 ของเคอร์เนลทั่วไปของ Android ได้ปิดใช้งาน CONFIG_ION ใน วันที่ 1 มีนาคม 2021

พื้นหลัง

ต่อไปนี้เป็นการเปรียบเทียบโดยย่อระหว่างฮีป ION และ DMA-BUF

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

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

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

ไอออนฮีป ฮีป DMA-BUF
การจัดสรร ION ทั้งหมดเสร็จสิ้นด้วย /dev/ion ฮีป DMA-BUF แต่ละฮีปเป็นอุปกรณ์อักขระที่มีอยู่ใน /dev/dma_heap/<heap_name>
ION รองรับแฟล็กส่วนตัวแบบฮีป ฮีป DMA-BUF ไม่รองรับแฟล็กส่วนตัวของฮีป การจัดสรรแต่ละประเภทที่แตกต่างกันจะดำเนินการจากฮีปที่แตกต่างกันแทน ตัวอย่างเช่น ตัวแปรฮีปของระบบที่แคชและไม่แคชนั้นเป็นฮีปที่แยกจากกันซึ่งอยู่ที่ /dev/dma_heap/system และ /dev/dma_heap/system_uncached
ต้องระบุ ID/มาสก์และแฟล็กฮีปสำหรับการจัดสรร ชื่อฮีปถูกใช้สำหรับการจัดสรร

ส่วนต่อไปนี้แสดงรายการส่วนประกอบที่เกี่ยวข้องกับ ION และอธิบายวิธีสลับไปใช้เฟรมเวิร์กฮีป DMA-BUF

การเปลี่ยนไดรเวอร์เคอร์เนลจากฮีป ION เป็น DMA-BUF

ไดรเวอร์เคอร์เนลที่ใช้ฮีป ION

ทั้งฮีป ION และ DMA-BUF อนุญาตให้แต่ละฮีปใช้ตัวจัดสรรของตัวเองและการดำเนินการ DMA-BUF ดังนั้นคุณจึงสามารถสลับจากการใช้งานฮีป ION ไปเป็นการใช้งานฮีป DMA-BUF ได้โดยใช้ชุด API อื่นเพื่อลงทะเบียนฮีป ตารางนี้แสดง API การลงทะเบียนฮีป ION และ API ฮีป DMA-BUF ที่เทียบเท่ากัน

ไอออนฮีป ฮีป 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 ที่เทียบเท่ากัน ไดรเวอร์เคอร์เนลสามารถใช้ dma_heap_find() API เพื่อสอบถามการมีอยู่ของฮีป API ส่งคืนตัวชี้ไปยังอินสแตนซ์ของ struct dma_heap ซึ่งสามารถส่งผ่านเป็นอาร์กิวเมนต์ไปยัง dma_heap_buffer_alloc() API ได้

ไอออนฮีป ฮีป 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

เพื่อให้การเปลี่ยนแปลงเป็นเรื่องง่ายสำหรับไคลเอนต์พื้นที่ผู้ใช้ของ ION ไลบรารีนามธรรมที่เรียกว่า libdmabufheap จึงพร้อมใช้งาน 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 ทำการจัดสรรฮีปของระบบที่ไม่ได้แคชอย่างไร

ประเภทของการจัดสรร ลิเบียน 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

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

การใช้งาน Hikey960 gralloc ใช้ libdmabufheap ดังนั้นคุณจึงสามารถใช้เป็นการ ดำเนินการอ้างอิงได้

จำเป็นต้องมีเหตุการณ์เพิ่มเติม

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

จำเป็นต้องมีการเพิ่มเติม sepolicy

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

การเข้าถึงฮีปผู้ขายจากโค้ดเฟรมเวิร์ก

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

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

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

ฮีปขึ้นอยู่กับฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะของอุปกรณ์หรือ SoC

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

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

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

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

การใช้งานฮีปที่ปลอดภัยต้องเป็นแบบเฉพาะของผู้จำหน่าย เนื่องจากเคอร์เนลทั่วไปของ Android ไม่รองรับการใช้งานฮีปที่ปลอดภัยทั่วไป

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

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

ตัวจัดสรร codec2 สำหรับอินเทอร์เฟซฮีป DMA-BUF มีอยู่ใน AOSP

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

ตัวอย่างโฟลว์การเปลี่ยนแปลงสำหรับฮีป ION

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

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

  • พฤติกรรม 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 ให้แก้ไข makefiles เพื่อลิงก์ไปยัง 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() โดยใช้ชื่อฮีปที่เหมาะสม

ประเภทการจัดสรร ลิเบียน 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 เนื่องจากไม่มีสิทธิ์การแยกส่วนที่จำเป็นในการเปิดฮีป DMA-BUF

ขั้นตอนที่ 5: สร้างสิทธิ์ sepolicy ที่จำเป็นสำหรับไคลเอนต์เพื่อเข้าถึงฮีป DMA-BUF ใหม่ ขณะนี้ไคลเอ็นต์พร้อมอย่างเต็มที่ในการจัดสรรจากฮีป DMA-BUF ใหม่

ขั้นตอนที่ 6: ตรวจสอบว่าการจัดสรรเกิดขึ้นจากฮีป DMA-BUF ใหม่โดยตรวจสอบ logcat

ขั้นตอนที่ 7: ปิดการใช้งาน ION heap my_heap ในเคอร์เนล หากโค้ดไคลเอ็นต์ไม่จำเป็นต้องรองรับอุปกรณ์อัปเกรด (ซึ่งเคอร์เนลอาจรองรับฮีป ION เท่านั้น) คุณยังสามารถลบการเรียกใช้ MapNameToIonHeap() ออกได้