ใน 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) | |
ไดรเวอร์เคอร์เนลที่ใช้ 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 ได้ระบุฮีปผู้ขายสองประเภทที่ต้องเข้าถึงจากโค้ดเฟรมเวิร์ก:
- ฮีปที่อิงตามฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะของอุปกรณ์หรือ SoC
- ฮีปเพื่อจัดสรรจากหน่วยความจำที่ได้รับการป้องกัน
ฮีปขึ้นอยู่กับฮีปของระบบที่มีการเพิ่มประสิทธิภาพเฉพาะของอุปกรณ์หรือ 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()
ออกได้