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

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

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

สาขา android12-5.10 ของ Kernel ร่วมของ Android ปิดใช้อยู่ 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 ไม่รองรับแฟล็กส่วนตัวฮีป โดยแต่ละประเภทก็ การจัดสรรจะดำเนินการจากฮีปอื่นแทน ตัวอย่างเช่น แคชและ ตัวแปรฮีปของระบบที่ไม่ได้แคชคือฮีปแยกต่างหากที่ /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: ฮีปส์: เทมเพลตตัวอย่าง เพื่อสร้างฮีป DMA-BUF ตั้งแต่ต้น

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

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

รายการต่อไปนี้แสดง API การจัดสรร ION ในเคอร์เนลและ DMA-BUF ที่เทียบเท่า API การจัดสรรฮีป ไดรเวอร์เคอร์เนลใช้ 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)

ไดรเวอร์ Kernel ที่ใช้ 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 ให้แก้ไขพฤติกรรมของลูกค้าเป็น ดังต่อไปนี้:

  • ติดตามชื่อฮีปที่จะใช้เพื่อการจัดสรร แทนที่จะเป็นส่วนหัว ID/มาสก์และฮีปแฟล็ก
  • แทนที่ 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 แล้ว Branch

MapNameToIonHeap() API จะอนุญาตให้แมปฮีปเพื่อรองรับการอัปเกรดอุปกรณ์ ลงในพารามิเตอร์ฮีปของ ION (ชื่อฮีปหรือมาสก์และแฟล็ก) เพื่อให้แท็กดังกล่าว จะใช้การจัดสรรตามชื่อ ต่อไปนี้คือการจัดสรรตามชื่อ ตัวอย่าง

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

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

การใช้งาน Hikey960 Gralloc ใช้ 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 ของตัวแปลงรหัส/HAL ที่ไม่มีการเชื่อมโยง กระบวนการเดียวกัน อย่างไรก็ตาม ฟีเจอร์เฟรมเวิร์กของ Android ทั่วไปจะใช้งานไม่ได้เนื่องจาก ความแปรปรวนในรายละเอียดการใช้งาน หากฮีปแบบปลอดภัยทั่วไป โดยจะเพิ่มเข้ามาใน Kernel ร่วมของ Android ในอนาคต อุปกรณ์จะต้องใช้ ABI อื่นเพื่อหลีกเลี่ยงความขัดแย้งกับการอัปเกรดอุปกรณ์

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

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

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

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

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

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

  • ลักษณะการทำงานของ my_heap ตรงกันทุกประการกับลักษณะการทำงานของฮีป ION กับ ปิดใช้ Flag ION_FLAG_MY_FLAG อยู่
  • ลักษณะการทำงานของ my_heap_special ตรงกันทุกประการกับลักษณะการทำงานของฮีป ION ที่เปิดใช้งานการแฟล็ก ION_FLAG_MY_FLAG

ขั้นตอนที่ 2: สร้างการเปลี่ยนแปลงที่เพิ่งเกิดขึ้นสำหรับ my_heap ใหม่และ my_heap_special ฮีป DMA-BUF ในตอนนี้ ฮีปจะแสดงเป็น /dev/dma_heap/my_heap และ /dev/dma_heap/my_heap_special โดยมี สิทธิ์ที่ต้องการ

ขั้นตอนที่ 3: แก้ไขไคลเอ็นต์ที่จัดสรรจาก my_heap เพื่อลิงก์กับ 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 พร้อมธง ยกเลิกการตั้งค่า 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() ออกได้ด้วย