เพิ่มประสิทธิภาพองค์กร DTO

หน้านี้อธิบายการเพิ่มประสิทธิภาพที่คุณทำได้ในการใช้งานการวางซ้อนต้นไม้อุปกรณ์ (DTO) อธิบายข้อจำกัดในการวางซ้อนโหนดรูท และรายละเอียดวิธีกำหนดค่าการวางซ้อนที่บีบอัดในรูปภาพ DTBO รวมถึงมีตัวอย่างวิธีการติดตั้งใช้งานและโค้ด

บรรทัดคำสั่งของเคอร์เนล

บรรทัดคำสั่งเคอร์เนลเดิมใน Device Tree (DT) อยู่ในโหนด chosen/bootargs บูตโหลดเดอร์ต้องต่อเชื่อมตำแหน่งนี้กับแหล่งที่มาอื่นๆ ของบรรทัดคำสั่งเคอร์เนล

/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};

DTO ไม่สามารถต่อค่าจาก DT หลักและ DT การวางซ้อนได้ คุณจึงต้องใส่บรรทัดคำสั่งเคอร์เนลของ DT หลักใน chosen/bootargs และบรรทัดคำสั่งเคอร์เนลของ DT การวางซ้อนใน chosen/bootargs_ext จากนั้น Bootloader จะต่อตำแหน่งเหล่านี้เข้าด้วยกันและส่งผลลัพธ์ไปยังเคอร์เนล

main.dts overlay.dts
/dts-v1/;

/ {
  chosen: chosen {
    bootargs = "...";
  };
};
/dts-v1/;
/plugin/;

&chosen {
  bootargs_ext = "...";
};

libufdt

แม้ว่า libfdt เวอร์ชันล่าสุดจะรองรับ DTO แต่เราขอแนะนำให้ใช้ libufdt เพื่อติดตั้งใช้งาน DTO (แหล่งที่มาของ AOSP ที่ platform/system/libufdt) libufdt จะสร้างโครงสร้างต้นไม้จริง (ต้นไม้อุปกรณ์แบบไม่ยุบหรือ ufdt) จากต้นไม้อุปกรณ์แบบยุบ (FDT) จึงช่วยปรับปรุงการผสานไฟล์ .dtb 2 ไฟล์จาก O(N2) เป็น O(N) โดยที่ N คือจํานวนโหนดในต้นไม้

การทดสอบประสิทธิภาพ

ในการทดสอบภายในของ Google การใช้ libufdt ใน 2405 .dtb และโหนด DT .dtbo 283 โหนดทำให้ไฟล์มีขนาด 70,618 และ 8,566 ไบต์หลังจากการคอมไพล์ เมื่อเทียบกับการใช้งาน DTO ที่ย้ายมาจาก FreeBSD (รันไทม์ 124 มิลลิวินาที) รันไทม์ DTO ของ libufdt เท่ากับ 10 มิลลิวินาที

การทดสอบประสิทธิภาพสำหรับอุปกรณ์ Pixel เทียบกับ libufdt และ libfdt จำนวนโหนดฐานจะมีผลคล้ายกัน แต่มีข้อแตกต่างต่อไปนี้

  • การดำเนินการวางซ้อน (เพิ่มต่อท้ายหรือลบล้าง) 500 ครั้งใช้เวลานานขึ้น 6-8 เท่า
  • การดำเนินการวางซ้อน (เพิ่มต่อท้ายหรือลบล้าง) 1,000 ครั้งใช้เวลานานขึ้น 8-10 เท่า

ตัวอย่างการตั้งค่าจำนวนต่อท้ายเป็น X

รูปที่ 1 จำนวนการต่อท้ายคือ X

ตัวอย่างที่มีการลบล้างจำนวนที่ตั้งค่าเป็น X

รูปที่ 2 จำนวนการลบล้างคือ X

libufdt พัฒนาขึ้นโดยใช้ libfdt API และโครงสร้างข้อมูลบางอย่าง เมื่อใช้ libufdt คุณต้องใส่และลิงก์ libfdt (แต่ในโค้ด คุณสามารถใช้ libfdt API เพื่อดําเนินการ DTB หรือ DTBO ได้)

libufdt DTO API

API หลักไปยัง DTO ใน libufdt มีดังนี้

struct fdt_header *ufdt_apply_overlay(
        struct fdt_header *main_fdt_header,
        size_t main_fdt_size,
        void *overlay_fdt,
        size_t overlay_size);

พารามิเตอร์ main_fdt_header คือ DT หลัก และ overlay_fdt คือบัฟเฟอร์ที่มีเนื้อหาของไฟล์ .dtbo ผลลัพธ์ที่ได้คือบัฟเฟอร์ใหม่ที่มี DT ที่ผสานแล้ว (หรือ null ในกรณีที่เกิดข้อผิดพลาด) DT ที่ผสานจะได้รับการฟอร์แมตเป็น FDT ซึ่งคุณสามารถส่งไปยังเคอร์เนลเมื่อเริ่มเคอร์เนล

บัฟเฟอร์ใหม่จากค่าที่แสดงผลสร้างขึ้นโดย dto_malloc() ซึ่งคุณควรนำไปใช้เมื่อพอร์ต libufdt ไปยังบูตโหลดเดอร์ ดูการใช้งานอ้างอิงได้ที่ sysdeps/libufdt_sysdeps_*.c

ข้อจํากัดของโหนดรูท

