เพิ่มประสิทธิภาพองค์กร 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) จาก DTO ที่มีลำดับชั้น (FDT) เพื่อให้ปรับปรุงการผสานไฟล์ .dtb 2 ไฟล์จาก O(N2) ในโหนด N เป็น O(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 การซ้อนทับ.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 บิตที่สำคัญที่สุดของช่องแฟล็กใน 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() ไปยัง DTO เพื่อขอ DT ที่ผสานในรูปแบบ FDT:
      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);
}