אופטימיזציה של DTOs

דף זה דן באופטימיזציות שאתה יכול לבצע למימוש ה-DTO שלך, מתאר הגבלות נגד שכבת-על של צומת השורש, ומפרט כיצד להגדיר שכבות-על דחוסות בתמונת DTBO. הוא גם מספק הוראות יישום וקוד לדוגמה.

שורת הפקודה של ליבה

שורת הפקודה המקורית של הליבה בעץ המכשיר ממוקמת בצומת chosen/bootargs . טוען האתחול חייב לשרשר מיקום זה עם מקורות אחרים של שורת הפקודה של הליבה:

/dts-v1/;

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

DTO אינו יכול לשרשר ערכים מה-DT הראשי ומ-Overlay DT, לכן עליך לשים את שורת הפקודה של הליבה של ה-DT הראשי ב- chosen/bootargs ואת שורת הפקודה של הליבה של שכבת-העל DT ב- chosen/bootargs_ext . מטעין האתחול יכול לאחר מכן לשרשר את המיקומים הללו ולהעביר את התוצאה לקרנל.

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 מ-O(N 2 ) ל-O(N), כאשר N הוא מספר הצמתים בעץ.

בדיקת ביצועים

בבדיקה הפנימית של גוגל, שימוש libufdt על 2405 .dtb ו-283 .dtbo DT צמתים מביא לגדלים של קבצים של 70,618 ו-8,566 בתים לאחר הידור. בהשוואה למימוש DTO שמועבר מ-FreeBSD (זמן ריצה של 124 אלפיות השנייה), זמן הריצה של libufdt DTO הוא 10 אלפיות השנייה.

בדיקות ביצועים עבור מכשירי Pixel השוו בין libufdt ו- libfdt . השפעת מספר צמתי הבסיס דומה, אך כוללת את ההבדלים הבאים:

  • ל-500 פעולות שכבת-על (הוספה או ביטול) יש הפרש זמן של פי 6 עד פי 8
  • ל-1000 פעולות שכבת-על (הוספה או ביטול) יש הפרש זמן של פי 8 עד פי 10

דוגמה עם ספירת הוספות מוגדרת ל-X:

איור 1. הספירה המצורפת היא X

דוגמה עם ספירת עוקפת מוגדרת ל-X:

איור 2. הספירה העוקפת היא X

libufdt פותח עם כמה ממשקי API ומבני נתונים libfdt . בעת שימוש 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 ל-bootloader. ליישומי עזר, עיין ב- sysdeps/libufdt_sysdeps_*.c .

הגבלות על צומת שורש

אינך יכול לכסות צומת או תכונה חדשים לצומת הבסיס של DT הראשי מכיוון שפעולות שכבת-על מסתמכות על תוויות. מכיוון שה-DT הראשי חייב להגדיר תווית וה-Overlay DT מקצה את הצמתים שיוצפו עם תוויות, לא ניתן לתת תווית לצומת השורש (ולכן לא ניתן לכסות את צומת השורש).

ספקי SoC חייבים להגדיר את יכולת שכבת העל של DT ראשי; ODM/OEMs יכולים רק לצרף או לעקוף צמתים עם תוויות שהוגדרו על ידי ספק ה-SoC. כדרך לעקיפת הבעיה, אתה יכול להגדיר צומת odm מתחת לצומת השורש בבסיס DT, מה שמאפשר לכל צמתי ODM ב-Overlay 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 {
        ...
    };
    ...
};

שימוש בשכבות על דחוסות

אנדרואיד 9 מוסיפה תמיכה בשימוש בשכבות-על דחוסות בתמונת DTBO בעת שימוש בגרסה 1 של כותרת טבלת עץ המכשיר. בעת שימוש בכותרת DTBO v1, ארבעת הסיביות הפחות משמעותיות של שדה הדגלים ב- 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' will be 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
};

אנדרואיד 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. שכבה על ה-DTs:
    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);
}