Opracuj kod jądra dla GKI

Generic Kernel Image (GKI) zmniejsza fragmentację jądra, dopasowując się ściśle do nadrzędnego jądra Linuksa. Istnieją jednak uzasadnione powody, dla których niektóre poprawki nie mogą zostać zaakceptowane wcześniej, a także istnieją harmonogramy produktów, których należy dotrzymać, dlatego niektóre poprawki są utrzymywane w źródłach wspólnego jądra systemu Android (ACK), z których zbudowano GKI.

Programiści muszą przesyłać zmiany w kodzie w górę, korzystając z listy mailingowej jądra Linux (LKML) jako pierwszego wyboru, i przesyłać zmiany w kodzie do android-mainline tylko wtedy, gdy istnieje poważny powód, dla którego upstream nie jest opłacalny. Przykłady ważnych powodów i sposobów postępowania z nimi są wymienione poniżej.

  • Łatka została przesłana do LKML, ale nie została zaakceptowana na czas przed wydaniem produktu. Aby obsłużyć tę poprawkę:

    • Podaj dowód przesłania łatki do LKML i otrzymane uwagi dotyczące łatki lub szacunkowy czas, w którym łatka zostanie przesłana do dostawcy.
    • Zdecyduj się na sposób działania, aby wylądować łatka w ACK, uzyskać jej zatwierdzenie w górę strumienia, a następnie usunąć ją z ACK, gdy ostateczna wersja nadrzędna zostanie scalona z ACK.
  • Łatka definiuje EXPORT_SYMBOLS_GPL() dla modułu dostawcy, ale nie można jej przesłać wcześniej, ponieważ nie ma modułów w drzewie, które korzystają z tego symbolu. Aby obsłużyć tę poprawkę, podaj szczegółowe informacje o tym, dlaczego moduł nie może zostać przesłany wcześniej, oraz o alternatywach, które rozważałeś przed złożeniem tej prośby.

  • Łatka nie jest wystarczająco ogólna, aby można ją było zastosować wcześniej i nie ma czasu na jej refaktoryzację przed wydaniem produktu. Aby obsłużyć tę łatkę, podaj szacowany czas, w którym zrefaktoryzowana łatka zostanie przesłana do źródła (poprawka nie zostanie zaakceptowana w potwierdzeniu ACK bez planu przesłania zrefaktoryzowanej łatki do sprawdzenia).

  • Łatka nie może zostać zaakceptowana przez firmę upstream, ponieważ... <wstaw tutaj powód> . Aby poradzić sobie z tą łatką, skontaktuj się z zespołem jądra Androida i popracuj z nami nad opcjami refaktoryzacji łatki, aby można było ją przesłać do sprawdzenia i zaakceptować wcześniej.

Potencjalnych uzasadnień jest znacznie więcej. Przesyłając swój błąd lub łatkę, dołącz prawidłowe uzasadnienie i spodziewaj się iteracji i dyskusji. Zdajemy sobie sprawę, że pakiet ACK będzie zawierać pewne poprawki, zwłaszcza we wczesnych fazach GKI, kiedy wszyscy uczą się, jak pracować na wyższym poziomie, ale nie mogą w tym celu rozluźniać harmonogramów produktów. Należy się spodziewać, że wymagania dotyczące upstreamingu z czasem staną się bardziej rygorystyczne.

Wymagania dotyczące poprawek

Poprawki muszą być zgodne ze standardami kodowania jądra Linuksa opisanymi w drzewie źródeł Linuksa , niezależnie od tego, czy są przesyłane wcześniej, czy do potwierdzenia. Skrypt scripts/checkpatch.pl jest uruchamiany w ramach testów przed przesłaniem Gerrita, więc uruchom go wcześniej, aby mieć pewność, że przejdzie pomyślnie. Aby uruchomić skrypt checkpatch z tą samą konfiguracją, co testowanie przed przesłaniem, użyj //build/kernel/static_analysis:checkpatch_presubmit . Aby uzyskać szczegółowe informacje, zobacz build/kernel/kleaf/docs/checkpatch.md .

poprawki ACK

Poprawki przesłane do ACK muszą być zgodne ze standardami kodowania jądra Linuksa i wytycznymi dotyczącymi wkładu . Do wiadomości zatwierdzenia musisz dołączyć znacznik Change-Id ; jeśli przesyłasz łatkę do wielu gałęzi (na przykład android-mainline i android12-5.4 ), musisz użyć tego samego Change-Id dla wszystkich wystąpień łatki.

