Tworzenie kodu jądra dla GKI

Ogólny obraz jądra (GKI) zmniejsza fragmentację jądra, ponieważ jest ściśle powiązany z jądrem Linux na wyższym poziomie. Istnieją jednak uzasadnione powody, dla których niektórych poprawek nie można zaakceptować na wyższym poziomie, a także harmonogramy produktów, których należy się trzymać. Dlatego niektóre poprawki są utrzymywane w źródłach wspólnego jądra Androida (ACK), z których jest tworzony GKI.

Deweloperzy muszą przesyłać zmiany kodu na wyższy poziom, korzystając w pierwszej kolejności z listy mailingowej jądra Linux (LKML). Zmiany kodu w gałęzi ACK android-mainline należy przesyłać tylko wtedy, gdy istnieje ważny powód, dla którego nie można tego zrobić na wyższym poziomie. Poniżej znajdziesz przykłady uzasadnionych powodów i sposoby ich rozwiązywania.

  • Poprawka została przesłana na listę LKML, ale nie została zaakceptowana na czas publikacji produktu. Aby zastosować tę poprawkę:

    • Prześlij dowód, że poprawka została przesłana na listę LKML, oraz komentarze do niej lub szacowany czas przesłania poprawki na wyższy poziom.
    • Określ sposób postępowania, aby wprowadzić poprawkę w ACK, uzyskać jej akceptację na wyższym poziomie, a następnie usunąć ją z ACK, gdy ostateczna wersja na wyższym poziomie zostanie scalona z ACK.
  • Poprawka definiuje EXPORT_SYMBOLS_GPL() dla modułu dostawcy, ale nie można jej przesłać na wyższy poziom, ponieważ nie ma modułów w drzewie, które korzystają z tego symbolu. Aby zastosować tę poprawkę, podaj szczegółowe informacje o tym, dlaczego nie można przesłać modułu na wyższy poziom, oraz alternatywy, które zostały rozważone przed przesłaniem tej prośby.

  • Poprawka nie jest wystarczająco ogólna, aby można ją było przesłać na wyższy poziom, a nie ma czasu na jej refaktoryzację przed publikacją produktu. Aby zastosować tę poprawkę, podaj szacowany czas przesłania poprawki po refaktoryzacji na wyższy poziom (poprawka nie zostanie zaakceptowana w ACK bez planu przesłania jej na wyższy poziom do sprawdzenia).

  • Poprawka nie może zostać zaakceptowana na wyższym poziomie, ponieważ... <tutaj wpisz powód>. Aby zastosować tę poprawkę, skontaktuj się z zespołem ds. jądra Androida i wspólnie z nim opracuj opcje refaktoryzacji poprawki, aby można ją było przesłać do sprawdzenia i zaakceptować na wyższym poziomie.

Istnieje wiele innych potencjalnych uzasadnień. Gdy przesyłasz błąd lub poprawkę, podaj uzasadnienie i przygotuj się na iteracje i dyskusje. Wiemy, że ACK zawiera niektóre poprawki, zwłaszcza w początkowych fazach GKI, gdy wszyscy uczą się, jak pracować na wyższym poziomie, ale nie mogą z tego powodu opóźniać harmonogramów produktów. Z czasem wymagania dotyczące przesyłania na wyższy poziom będą się stawać coraz bardziej rygorystyczne.

Wymagania dotyczące poprawek

Poprawki muszą być zgodne ze standardami kodowania jądra Linux opisanymi w drzewie źródłowym Linux, niezależnie od tego, czy są przesyłane na wyższy poziom, czy do ACK. Skrypt scripts/checkpatch.pl jest uruchamiany w ramach testów wstępnych Gerrit, więc uruchom go z wyprzedzeniem, aby upewnić się, że przejdzie testy. Aby uruchomić skrypt checkpatch z tą samą konfiguracją co w przypadku testów wstępnych, użyj polecenia //build/kernel/static_analysis:checkpatch_presubmit. Szczegółowe informacje znajdziesz w artykule build/kernel/kleaf/docs/checkpatch.md.

Poprawki ACK

