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

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

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

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

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

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

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

  • 500 פעולות שכבת-על (הוספה או שינוי) – הפרש זמן של פי 6 עד פי 8
  • 1,000 פעולות שכבת-על (הוספה או שינוי) נמשכות פי 8 עד פי 10 יותר זמן

דוגמה שבה מספר הוספת התווים מוגדר כ-X:

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

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

איור 2. המספר שמשמש לביטול הוא X.

libufdt פותח באמצעות ממשקי API וחלק ממבני הנתונים של libfdt. כשמשתמשים ב-libufdt, צריך לכלול ולקשר את libfdt (עם זאת, בקוד אפשר להשתמש ב-API של libfdt כדי להפעיל את 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 ראשי. יצרני ציוד מקורי (OEM) או יצרני ציוד מקורי בהתאמה אישית (ODM) יכולים רק להוסיף צמתים או לשנות אותם באמצעות תוויות שהוגדרו על ידי ספק ה-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 כשמשתמשים בגרסה 1 של כותרת טבלת ה-DT. כשמשתמשים בכותרת 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' 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. מניחים שכבת-על על ה-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);
}