Najpierw prześlij poprawki do LKML w celu sprawdzenia. Jeśli poprawka to:

  • Zaakceptowany wcześniej, jest automatycznie łączony z android-mainline .
  • Niezaakceptowany w górę łańcucha dostaw, prześlij go do android-mainline z odniesieniem do przesłania w górę strumienia lub wyjaśnieniem, dlaczego nie został przesłany do LKML.

Po zaakceptowaniu łatki albo w wersji pierwotnej, albo w android-mainline , można ją przenieść do odpowiedniego pakietu ACK opartego na LTS (takiego jak android12-5.4 i android11-5.4 w przypadku poprawek naprawiających kod specyficzny dla Androida). Przesłanie do android-mainline umożliwia testowanie nowych kandydatów do wydania i gwarantuje, że łatka znajdzie się w następnym potwierdzeniu ACK opartym na LTS. Wyjątkiem są przypadki, w których łatka nadrzędna jest przeportowana do android12-5.4 (ponieważ łatka prawdopodobnie znajduje się już w android-mainline ).

Upstreamowe poprawki

Jak określono w wytycznych dotyczących wkładu , poprawki nadrzędne przeznaczone dla jąder ACK dzielą się na następujące grupy (wymienione w kolejności prawdopodobieństwa zaakceptowania).

  • UPSTREAM: - Poprawki wybrane z „Android-mainline” prawdopodobnie zostaną zaakceptowane do ACK, jeśli istnieje uzasadniony przypadek użycia.
  • BACKPORT: - Poprawki pochodzące z wcześniejszego źródła, które nie działają prawidłowo i wymagają modyfikacji, również prawdopodobnie zostaną zaakceptowane, jeśli istnieje uzasadniony przypadek ich użycia.
  • FROMGIT: - Poprawki wybrane z oddziału opiekunów w ramach przygotowań do przesłania do głównej linii Linuksa mogą zostać zaakceptowane, jeśli zbliża się ostateczny termin. Należy je uzasadnić zarówno pod względem treści, jak i harmonogramu.
  • FROMLIST: - Poprawki, które zostały przesłane do LKML, ale nie zostały jeszcze przyjęte do gałęzi opiekunów, prawdopodobnie nie zostaną zaakceptowane, chyba że uzasadnienie jest na tyle przekonujące, że łatka zostanie zaakceptowana niezależnie od tego, czy wyląduje w starszym systemie Linux, czy nie (zakładamy, że że tak się nie stanie). Musi występować problem związany z poprawkami FROMLIST , aby ułatwić dyskusję z zespołem jądra Androida.

Poprawki specyficzne dla Androida

Jeśli nie możesz wylądować wymaganych zmian w górę, możesz spróbować przesłać poprawki spoza drzewa bezpośrednio do ACK. Przesyłanie łatek spoza drzewa wymaga utworzenia w dziale IT problemu, który powołuje się na łatkę i podaje uzasadnienie, dlaczego łatki nie można przesłać wcześniej (przykłady można znaleźć na poprzedniej liście). Istnieje jednak kilka przypadków, w których kod nie może zostać przesłany wcześniej. Przypadki te są omówione w następujący sposób i muszą być zgodne z wytycznymi dotyczącymi wkładu poprawek specyficznych dla systemu Android i być oznaczone przedrostkiem ANDROID: w temacie.

Zmiany w gki_defconfig

Wszystkie zmiany w CONFIG w gki_defconfig muszą zostać zastosowane zarówno do wersji arm64, jak i x86, chyba że CONFIG jest specyficzny dla architektury. Aby poprosić o zmianę ustawienia CONFIG , utwórz problem w dziale IT, aby omówić zmianę. Każda zmiana CONFIG , która wpływa na interfejs modułu jądra (KMI) po jego zamrożeniu, jest odrzucana. W przypadkach, gdy partnerzy żądają sprzecznych ustawień dla pojedynczej konfiguracji, rozwiązujemy konflikty poprzez dyskusję na temat powiązanych błędów.

Kod, który nie istnieje wcześniej

Modyfikacje kodu, które są już specyficzne dla Androida, nie mogą być wysyłane w górę. Na przykład, mimo że sterownik spoiwa jest utrzymywany w wersji wcześniejszej, modyfikacje funkcji dziedziczenia priorytetów sterownika spoiwa nie mogą być przesyłane w górę, ponieważ są one specyficzne dla systemu Android. Wyjaśnij swój błąd i poprawkę, dlaczego kod nie może zostać wysłany w górę. Jeśli to możliwe, podziel poprawki na części, które można przesłać wcześniej, i części specyficzne dla Androida, których nie można przesłać wcześniej, aby zminimalizować ilość kodu spoza drzewa utrzymywanego w potwierdzeniu ACK.