Poprawki przesyłane do ACK muszą być zgodne ze standardami kodowania jądra Linux i wytycznymi dotyczącymi przesyłania. W wiadomości o zatwierdzeniu musisz umieścić Change-Id tag. Jeśli przesyłasz poprawkę do kilku gałęzi (np. android-mainline i android12-5.4), musisz użyć tego samego Change-Id we wszystkich jej instancjach.

Najpierw prześlij poprawki na listę LKML, aby zostały sprawdzone na wyższym poziomie. Jeśli poprawka:

  • zostanie zaakceptowana na wyższym poziomie, zostanie automatycznie scalona z gałęzią android-mainline.
  • nie zostanie zaakceptowana na wyższym poziomie, prześlij ją do gałęzi android-mainline z odniesieniem do przesłania na wyższy poziom lub wyjaśnieniem, dlaczego nie została przesłana na listę LKML.

Gdy poprawka zostanie zaakceptowana na wyższym poziomie lub w gałęzi android-mainline, można ją przenieść do odpowiedniej gałęzi ACK opartej na LTS (np. android12-5.4 i android11-5.4 w przypadku poprawek, które naprawiają kod specyficzny dla Androida). Przesłanie do gałęzi android-mainline umożliwia testowanie z nowymi wersjami kandydującymi na wyższy poziom i gwarantuje, że poprawka znajdzie się w następnej gałęzi ACK opartej na LTS. Wyjątkiem są przypadki, w których poprawka na wyższym poziomie jest przenoszona do gałęzi android12-5.4 (ponieważ prawdopodobnie znajduje się już w gałęzi android-mainline).

Poprawki na wyższym poziomie

Zgodnie z wytycznymi dotyczącymi, przesyłania poprawki na wyższym poziomie przeznaczone dla jąder ACK dzielą się na te grupy (w kolejności prawdopodobieństwa zaakceptowania):

  • UPSTREAM: – poprawki wybrane z gałęzi 'android-mainline` prawdopodobnie zostaną zaakceptowane w ACK, jeśli istnieje uzasadniony przypadek użycia.
  • BACKPORT: – poprawki z wyższego poziomu, które nie są czysto wybrane i wymagają modyfikacji, również prawdopodobnie zostaną zaakceptowane, jeśli istnieje uzasadniony przypadek użycia.
  • FROMGIT: – poprawki wybrane z gałęzi opiekuna w ramach przygotowań do przesłania do głównej gałęzi Linux mogą zostać zaakceptowane, jeśli zbliża się termin. Muszą być uzasadnione zarówno pod względem treści, jak i harmonogramu.
  • FROMLIST: – poprawki, które zostały przesłane na listę LKML, ale nie zostały jeszcze zaakceptowane w gałęzi opiekuna, prawdopodobnie nie zostaną zaakceptowane, chyba że uzasadnienie jest wystarczająco przekonujące, aby poprawka została zaakceptowana niezależnie od tego, czy trafi do Linux na wyższym poziomie (zakładamy, że tak się nie stanie). Aby ułatwić dyskusję z zespołem ds. jądra Androida, z poprawkami FROMLIST musi być powiązany problem.

Poprawki specyficzne dla Androida

Jeśli nie możesz wprowadzić wymaganych zmian na wyższym poziomie, możesz spróbować przesłać poprawki spoza drzewa bezpośrednio do ACK. Przesyłanie poprawek spoza drzewa wymaga utworzenia problemu w systemie IT, w którym należy podać poprawkę i uzasadnienie, dlaczego nie można jej przesłać na wyższy poziom (przykłady znajdziesz na poprzedniej liście). Istnieje jednak kilka przypadków, w których kodu nie można przesłać na wyższy poziom. Te przypadki są opisane poniżej. Muszą być zgodne z wytycznymi dotyczącymi przesyłania poprawek specyficznych dla Androida i mieć w temacie ANDROID: prefiks w temacie.

Zmiany w gki_defconfig

Wszystkie zmiany CONFIG w gki_defconfig muszą być stosowane 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 systemie IT, aby omówić tę 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 proszą o sprzeczne ustawienia dla jednej konfiguracji, rozwiązujemy konflikty, omawiając powiązane błędy.