คุณไม่สามารถวางซ้อนโหนดหรือพร็อพเพอร์ตี้ใหม่ลงในโหนดรูทของ DT หลักได้เนื่องจากการดำเนินการวางซ้อนอาศัยป้ายกำกับ เนื่องจาก DT หลักต้องกําหนดป้ายกํากับ และ DT การวางซ้อนกําหนดโหนดที่จะวางซ้อนด้วยป้ายกํากับ คุณจึงไม่สามารถกําหนดป้ายกํากับสําหรับโหนดรูทได้ (และจึงวางซ้อนโหนดรูทไม่ได้)

ผู้ให้บริการ SoC ต้องกำหนดความสามารถในการวางซ้อนของ DT หลัก โดย ODM/OEM จะเพิ่มหรือลบล้างโหนดด้วยป้ายกำกับที่ผู้ให้บริการ SoC กำหนดไว้เท่านั้น วิธีแก้ปัญหาชั่วคราวคือ คุณกำหนดโหนด odm ใต้โหนดรูทใน DT ฐาน ซึ่งจะช่วยให้โหนด ODM ทั้งหมดใน DT การวางซ้อนเพิ่มโหนดใหม่ได้ หรือจะใส่โหนดที่เกี่ยวข้องกับ SoC ทั้งหมดใน DT พื้นฐานไว้ในโหนด soc ใต้โหนดรูทตามที่อธิบายไว้ด้านล่างก็ได้

main.dts overlay.dts
/dts-v1/;

/ {
    compatible = "corp,bar";
    ...

    chosen: chosen {
        bootargs = "...";
    };

    /* nodes for all soc nodes */
    soc {
        ...
        soc_device@0: soc_device@0 {
            compatible = "corp,bar";
            ...
        };
        ...
    };

    odm: odm {
        /* reserved for overlay by odm */
    };
};
/dts-v1/;
/plugin/;

/ {
};

&chosen {
    bootargs_ex = "...";
};

&odm {
    odm_device@0 {
        ...
    };
    ...
};

ใช้การซ้อนทับแบบบีบอัด

Android 9 รองรับการใช้การวางซ้อนที่บีบอัดในรูปภาพ DTBO เมื่อใช้ส่วนหัวตาราง DT เวอร์ชัน 1 เมื่อใช้ส่วนหัว DTBO v1 บิตที่มีนัยสำคัญน้อยที่สุด 4 บิตของช่อง Flag ใน dt_table_entry จะระบุรูปแบบการบีบอัดของรายการ DT

struct dt_table_entry_v1 {
  uint32_t dt_size;
  uint32_t dt_offset;  /* offset from head of dt_table_header */
  uint32_t id;         /* optional, must be zero if unused */
  uint32_t rev;        /* optional, must be zero if unused */
  uint32_t flags;      /* For version 1 of dt_table_header, the 4 least significant bits
                        of 'flags' are used to indicate the compression
                        format of the DT entry as per the enum 'dt_compression_info' */
  uint32_t custom[3];  /* optional, must be zero if unused */
};

ปัจจุบันระบบรองรับการบีบอัด zlib และ gzip

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 รองรับการทดสอบการวางซ้อนที่บีบอัดแล้วในการทดสอบ VtsFirmwareDtboVerification เพื่อช่วยคุณยืนยันความถูกต้องของแอปวางซ้อน

ตัวอย่างการใช้งาน DTO

วิธีการต่อไปนี้จะอธิบายตัวอย่างการใช้งาน DTO ด้วย libufdt (โค้ดตัวอย่างด้านล่าง)

ตัวอย่างวิธีการ DTO

  1. รวมไลบรารี หากต้องการใช้ libufdt ให้ใส่ libfdt สำหรับโครงสร้างข้อมูลและ API ดังนี้
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. โหลด DT หลักและ DT วางซ้อน โหลด .dtb และ .dtboจากพื้นที่เก็บข้อมูลไปยังหน่วยความจำ (ขั้นตอนที่แน่นอนขึ้นอยู่กับการออกแบบของคุณ) ณ จุดนี้ คุณควรมีบัฟเฟอร์และขนาด .dtb/.dtbo ดังนี้
    main_size = my_load_main_dtb(main_buf, main_buf_size)
    overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);
  3. วางซ้อน DT โดยทำดังนี้
    1. ใช้ ufdt_install_blob() เพื่อรับส่วนหัว FDT สำหรับ DT หลัก
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. โทรหา ufdt_apply_overlay() เพื่อส่ง DT ที่ผสานแล้วในรูปแบบ FDT ไปยัง DTO โดยทำดังนี้
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. ใช้ merged_fdt เพื่อรับขนาดของ dtc_totalsize()
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. ส่ง DT ที่ผสานเพื่อเริ่มเคอร์เนล
      my_kernel_entry(0, machine_type, merged_fdt);

โค้ด DTO ตัวอย่าง

#include <libfdt.h>
#include <ufdt_overlay.h>



{
  struct fdt_header *main_fdt_header;
  struct fdt_header *merged_fdt;

  /* load main dtb into memory and get the size */
  main_size = my_load_main_dtb(main_buf, main_buf_size);

  /* load overlay dtb into memory and get the size */
  overlay_size = my_load_overlay_dtb(overlay_buf, overlay_buf_size);

  /* overlay */
  main_fdt_header = ufdt_install_blob(main_buf, main_size);
  main_fdt_size = main_size;
  merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                  overlay_buf, overlay_size);
  merged_fdt_size = dtc_totalsize(merged_fdt);

  /* pass to kernel */
  my_kernel_entry(0, machine_type, merged_fdt);
}