Optymalizacja organizacji zajmujących się handlem narkotykami

Na tej stronie omawiamy optymalizacje, które możesz wprowadzić w implementacji nakładki drzewa urządzeń (DTO), opisujemy ograniczenia dotyczące nakładania węzła głównego i wyjaśniamy, jak skonfigurować skompresowane nakładki w obrazie DTBO. Zawiera też przykładowe instrukcje implementacji i kod.

Wiersz poleceń jądra

Oryginalny wiersz poleceń jądra w drzewie urządzeń (DT) znajduje się w węźle chosen/bootargs. Program rozruchowy musi połączyć tę lokalizację z innymi źródłami wiersza poleceń jądra:

/dts-v1/;

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

DTO nie może łączyć wartości z głównego DT i nakładki DT, więc wiersz poleceń jądra głównego DT musisz umieścić w chosen/bootargs, a wiersz poleceń jądra nakładki DT w chosen/bootargs_ext. Program rozruchowy może następnie połączyć te lokalizacje i przekazać wynik do jądra.

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

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

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

libufdt

Choć najnowsze libfdt obsługuje nakładki drzewa urządzeń, do ich wdrożenia zalecamy użycie zasady libufdt (Źródło AOSP: platform/system/libufdt). libufdt tworzy prawdziwą strukturę drzewa (niespłaszczone drzewo urządzeń, lub ufdt) z płaskiego drzewa urządzeń (FDT), aby poprawić połączenie dwóch plików .dtb z zakresu O(N2) do O(N), gdzie N określa liczbę węzłów w drzewie.

Testy wydajności

W testach wewnętrznych Google użycie libufdt na 2405 .dtb i 283 .dtbo węzłach DT powoduje, że po kompilacji rozmiary plików wynoszą 70 618 i 8566 bajtów. W porównaniu z implementacją nakładek drzewa urządzeń przeniesioną z FreeBSD (czas wykonania 124 ms) czas wykonania nakładek drzewa urządzeń wynosi 10 ms.libufdt

Testy wydajności urządzeń Pixel w porównaniu z libufdtlibfdt. Efekt liczby węzłów bazowych jest podobny, ale ma następujące różnice:

  • 500 operacji nakładania (dołączania lub zastępowania) ma 6–8-krotnie większą różnicę czasu
  • 1000 operacji nakładania (dołączania lub zastępowania) ma 8–10-krotnie większą różnicę czasu

Przykład z ustawionym na X dołączaniem liczby:

Rysunek 1. Liczba dołączeń to X.

Przykład z zastąpieniem liczby ustawionym na X:

Rysunek 2. Liczba zastąpień to X.

libufdt jest opracowywana przy użyciu interfejsów API i struktur danych libfdt. Gdy używasz libufdt, musisz dołączyć i połączyć libfdt (w kodzie możesz jednak używać interfejsu API libfdt do obsługi DTB lub DTBO).

libufdt DTO API

Główny interfejs API do DTO w libufdt wygląda tak:

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

Parametr main_fdt_header to główny DT, a overlay_fdt to bufor zawierający zawartość pliku .dtbo. Wartością zwracaną jest nowy bufor zawierający scalony DT (lub null w przypadku błędu). Połączone drzewo urządzeń jest sformatowane w FDT, które możesz przekazać do jądra podczas jego uruchamiania.

Nowy bufor z wartością zwracaną jest tworzony przez dto_malloc(), co należy zaimplementować podczas przenoszenia libufdt do programu rozruchowego. Przykłady implementacji znajdziesz w sekcji sysdeps/libufdt_sysdeps_*.c.

Ograniczenia dotyczące węzła głównego

Nie możesz nałożyć nowego węzła ani właściwości na węzeł główny głównego drzewa danych, ponieważ operacje nakładania zależą od etykiet. Główny DT musi definiować etykietę, a nakładka DT przypisuje węzły do nakładania etykietami, więc nie możesz podać etykiety dla węzła głównego (a tym samym nie możesz nałożyć węzła głównego).

Dostawcy układów SoC muszą zdefiniować możliwość nakładania głównego DT; ODM/OEM mogą tylko dołączać lub zastępować węzły etykietami zdefiniowanymi przez dostawcę układu SoC. Aby obejść ten problem, możesz zdefiniować węzeł odm w węźle głównym w podstawowym pliku DT, co umożliwi wszystkim węzłom ODM w nakładce DT dodawanie nowych węzłów. Możesz też umieścić wszystkie węzły związane z układem SoC w podstawowym pliku DT w węźle soc pod węzłem głównym, jak opisano poniżej:

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

Używanie skompresowanych nakładek

Android 9 dodaje obsługę skompresowanych nakładek w obrazie DTBO podczas korzystania z wersji 1 nagłówka tabeli DT. W przypadku używania nagłówka DTBO w wersji 1 cztery najmniej znaczące bity pola flagi w strukturze dt_table_entry wskazują format kompresji wpisu 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 */
};

Obecnie obsługiwane są kompresje zlibgzip.

enum dt_compression_info {
    NO_COMPRESSION,
    ZLIB_COMPRESSION,
    GZIP_COMPRESSION
};

Android 9 dodaje obsługę testowania skompresowanych nakładek do testu VtsFirmwareDtboVerification, aby pomóc Ci w weryfikacji poprawności aplikacji nakładki.

Przykładowa implementacja nakładek drzewa urządzeń

Poniżej znajdziesz instrukcje przykładowej implementacji nakładek drzewa urządzeń za pomocą libufdt (przykładowy kod poniżej).

Przykładowe instrukcje dotyczące nakładek drzewa urządzeń

  1. Uwzględnij biblioteki. Aby użyć libufdt, dodaj libfdt w przypadku struktur danych i interfejsów API:
    #include <libfdt.h>
    #include <ufdt_overlay.h>
  2. Wczytaj główny DT i nakładkowy DT. Wczytaj .dtb.dtbo z pamięci do pamięci (dokładne czynności zależą od projektu). W tym momencie bufor i rozmiar powinny mieć wartość .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. Nałóż DT:
    1. Aby uzyskać nagłówek FDT dla głównego drzewa urządzeń, użyj polecenia ufdt_install_blob():
      main_fdt_header = ufdt_install_blob(main_buf, main_size);
      main_fdt_size = main_size;
    2. Wywołaj narzędzie ufdt_apply_overlay(), aby uzyskać scalone drzewo urządzeń w formacie FDT:
      merged_fdt = ufdt_apply_overlay(main_fdt_header, main_fdt_size,
                                      overlay_buf, overlay_size);
    3. Użyj merged_fdt, aby uzyskać rozmiar dtc_totalsize():
      merged_fdt_size = dtc_totalsize(merged_fdt);
    4. Przekaż scalone drzewo urządzeń, aby uruchomić jądro:
      my_kernel_entry(0, machine_type, merged_fdt);

Przykładowy kod nakładek drzewa urządzeń

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