Kod, który nie istnieje na wyższym poziomie

Modyfikacji kodu, który jest już specyficzny dla Androida, nie można przesłać na wyższy poziom. Na przykład mimo że sterownik binder jest utrzymywany na wyższym poziomie, modyfikacji funkcji dziedziczenia priorytetów sterownika binder nie można przesłać na wyższy poziom, ponieważ są one specyficzne dla Androida. W opisie błędu i poprawce wyraźnie wyjaśnij, dlaczego kodu nie można przesłać na wyższy poziom. Jeśli to możliwe, podziel poprawki na części, które można przesłać na wyższy poziom, i części specyficzne dla Androida, których nie można przesłać na wyższy poziom, aby zminimalizować ilość kodu spoza drzewa utrzymywanego w ACK.

Inne zmiany w tej kategorii to aktualizacje plików reprezentacji KMI, list symboli KMI, gki_defconfig, skryptów kompilacji lub konfiguracji oraz innych skryptów, które nie istnieją na wyższym poziomie.

Moduły spoza drzewa

Linux na wyższym poziomie aktywnie zniechęca do obsługi kompilowania modułów spoza drzewa. Jest to uzasadnione stanowisko, ponieważ opiekunowie Linux nie gwarantują zgodności źródłowej ani binarnej w jądrze i nie chcą obsługiwać kodu, który nie znajduje się w drzewie. Jednak GKI gwarantuje zgodność ABI w przypadku modułów dostawców, zapewniając stabilność interfejsów KMI przez cały okres obsługi jądra. Dlatego istnieje klasa zmian, które obsługują moduły dostawców i są akceptowane w ACK, ale nie na wyższym poziomie.

Na przykład rozważmy poprawkę, która dodaje makra EXPORT_SYMBOL_GPL(), gdy moduły korzystające z eksportu nie znajdują się w drzewie źródłowym. Musisz spróbować poprosić o EXPORT_SYMBOL_GPL() na wyższym poziomie i dostarczyć moduł, który korzysta z nowo wyeksportowanego symbolu. Jeśli jednak istnieje uzasadniony powód, dla którego modułu nie można przesłać na wyższy poziom, możesz przesłać poprawkę do ACK. W opisie problemu musisz podać uzasadnienie, dlaczego modułu nie można przesłać na wyższy poziom. (Nie proś o wariant inny niż GPL, czyli 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 wybierana automatycznie, gdy skonfigurowana jest CONFIG_SND_SOC_SOF=y. Aby umożliwić kompilowanie modułów spoza drzewa, GKI zawiera mechanizm włączania ukrytych konfiguracji.

Aby włączyć ukrytą konfigurację, dodaj instrukcję select w init/Kconfig.gki, aby była ona automatycznie wybierana 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, musi być określona w gki_defconfig bezpośrednio lub jako zależność.

Ładowalne gubernatory

W przypadku struktur jądra (np. cpufreq), które obsługują ładowalne gubernatory, możesz zastąpić domyślny gubernator (np. gubernator schedutil w cpufreq). W przypadku platform (np. platformy termicznej), które nie obsługują ładowalnych gubernatorów ani sterowników, ale nadal wymagają implementacji specyficznej dla dostawcy, utwórz problem w systemie IT i skonsultuj się z zespołem ds. jądra systemu Androida.

Wspólnie z Tobą i opiekunami na wyższym poziomie dodamy niezbędną obsługę.

Haczyki dostawcy

W poprzednich wersjach można było dodawać modyfikacje specyficzne dla dostawcy bezpośrednio do jądra głównego. W GKI 2.0 nie jest to możliwe, ponieważ kod specyficzny dla produktu musi być implementowany w modułach i nie zostanie zaakceptowany w jądrach głównych na wyższym poziomie ani w ACK. Aby umożliwić korzystanie z funkcji o wartości dodanej, na których polegają partnerzy, przy minimalnym wpływie na kod jądra głównego, GKI akceptuje haczyki dostawcy, które umożliwiają wywoływanie modułów z kodu jądra głównego. Dodatkowo kluczowe struktury danych można uzupełnić polami danych dostawcy, które są dostępne do przechowywania danych specyficznych dla dostawcy w celu implementacji tych funkcji.

Haczyki dostawcy występują w 2 wariantach (normalnym i ograniczonym), które są oparte na punktach śledzenia (nie na zdarzeniach śledzenia), do których mogą się dołączać moduły dostawcy. Na przykład zamiast dodawać nową funkcję sched_exit() do rozliczania przy wyjściu zadania, dostawcy mogą dodać haczyk w do_exit(), do którego może się dołączyć moduł dostawcy w celu przetworzenia. Przykładowa implementacja obejmuje te haczyki dostawcy:

  • Normalne haczyki dostawcy używają DECLARE_HOOK() do utworzenia funkcji punktu śledzenia o nazwie trace_name gdzie name jest unikalnym identyfikatorem dla śledzenia. Zgodnie z konwencją nazwy normalnych haczyków dostawcy zaczynają się od android_vh, więc nazwa haczyka sched_exit() to android_vh_sched_exit.
  • Ograniczone haczyki dostawcy są potrzebne w przypadkach takich jak haczyki harmonogramu, w których dołączona funkcja musi być wywoływana nawet wtedy, gdy procesor jest offline lub wymaga kontekstu nieatomowego. Ograniczonych haczyków dostawcy nie można odłączyć, więc moduły, które się do nich dołączają, nie mogą się nigdy odłączyć. Nazwy ograniczonych haczyków dostawcy zaczynają się od android_rvh.

Aby dodać haczyk dostawcy, zgłoś problem w systemie IT i prześlij poprawki (tak jak w przypadku wszystkich poprawek specyficznych dla Androida, problem musi istnieć i musisz podać uzasadnienie). Obsługa haczyków dostawcy jest dostępna tylko w ACK, więc nie przesyłaj tych poprawek do Linux na wyższym poziomie.

Dodawanie pól dostawcy do struktur

Dane dostawcy możesz powiązać z kluczowymi strukturami danych, dodając pola android_vendor_data za pomocą makr ANDROID_VENDOR_DATA(). Aby na przykład obsługiwać funkcje o wartości dodanej, dołącz pola do struktur, jak pokazano w tym przykładowym kodzie.

Aby uniknąć potencjalnych konfliktów między polami potrzebnymi dostawcom a polami potrzebnymi producentom OEM, producenci OEM nie mogą nigdy używać pól zadeklarowanych za pomocą 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 */
}