Inne zmiany w tej kategorii to aktualizacje plików reprezentacji KMI, list symboli KMI, gki_defconfig , skryptów kompilacji lub konfiguracji lub innych skryptów, które nie istnieją wcześniej.

Moduły spoza drzewa

Upstream Linux aktywnie odradza obsługę budowania modułów spoza drzewa. Jest to rozsądne stanowisko, biorąc pod uwagę, że opiekunowie Linuksa nie dają gwarancji co do zgodności źródeł w jądrze lub plików binarnych i nie chcą wspierać kodu, którego nie ma w drzewie. Jednakże GKI udziela gwarancji ABI dla modułów dostawców, zapewniając, że interfejsy KMI są stabilne przez cały obsługiwany okres życia jądra. Dlatego istnieje klasa zmian obsługujących moduły dostawców, które są dopuszczalne w przypadku protokołu ACK, ale nie są akceptowalne w przypadku wcześniejszych rozwiązań.

Rozważmy na przykład poprawkę, która dodaje makra EXPORT_SYMBOL_GPL() tam, gdzie moduły korzystające z eksportu nie znajdują się w drzewie źródłowym. Chociaż musisz spróbować zażądać EXPORT_SYMBOL_GPL() w górę i dostarczyć moduł, który używa nowo wyeksportowanego symbolu, jeśli istnieje uzasadnione uzasadnienie, dlaczego moduł nie jest przesyłany w górę, możesz zamiast tego przesłać poprawkę do ACK. W zgłoszeniu należy podać uzasadnienie, dlaczego moduł nie może zostać przesłany wcześniej. (Nie żądaj wariantu innego niż GPL, EXPORT_SYMBOL() .)

Ukryte konfiguracje

Niektóre moduły w drzewie automatycznie wybierają ukryte konfiguracje, których nie można określić w gki_defconfig . Na przykład CONFIG_SND_SOC_TOPOLOGY jest wybierane automatycznie, gdy skonfigurowano CONFIG_SND_SOC_SOF=y . Aby umożliwić budowanie modułów poza drzewem, GKI zawiera mechanizm umożliwiający ukryte konfiguracje.

Aby włączyć ukrytą konfigurację, dodaj select w init/Kconfig.gki , aby została automatycznie wybrana na podstawie konfiguracji jądra CONFIG_GKI_HACKS_TO_FIX , która jest włączona w gki_defconfig . Używaj tego mechanizmu tylko w przypadku ukrytych konfiguracji; jeśli konfiguracja nie jest ukryta, należy ją określić w gki_defconfig jawnie lub jako zależność.

Możliwość ładowania gubernatorów

