Tối ưu hoá các tổ chức buôn bán ma tuý

Trang này thảo luận về các phương pháp tối ưu hoá mà bạn có thể thực hiện đối với việc triển khai lớp phủ cây thiết bị (DTO), mô tả các quy định hạn chế đối với việc phủ lớp nút gốc và trình bày chi tiết cách định cấu hình các lớp phủ được nén trong hình ảnh DTBO. Trang này cũng cung cấp hướng dẫn và mã triển khai mẫu.

Dòng lệnh kernel

Dòng lệnh kernel ban đầu trong cây thiết bị (DT) nằm trong nút chosen/bootargs. Trình tải khởi động phải nối vị trí này với các nguồn khác của dòng lệnh kernel:

/dts-v1/;

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

DTO không thể nối các giá trị từ DT chính và lớp phủ DT, vì vậy bạn phải đặt dòng lệnh kernel của DT chính trong chosen/bootargs và dòng lệnh kernel của lớp phủ DT trong chosen/bootargs_ext. Sau đó, trình tải khởi động có thể nối các vị trí này và chuyển kết quả đến kernel.

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

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

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

libufdt

Mặc dù libfdt mới nhất hỗ trợ DTO, nhưng bạn nên sử dụng libufdt để triển khai DTO (nguồn AOSP tại platform/system/libufdt). libufdt xây dựng cấu trúc cây thực (cây thiết bị không được làm phẳng, hoặc ufdt) từ cây thiết bị được làm phẳng (FDT), vì vậy, nó có thể cải thiện việc hợp nhất 2 tệp .dtb từ O(N2) thành O(N), trong đó N là số nút trong cây.

Kiểm thử hiệu suất

Trong quá trình kiểm thử nội bộ của Google, việc sử dụng libufdt trên 2405 .dtb và 283 .dtbo nút DT sẽ tạo ra các tệp có kích thước 70.618 và 8.566 byte sau khi biên dịch. So với việc triển khai DTO được chuyển từ FreeBSD (thời gian chạy 124 ms), libufdt thời gian chạy DTO là 10 ms.

Quá trình kiểm thử hiệu suất cho các thiết bị Pixel đã so sánh libufdtlibfdt. Số lượng nút cơ sở có hiệu ứng tương tự, nhưng bao gồm các điểm khác biệt sau:

  • 500 thao tác phủ lớp (nối hoặc ghi đè) có sự khác biệt về thời gian từ 6 đến 8 lần
  • 1000 thao tác phủ lớp (nối hoặc ghi đè) có sự khác biệt về thời gian từ 8 đến 10 lần

Ví dụ về việc nối số lượng được đặt thành X:

Hình 1. Số lượng nối là X.

Ví dụ về việc ghi đè số lượng được đặt thành X:

Hình 2. Số lượng ghi đè là X.

libufdt được phát triển bằng một số API và cấu trúc dữ liệu libfdt. Khi sử dụng libufdt, bạn phải thêm và liên kết libfdt (tuy nhiên, trong mã của mình, bạn có thể sử dụng API libfdt để vận hành DTB hoặc DTBO).

API DTO libufdt

API chính cho DTO trong libufdt như sau:

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

Tham số main_fdt_header là DT chính và overlay_fdt là bộ đệm chứa nội dung của tệp .dtbo Giá trị trả về là một bộ đệm mới chứa DT đã hợp nhất (hoặc null trong trường hợp xảy ra lỗi). DT đã hợp nhất được định dạng trong FDT, bạn có thể chuyển DT này đến kernel khi khởi động kernel.

Bộ đệm mới từ giá trị trả về được tạo bởi dto_malloc(), bạn nên triển khai bộ đệm này khi chuyển libufdt vào trình tải khởi động. Để biết các cách triển khai tham chiếu, hãy tham khảo sysdeps/libufdt_sysdeps_*.c.

Quy định hạn chế đối với nút gốc

Bạn không thể phủ lớp một nút hoặc thuộc tính mới vào nút gốc của DT chính vì các thao tác phủ lớp dựa vào nhãn. Vì DT chính phải xác định một nhãn và lớp phủ DT chỉ định các nút được phủ lớp bằng nhãn, nên bạn không thể đặt nhãn cho nút gốc (và do đó không thể phủ lớp nút gốc ).

Nhà cung cấp SoC phải xác định khả năng phủ lớp của DT chính; ODM/OEM chỉ có thể nối hoặc ghi đè các nút có nhãn do nhà cung cấp SoC xác định. Để khắc phục vấn đề này, bạn có thể xác định nút odm trong nút gốc trong DT cơ sở, cho phép tất cả các nút ODM trong lớp phủ DT thêm các nút mới. Ngoài ra, bạn có thể đặt tất cả các nút liên quan đến SoC trong DT cơ sở vào nút soc trong nút gốc như mô tả bên dưới:

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 {
        ...
    };
    ...
};

Sử dụng các lớp phủ được nén

Android 9 bổ sung hỗ trợ sử dụng các lớp phủ được nén trong hình ảnh DTBO khi sử dụng tiêu đề bảng DT phiên bản 1. Khi sử dụng tiêu đề DTBO phiên bản 1, 4 bit ít quan trọng nhất của trường cờ trong dt_table_entry cho biết định dạng nén của mục 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 */
};

Hiện tại, hệ thống hỗ trợ các phương thức nén zlibgzip.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 bổ sung hỗ trợ kiểm thử các lớp phủ được nén cho bài kiểm thử VtsFirmwareDtboVerification để giúp bạn xác minh tính chính xác của ứng dụng phủ lớp.

Triển khai DTO mẫu

Các hướng dẫn sau đây sẽ hướng dẫn bạn cách triển khai DTO mẫu bằng libufdt (mã mẫu bên dưới).

Hướng dẫn DTO mẫu

  1. Thêm thư viện. Để sử dụng libufdt, hãy thêm libfdt cho các cấu trúc dữ liệu và API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Tải DT chính và lớp phủ DT. Tải .dtb.dtbo từ bộ nhớ vào bộ nhớ (các bước chính xác phụ thuộc vào thiết kế của bạn). Tại thời điểm này, bạn sẽ có bộ đệm và kích thước của .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. Phủ lớp các DT:
    1. Sử dụng ufdt_install_blob() để lấy tiêu đề FDT cho DT chính:
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. Gọi ufdt_apply_overlay() đến DTO để lấy DT đã hợp nhất ở định dạng FDT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. Sử dụng merged_fdt để lấy kích thước của dtc_totalsize():
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Chuyển DT đã hợp nhất để khởi động kernel:
      my_kernel_entry(0, machine_type, merged_fdt);

Mã DTO mẫu

#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);
}