Definiowanie haczyków dostawcy

Dodaj haczyki 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. Aby na przykład dodać 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 obsługę za pomocą register_trace_android_vh_sched_exit(), zostanie wywołana zarejestrowana funkcja. Obsługa musi znać kontekst dotyczący blokad, stanu RCS i innych czynników. Haczyk musi być zdefiniowany w pliku nagłówka w katalogu include/trace/hooks.

Na przykład ten kod zawiera możliwą deklarację 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 przez haczyk dostawcy, dodaj plik nagłówka z deklaracją haczyka do drivers/android/vendor_hooks.c i wyeksportuj symbole. Na przykład ten kod uzupełnia deklarację haczyka 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 haczyka muszą być w pełni zdefiniowane, aby zagwarantować stabilność ABI. W przeciwnym razie nie można bezpiecznie dereferować nieprzezroczystych wskaźników ani używać struktury w kontekstach o określonym rozmiarze. Plik dołączany, który zawiera pełną definicję takich struktur danych, powinien znajdować się w sekcji #ifndef __GENKSYMS__ w pliku drivers/android/vendor_hooks.c. Pliki nagłówka w include/trace/hooks nie powinny zawierać pliku nagłówka jądra z definicjami typów, aby uniknąć zmian CRC, które powodują uszkodzenie KMI. Zamiast tego zadeklaruj typy z wyprzedzeniem.

Dołączanie do haczyków dostawcy

Aby używać haczyków dostawcy, moduł dostawcy musi zarejestrować obsługę haczyka (zwykle podczas inicjowania modułu). Na przykład ten kod pokazuje obsługę modułu 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);
    ...
}

Używanie haczyków dostawcy z plików nagłówka