W przypadku struktur jądra (takich jak cpufreq ), które obsługują ładowalne zarządcy, możesz zastąpić domyślny zarządca (taki jak gubernator schedutil cpufreq . W przypadku struktur (takich jak struktura termiczna), które nie obsługują ładowalnych zarządców lub sterowników, ale nadal wymagają implementację specyficzną dla dostawcy, utwórz problem w IT i skonsultuj się z zespołem jądra Androida .

Będziemy współpracować z Tobą i opiekunami wyższego szczebla, aby dodać niezbędne wsparcie.

Haki sprzedawcy

W poprzednich wersjach można było dodawać modyfikacje specyficzne dla dostawcy bezpośrednio do jądra podstawowego. Nie jest to możliwe w przypadku GKI 2.0, ponieważ kod specyficzny dla produktu musi być zaimplementowany w modułach i nie będzie akceptowany w jądrach rdzenia lub w potwierdzeniu ACK. Aby udostępnić funkcje o wartości dodanej, na których polegają partnerzy, przy minimalnym wpływie na rdzeń kodu jądra, GKI akceptuje zaczepy dostawców, które umożliwiają wywoływanie modułów z podstawowego kodu jądra. Ponadto kluczowe struktury danych można uzupełnić polami danych dostawcy, w których można przechowywać dane specyficzne dla dostawcy w celu wdrożenia tych funkcji.

Haki dostawców są dostępne w dwóch wariantach (normalnym i ograniczonym), które opierają się na punktach śledzenia (a nie zdarzeniach śledzenia), do których mogą dołączać moduły dostawców. Na przykład zamiast dodawać nową funkcję sched_exit() do rozliczania przy zakończeniu zadania, dostawcy mogą dodać funkcję do_exit() , do której moduł dostawcy może podłączyć się w celu przetwarzania. Przykładowa implementacja obejmuje następujące haczyki dostawcy.

  • Zwykłe hooki dostawców używają DECLARE_HOOK() do utworzenia funkcji punktu śledzenia o nazwie trace_ name , gdzie name jest unikalnym identyfikatorem śledzenia. Zgodnie z konwencją, normalne nazwy haków dostawców zaczynają się od android_vh , więc nazwa haka sched_exit() będzie brzmieć android_vh_sched_exit .
  • Ograniczone zaczepy dostawców są potrzebne w przypadkach takich jak zaczepy harmonogramu, w których dołączona funkcja musi zostać wywołana, nawet jeśli procesor jest w trybie offline lub wymaga kontekstu nieatomowego. Haczyków dostawców z ograniczeniami nie można odłączyć, zatem moduły przyczepione do haków z ograniczeniami nigdy nie będą mogły zostać wyładowane. Nazwy haków dostawców z ograniczeniami zaczynają się od android_rvh .

Aby dodać hak dostawcy, zgłoś problem w dziale IT i prześlij poprawki (tak jak w przypadku wszystkich poprawek specyficznych dla Androida, problem musi istnieć i musisz podać uzasadnienie). Obsługa haków dostawców jest dostępna tylko w potwierdzeniu ACK, więc nie wysyłaj tych poprawek do wyższego systemu Linux.

Dodaj pola dostawców do struktur

Możesz powiązać dane dostawcy z kluczowymi strukturami danych, dodając pola android_vendor_data za pomocą makr ANDROID_VENDOR_DATA() . Na przykład, aby obsługiwać funkcje o wartości dodanej, dołącz pola do struktur, jak pokazano w poniższym przykładzie kodu.

Aby uniknąć potencjalnych konfliktów między polami wymaganymi przez dostawców i polami wymaganymi przez producentów OEM, producentom OEM nie wolno nigdy używać pól zadeklarowanych przy użyciu makr ANDROID_VENDOR_DATA() . Zamiast tego producenci OEM muszą używać ANDROID_OEM_DATA() do deklarowania pól android_oem_data .

#include <linux/android_vendor.h>
...
struct important_kernel_data {
  [all the standard fields];
  /* Create vendor data for use by hook implementations. The
   * size of vendor data is based on vendor input. Vendor data
   * can be defined as single u64 fields like the following that
   * declares a single u64 field named "android_vendor_data1" :
   */
  ANDROID_VENDOR_DATA(1);

  /*
   * ...or an array can be declared. The following is equivalent to
   * u64 android_vendor_data2[20]:
   */
  ANDROID_VENDOR_DATA_ARRAY(2, 20);

  /*
   * SoC vendors must not use fields declared for OEMs and
   * OEMs must not use fields declared for SoC vendors.
   */
  ANDROID_OEM_DATA(1);

  /* no further fields */
}

Zdefiniuj haki dostawcy

Dodaj zaczepy dostawcy do kodu jądra jako punkty śledzenia, deklarując je za pomocą DECLARE_HOOK() lub DECLARE_RESTRICTED_HOOK() , a następnie dodając je do kodu jako punkt śledzenia. Na przykład, aby dodać funkcję trace_android_vh_sched_exit() do istniejącej funkcji jądra do_exit() :

#include <trace/hooks/exit.h>
void do_exit(long code)
{
    struct task_struct *tsk = current;
    ...
    trace_android_vh_sched_exit(tsk);
    ...
}

Funkcja trace_android_vh_sched_exit() początkowo sprawdza tylko, czy coś jest dołączone. Jeśli jednak moduł dostawcy zarejestruje procedurę obsługi za pomocą register_trace_android_vh_sched_exit() , wywoływana jest zarejestrowana funkcja. Osoba obsługująca musi być świadoma kontekstu w odniesieniu do utrzymywanych blokad, stanu RCS i innych czynników. Hook musi być zdefiniowany w pliku nagłówkowym w katalogu include/trace/hooks .

Na przykład poniższy kod podaje możliwą deklarację funkcji trace_android_vh_sched_exit() w pliku include/trace/hooks/exit.h .

/* SPDX-License-Identifier: GPL-2.0 */
#undef TRACE_SYSTEM
#define TRACE_SYSTEM sched
#define TRACE_INCLUDE_PATH trace/hooks

#if !defined(_TRACE_HOOK_SCHED_H) || defined(TRACE_HEADER_MULTI_READ)
#define _TRACE_HOOK_SCHED_H
#include <trace/hooks/vendor_hooks.h>
/*
 * Following tracepoints are not exported in tracefs and provide a
 * mechanism for vendor modules to hook and extend functionality
 */

struct task_struct;

DECLARE_HOOK(android_vh_sched_exit,
             TP_PROTO(struct task_struct *p),
             TP_ARGS(p));

#endif /* _TRACE_HOOK_SCHED_H */

/* This part must be outside protection */
#include <trace/define_trace.h>

Aby utworzyć instancję interfejsów wymaganych dla haka dostawcy, dodaj plik nagłówkowy z deklaracją haka do drivers/android/vendor_hooks.c i wyeksportuj symbole. Na przykład poniższy kod uzupełnia deklarację haka android_vh_sched_exit() .

#ifndef __GENKSYMS__
/* struct task_struct */
#include <linux/sched.h>
#endif

#define CREATE_TRACE_POINTS
#include <trace/hooks/vendor_hooks.h>
#include <trace/hooks/exit.h>
/*
 * Export tracepoints that act as a bare tracehook (i.e. have no trace
 * event associated with them) to allow external modules to probe
 * them.
 */
EXPORT_TRACEPOINT_SYMBOL_GPL(android_vh_sched_exit);

UWAGA : Struktury danych używane w deklaracji haka muszą być w pełni zdefiniowane, aby zagwarantować stabilność ABI. W przeciwnym razie wyłuskiwanie nieprzezroczystych wskaźników lub używanie struktury w kontekstach o odpowiednim rozmiarze jest niebezpieczne. Dołączenie zapewniające pełną definicję takich struktur danych powinno znaleźć się w sekcji #ifndef __GENKSYMS__ pliku drivers/android/vendor_hooks.c . Pliki nagłówkowe w include/trace/hooks nie powinny zawierać pliku nagłówkowego jądra z definicjami typów, aby uniknąć zmian CRC, które psują KMI. Zamiast tego forward zadeklaruj typy.

Przymocuj do haków dostawcy

Aby użyć haków dostawcy, moduł dostawcy musi zarejestrować procedurę obsługi haka (zazwyczaj robi się to podczas inicjalizacji modułu). Na przykład poniższy kod przedstawia moduł obsługi foo.ko dla trace_android_vh_sched_exit() .

#include <trace/hooks/sched.h>
...
static void foo_sched_exit_handler(void *data, struct task_struct *p)
{
    foo_do_exit_accounting(p);
}
...
static int foo_probe(..)
{
    ...
    rc = register_trace_android_vh_sched_exit(foo_sched_exit_handler, NULL);
    ...
}

Podstawowe funkcje jądra

Jeśli żadna z powyższych technik nie umożliwia zaimplementowania funkcji z modułu, musisz dodać tę funkcję jako modyfikację specyficzną dla Androida do jądra podstawowego. Utwórz problem w narzędziu do śledzenia problemów (IT), aby rozpocząć rozmowę.

Interfejs programowania aplikacji użytkownika (UAPI)

  • Pliki nagłówkowe UAPI. Zmiany w plikach nagłówkowych UAPI muszą nastąpić wcześniej, chyba że zmiany dotyczą interfejsów specyficznych dla Androida. Użyj plików nagłówkowych specyficznych dla dostawcy, aby zdefiniować interfejsy między modułami dostawcy a kodem przestrzeni użytkownika dostawcy.
  • węzły sysfs. Nie dodawaj nowych węzłów sysfs do jądra GKI (takie dodatki obowiązują tylko w modułach dostawców). węzły sysfs używane przez biblioteki niezależne od SoC i urządzenia oraz kod Java składający się z platformy Android można zmieniać tylko w kompatybilny sposób i należy je zmieniać wcześniej, jeśli nie są to węzły sysfs specyficzne dla Androida. Można tworzyć węzły sysfs specyficzne dla dostawcy, które będą używane przez przestrzeń użytkownika dostawcy. Domyślnie dostęp do węzłów sysfs przez przestrzeń użytkownika jest zabroniony przy użyciu SELinux. Do dostawcy należy dodanie odpowiednich etykiet SELinux, aby umożliwić dostęp autoryzowanemu oprogramowaniu dostawcy.
  • Węzły debugowania plików. Moduły dostawcy mogą definiować węzły w debugfs tylko do debugowania (ponieważ debugfs nie jest montowany podczas normalnej pracy urządzenia).