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

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

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

ตัวจัดสรร codec2 สำหรับอินเทอร์เฟซ DMA-BUF heaps พร้อมใช้งานใน 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 ให้แก้ไขไฟล์ 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 เป็นค่าว่าง แทนการใช้ 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() ออกได้เช่นกัน