Aby używać haczyków dostawcy z plików nagłówka, może być konieczne zaktualizowanie pliku nagłówka haczyka dostawcy, aby cofnąć definicję TRACE_INCLUDE_PATH i uniknąć błędów kompilacji wskazujących, że nie można znaleźć pliku nagłówka punktu śledzenia. Na przykład:

In file included from .../common/init/main.c:111:
In file included from .../common/include/trace/events/initcall.h:74:
.../common/include/trace/define_trace.h:95:10: fatal error: 'trace/hooks/initcall.h' file not found
   95 | #include TRACE_INCLUDE(TRACE_INCLUDE_FILE)
      |          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:90:32: note: expanded from macro 'TRACE_INCLUDE'
   90 | # define TRACE_INCLUDE(system) __TRACE_INCLUDE(system)
      |                                ^~~~~~~~~~~~~~~~~~~~~~~
.../common/include/trace/define_trace.h:87:34: note: expanded from macro '__TRACE_INCLUDE'
   87 | # define __TRACE_INCLUDE(system) __stringify(TRACE_INCLUDE_PATH/system.h)
      |                                  ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:10:27: note: expanded from macro '__stringify'
   10 | #define __stringify(x...)       __stringify_1(x)
      |                                 ^~~~~~~~~~~~~~~~
.../common/include/linux/stringify.h:9:29: note: expanded from macro '__stringify_1'
    9 | #define __stringify_1(x...)     #x
      |                                 ^~
<scratch space>:14:1: note: expanded from here
   14 | "trace/hooks/initcall.h"
      | ^~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.

Aby naprawić ten typ błędu kompilacji, zastosuj odpowiednią poprawkę w pliku nagłówka haczyka dostawcy, który dołączasz. Więcej informacji znajdziesz na stronie https://r.android.com/3066703.

diff --git a/include/trace/hooks/mm.h b/include/trace/hooks/mm.h
index bc6de7e53d66..039926f7701d 100644
--- a/include/trace/hooks/mm.h
+++ b/include/trace/hooks/mm.h
@@ -2,7 +2,10 @@
 #undef TRACE_SYSTEM
 #define TRACE_SYSTEM mm

+#ifdef CREATE_TRACE_POINTS
 #define TRACE_INCLUDE_PATH trace/hooks
+#define UNDEF_TRACE_INCLUDE_PATH
+#endif

Zdefiniowanie UNDEF_TRACE_INCLUDE_PATH powoduje, że include/trace/define_trace.h cofa definicję TRACE_INCLUDE_PATH po utworzeniu punktów śledzenia.

Funkcje jądra głównego

Jeśli żadna z poprzednich metod nie umożliwia implementacji funkcji z modułu, musisz dodać tę funkcję jako modyfikację specyficzną dla Androida w jądrze głównym. Aby rozpocząć rozmowę, utwórz problem w narzędziu do śledzenia problemów (IT).

Interfejs programowania aplikacji użytkownika (UAPI)

  • Pliki nagłówka UAPI. Zmiany w plikach nagłówka UAPI muszą być wprowadzane na wyższym poziomie, chyba że dotyczą interfejsów specyficznych dla Androida. Do definiowania interfejsów między modułami dostawcy a kodem przestrzeni użytkownika dostawcy używaj plików nagłówka specyficznych dla dostawcy.
  • Węzły sysfs. Nie dodawaj nowych węzłów sysfs do jądra GKI (takie dodatki są prawidłowe tylko w modułach dostawcy). Węzły sysfs używane przez biblioteki niezależne od SOC i urządzenia oraz kod Java, który tworzy platformę Androida, można zmieniać tylko w sposób zgodny. Jeśli nie są to węzły sysfs specyficzne dla Androida, należy je zmieniać na wyższym poziomie. Możesz 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 odmawiany za pomocą SELinux. Dostawca musi dodać odpowiednie etykiety SELinux, aby umożliwić dostęp autoryzowanemu oprogramowaniu dostawcy.
  • Węzły DebugFS. Moduły dostawcy mogą definiować węzły w debugfs tylko na potrzeby debugowania (ponieważ debugfs nie jest montowany podczas normalnego działania urządzenia).