Ogólny obraz jądra (GKI) zmniejsza fragmentaryzację jądra przez dokładne dopasowanie do upstreamowego jądra Linuxa. Istnieją jednak ważne powody, dla których niektóre poprawki nie mogą być akceptowane w górę, a także harmonogramy produktów, które muszą być dotrzymywane. Dlatego niektóre poprawki są utrzymywane w źródłach Android Common Kernel (ACK), z których jest tworzony GKI.
Deweloperzy muszą przesyłać zmiany kodu do upstream za pomocą listy mailingowej jądra Linuksa (LKML) jako pierwszego wyboru i przesyłać zmiany kodu do gałęzi ACKandroid-mainline
tylko wtedy, gdy istnieje ważny powód, dla którego upstream nie jest odpowiedni. Poniżej znajdziesz przykłady prawidłowych przyczyn i sposobów ich rozwiązania.
Aktualizacja została przesłana do LKML, ale nie została zaakceptowana na czas, aby można było ją uwzględnić w wersji produktu. Aby zastosować tę poprawkę:
- Podaj dowody, że poprawka została przesłana do LKML i że otrzymano komentarze dotyczące tej poprawki, lub oszacowany czas, w którym poprawka zostanie przesłana do upstream.
- Zdecyduj, jak wdrożyć poprawkę w ACK, uzyskać jej zatwierdzenie w upstreamie, a potem usunąć z ACK, gdy ostateczna wersja upstream zostanie scalona z ACK.
Aktualizacja definiuje
EXPORT_SYMBOLS_GPL()
dla modułu dostawcy, ale nie można jej przesłać do upstream, ponieważ nie ma modułów w drzewie, które używają tego symbolu. Aby obsłużyć tę łatkę, podaj szczegóły dotyczące tego, dlaczego Twój moduł nie może zostać przesłany do upstream, oraz alternatywy, które zostały rozważone przed wysłaniem prośby.Aktualizacja nie jest wystarczająco uniwersalna, aby można było ją wdrożyć w górę łańcucha, a nie ma czasu na jej przekształcenie przed wydaniem produktu. Aby obsłużyć tę łatkę, podaj szacowany czas, w którym zrefaktoryzowana łatka zostanie przesłana do przejęcia (łatka nie zostanie zaakceptowana w ACK bez planu przesłania zrefaktoryzowanej łatki do przejęcia w celu sprawdzenia).
Nie można zaakceptować poprawki przez upstream, ponieważ... <insert reason here>. Aby obsłużyć tę łatkę, skontaktuj się z zespołem odpowiedzialnym za jądro Androida i wspólnie z nami zastanów się nad możliwościami przekształcenia łatki, aby można ją było przesłać do sprawdzenia i zaakceptować w głównym systemie.
Istnieje wiele innych potencjalnych uzasadnienia. Przesyłając błąd lub poprawkę, dołącz wiarygodne uzasadnienie. Pamiętaj, że może być konieczne kilka iteracji i dyskusji. Zdajemy sobie sprawę, że ACK zawiera pewne poprawki, zwłaszcza na wczesnych etapach GKI, gdy wszyscy dopiero uczą się, jak pracować w górę, ale nie mogą zrelaksować harmonogramów swoich usług. Z czasem wymagania dotyczące przesyłania w górę staną się bardziej rygorystyczne.
Wymagania dotyczące poprawek
Aktualizacje muszą być zgodne ze standardami kodowania jądra Linuxa opisanymi w źródle drzewa Linuxa, niezależnie od tego, czy są przesyłane do upstream czy do ACK. Skrypt scripts/checkpatch.pl
jest uruchamiany w ramach testowania przed przesłaniem w Gerricie, więc uruchom go wcześniej, aby sprawdzić, czy przejdzie. Aby uruchomić skrypt checkpatch z tą samą konfiguracją co testowanie przed przesłaniem, użyj opcji //build/kernel/static_analysis:checkpatch_presubmit
.
Szczegółowe informacje znajdziesz w build/kernel/kleaf/docs/checkpatch.md.
Poprawki ACK
Aktualizacje przesyłane do ACK muszą być zgodne ze standardami kodowania jądra Linuksa oraz wytycznymi dotyczącymi publikowania źródeł.
W wiadomości zatwierdzenia musisz użyć tagu Change-Id
. Jeśli przesyłasz poprawkę do wielu gałęzi (np. android-mainline
i android12-5.4
), musisz użyć tego samego tagu Change-Id
dla wszystkich wystąpień poprawki.
Najpierw prześlij poprawki do LKML w celu sprawdzenia w górę łańcucha. Jeśli poprawka:
- Po zaakceptowaniu na upstreamie zostanie ona automatycznie scalona z
android-mainline
. - Nie akceptowane na wyższym poziomie, prześlij je na adres
android-mainline
, podając odniesienie do przesłanych danych lub wyjaśnienie, dlaczego nie zostały one przesłane do LKML.
Po zaakceptowaniu poprawki w upstream lub w android-mainline
można ją przenieść do odpowiedniego ACK na podstawie LTS (np. android12-5.4
i android11-5.4
w przypadku poprawek, które naprawiają kod specyficzny dla Androida). Przesłanie do
android-mainline
umożliwia testowanie z nowymi wersjami kandydatów do publikacji w upstreamie i gwarantuje, że łatka zostanie uwzględniona w następnej wersji ACK opartej na LTS. Wyjątkami są przypadki, w których poprawka z upstreamu jest przenoszona do wersji android12-5.4
(ponieważ prawdopodobnie jest już dostępna w wersji android-mainline
).
Poprawki na wyższym poziomie
Zgodnie z wytycznymi dotyczącymi zgłaszania poprawek poprawki upstream przeznaczone dla jąder ACK należą do tych grup (w kolejności prawdopodobieństwa zaakceptowania):
UPSTREAM:
– poprawki wybrane z gałęzi „android-mainline” prawdopodobnie zostaną zaakceptowane w gałęzi ACK, jeśli istnieje uzasadniony przypadek użycia.BACKPORT:
– poprawki z upstream, które nie są dobierane selektywnie i wymagają modyfikacji, również mogą zostać zaakceptowane, jeśli istnieje uzasadnione zastosowanie.FROMGIT:
– poprawki wybrane z gałęzi konserwacyjnej w ramach przygotowań do przesłania do głównej gałęzi Linuksa mogą zostać zaakceptowane, jeśli zbliża się termin. Muszą być one uzasadnione zarówno pod względem treści, jak i harmonogramu.FROMLIST:
– łaty, które zostały przesłane do LKML, ale nie zostały jeszcze zaakceptowane w gałęzi konserwatora, prawdopodobnie nie zostaną zaakceptowane, chyba że uzasadnienie jest wystarczająco przekonujące, aby łata została zaakceptowana niezależnie od tego, czy trafi do upstream Linuxa (zakładamy, że tak się nie stanie). Aby umożliwić dyskusję z zespołem odpowiedzialnym za jądro Androida, musisz zgłosić problem związany z poprawkamiFROMLIST
.
Poprawki dotyczące Androida
Jeśli nie możesz wprowadzić wymaganych zmian w gałęzi głównej, możesz przesłać poprawki spoza drzewa bezpośrednio do ACK. Przesyłanie poprawek spoza gałęzi wymaga utworzenia problemu w IT, który zawiera poprawkę i uzasadnienie, dlaczego nie można jej przesłać w górę (przykłady znajdziesz na poprzedniej liście).
Są jednak przypadki, w których nie można przesłać kodu do źródła. Te przypadki są obsługiwane w ten sposób: muszą być zgodne z wytycznymi dotyczącymi poprawek dotyczących Androida i muszą być oznaczone w temacie wiadomości prefiksem ANDROID:
.
Zmiany w pliku gki_defconfig
Wszystkie zmiany CONFIG
w gki_defconfig
muszą być stosowane zarówno w wersji arm64, jak i x86, chyba że CONFIG
jest specyficzne dla architektury. Aby poprosić o zmianę ustawienia CONFIG
, utwórz problem w dziale IT, aby omówić tę zmianę. Każda zmiana CONFIG
, która wpływa na interfejs modułu jądra (KMI) po zamrożeniu, jest odrzucana. W przypadku, gdy partnerzy proszą o sprzeczne ustawienia dla jednej konfiguracji, rozwiązujemy konflikty podczas dyskusji na temat powiązanych błędów.
Kod, który nie istnieje w górę łańcucha
Modyfikacji kodu, który jest już dostosowany do Androida, nie można przesłać do zespołu Google. Na przykład mimo że sterownik bindera jest utrzymywany w górę, modyfikacji funkcji dziedziczenia priorytetów sterownika bindera nie można wysłać w górę, ponieważ są one specyficzne dla Androida. W opisie błędu i łatki wyraźnie wyjaśnij, dlaczego kod nie może zostać wysłany do zespołu. Jeśli to możliwe, podziel poprawki na części, które można przesłać w górę łańcucha, oraz części dotyczące Androida, których nie można przesłać w górę łańcucha, 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ą w poprzednich wersjach.
Moduł poza drzewem
Współtwórcy Linuksa aktywnie odradzają obsługę kompilowania modułów spoza drzewa. Jest to rozsądne stanowisko, ponieważ opiekunowie systemu Linux nie dają gwarancji dotyczących zgodności binarnej lub źródłowej w jądrze i nie chcą obsługiwać kodu, który nie znajduje się w drzewie. Jednak GKI zapewnia gwarancje ABI dla modułów dostawców, co gwarantuje stabilność interfejsów KMI przez cały okres obsługiwany przez jądro. Dlatego istnieje klasa zmian, które obsługują moduły dostawców i są akceptowane przez ACK, ale nie przez upstream.
Rozważ na przykład poprawkę, która dodaje makro EXPORT_SYMBOL_GPL()
, gdy moduły korzystające z eksportu nie znajdują się w drzewie źródłowym. Musisz przesłać prośbę do upstream EXPORT_SYMBOL_GPL()
i dostarczyć moduł, który używa nowo wyeksportowanego symbolu. Jeśli jednak istnieje uzasadniona przyczyna, dla której moduł nie jest przesyłany do upstream, możesz zamiast tego przesłać poprawkę do ACK. Musisz uzasadnić, dlaczego moduł nie może być przesyłany w górę. (Nie żądaj wersji bez GPL, EXPORT_SYMBOL()
).
Ukryte konfiguracje
Niektóre moduły w drzewie automatycznie wybierają ukryte konfiguracje, których nie można określić w sekcji gki_defconfig
. Na przykład CONFIG_SND_SOC_TOPOLOGY
jest wybierana automatycznie, gdy CONFIG_SND_SOC_SOF=y
jest skonfigurowana. Aby umożliwić kompilowanie modułów poza drzewem, GKI zawiera mechanizm umożliwiający stosowanie ukrytych konfiguracji.
Aby włączyć ukrytą konfigurację, dodaj instrukcję select
w pliku init/Kconfig.gki
, aby była automatycznie wybierana na podstawie konfiguracji jądra CONFIG_GKI_HACKS_TO_FIX
, która jest włączona w pliku gki_defconfig
. Używaj tego mechanizmu tylko w przypadku ukrytych konfiguracji. Jeśli konfiguracja nie jest ukryta, musi być określona w elementach gki_defconfig
w sposób jawny lub jako zależność.
Ładowane ograniczenia
W przypadku frameworków jądra (takich jak cpufreq
), które obsługują ładowanie regulatorów, można zastąpić domyślny regulator (taki jak regulator schedutil
w cpufreq
). W przypadku frameworków (np. frameworków termicznych), które nie obsługują sterowników ładowanych z pamięci, ale nadal wymagają implementacji specyficznej dla danego producenta, utwórz problem w IT i skonsultuj się z zespołem odpowiedzialnym za jądro Androida.
Wspólnie z Tobą i konserwatorami upstream dodamy niezbędne wsparcie.
Punkty zaczepienia dostawcy
W poprzednich wersjach można było dodawać modyfikacje specyficzne dla dostawcy bezpośrednio do jądra. Nie jest to możliwe w przypadku interfejsu GKI 2.0, ponieważ kod związany z konkretnym produktem musi być implementowany w modułach i nie będzie akceptowany w rdzeniach jądra ani w ACK. Aby umożliwić korzystanie z funkcji o dodanej wartości, na których polegają partnerzy, przy minimalnym wpływie na kod jądra, GKI akceptuje elementy wywoływane przez dostawców, które umożliwiają wywoływanie modułów z kodu jądra. Dodatkowo kluczowe struktury danych mogą być wypełniane polami danych dostawcy, które są dostępne do przechowywania danych dotyczących dostawcy, aby zaimplementować te funkcje.
Elementy wywoływane przez dostawców występują w 2 wariantach (normalnym i ograniczonym) na podstawie punktów śledzenia (a nie zdarzeń śledzenia), do których mogą się one przyłączać. Na przykład zamiast dodawać nową funkcję sched_exit()
, która będzie odpowiedzialna za księgowanie po zakończeniu zadania, dostawcy mogą dodać wątek w funkcji do_exit()
, do którego moduł dostawcy może się przyłączyć w celu przetworzenia. Przykładowa implementacja zawiera te elementy dostawcy.
- Zwykłe uchwyty dostawcy używają funkcji
DECLARE_HOOK()
do tworzenia punktu śledzenia o nazwietrace_name
, gdziename
to unikalny identyfikator ścieżki. Zgodnie z konwencją nazwy zwykłych haka dostawcy zaczynają się odandroid_vh
, więc nazwa hakasched_exit()
toandroid_vh_sched_exit
. - Funkcje dostawcy z ograniczonym dostępem są potrzebne w przypadkach takich jak funkcje harmonogramu, w których dołączona funkcja musi być wywoływana nawet wtedy, gdy procesor jest offline lub wymaga kontekstu nieatomowego. Elementy dostawcy z ograniczonym dostępem nie mogą być odłączane, więc moduły, które się do nich przyłączają, nigdy nie mogą zostać usunięte. Nazwy ograniczonych haków dostawcy zaczynają się od
android_rvh
.
Aby dodać element dostawcy, prześlij problem w IT i prześlij poprawki (jak w przypadku wszystkich poprawek dotyczących Androida, problem musi istnieć i musisz podać uzasadnienie). Obsługa haka dostawcy jest dostępna tylko w ACK, więc nie wysyłaj tych poprawek do upstream Linux.
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()
. Na przykład, aby obsługiwać funkcje o wartości dodanej, dołącz pola do struktury, jak pokazano w tym przykładowym kodzie.
Aby uniknąć potencjalnych konfliktów między polami potrzebnymi dostawcom a polami potrzebnymi OEM-om, OEM-y nie mogą używać pól zadeklarowanych za pomocą makr ANDROID_VENDOR_DATA()
. Zamiast tego producenci OEM muszą używać wartości 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 haka dostawcy
Dodaj do kodu jądra punkty zaczepienia dostawcy jako punkty śledzenia, deklarując je za pomocą funkcji DECLARE_HOOK()
lub DECLARE_RESTRICTED_HOOK()
, a następnie dodając je do kodu jako punkt śledzenia. Jeśli na przykład chcesz 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()
sprawdza najpierw tylko, czy coś jest dołączone. Jeśli jednak moduł dostawcy zarejestruje moduł obsługi za pomocą funkcji register_trace_android_vh_sched_exit()
, zostanie wywołana zarejestrowana funkcja. Menedżer musi być świadomy kontekstu w odniesieniu do zablokowanych blokad, stanu RCS i innych czynników. Hak musi być zdefiniowany w pliku nagłówka w katalogu include/trace/hooks
.
Na przykład poniższy kod przedstawia 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ć instancje interfejsów wymaganych przez element dostawcy, dodaj plik nagłówka z deklaracją elementu do pliku drivers/android/vendor_hooks.c
i wyeksportuj symbole. Na przykład ten kod uzupełnia deklarację elementu 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: aby zapewnić stabilność ABI, należy w pełni zdefiniować struktury danych używane w deklaracji haka. W przeciwnym razie odwoływanie się do nieprzezroczystych wskaźników lub używanie struktury w kontekście rozmiarów jest niebezpieczne. Element include, który zawiera pełną definicję takich struktur danych, powinien znajdować się w sekcji #ifndef __GENKSYMS__
w elemencie drivers/android/vendor_hooks.c
. Pliki nagłówków w folderze include/trace/hooks
nie powinny zawierać pliku nagłówka jądra z definicjami typów, aby uniknąć zmian CRC, które naruszają KMI. Zamiast tego przekaż deklarację typów.
Dołączanie do punktów wywołania dostawcy
Aby używać haka dostawcy, moduł dostawcy musi zarejestrować jego obsługę (zwykle odbywa się to podczas inicjalizacji modułu). Na przykład kod poniżej pokazuje moduł foo.ko
obsługujący 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 elementów dostawcy z plików nagłówków
Aby używać elementów dostawcy z plików nagłówka, konieczne może być zaktualizowanie pliku nagłówka elementu dostawcy w celu odkomentowania TRACE_INCLUDE_PATH
. Pozwoli to uniknąć błędów kompilacji, które wskazują, ż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ę do pliku nagłówka dostawcy, który uwzględniasz. 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
Definiowanie UNDEF_TRACE_INCLUDE_PATH
powoduje, że include/trace/define_trace.h
odwołuje definicję TRACE_INCLUDE_PATH
po utworzeniu punktów śledzenia.
Podstawowe funkcje jądra
Jeśli żadna z poprzednich metod nie umożliwia implementacji funkcji z modułu, musisz dodać tę funkcję jako modyfikację specyficzną dla Androida do jądra. Aby rozpocząć rozmowę, utwórz zgłoszenie w narzędziu do śledzenia zgłoszeń (IT).
Interfejs programowania aplikacji użytkownika (UAPI)
- Pliki nagłówka UAPI Zmiany w plikach nagłówków UAPI muszą być wprowadzane w górę, chyba że dotyczą interfejsów specyficznych dla Androida. Aby zdefiniować interfejsy między modułami dostawcy a jego kodem użytkownika, użyj 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ą ważne tylko w modułach dostawców). Węzły sysfs używane przez biblioteki niezależne od SoC i urządzenia oraz kod Java, które stanowią podstawę platformy Android, można zmieniać tylko w zgodny sposób. Jeśli nie są to węzły sysfs specyficzne dla Androida, muszą zostać zmienione w górę. Możesz utworzyć węzły sysfs dla konkretnego producenta, które będą używane przez jego przestrzeń użytkownika. Domyślnie dostęp do węzłów sysfs przez przestrzeń użytkownika jest zabroniony za pomocą SELinux. Dostawca musi dodać odpowiednie etykiety SELinux, aby umożliwić dostęp autoryzowanemu oprogramowaniu dostawcy.
- Węzły DebugFS. Moduł dostawcy może definiować węzły w
debugfs
tylko do debugowania (ponieważdebugfs
nie jest montowany podczas normalnej pracy urządzenia).